2
2
import regsub
3
3
import string
4
4
import sys
5
+ from types import StringType
5
6
6
7
7
8
AS_IS = None
@@ -12,66 +13,94 @@ class NullFormatter:
12
13
def __init__ (self ): pass
13
14
def end_paragraph (self , blankline ): pass
14
15
def add_line_break (self ): pass
15
- def add_hor_rule (self ): pass
16
+ def add_hor_rule (self , abswidth = None , percentwidth = 1.0 ,
17
+ height = None , align = None ): pass
16
18
def add_label_data (self , format , counter ): pass
17
19
def add_flowing_data (self , data ): pass
18
20
def add_literal_data (self , data ): pass
19
21
def flush_softspace (self ): pass
22
+ def push_alignment (self , align ): pass
23
+ def pop_alignment (self ): pass
20
24
def push_font (self , x ): pass
21
25
def pop_font (self ): pass
22
26
def push_margin (self , margin ): pass
23
27
def pop_margin (self ): pass
24
28
def set_spacing (self , spacing ): pass
25
- def push_style (self , style ): pass
26
- def pop_style (self ): 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
27
32
28
33
29
34
class AbstractFormatter :
30
35
31
36
def __init__ (self , writer ):
32
37
self .writer = writer # Output device
38
+ self .align = None # Current alignment
39
+ self .align_stack = [] # Alignment stack
33
40
self .font_stack = [] # Font state
34
41
self .margin_stack = [] # Margin state
35
42
self .spacing = None # Vertical spacing state
36
43
self .style_stack = [] # Other state, e.g. color
37
44
self .nospace = 1 # Should leading space be suppressed
38
45
self .softspace = 0 # Should a space be inserted
46
+ self .para_end = 1 # Just ended a paragraph
47
+ self .parskip = 0 # Skipped space between paragraphs?
48
+ self .hard_break = 1 # Have a hard break
49
+ self .have_label = 0
39
50
40
51
def end_paragraph (self , blankline ):
41
- if not self .nospace :
42
- self .writer .send_paragraph (blankline )
43
- self .nospace = 1
52
+ if not self .hard_break :
53
+ self .writer .send_line_break ()
54
+ self .have_label = 0
55
+ if self .parskip < blankline and not self .have_label :
56
+ self .writer .send_paragraph (blankline - self .parskip )
57
+ self .parskip = blankline
58
+ self .have_label = 0
59
+ self .hard_break = self .nospace = self .para_end = 1
44
60
self .softspace = 0
45
61
46
62
def add_line_break (self ):
47
- self .writer .send_line_break ()
48
- self .nospace = 1
63
+ if not (self .hard_break or self .para_end ):
64
+ self .writer .send_line_break ()
65
+ self .have_label = self .parskip = 0
66
+ self .hard_break = self .nospace = 1
49
67
self .softspace = 0
50
68
51
- def add_hor_rule (self ):
52
- self .writer .send_hor_rule ()
53
- self .nospace = 1
54
- self .softspace = 0
55
-
56
- def add_label_data (self , format , counter ):
57
- data = self .format_counter (format , counter )
58
- self .writer .send_label_data (data )
69
+ def add_hor_rule (self , * args , ** kw ):
70
+ if not self .hard_break :
71
+ self .writer .send_line_break ()
72
+ apply (self .writer .send_hor_rule , args , kw )
73
+ self .hard_break = self .nospace = 1
74
+ self .have_label = self .para_end = self .softspace = self .parskip = 0
75
+
76
+ def add_label_data (self , format , counter , blankline = None ):
77
+ if self .have_label or not self .hard_break :
78
+ self .writer .send_line_break ()
79
+ if not self .para_end :
80
+ self .writer .send_paragraph ((blankline and 1 ) or 0 )
81
+ if type (format ) is StringType :
82
+ self .writer .send_label_data (self .format_counter (format , counter ))
83
+ else :
84
+ self .writer .send_label_data (format )
85
+ self .nospace = self .have_label = self .hard_break = self .para_end = 1
86
+ self .softspace = self .parskip = 0
59
87
60
88
def format_counter (self , format , counter ):
61
- if counter <= 0 :
62
- return format
63
89
label = ''
64
90
for c in format :
65
91
try :
66
92
if c == '1' :
67
- c = '%d' % counter
93
+ label = label + ( '%d' % counter )
68
94
elif c in 'aA' :
69
- c = self .format_letter (c , counter )
95
+ if counter > 0 :
96
+ label = label + self .format_letter (c , counter )
70
97
elif c in 'iI' :
71
- c = self .format_roman (c , counter )
98
+ if counter > 0 :
99
+ label = label + self .format_roman (c , counter )
100
+ else :
101
+ label = label + c
72
102
except :
73
- pass
74
- label = label + c
103
+ label = label + c
75
104
return label
76
105
77
106
def format_letter (self , case , counter ):
@@ -85,57 +114,88 @@ def format_letter(self, case, counter):
85
114
def format_roman (self , case , counter ):
86
115
ones = ['i' , 'x' , 'c' , 'm' ]
87
116
fives = ['v' , 'l' , 'd' ]
88
- label = ''
89
- index = 0
117
+ label , index = '' , 0
90
118
# This will die of IndexError when counter is too big
91
119
while counter > 0 :
92
120
counter , x = divmod (counter , 10 )
93
121
if x == 9 :
94
- s = ones [index ] + ones [index + 1 ]
122
+ label = ones [index ] + ones [index + 1 ] + label
95
123
elif x == 4 :
96
- s = ones [index ] + fives [index ]
124
+ label = ones [index ] + fives [index ] + label
97
125
else :
98
126
if x >= 5 :
99
127
s = fives [index ]
100
128
x = x - 5
101
129
else :
102
130
s = ''
103
131
s = s + ones [index ]* x
104
- label = s + label
132
+ label = s + label
105
133
index = index + 1
106
- if case == 'I' : label = string .upper (label )
134
+ if case == 'I' :
135
+ return string .upper (label )
107
136
return label
108
137
109
- def add_flowing_data (self , data ):
138
+ def add_flowing_data (self , data ,
139
+ # These are only here to load them into locals:
140
+ whitespace = string .whitespace ,
141
+ join = string .join , split = string .split ):
110
142
if not data : return
111
143
# The following looks a bit convoluted but is a great improvement over
112
144
# data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
113
- prespace = data [0 ] in string .whitespace
114
- postspace = data [- 1 ] in string .whitespace
115
- data = string .join (string .split (data ))
116
- if self .nospace and prespace :
117
- if not data : return
118
- prespace = 0
119
- elif self .softspace :
120
- prespace = 1
121
- self .nospace = self .softspace = 0
122
- if postspace :
123
- self .softspace = 1
124
- if prespace : data = ' ' + data
145
+ prespace = data [:1 ] in whitespace
146
+ postspace = data [- 1 :] in whitespace
147
+ data = join (split (data ))
148
+ if self .nospace and not data :
149
+ return
150
+ elif prespace or self .softspace :
151
+ if not data :
152
+ if not self .nospace :
153
+ self .softspace = 1
154
+ self .parskip = 0
155
+ return
156
+ if not self .nospace :
157
+ data = ' ' + data
158
+ self .hard_break = self .nospace = self .para_end = \
159
+ self .parskip = self .have_label = 0
160
+ self .softspace = postspace
125
161
self .writer .send_flowing_data (data )
126
162
127
163
def add_literal_data (self , data ):
128
- if self .softspace and data [:1 ] != '\n ' :
129
- data = ' ' + data
130
- self .nospace = self .softspace = 0
164
+ if not data : return
165
+ # Caller is expected to cause flush_softspace() if needed.
166
+ self .hard_break = data [- 1 :] == '\n '
167
+ self .nospace = self .para_end = self .softspace = \
168
+ self .parskip = self .have_label = 0
131
169
self .writer .send_literal_data (data )
132
170
133
171
def flush_softspace (self ):
134
172
if self .softspace :
135
- self .nospace = self .softspace = 0
173
+ self .hard_break = self .nospace = self .para_end = self .parskip = \
174
+ self .have_label = self .softspace = 0
136
175
self .writer .send_flowing_data (' ' )
137
176
177
+ def push_alignment (self , align ):
178
+ if align and align != self .align :
179
+ self .writer .new_alignment (align )
180
+ self .align = align
181
+ self .align_stack .append (align )
182
+ else :
183
+ self .align_stack .append (self .align )
184
+
185
+ def pop_alignment (self ):
186
+ if self .align_stack :
187
+ del self .align_stack [- 1 ]
188
+ if self .align_stack :
189
+ self .align = align = self .align_stack [- 1 ]
190
+ self .writer .new_alignment (align )
191
+ else :
192
+ self .align = None
193
+ self .writer .new_alignment (None )
194
+
138
195
def push_font (self , (size , i , b , tt )):
196
+ if self .softspace :
197
+ self .hard_break = self .nospace = self .para_end = self .softspace = 0
198
+ self .writer .send_flowing_data (' ' )
139
199
if self .font_stack :
140
200
csize , ci , cb , ctt = self .font_stack [- 1 ]
141
201
if size is AS_IS : size = csize
@@ -147,6 +207,9 @@ def push_font(self, (size, i, b, tt)):
147
207
self .writer .new_font (font )
148
208
149
209
def pop_font (self ):
210
+ if self .softspace :
211
+ self .hard_break = self .nospace = self .para_end = self .softspace = 0
212
+ self .writer .send_flowing_data (' ' )
150
213
if self .font_stack :
151
214
del self .font_stack [- 1 ]
152
215
if self .font_stack :
@@ -157,36 +220,69 @@ def pop_font(self):
157
220
158
221
def push_margin (self , margin ):
159
222
self .margin_stack .append (margin )
160
- self .writer .new_margin (margin , len (self .margin_stack ))
223
+ fstack = filter (None , self .margin_stack )
224
+ if not margin and fstack :
225
+ margin = fstack [- 1 ]
226
+ self .writer .new_margin (margin , len (fstack ))
161
227
162
228
def pop_margin (self ):
163
229
if self .margin_stack :
164
230
del self .margin_stack [- 1 ]
165
- if self .margin_stack :
166
- margin = self .margin_stack [- 1 ]
231
+ fstack = filter (None , self .margin_stack )
232
+ if fstack :
233
+ margin = fstack [- 1 ]
167
234
else :
168
235
margin = None
169
- self .writer .new_margin (margin , len (self . margin_stack ))
236
+ self .writer .new_margin (margin , len (fstack ))
170
237
171
238
def set_spacing (self , spacing ):
172
239
self .spacing = spacing
173
240
self .writer .new_spacing (spacing )
174
241
175
- def push_style (self , style ):
176
- self .style_stack .append (style )
242
+ def push_style (self , * styles ):
243
+ if self .softspace :
244
+ self .hard_break = self .nospace = self .para_end = self .softspace = 0
245
+ self .writer .send_flowing_data (' ' )
246
+ for style in styles :
247
+ self .style_stack .append (style )
177
248
self .writer .new_styles (tuple (self .style_stack ))
178
249
179
- def pop_style (self ):
180
- if self .style_stack :
181
- del self .style_stack [- 1 ]
250
+ def pop_style (self , n = 1 ):
251
+ if self .softspace :
252
+ self .hard_break = self .nospace = self .para_end = self .softspace = 0
253
+ self .writer .send_flowing_data (' ' )
254
+ del self .style_stack [- n :]
182
255
self .writer .new_styles (tuple (self .style_stack ))
183
256
257
+ def assert_line_data (self , flag = 1 ):
258
+ self .nospace = self .hard_break = not flag
259
+ self .para_end = self .have_label = 0
260
+
261
+
262
+ class NullWriter :
263
+ """Minimal writer interface to use in testing.
264
+ """
265
+ def new_alignment (self , align ): pass
266
+ def new_font (self , font ): pass
267
+ def new_margin (self , margin , level ): pass
268
+ def new_spacing (self , spacing ): pass
269
+ def new_styles (self , styles ): pass
270
+ def send_paragraph (self , blankline ): pass
271
+ def send_line_break (self ): pass
272
+ def send_hor_rule (self , * args , ** kw ): pass
273
+ def send_label_data (self , data ): pass
274
+ def send_flowing_data (self , data ): pass
275
+ def send_literal_data (self , data ): pass
276
+
184
277
185
278
class AbstractWriter :
186
279
187
280
def __init__ (self ):
188
281
pass
189
282
283
+ def new_alignment (self , align ):
284
+ print "new_alignment(%s)" % `align`
285
+
190
286
def new_font (self , font ):
191
287
print "new_font(%s)" % `font`
192
288
@@ -205,7 +301,7 @@ def send_paragraph(self, blankline):
205
301
def send_line_break (self ):
206
302
print "send_line_break()"
207
303
208
- def send_hor_rule (self ):
304
+ def send_hor_rule (self , * args , ** kw ):
209
305
print "send_hor_rule()"
210
306
211
307
def send_label_data (self , data ):
@@ -240,7 +336,7 @@ def send_line_break(self):
240
336
self .col = 0
241
337
self .atbreak = 0
242
338
243
- def send_hor_rule (self ):
339
+ def send_hor_rule (self , * args , ** kw ):
244
340
self .file .write ('\n ' )
245
341
self .file .write ('-' * self .maxcol )
246
342
self .file .write ('\n ' )
0 commit comments