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