-
Notifications
You must be signed in to change notification settings - Fork 0
/
Slide.py
374 lines (341 loc) · 11.6 KB
/
Slide.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
#! /usr/bin/env python
#########################################################################
# #
# This program is free software; you can redistribute it and/or #
# modify it under the terms of the GNU General Public License #
# version 2, as published by the Free Software Foundation. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the Free Software #
# Foundation, Inc., 51 Franklin Street, Fifth Floor, #
# Boston, MA 02110-1301, USA. #
# #
# --- #
# Copyright (C) 2014, Alex Armstrong <AlexRArmstrong@gmail.com> #
# #
#########################################################################
# Naming convention:
# CONSTANTS
# ClassesLikeThis
# functionLikeThis
# variables_like_this
import math
import pygame
from Chart import Chart
# Define global constants.
BLACK = (0, 0, 0)
NOTBLACK = (1, 1, 1)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
MAGENTA = (255, 0, 255)
CYAN = (0, 255, 255)
class Slide(object):
'''
The text of a chart layed out and rendered.
'''
def __init__(self, chart_name = None):
'''
Initilize a Slide Class. Call Slide([chart-name]) to create a Slide
object. Optionally passing a fully pathed chart name.
'''
# These variables are created in Slide.
# The chart object to display.
self._chart = None
self._chart_name = ''
if chart_name:
self.setChartName(chart_name)
# The coordinates of pages breaks.
self._pages = [[0, 0]]
# TODO: def chrs. ???
self._default_characters = []
# The text of the chart layed out and rendered.
self._surface = None
# These variables must be set before calling slide.layout()
# The width and height of the slide display surface.
self._width = 0 # Pixels
self._height = 0 # Pixels
# Fonts.
self._default_font = ''
self._font_dir = ''
# Factors for size calculations.
self._dpi = 72 # dots/inch
self._lane_length = 240 # inches
def chart(self):
'''
Returns a chart object.
'''
return self._chart
def chartName(self):
'''
Return the current chart name.
'''
return self._chart_name
def setChartName(self, chart_name):
'''
Takes the path to a chart file and creates a chart object. Additionally
sets the chart name variable.
'''
try:
self._chart_name = chart_name
self._chart = Chart(chart_name)
return True
except:
return False
def pageCoordinates(self):
'''
Return a list of the (x,y) coordinates of the page breakes.
'''
return self._pages
def defaultCharacters(self):
'''
Return a list of the default characters coordinates and size information.
The line scale factor (ie. 1.25 - the Snellen ratio divided out). Also
included is the width of a character and the space between characters on that line.
Data Structure:
list = [[x, y, line scale factor, character width, space width],
[x, y, scale factor, chr width, space width]]
'''
return self._default_characters
def surface(self):
'''
Returns the rendered surface.
'''
return self._surface
# TODO: Maybe calling layout() should return the rendered surface?
def slideWidth(self):
'''
Returns the slide width in pixels.
'''
return self._width
def setSlideWidth(self, w):
'''
Set the width for the slide to w. This is the
slide's display area in inches. If using a custom dpi,
Must call setDpi first - else will have the default 72 dpi.
Returns True on succes, False otherwise.
'''
try:
w_px = w * self._dpi
self._width = int(w_px)
return True
except:
return False
def slideHeight(self):
'''
Returns the height of the slide in pixels.
'''
return self._height
def setSlideHeight(self, h):
'''
Set the height of the slides display area to h.
Takes the height in inches to set.
Returns True on succes, False otherwise.
'''
try:
h_px = h * self._dpi
self._height = int(h_px)
return True
except:
return False
def dpi(self):
'''
Returns the slide's current dpi.
'''
return self._dpi
def setDpi(self, dpi):
'''
Set the dpi for the slide.
Returns True on succes, False otherwise.
'''
try:
self._dpi = dpi
return True
except:
return False
def laneLength(self):
'''
Return the length of the lane for this slide.
'''
return self._lane_length
def setLaneLength(self, length):
'''
Set the lane length for the slide.
Returns True on succes, False otherwise.
'''
try:
self._lane_length = int(length)
return True
except:
return False
def defaultFont(self):
'''
Return the default font used by this slide.
'''
return self._default_font
def setDefaultFont(self, font_name):
'''
Sets the default font to use for this slide.
Returns True on succes, False otherwise.
'''
try:
self._default_font = font_name
return True
except:
return False
def fontDirectory(self):
'''
Returns the directory searched for fonts.
'''
return self._font_dir
def setFontDirectory(self, font_dir):
'''
Sets the directory containing the fonts to 'font_dir'.
Returns True on succes, False otherwise.
'''
try:
self._font_dir = font_dir
return True
except:
return False
def fixFontName(self, font_name):
'''
Takes a font name and fixes the path so it can be used with pygame.
Returns the full path name of the font.
'''
import os.path
# First check to see if we have a font specified or should use default.
if not font_name:
return os.path.join(self._font_dir, self._default_font)
elif os.path.isabs(font_name):
return font_name
elif os.path.exists(os.path.join(self._font_dir, font_name)):
return os.path.join(self._font_dir, font_name)
else:
return os.path.join(self._font_dir, self._default_font)
def calculateSize(self, lane_length, scale_factor, dpi):
'''
Calculates the vertical size for a letter with a given scale factor at
a distance on a specified resoulation monitor.
'''
# TODO: Question? Should this be here or part of a line?
vertical_inch_size = float(lane_length) * math.tan(math.radians(5.0 / 60.0)) * float(scale_factor)
vertical_dpi_size = vertical_inch_size * dpi
return vertical_dpi_size
def layout(self):
'''
Lays out the currently set chart.
This needs to be called before trying to display the anything.
Does display layout and renders the text onto a big surface. Also
checks for pages and default characters while doing this.
'''
# # Clear per slide/chart variables.
self._pages = [[0, 0]]
self._default_characters = []
self._surface = None
all_rendered_lines = []
all_lines = self._chart.lines()
page_numbers = self._chart.pages()
slide_width = self._width # the width of the slide display area from config file.
lane_length = self._lane_length
dpi = self._dpi
for line_no, current_line in enumerate(all_lines):
line_font = current_line.font()
full_font_name = self.fixFontName(line_font)
num_sections = len(current_line.sections())
all_section_widths = current_line.columnSizes()
sect_x = 0
sect_y = 0
all_rendered_sections = []
line_height = 0
default_chr_position = 0
for i, each_section in enumerate(current_line.sections()):
# Figure the current section width.
section_width = slide_width * all_section_widths[i] / 100
# Get the scaling factor and calculate the size in pixels
scale_factor = each_section.scaleFactor()
line_size = self.calculateSize(lane_length, scale_factor, dpi)
# Create a font.
section_font = pygame.font.Font(full_font_name, int(line_size))
# Get the text for this section.
text = each_section.text()
# Calculate the spacing of the letters for this section.
num_chrs = len(text)
text_width, text_height = section_font.size(text)
chr_width = text_width / num_chrs
space_width = (section_width - text_width) / (num_chrs + 1.0)
# Create a surface for this section.
section_surface = pygame.Surface([section_width, text_height])
section_surface.fill(WHITE)
# Keep track of how big the line is - different sections will change this.
if text_height > line_height:
line_height = text_height
# Render each character onto a surface at the correctly spaced position.
x_pos = 0
y_pos = 0
x_pos += space_width
for each_chr in text:
chr_surface = section_font.render(each_chr, True, BLACK, NOTBLACK)
chr_surface.set_colorkey(NOTBLACK)
chr_position = [x_pos, y_pos]
section_surface.blit(chr_surface, chr_position)
# Check for default character position.
if default_chr_position == current_line.defaultCharacterPosition():
self._default_characters.append([x_pos, y_pos, scale_factor, chr_width, space_width])
x_pos = x_pos + chr_width + space_width
default_chr_position += 1
all_rendered_sections.append(section_surface)
# Now blit all the section surfaces onto a line surface.
line_surface = pygame.Surface([slide_width, line_height])
line_surface.fill(WHITE)
for i, each_sect_surf in enumerate(all_rendered_sections):
# Get the correct width for this section.
section_width = slide_width * all_section_widths[i] / 100
sect_y = (line_surface.get_height() - each_sect_surf.get_height()) / 2.0
section_position = [sect_x, sect_y]
line_surface.blit(each_sect_surf, section_position)
sect_x += section_width
all_rendered_lines.append(line_surface)
# Find the total height of the chart.
total_chart_heigh_px = 0
total_chart_width_px = 0
num_lines = range(len(all_rendered_lines))
for i in num_lines:
cur_ren_line = all_rendered_lines[i]
ln_width, ln_height = cur_ren_line.get_size()
# Calculate the space between each line - can vary per line.
scale_factor = all_lines[i].lineSpaceingScaleFactor()
line_spaceing = self.calculateSize(lane_length, scale_factor, dpi)
total_chart_heigh_px = total_chart_heigh_px + ln_height + line_spaceing
# Make a big surface to hold all the lines.
#total_size = [total_chart_width_px, total_chart_heigh_px]
total_size = [slide_width, total_chart_heigh_px]
self._surface = pygame.Surface(total_size)
self._surface.fill(WHITE)
# Now render all the text to the big surface.
position = [slide_width / 2, 0]
for line_no, each_line in enumerate(all_rendered_lines):
x_r, y_r = each_line.get_size()
position[0] = position[0] - (x_r / 2)
self._surface.blit(each_line, position)
# Y for def chrs.
self._default_characters[line_no][1] = position[1]
# Figure out the coordinates for the page breaks.
if line_no in page_numbers:
x = 0
y = position[1]
pg_coords = [x, y]
self._pages.append(pg_coords)
# Find the line spaceing for this line.
scale_factor = all_lines[line_no].lineSpaceingScaleFactor()
line_spaceing = self.calculateSize(lane_length, scale_factor, dpi)
# Incriment the position for the next line.
position[0] = slide_width / 2
position[1] += y_r + line_spaceing