3 from types
import StringType
11 def __init__(self
, writer
=None):
15 def end_paragraph(self
, blankline
): pass
16 def add_line_break(self
): pass
17 def add_hor_rule(self
, *args
, **kw
): pass
18 def add_label_data(self
, format
, counter
, blankline
=None): pass
19 def add_flowing_data(self
, data
): pass
20 def add_literal_data(self
, data
): pass
21 def flush_softspace(self
): pass
22 def push_alignment(self
, align
): pass
23 def pop_alignment(self
): pass
24 def push_font(self
, x
): pass
25 def pop_font(self
): pass
26 def push_margin(self
, margin
): pass
27 def pop_margin(self
): pass
28 def set_spacing(self
, spacing
): pass
29 def push_style(self
, *styles
): pass
30 def pop_style(self
, n
=1): pass
31 def assert_line_data(self
, flag
=1): pass
34 class AbstractFormatter
:
36 # Space handling policy: blank spaces at the boundary between elements
37 # are handled by the outermost context. "Literal" data is not checked
38 # to determine context, so spaces in literal data are handled directly
39 # in all circumstances.
41 def __init__(self
, writer
):
42 self
.writer
= writer
# Output device
43 self
.align
= None # Current alignment
44 self
.align_stack
= [] # Alignment stack
45 self
.font_stack
= [] # Font state
46 self
.margin_stack
= [] # Margin state
47 self
.spacing
= None # Vertical spacing state
48 self
.style_stack
= [] # Other state, e.g. color
49 self
.nospace
= 1 # Should leading space be suppressed
50 self
.softspace
= 0 # Should a space be inserted
51 self
.para_end
= 1 # Just ended a paragraph
52 self
.parskip
= 0 # Skipped space between paragraphs?
53 self
.hard_break
= 1 # Have a hard break
56 def end_paragraph(self
, blankline
):
57 if not self
.hard_break
:
58 self
.writer
.send_line_break()
60 if self
.parskip
< blankline
and not self
.have_label
:
61 self
.writer
.send_paragraph(blankline
- self
.parskip
)
62 self
.parskip
= blankline
64 self
.hard_break
= self
.nospace
= self
.para_end
= 1
67 def add_line_break(self
):
68 if not (self
.hard_break
or self
.para_end
):
69 self
.writer
.send_line_break()
70 self
.have_label
= self
.parskip
= 0
71 self
.hard_break
= self
.nospace
= 1
74 def add_hor_rule(self
, *args
, **kw
):
75 if not self
.hard_break
:
76 self
.writer
.send_line_break()
77 apply(self
.writer
.send_hor_rule
, args
, kw
)
78 self
.hard_break
= self
.nospace
= 1
79 self
.have_label
= self
.para_end
= self
.softspace
= self
.parskip
= 0
81 def add_label_data(self
, format
, counter
, blankline
= None):
82 if self
.have_label
or not self
.hard_break
:
83 self
.writer
.send_line_break()
85 self
.writer
.send_paragraph((blankline
and 1) or 0)
86 if type(format
) is StringType
:
87 self
.writer
.send_label_data(self
.format_counter(format
, counter
))
89 self
.writer
.send_label_data(format
)
90 self
.nospace
= self
.have_label
= self
.hard_break
= self
.para_end
= 1
91 self
.softspace
= self
.parskip
= 0
93 def format_counter(self
, format
, counter
):
98 label
= label
+ ('%d' % counter
)
101 label
= label
+ self
.format_letter(c
, counter
)
104 label
= label
+ self
.format_roman(c
, counter
)
111 def format_letter(self
, case
, counter
):
114 counter
, x
= divmod(counter
-1, 26)
115 s
= chr(ord(case
) + x
)
119 def format_roman(self
, case
, counter
):
120 ones
= ['i', 'x', 'c', 'm']
121 fives
= ['v', 'l', 'd']
123 # This will die of IndexError when counter is too big
125 counter
, x
= divmod(counter
, 10)
127 label
= ones
[index
] + ones
[index
+1] + label
129 label
= ones
[index
] + fives
[index
] + label
136 s
= s
+ ones
[index
]*x
140 return string
.upper(label
)
143 def add_flowing_data(self
, data
,
144 # These are only here to load them into locals:
145 whitespace
= string
.whitespace
,
146 join
= string
.join
, split
= string
.split
):
148 # The following looks a bit convoluted but is a great improvement over
149 # data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
150 prespace
= data
[:1] in whitespace
151 postspace
= data
[-1:] in whitespace
152 data
= join(split(data
))
153 if self
.nospace
and not data
:
155 elif prespace
or self
.softspace
:
163 self
.hard_break
= self
.nospace
= self
.para_end
= \
164 self
.parskip
= self
.have_label
= 0
165 self
.softspace
= postspace
166 self
.writer
.send_flowing_data(data
)
168 def add_literal_data(self
, data
):
171 self
.writer
.send_flowing_data(" ")
172 self
.hard_break
= data
[-1:] == '\n'
173 self
.nospace
= self
.para_end
= self
.softspace
= \
174 self
.parskip
= self
.have_label
= 0
175 self
.writer
.send_literal_data(data
)
177 def flush_softspace(self
):
179 self
.hard_break
= self
.para_end
= self
.parskip
= \
180 self
.have_label
= self
.softspace
= 0
182 self
.writer
.send_flowing_data(' ')
184 def push_alignment(self
, align
):
185 if align
and align
!= self
.align
:
186 self
.writer
.new_alignment(align
)
188 self
.align_stack
.append(align
)
190 self
.align_stack
.append(self
.align
)
192 def pop_alignment(self
):
194 del self
.align_stack
[-1]
196 self
.align
= align
= self
.align_stack
[-1]
197 self
.writer
.new_alignment(align
)
200 self
.writer
.new_alignment(None)
202 def push_font(self
, (size
, i
, b
, tt
)):
204 self
.hard_break
= self
.para_end
= self
.softspace
= 0
206 self
.writer
.send_flowing_data(' ')
208 csize
, ci
, cb
, ctt
= self
.font_stack
[-1]
209 if size
is AS_IS
: size
= csize
210 if i
is AS_IS
: i
= ci
211 if b
is AS_IS
: b
= cb
212 if tt
is AS_IS
: tt
= ctt
213 font
= (size
, i
, b
, tt
)
214 self
.font_stack
.append(font
)
215 self
.writer
.new_font(font
)
219 del self
.font_stack
[-1]
221 font
= self
.font_stack
[-1]
224 self
.writer
.new_font(font
)
226 def push_margin(self
, margin
):
227 self
.margin_stack
.append(margin
)
228 fstack
= filter(None, self
.margin_stack
)
229 if not margin
and fstack
:
231 self
.writer
.new_margin(margin
, len(fstack
))
233 def pop_margin(self
):
234 if self
.margin_stack
:
235 del self
.margin_stack
[-1]
236 fstack
= filter(None, self
.margin_stack
)
241 self
.writer
.new_margin(margin
, len(fstack
))
243 def set_spacing(self
, spacing
):
244 self
.spacing
= spacing
245 self
.writer
.new_spacing(spacing
)
247 def push_style(self
, *styles
):
249 self
.hard_break
= self
.para_end
= self
.softspace
= 0
251 self
.writer
.send_flowing_data(' ')
253 self
.style_stack
.append(style
)
254 self
.writer
.new_styles(tuple(self
.style_stack
))
256 def pop_style(self
, n
=1):
257 del self
.style_stack
[-n
:]
258 self
.writer
.new_styles(tuple(self
.style_stack
))
260 def assert_line_data(self
, flag
=1):
261 self
.nospace
= self
.hard_break
= not flag
262 self
.para_end
= self
.parskip
= self
.have_label
= 0
266 """Minimal writer interface to use in testing & inheritance."""
267 def __init__(self
): pass
268 def flush(self
): pass
269 def new_alignment(self
, align
): pass
270 def new_font(self
, font
): pass
271 def new_margin(self
, margin
, level
): pass
272 def new_spacing(self
, spacing
): pass
273 def new_styles(self
, styles
): pass
274 def send_paragraph(self
, blankline
): pass
275 def send_line_break(self
): pass
276 def send_hor_rule(self
, *args
, **kw
): pass
277 def send_label_data(self
, data
): pass
278 def send_flowing_data(self
, data
): pass
279 def send_literal_data(self
, data
): pass
282 class AbstractWriter(NullWriter
):
287 def new_alignment(self
, align
):
288 print "new_alignment(%s)" % `align`
290 def new_font(self
, font
):
291 print "new_font(%s)" % `font`
293 def new_margin(self
, margin
, level
):
294 print "new_margin(%s, %d)" % (`margin`
, level
)
296 def new_spacing(self
, spacing
):
297 print "new_spacing(%s)" % `spacing`
299 def new_styles(self
, styles
):
300 print "new_styles(%s)" % `styles`
302 def send_paragraph(self
, blankline
):
303 print "send_paragraph(%s)" % `blankline`
305 def send_line_break(self
):
306 print "send_line_break()"
308 def send_hor_rule(self
, *args
, **kw
):
309 print "send_hor_rule()"
311 def send_label_data(self
, data
):
312 print "send_label_data(%s)" % `data`
314 def send_flowing_data(self
, data
):
315 print "send_flowing_data(%s)" % `data`
317 def send_literal_data(self
, data
):
318 print "send_literal_data(%s)" % `data`
321 class DumbWriter(NullWriter
):
323 def __init__(self
, file=None, maxcol
=72):
324 self
.file = file or sys
.stdout
326 NullWriter
.__init
__(self
)
333 def send_paragraph(self
, blankline
):
334 self
.file.write('\n'*blankline
)
338 def send_line_break(self
):
339 self
.file.write('\n')
343 def send_hor_rule(self
, *args
, **kw
):
344 self
.file.write('\n')
345 self
.file.write('-'*self
.maxcol
)
346 self
.file.write('\n')
350 def send_literal_data(self
, data
):
351 self
.file.write(data
)
352 i
= string
.rfind(data
, '\n')
356 data
= string
.expandtabs(data
)
357 self
.col
= self
.col
+ len(data
)
360 def send_flowing_data(self
, data
):
362 atbreak
= self
.atbreak
or data
[0] in string
.whitespace
365 write
= self
.file.write
366 for word
in string
.split(data
):
368 if col
+ len(word
) >= maxcol
:
375 col
= col
+ len(word
)
378 self
.atbreak
= data
[-1] in string
.whitespace
381 def test(file = None):
383 f
= AbstractFormatter(w
)
387 fp
= open(sys
.argv
[1])
397 f
.add_flowing_data(line
)
401 if __name__
== '__main__':