blob: 50615f4176b23b9e052c81075e2c381dda26939b [file] [log] [blame]
Guido van Rossuma0eab1d1995-08-07 20:07:36 +00001import regex
2import regsub
3import string
4import sys
5
6
7AS_IS = None
8
9whitespace = '[' + string.whitespace + ']+'
10
11
Guido van Rossum909507d1995-10-06 15:31:30 +000012class NullFormatter:
13
14 def __init__(self): pass
15 def end_paragraph(self, blankline): pass
16 def add_line_break(self): pass
17 def add_hor_rule(self): pass
18 def add_label_data(self, format, counter): 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_font(self, x): pass
23 def pop_font(self): pass
24 def push_margin(self, margin): pass
25 def pop_margin(self): pass
26 def set_spacing(self, spacing): pass
27 def push_style(self, style): pass
28 def pop_style(self): pass
29
30
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000031class AbstractFormatter:
32
33 def __init__(self, writer):
34 self.writer = writer # Output device
35 self.font_stack = [] # Font state
36 self.margin_stack = [] # Margin state
37 self.spacing = None # Vertical spacing state
38 self.style_stack = [] # Other state, e.g. color
Guido van Rossum909507d1995-10-06 15:31:30 +000039 self.nospace = 1 # Should leading space be suppressed
40 self.softspace = 0 # Should a space be inserted
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000041
42 def end_paragraph(self, blankline):
43 if not self.nospace:
44 self.writer.send_paragraph(blankline)
45 self.nospace = 1
46 self.softspace = 0
47
48 def add_line_break(self):
49 self.writer.send_line_break()
50 self.nospace = 1
51 self.softspace = 0
52
53 def add_hor_rule(self):
54 self.writer.send_hor_rule()
55 self.nospace = 1
56 self.softspace = 0
57
58 def add_label_data(self, format, counter):
59 data = self.format_counter(format, counter)
60 self.writer.send_label_data(data)
61
62 def format_counter(self, format, counter):
63 if counter <= 0:
64 return format
65 label = ''
66 for c in format:
67 try:
68 if c == '1':
69 c = '%d' % counter
70 elif c in 'aA':
71 c = self.format_letter(c, counter)
72 elif c in 'iI':
73 c = self.format_roman(c, counter)
74 except:
75 pass
76 label = label + c
77 return label
78
79 def format_letter(self, case, counter):
80 label = ''
81 while counter > 0:
82 counter, x = divmod(counter-1, 26)
83 s = chr(ord(case) + x)
84 label = s + label
85 return label
86
87 def format_roman(self, case, counter):
88 ones = ['i', 'x', 'c', 'm']
89 fives = ['v', 'l', 'd']
90 label = ''
91 index = 0
92 # This will die of IndexError when counter is too big
93 while counter > 0:
94 counter, x = divmod(counter, 10)
95 if x == 9:
96 s = ones[index] + ones[index+1]
97 elif x == 4:
98 s = ones[index] + fives[index]
99 else:
100 if x >= 5:
101 s = fives[index]
102 x = x-5
103 else:
104 s = ''
105 s = s + ones[index]*x
106 label = s + label
107 index = index + 1
108 if case == 'I': label = string.upper(label)
109 return label
110
111 def add_flowing_data(self, data):
112 if not data: return
113 data = regsub.gsub(whitespace, ' ', data)
114 if self.nospace and data[0] == ' ':
115 data = data[1:]
116 if not data: return
117 elif self.softspace and data[0] != ' ':
118 data = ' ' + data
119 self.nospace = self.softspace = 0
120 if data[-1] == ' ':
121 data = data[:-1]
122 self.softspace = 1
123 self.writer.send_flowing_data(data)
124
125 def add_literal_data(self, data):
126 if self.softspace and data[:1] != '\n':
127 data = ' ' + data
128 self.nospace = self.softspace = 0
129 self.writer.send_literal_data(data)
130
Guido van Rossumc7ae9201995-09-30 16:49:58 +0000131 def flush_softspace(self):
132 if self.softspace:
133 self.nospace = self.softspace = 0
134 self.writer.send_flowing_data(' ')
135
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000136 def push_font(self, (size, i, b, tt)):
137 if self.font_stack:
138 csize, ci, cb, ctt = self.font_stack[-1]
139 if size is AS_IS: size = csize
140 if i is AS_IS: i = ci
141 if b is AS_IS: b = cb
142 if tt is AS_IS: tt = ctt
143 font = (size, i, b, tt)
144 self.font_stack.append(font)
145 self.writer.new_font(font)
146
147 def pop_font(self):
148 if self.font_stack:
149 del self.font_stack[-1]
150 if self.font_stack:
151 font = self.font_stack[-1]
152 else:
153 font = None
154 self.writer.new_font(font)
155
156 def push_margin(self, margin):
157 self.margin_stack.append(margin)
158 self.writer.new_margin(margin, len(self.margin_stack))
159
160 def pop_margin(self):
161 if self.margin_stack:
162 del self.margin_stack[-1]
163 if self.margin_stack:
164 margin = self.margin_stack[-1]
165 else:
166 margin = None
167 self.writer.new_margin(margin, len(self.margin_stack))
168
169 def set_spacing(self, spacing):
170 self.spacing = spacing
171 self.writer.new_spacing(spacing)
172
173 def push_style(self, style):
174 self.style_stack.append(style)
175 self.writer.new_styles(tuple(self.style_stack))
176
177 def pop_style(self):
178 if self.style_stack:
179 del self.style_stack[-1]
180 self.writer.new_styles(tuple(self.style_stack))
181
182
183class AbstractWriter:
184
185 def __init__(self):
186 pass
187
188 def new_font(self, font):
189 print "new_font(%s)" % `font`
190
191 def new_margin(self, margin, level):
192 print "new_margin(%s, %d)" % (`margin`, level)
193
194 def new_spacing(self, spacing):
195 print "new_spacing(%s)" % `spacing`
196
197 def new_styles(self, styles):
198 print "new_styles(%s)" % `styles`
199
200 def send_paragraph(self, blankline):
201 print "send_paragraph(%s)" % `blankline`
202
203 def send_line_break(self):
204 print "send_line_break()"
205
206 def send_hor_rule(self):
207 print "send_hor_rule()"
208
209 def send_label_data(self, data):
210 print "send_label_data(%s)" % `data`
211
212 def send_flowing_data(self, data):
213 print "send_flowing_data(%s)" % `data`
214
215 def send_literal_data(self, data):
216 print "send_literal_data(%s)" % `data`
217
218
219class DumbWriter(AbstractWriter):
220
221 def __init__(self, file=None, maxcol=72):
222 self.file = file or sys.stdout
223 self.maxcol = maxcol
224 AbstractWriter.__init__(self)
225 self.reset()
226
227 def reset(self):
228 self.col = 0
229 self.atbreak = 0
230
231 def send_paragraph(self, blankline):
232 self.file.write('\n' + '\n'*blankline)
233 self.col = 0
234 self.atbreak = 0
235
236 def send_line_break(self):
237 self.file.write('\n')
238 self.col = 0
239 self.atbreak = 0
240
241 def send_hor_rule(self):
242 self.file.write('\n')
243 self.file.write('-'*self.maxcol)
244 self.file.write('\n')
245 self.col = 0
246 self.atbreak = 0
247
248 def send_literal_data(self, data):
249 self.file.write(data)
250 i = string.rfind(data, '\n')
251 if i >= 0:
252 self.col = 0
253 data = data[i+1:]
254 data = string.expandtabs(data)
255 self.col = self.col + len(data)
256 self.atbreak = 0
257
258 def send_flowing_data(self, data):
259 if not data: return
260 atbreak = self.atbreak or data[0] in string.whitespace
261 col = self.col
262 maxcol = self.maxcol
263 write = self.file.write
264 for word in string.split(data):
265 if atbreak:
266 if col + len(word) >= maxcol:
267 write('\n')
268 col = 0
269 else:
270 write(' ')
271 col = col + 1
272 write(word)
273 col = col + len(word)
274 atbreak = 1
275 self.col = col
276 self.atbreak = data[-1] in string.whitespace
277
278
279def test(file = None):
280 w = DumbWriter()
281 f = AbstractFormatter(w)
282 if file:
283 fp = open(file)
284 elif sys.argv[1:]:
285 fp = open(sys.argv[1])
286 else:
287 fp = sys.stdin
288 while 1:
289 line = fp.readline()
290 if not line:
291 break
292 if line == '\n':
293 f.end_paragraph(1)
294 else:
295 f.add_flowing_data(line)
296 f.end_paragraph(0)
297
298
299if __name__ == '__main__':
300 test()