1 # A class to help applications that do fancy text formatting.
2 # You create an instance each time you must redraw the window.
3 # Set the initial left, top and right coordinates;
4 # then feed it words, font changes and vertical movements.
6 # This class should eventually be extended to support much fancier
7 # formatting, along the lines of TeX; for now, a very simple model
12 # Initialize a formatter instance.
13 # Pass the window's drawing object, and left, top, right
14 # coordinates of the drawing space as arguments.
16 def __init__(self
, d
, left
, top
, right
):
17 self
.d
= d
# Drawing object
18 self
.left
= left
# Left margin
19 self
.right
= right
# Right margin
20 self
.v
= top
# Top of current line
23 self
.setfont('') # Default font
24 self
._reset
() # Prepare for new line
26 # Reset for start of fresh line.
29 self
.boxes
= [] # Boxes and glue still to be output
30 self
.sum_width
= 0 # Total width of boxes
31 self
.sum_space
= 0 # Total space between boxes
32 self
.sum_stretch
= 0 # Total stretch for space between boxes
33 self
.max_ascent
= 0 # Max ascent of current line
34 self
.max_descent
= 0 # Max descent of current line
35 self
.avail_width
= self
.right
- self
.left
38 # Set the current font, and compute some values from it.
40 def setfont(self
, font
):
43 self
.font_space
= self
.d
.textwidth(' ')
44 self
.font_ascent
= self
.d
.baseline()
45 self
.font_descent
= self
.d
.lineheight() - self
.font_ascent
47 # Add a word to the list of boxes; first flush if line is full.
48 # Space and stretch factors are expressed in fractions
49 # of the current font's space width.
50 # (Two variations: one without, one with explicit stretch factor.)
52 def addword(self
, word
, spacefactor
):
53 self
.addwordstretch(word
, spacefactor
, spacefactor
)
55 def addwordstretch(self
, word
, spacefactor
, stretchfactor
):
56 width
= self
.d
.textwidth(word
)
57 if width
> self
.avail_width
:
59 space
= int(float(self
.font_space
) * float(spacefactor
))
60 stretch
= int(float(self
.font_space
) * float(stretchfactor
))
61 box
= (self
.font
, word
, width
, space
, stretch
)
62 self
.boxes
.append(box
)
63 self
.sum_width
= self
.sum_width
+ width
64 self
.sum_space
= self
.sum_space
+ space
65 self
.sum_stretch
= self
.sum_stretch
+ stretch
66 self
.max_ascent
= max(self
.font_ascent
, self
.max_ascent
)
67 self
.max_descent
= max(self
.font_descent
, self
.max_descent
)
68 self
.avail_width
= self
.avail_width
- width
- space
70 # Flush current line and start a new one.
71 # Flushing twice is harmless (i.e. does not introduce a blank line).
72 # (Two versions: the internal one has a parameter for justification.)
77 def _flush(self
, justify
):
81 # Compute amount of stretch needed.
83 if justify
and self
.justify
or self
.center
:
85 # Compute extra space to fill;
86 # this is avail_width plus glue from last box.
87 # Also compute available stretch.
89 last_box
= self
.boxes
[len(self
.boxes
)-1]
90 font
, word
, width
, space
, stretch
= last_box
91 tot_extra
= self
.avail_width
+ space
92 tot_stretch
= self
.sum_stretch
- stretch
94 tot_extra
= tot_stretch
= 0
98 baseline
= self
.v
+ self
.max_ascent
99 h
= self
.left
+ self
.hang_indent
101 h
= h
+ tot_extra
/ 2
102 tot_extra
= tot_stretch
= 0
103 for font
, word
, width
, space
, stretch
in self
.boxes
:
105 v
= baseline
- self
.d
.baseline()
106 self
.d
.text((h
, v
), word
)
107 h
= h
+ width
+ space
108 if tot_extra
> 0 and tot_stretch
> 0:
109 extra
= stretch
* tot_extra
/ tot_stretch
111 tot_extra
= tot_extra
- extra
112 tot_stretch
= tot_stretch
- stretch
114 # Prepare for next line.
116 self
.v
= baseline
+ self
.max_descent
117 self
.d
.setfont(self
.font
)
120 # Add vertical space; first flush.
121 # Vertical space is expressed in fractions of the current
122 # font's line height.
124 def vspace(self
, lines
):
125 self
.vspacepixels(int(lines
* self
.d
.lineheight()))
127 # Add vertical space given in pixels.
129 def vspacepixels(self
, dv
):
133 # Set temporary (hanging) indent, for paragraph start.
136 def tempindent(self
, space
):
138 hang
= int(float(self
.font_space
) * float(space
))
139 self
.hang_indent
= hang
140 self
.avail_width
= self
.avail_width
- hang
142 # Add (permanent) left indentation. First flush.
144 def addleftindent(self
, space
):
146 self
.left
= self
.left \
147 + int(float(self
.font_space
) * float(space
))
155 import stdwin
, stdwinq
156 from stdwinevents
import *
159 # Mac font assignments:
160 font1
= 'times', '', 12
161 font2
= 'times', 'b', 14
163 # X11R4 font assignments
164 font1
= '*times-medium-r-*-120-*'
165 font2
= '*times-bold-r-*-140-*'
167 ['The','quick','brown','fox','jumps','over','the','lazy','dog.']
170 stages
= [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
171 justify
, center
, title
= stages
[stage
]
172 stdwin
.setdefwinsize(300,200)
173 w
= stdwin
.open(title
)
174 winsize
= w
.getwinsize()
176 type, window
, detail
= stdwinq
.getevent()
179 elif type == WE_SIZE
:
180 newsize
= w
.getwinsize()
181 if newsize
<> winsize
:
182 w
.change((0,0), winsize
)
184 w
.change((0,0), winsize
)
185 elif type == WE_MOUSE_DOWN
:
186 stage
= (stage
+ 1) % len(stages
)
187 justify
, center
, title
= stages
[stage
]
189 w
.change((0, 0), (1000, 1000))
190 elif type == WE_DRAW
:
191 width
, height
= winsize
192 f
= formatter(w
.begindrawing(), 0, 0, width
)
197 for font
in font1
, font2
, font1
:
200 space
= 1 + (word
[-1:] == '.')
201 f
.addword(word
, space
)
202 if center
and space
> 1:
207 w
.setdocsize(0, height
)