blob: 67d2b16e9a76be585cbb8e08da05ce26c531c2c6 [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
112 def push_font(self, (size, i, b, tt)):
113 if self.font_stack:
114 csize, ci, cb, ctt = self.font_stack[-1]
115 if size is AS_IS: size = csize
116 if i is AS_IS: i = ci
117 if b is AS_IS: b = cb
118 if tt is AS_IS: tt = ctt
119 font = (size, i, b, tt)
120 self.font_stack.append(font)
121 self.writer.new_font(font)
122
123 def pop_font(self):
124 if self.font_stack:
125 del self.font_stack[-1]
126 if self.font_stack:
127 font = self.font_stack[-1]
128 else:
129 font = None
130 self.writer.new_font(font)
131
132 def push_margin(self, margin):
133 self.margin_stack.append(margin)
134 self.writer.new_margin(margin, len(self.margin_stack))
135
136 def pop_margin(self):
137 if self.margin_stack:
138 del self.margin_stack[-1]
139 if self.margin_stack:
140 margin = self.margin_stack[-1]
141 else:
142 margin = None
143 self.writer.new_margin(margin, len(self.margin_stack))
144
145 def set_spacing(self, spacing):
146 self.spacing = spacing
147 self.writer.new_spacing(spacing)
148
149 def push_style(self, style):
150 self.style_stack.append(style)
151 self.writer.new_styles(tuple(self.style_stack))
152
153 def pop_style(self):
154 if self.style_stack:
155 del self.style_stack[-1]
156 self.writer.new_styles(tuple(self.style_stack))
157
158
159class AbstractWriter:
160
161 def __init__(self):
162 pass
163
164 def new_font(self, font):
165 print "new_font(%s)" % `font`
166
167 def new_margin(self, margin, level):
168 print "new_margin(%s, %d)" % (`margin`, level)
169
170 def new_spacing(self, spacing):
171 print "new_spacing(%s)" % `spacing`
172
173 def new_styles(self, styles):
174 print "new_styles(%s)" % `styles`
175
176 def send_paragraph(self, blankline):
177 print "send_paragraph(%s)" % `blankline`
178
179 def send_line_break(self):
180 print "send_line_break()"
181
182 def send_hor_rule(self):
183 print "send_hor_rule()"
184
185 def send_label_data(self, data):
186 print "send_label_data(%s)" % `data`
187
188 def send_flowing_data(self, data):
189 print "send_flowing_data(%s)" % `data`
190
191 def send_literal_data(self, data):
192 print "send_literal_data(%s)" % `data`
193
194
195class DumbWriter(AbstractWriter):
196
197 def __init__(self, file=None, maxcol=72):
198 self.file = file or sys.stdout
199 self.maxcol = maxcol
200 AbstractWriter.__init__(self)
201 self.reset()
202
203 def reset(self):
204 self.col = 0
205 self.atbreak = 0
206
207 def send_paragraph(self, blankline):
208 self.file.write('\n' + '\n'*blankline)
209 self.col = 0
210 self.atbreak = 0
211
212 def send_line_break(self):
213 self.file.write('\n')
214 self.col = 0
215 self.atbreak = 0
216
217 def send_hor_rule(self):
218 self.file.write('\n')
219 self.file.write('-'*self.maxcol)
220 self.file.write('\n')
221 self.col = 0
222 self.atbreak = 0
223
224 def send_literal_data(self, data):
225 self.file.write(data)
226 i = string.rfind(data, '\n')
227 if i >= 0:
228 self.col = 0
229 data = data[i+1:]
230 data = string.expandtabs(data)
231 self.col = self.col + len(data)
232 self.atbreak = 0
233
234 def send_flowing_data(self, data):
235 if not data: return
236 atbreak = self.atbreak or data[0] in string.whitespace
237 col = self.col
238 maxcol = self.maxcol
239 write = self.file.write
240 for word in string.split(data):
241 if atbreak:
242 if col + len(word) >= maxcol:
243 write('\n')
244 col = 0
245 else:
246 write(' ')
247 col = col + 1
248 write(word)
249 col = col + len(word)
250 atbreak = 1
251 self.col = col
252 self.atbreak = data[-1] in string.whitespace
253
254
255def test(file = None):
256 w = DumbWriter()
257 f = AbstractFormatter(w)
258 if file:
259 fp = open(file)
260 elif sys.argv[1:]:
261 fp = open(sys.argv[1])
262 else:
263 fp = sys.stdin
264 while 1:
265 line = fp.readline()
266 if not line:
267 break
268 if line == '\n':
269 f.end_paragraph(1)
270 else:
271 f.add_flowing_data(line)
272 f.end_paragraph(0)
273
274
275if __name__ == '__main__':
276 test()