author | rgbecker |
Mon, 01 Oct 2001 09:58:42 +0000 | |
changeset 1268 | 158ffc339d38 |
parent 1138 | 9dd6ee76c791 |
child 1677 | 1450177dd19e |
permissions | -rw-r--r-- |
494 | 1 |
#copyright ReportLab Inc. 2000 |
2 |
#see license.txt for license details |
|
3 |
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/xpreformatted.py?cvsroot=reportlab |
|
1138 | 4 |
#$Header: /tmp/reportlab/reportlab/platypus/xpreformatted.py,v 1.14 2001/08/17 10:35:30 dinu_gherman Exp $ |
5 |
__version__=''' $Id: xpreformatted.py,v 1.14 2001/08/17 10:35:30 dinu_gherman Exp $ ''' |
|
6 |
||
439 | 7 |
import string |
8 |
from types import StringType, ListType |
|
1138 | 9 |
|
10 |
from reportlab.lib import PyFontify |
|
11 |
from paragraph import Paragraph, cleanBlockQuotedText, _handleBulletWidth, \ |
|
12 |
ParaLines, _getFragWords, stringWidth, _sameFrag |
|
442 | 13 |
from flowables import _dedenter |
439 | 14 |
|
1138 | 15 |
|
439 | 16 |
def _getFragLines(frags): |
17 |
lines = [] |
|
18 |
cline = [] |
|
19 |
W = frags[:] |
|
20 |
while W != []: |
|
21 |
w = W[0] |
|
22 |
t = w.text |
|
23 |
del W[0] |
|
24 |
i = string.find(t,'\n') |
|
25 |
if i>=0: |
|
26 |
tleft = t[i+1:] |
|
473 | 27 |
cline.append(w.clone(text=t[:i])) |
439 | 28 |
lines.append(cline) |
29 |
cline = [] |
|
30 |
if tleft!='': |
|
31 |
W.insert(0,w.clone(text=tleft)) |
|
32 |
else: |
|
33 |
cline.append(w) |
|
449 | 34 |
if cline!=[]: |
35 |
lines.append(cline) |
|
439 | 36 |
return lines |
37 |
||
518 | 38 |
def _split_blPara(blPara,start,stop): |
39 |
f = blPara.clone() |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
40 |
for a in ('lines', 'text'): |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
41 |
if hasattr(f,a): delattr(f,a) |
518 | 42 |
f.lines = blPara.lines[start:stop] |
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
43 |
return [f] |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
44 |
|
629
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
45 |
# Will be removed shortly. |
465 | 46 |
def _countSpaces(text): |
629
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
47 |
return string.count(text, ' ') |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
48 |
## i = 0 |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
49 |
## s = 0 |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
50 |
## while 1: |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
51 |
## j = string.find(text,' ',i) |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
52 |
## if j<0: return s |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
53 |
## s = s + 1 |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
54 |
## i = j + 1 |
465 | 55 |
|
56 |
def _getFragWord(frags): |
|
464 | 57 |
''' given a fragment list return a list of lists |
465 | 58 |
[size, spaces, (f00,w00), ..., (f0n,w0n)] |
464 | 59 |
each pair f,w represents a style and some string |
60 |
''' |
|
61 |
W = [] |
|
62 |
n = 0 |
|
465 | 63 |
s = 0 |
464 | 64 |
for f in frags: |
65 |
text = f.text[:] |
|
66 |
W.append((f,text)) |
|
67 |
n = n + stringWidth(text, f.fontName, f.fontSize) |
|
629
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
68 |
|
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
69 |
#s = s + _countSpaces(text) |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
70 |
s = s + string.count(text, ' ') # much faster for many blanks |
0252c3741f7c
Commented _countSpaces() and replaced with string.count().
dinu_gherman
parents:
518
diff
changeset
|
71 |
|
464 | 72 |
#del f.text # we can't do this until we sort out splitting |
73 |
# of paragraphs |
|
465 | 74 |
return n, s, W |
464 | 75 |
|
1138 | 76 |
|
439 | 77 |
class XPreformatted(Paragraph): |
442 | 78 |
def __init__(self, text, style, bulletText = None, dedent=0, frags=None): |
79 |
cleaner = lambda text, dedent=dedent: string.join(_dedenter(text,dedent),'\n') |
|
80 |
self._setup(text, style, bulletText, frags, cleaner) |
|
439 | 81 |
|
82 |
def breakLines(self, width): |
|
83 |
""" |
|
84 |
Returns a broken line structure. There are two cases |
|
85 |
||
86 |
A) For the simple case of a single formatting input fragment the output is |
|
87 |
A fragment specifier with |
|
88 |
kind = 0 |
|
89 |
fontName, fontSize, leading, textColor |
|
90 |
lines= A list of lines |
|
91 |
Each line has two items. |
|
92 |
1) unused width in points |
|
93 |
2) a list of words |
|
94 |
||
95 |
B) When there is more than one input formatting fragment the out put is |
|
96 |
A fragment specifier with |
|
97 |
kind = 1 |
|
98 |
lines= A list of fragments each having fields |
|
99 |
extraspace (needed for justified) |
|
100 |
fontSize |
|
101 |
words=word list |
|
102 |
each word is itself a fragment with |
|
103 |
various settings |
|
104 |
||
105 |
This structure can be used to easily draw paragraphs with the various alignments. |
|
106 |
You can supply either a single width or a list of widths; the latter will have its |
|
107 |
last item repeated until necessary. A 2-element list is useful when there is a |
|
108 |
different first line indent; a longer list could be created to facilitate custom wraps |
|
109 |
around irregular objects.""" |
|
110 |
||
111 |
if type(width) <> ListType: maxWidths = [width] |
|
112 |
else: maxWidths = width |
|
113 |
lines = [] |
|
114 |
lineno = 0 |
|
115 |
maxWidth = maxWidths[lineno] |
|
116 |
style = self.style |
|
117 |
fFontSize = float(style.fontSize) |
|
118 |
requiredWidth = 0 |
|
119 |
||
120 |
#for bullets, work out width and ensure we wrap the right amount onto line one |
|
121 |
_handleBulletWidth(self.bulletText,style,maxWidths) |
|
122 |
||
123 |
self.height = 0 |
|
124 |
frags = self.frags |
|
125 |
nFrags= len(frags) |
|
126 |
if nFrags==1: |
|
127 |
f = frags[0] |
|
128 |
if hasattr(f,'text'): |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
129 |
fontSize = f.fontSize |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
130 |
fontName = f.fontName |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
131 |
kind = 0 |
439 | 132 |
L=string.split(f.text, '\n') |
133 |
for l in L: |
|
134 |
currentWidth = stringWidth(l,fontName,fontSize) |
|
135 |
requiredWidth = max(currentWidth,requiredWidth) |
|
136 |
extraSpace = maxWidth-currentWidth |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
137 |
lines.append((extraSpace,string.split(l,' '),currentWidth)) |
439 | 138 |
lineno = lineno+1 |
442 | 139 |
maxWidth = lineno<len(maxWidths) and maxWidths[lineno] or maxWidths[-1] |
439 | 140 |
else: |
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
141 |
kind = f.kind |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
142 |
lines = f.lines |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
143 |
for L in lines: |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
144 |
if kind==0: |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
145 |
currentWidth = L[2] |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
146 |
else: |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
147 |
currentWidth = L.currentWidth |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
148 |
requiredWidth = max(currentWidth,requiredWidth) |
439 | 149 |
|
150 |
self.width = max(self.width,requiredWidth) |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
151 |
return f.clone(kind=kind, lines=lines) |
439 | 152 |
elif nFrags<=0: |
518 | 153 |
return ParaLines(kind=0, fontSize=style.fontSize, fontName=style.fontName, |
439 | 154 |
textColor=style.textColor, lines=[]) |
155 |
else: |
|
156 |
for L in _getFragLines(frags): |
|
157 |
maxSize = 0 |
|
465 | 158 |
currentWidth, n, w = _getFragWord(L) |
159 |
f = w[0][0] |
|
160 |
maxSize = max(maxSize,f.fontSize) |
|
161 |
words = [f.clone()] |
|
162 |
words[-1].text = w[0][1] |
|
163 |
for i in w[1:]: |
|
164 |
f = i[0].clone() |
|
165 |
f.text=i[1] |
|
166 |
words.append(f) |
|
439 | 167 |
maxSize = max(maxSize,f.fontSize) |
168 |
||
442 | 169 |
lineno = lineno+1 |
170 |
maxWidth = lineno<len(maxWidths) and maxWidths[lineno] or maxWidths[-1] |
|
439 | 171 |
requiredWidth = max(currentWidth,requiredWidth) |
172 |
extraSpace = maxWidth - currentWidth |
|
518 | 173 |
lines.append(ParaLines(extraSpace=extraSpace,wordCount=n, words=words, fontSize=maxSize, currentWidth=currentWidth)) |
439 | 174 |
|
175 |
self.width = max(self.width,requiredWidth) |
|
518 | 176 |
return ParaLines(kind=1, lines=lines) |
439 | 177 |
|
178 |
return lines |
|
179 |
||
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
180 |
# we need this her to get the right splitter |
518 | 181 |
def _get_split_blParaFunc(self): |
182 |
return _split_blPara |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
183 |
|
1138 | 184 |
|
185 |
class PythonPreformatted(XPreformatted): |
|
186 |
"""Used for syntax-colored Python code, otherwise like XPreformatted. |
|
187 |
""" |
|
188 |
||
189 |
formats = { |
|
190 |
'rest' : ('', ''), |
|
191 |
'comment' : ('<font color="green">', '</font>'), |
|
192 |
'keyword' : ('<font color="blue"><b>', '</b></font>'), |
|
193 |
'parameter' : ('<font color="black">', '</font>'), |
|
194 |
'identifier' : ('<font color="red">', '</font>'), |
|
195 |
'string' : ('<font color="gray">', '</font>') } |
|
196 |
||
197 |
def __init__(self, text, style, bulletText = None, dedent=0, frags=None): |
|
198 |
if text: |
|
199 |
text = self.fontify(self.escapeHtml(text)) |
|
200 |
apply(XPreformatted.__init__, |
|
201 |
(self, text, style), |
|
202 |
{'bulletText':bulletText, 'dedent':dedent, 'frags':frags}) |
|
203 |
||
204 |
def escapeHtml(self, text): |
|
205 |
s = string.replace(text, '&', '&') |
|
206 |
s = string.replace(s, '<', '<') |
|
207 |
s = string.replace(s, '>', '>') |
|
208 |
return s |
|
209 |
||
210 |
def fontify(self, code): |
|
211 |
"Return a fontified version of some Python code." |
|
212 |
||
213 |
if code[0] == '\n': |
|
214 |
code = code[1:] |
|
215 |
||
216 |
tags = PyFontify.fontify(code) |
|
217 |
fontifiedCode = '' |
|
218 |
pos = 0 |
|
219 |
for k, i, j, dummy in tags: |
|
220 |
fontifiedCode = fontifiedCode + code[pos:i] |
|
221 |
s, e = self.formats[k] |
|
222 |
fontifiedCode = fontifiedCode + s + code[i:j] + e |
|
223 |
pos = j |
|
224 |
||
225 |
fontifiedCode = fontifiedCode + code[pos:] |
|
226 |
||
227 |
return fontifiedCode |
|
228 |
||
229 |
||
439 | 230 |
if __name__=='__main__': #NORUNTESTS |
231 |
def dumpXPreformattedLines(P): |
|
473 | 232 |
print '\n############dumpXPreforemattedLines(%s)' % str(P) |
518 | 233 |
lines = P.blPara.lines |
439 | 234 |
n =len(lines) |
235 |
for l in range(n): |
|
236 |
line = lines[l] |
|
237 |
words = line.words |
|
238 |
nwords = len(words) |
|
239 |
print 'line%d: %d(%d)\n ' % (l,nwords,line.wordCount), |
|
240 |
for w in range(nwords): |
|
241 |
print "%d:'%s'"%(w,words[w].text), |
|
242 |
||
243 |
||
244 |
def dumpXPreformattedFrags(P): |
|
473 | 245 |
print '\n############dumpXPreforemattedFrags(%s)' % str(P) |
439 | 246 |
frags = P.frags |
247 |
n =len(frags) |
|
248 |
for l in range(n): |
|
249 |
print "frag%d: '%s'" % (l, frags[l].text) |
|
250 |
||
251 |
l = 0 |
|
449 | 252 |
for L in _getFragLines(frags): |
253 |
n=0 |
|
254 |
for W in _getFragWords(L): |
|
255 |
print "frag%d.%d: size=%d" % (l, n, W[0]), |
|
256 |
n = n + 1 |
|
257 |
for w in W[1:]: |
|
258 |
print "'%s'" % w[1], |
|
259 |
||
439 | 260 |
l = l + 1 |
261 |
||
473 | 262 |
def try_it(text,style,dedent,aW,aH): |
449 | 263 |
P=XPreformatted(text,style,dedent=dedent) |
264 |
dumpXPreformattedFrags(P) |
|
265 |
w,h = P.wrap(aW, aH) |
|
266 |
dumpXPreformattedLines(P) |
|
267 |
S = P.split(aW,aH) |
|
473 | 268 |
dumpXPreformattedLines(P) |
449 | 269 |
for s in S: |
270 |
s.wrap(aW,aH) |
|
271 |
dumpXPreformattedLines(s) |
|
272 |
aH = 500 |
|
273 |
||
439 | 274 |
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle |
275 |
styleSheet = getSampleStyleSheet() |
|
276 |
B = styleSheet['BodyText'] |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
277 |
DTstyle = ParagraphStyle("discussiontext", parent=B) |
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
278 |
DTstyle.fontName= 'Helvetica' |
473 | 279 |
for (text,dedent,style, aW, aH, active) in [(''' |
445 | 280 |
|
281 |
||
282 |
The <font name=courier color=green>CMYK</font> or subtractive |
|
283 |
||
284 |
method follows the way a printer |
|
439 | 285 |
mixes three pigments (cyan, magenta, and yellow) to form colors. |
286 |
Because mixing chemicals is more difficult than combining light there |
|
287 |
is a fourth parameter for darkness. For example a chemical |
|
288 |
combination of the <font name=courier color=green>CMY</font> pigments generally never makes a perfect |
|
445 | 289 |
|
439 | 290 |
black -- instead producing a muddy color -- so, to get black printers |
291 |
don't use the <font name=courier color=green>CMY</font> pigments but use a direct black ink. Because |
|
292 |
<font name=courier color=green>CMYK</font> maps more directly to the way printer hardware works it may |
|
293 |
be the case that &| & | colors specified in <font name=courier color=green>CMYK</font> will provide better fidelity |
|
294 |
and better control when printed. |
|
445 | 295 |
|
296 |
||
473 | 297 |
''',0,DTstyle, 456.0, 42.8, 0), |
449 | 298 |
(''' |
299 |
||
300 |
This is a non rearranging form of the <b>Paragraph</b> class; |
|
301 |
<b><font color=red>XML</font></b> tags are allowed in <i>text</i> and have the same |
|
450 | 302 |
|
449 | 303 |
meanings as for the <b>Paragraph</b> class. |
304 |
As for <b>Preformatted</b>, if dedent is non zero <font color=red size=+1>dedent</font> |
|
305 |
common leading spaces will be removed from the |
|
306 |
front of each line. |
|
307 |
||
473 | 308 |
''',3, DTstyle, 456.0, 42.8, 0), |
309 |
("""\ |
|
310 |
<font color=blue>class </font><font color=red>FastXMLParser</font>: |
|
311 |
# Nonsense method |
|
312 |
def nonsense(self): |
|
313 |
self.foo = 'bar' |
|
314 |
""",0, styleSheet['Code'], 456.0, 4.8, 1), |
|
467
34593cc102de
Splitting fixes. Mostly caused by XPreformatted not doing it right.
rgbecker
parents:
465
diff
changeset
|
315 |
]: |
473 | 316 |
if active: try_it(text,style,dedent,aW,aH) |