author | rgbecker |
Wed, 05 Apr 2006 15:18:32 +0000 | |
changeset 2575 | 0cba68b93555 |
parent 2569 | f7ac857ad20d |
child 2660 | c147aff8edae |
permissions | -rw-r--r-- |
2332 | 1 |
#Copyright ReportLab Europe Ltd. 2000-2004 |
494 | 2 |
#see license.txt for license details |
2332 | 3 |
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/flowables.py |
2321 | 4 |
__version__=''' $Id$ ''' |
253 | 5 |
__doc__=""" |
268 | 6 |
A flowable is a "floating element" in a document whose exact position is determined by the |
7 |
other elements that precede it, such as a paragraph, a diagram interspersed between paragraphs, |
|
8 |
a section header, etcetera. Examples of non-flowables include page numbering annotations, |
|
9 |
headers, footers, fixed diagrams or logos, among others. |
|
10 |
||
11 |
Flowables are defined here as objects which know how to determine their size and which |
|
12 |
can draw themselves onto a page with respect to a relative "origin" position determined |
|
426 | 13 |
at a higher level. The object's draw() method should assume that (0,0) corresponds to the |
14 |
bottom left corner of the enclosing rectangle that will contain the object. The attributes |
|
15 |
vAlign and hAlign may be used by 'packers' as hints as to how the object should be placed. |
|
16 |
||
17 |
Some Flowables also know how to "split themselves". For example a |
|
268 | 18 |
long paragraph might split itself between one page and the next. |
19 |
||
1490 | 20 |
Packers should set the canv attribute during wrap, split & draw operations to allow |
21 |
the flowable to work out sizes etc in the proper context. |
|
22 |
||
268 | 23 |
The "text" of a document usually consists mainly of a sequence of flowables which |
24 |
flow into a document from top to bottom (with column and page breaks controlled by |
|
25 |
higher level components). |
|
253 | 26 |
""" |
27 |
import os |
|
28 |
import string |
|
29 |
from copy import deepcopy |
|
557 | 30 |
from types import ListType, TupleType, StringType |
253 | 31 |
|
2276 | 32 |
from reportlab.lib.colors import red, gray, lightgrey |
2525 | 33 |
from reportlab.lib.utils import fp_str |
253 | 34 |
from reportlab.pdfbase import pdfutils |
35 |
||
2525 | 36 |
from reportlab.rl_config import _FUZZ, overlapAttachedSpace |
37 |
__all__=('TraceInfo','Flowable','XBox','Preformatted','Image','Spacer','PageBreak','SlowPageBreak', |
|
38 |
'CondPageBreak','KeepTogether','Macro','CallerMacro','ParagraphAndImage', |
|
2535 | 39 |
'FailOnWrap','HRFlowable','PTOContainer','KeepInFrame','UseUpSpace') |
253 | 40 |
|
2113
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
41 |
|
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
42 |
class TraceInfo: |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
43 |
"Holder for info about where an object originated" |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
44 |
def __init__(self): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
45 |
self.srcFile = '(unknown)' |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
46 |
self.startLineNo = -1 |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
47 |
self.startLinePos = -1 |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
48 |
self.endLineNo = -1 |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
49 |
self.endLinePos = -1 |
2200
be0cfccc662a
Fixed up tabs and whitespace in all source files
andy_robinson
parents:
2192
diff
changeset
|
50 |
|
253 | 51 |
############################################################# |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
52 |
# Flowable Objects - a base class and a few examples. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
53 |
# One is just a box to get some metrics. We also have |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
54 |
# a paragraph, an image and a special 'page break' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
55 |
# object which fills the space. |
253 | 56 |
############################################################# |
57 |
class Flowable: |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
58 |
"""Abstract base class for things to be drawn. Key concepts: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
59 |
1. It knows its size |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
60 |
2. It draws in its own coordinate system (this requires the |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
61 |
base API to provide a translate() function. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
62 |
""" |
1917 | 63 |
_fixedWidth = 0 #assume wrap results depend on arguments? |
64 |
_fixedHeight = 0 |
|
65 |
||
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
66 |
def __init__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
67 |
self.width = 0 |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
68 |
self.height = 0 |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
69 |
self.wrapped = 0 |
253 | 70 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
71 |
#these are hints to packers/frames as to how the floable should be positioned |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
72 |
self.hAlign = 'LEFT' #CENTER/CENTRE or RIGHT |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
73 |
self.vAlign = 'BOTTOM' #MIDDLE or TOP |
426 | 74 |
|
2113
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
75 |
#optional holder for trace info |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
76 |
self._traceInfo = None |
2158 | 77 |
self._showBoundary = None |
2200
be0cfccc662a
Fixed up tabs and whitespace in all source files
andy_robinson
parents:
2192
diff
changeset
|
78 |
|
2575 | 79 |
#many flowables handle text and must be processed in the |
80 |
#absence of a canvas. tagging them with their encoding |
|
81 |
#helps us to get conversions right. Use Python codec names. |
|
82 |
self.encoding = None |
|
83 |
||
1494 | 84 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
85 |
def _drawOn(self,canv): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
86 |
'''ensure canv is set on and then draw''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
87 |
self.canv = canv |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
88 |
self.draw()#this is the bit you overload |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
89 |
del self.canv |
1494 | 90 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
91 |
def drawOn(self, canvas, x, y, _sW=0): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
92 |
"Tell it to draw itself on the canvas. Do not override" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
93 |
if _sW and hasattr(self,'hAlign'): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
94 |
a = self.hAlign |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
95 |
if a in ['CENTER','CENTRE']: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
96 |
x = x + 0.5*_sW |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
97 |
elif a == 'RIGHT': |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
98 |
x = x + _sW |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
99 |
elif a != 'LEFT': |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
100 |
raise ValueError, "Bad hAlign value "+str(a) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
101 |
canvas.saveState() |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
102 |
canvas.translate(x, y) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
103 |
self._drawOn(canvas) |
2158 | 104 |
if hasattr(self, '_showBoundary') and self._showBoundary: |
105 |
#diagnostic tool support |
|
106 |
canvas.setStrokeColor(gray) |
|
107 |
canvas.rect(0,0,self.width, self.height) |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
108 |
canvas.restoreState() |
253 | 109 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
110 |
def wrapOn(self, canv, aW, aH): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
111 |
'''intended for use by packers allows setting the canvas on |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
112 |
during the actual wrap''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
113 |
self.canv = canv |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
114 |
w, h = self.wrap(aW,aH) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
115 |
del self.canv |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
116 |
return w, h |
253 | 117 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
118 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
119 |
"""This will be called by the enclosing frame before objects |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
120 |
are asked their size, drawn or whatever. It returns the |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
121 |
size actually used.""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
122 |
return (self.width, self.height) |
253 | 123 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
124 |
def minWidth(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
125 |
"""This should return the minimum required width""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
126 |
return getattr(self,'_minWidth',self.width) |
954 | 127 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
128 |
def splitOn(self, canv, aW, aH): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
129 |
'''intended for use by packers allows setting the canvas on |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
130 |
during the actual split''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
131 |
self.canv = canv |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
132 |
S = self.split(aW,aH) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
133 |
del self.canv |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
134 |
return S |
1491 | 135 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
136 |
def split(self, availWidth, availheight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
137 |
"""This will be called by more sophisticated frames when |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
138 |
wrap fails. Stupid flowables should return []. Clever flowables |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
139 |
should split themselves and return a list of flowables""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
140 |
return [] |
253 | 141 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
142 |
def getKeepWithNext(self): |
1879 | 143 |
"""returns boolean determining whether the next flowable should stay with this one""" |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
144 |
if hasattr(self,'keepWithNext'): return self.keepWithNext |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
145 |
elif hasattr(self,'style') and hasattr(self.style,'keepWithNext'): return self.style.keepWithNext |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
146 |
else: return 0 |
253 | 147 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
148 |
def getSpaceAfter(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
149 |
"""returns how much space should follow this item if another item follows on the same page.""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
150 |
if hasattr(self,'spaceAfter'): return self.spaceAfter |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
151 |
elif hasattr(self,'style') and hasattr(self.style,'spaceAfter'): return self.style.spaceAfter |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
152 |
else: return 0 |
253 | 153 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
154 |
def getSpaceBefore(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
155 |
"""returns how much space should precede this item if another item precedess on the same page.""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
156 |
if hasattr(self,'spaceBefore'): return self.spaceBefore |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
157 |
elif hasattr(self,'style') and hasattr(self.style,'spaceBefore'): return self.style.spaceBefore |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
158 |
else: return 0 |
1426 | 159 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
160 |
def isIndexing(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
161 |
"""Hook for IndexingFlowables - things which have cross references""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
162 |
return 0 |
512 | 163 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
164 |
def identity(self, maxLen=None): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
165 |
''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
166 |
This method should attempt to return a string that can be used to identify |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
167 |
a particular flowable uniquely. The result can then be used for debugging |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
168 |
and or error printouts |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
169 |
''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
170 |
if hasattr(self, 'getPlainText'): |
2172 | 171 |
r = self.getPlainText(identify=1) |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
172 |
elif hasattr(self, 'text'): |
2461 | 173 |
r = str(self.text) |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
174 |
else: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
175 |
r = '...' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
176 |
if r and maxLen: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
177 |
r = r[:maxLen] |
2531 | 178 |
return "<%s at %s%s>%s" % (self.__class__.__name__, hex(id(self)), self._frameName(), r) |
179 |
||
180 |
def _frameName(self): |
|
181 |
f = getattr(self,'_frame',None) |
|
182 |
if f and f.id: return ' frame=%s' % f.id |
|
183 |
return '' |
|
1103 | 184 |
|
253 | 185 |
class XBox(Flowable): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
186 |
"""Example flowable - a box with an x through it and a caption. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
187 |
This has a known size, so does not need to respond to wrap().""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
188 |
def __init__(self, width, height, text = 'A Box'): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
189 |
Flowable.__init__(self) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
190 |
self.width = width |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
191 |
self.height = height |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
192 |
self.text = text |
253 | 193 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
194 |
def __repr__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
195 |
return "XBox(w=%s, h=%s, t=%s)" % (self.width, self.height, self.text) |
541
33de80b3655c
added __repr__s and enhanced exception messages for debugging
aaron_watters
parents:
512
diff
changeset
|
196 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
197 |
def draw(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
198 |
self.canv.rect(0, 0, self.width, self.height) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
199 |
self.canv.line(0, 0, self.width, self.height) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
200 |
self.canv.line(0, self.height, self.width, 0) |
253 | 201 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
202 |
#centre the text |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
203 |
self.canv.setFont('Times-Roman',12) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
204 |
self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text) |
253 | 205 |
|
445 | 206 |
def _trimEmptyLines(lines): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
207 |
#don't want the first or last to be empty |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
208 |
while len(lines) and string.strip(lines[0]) == '': |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
209 |
lines = lines[1:] |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
210 |
while len(lines) and string.strip(lines[-1]) == '': |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
211 |
lines = lines[:-1] |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
212 |
return lines |
445 | 213 |
|
442 | 214 |
def _dedenter(text,dedent=0): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
215 |
''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
216 |
tidy up text - carefully, it is probably code. If people want to |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
217 |
indent code within a source script, you can supply an arg to dedent |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
218 |
and it will chop off that many character, otherwise it leaves |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
219 |
left edge intact. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
220 |
''' |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
221 |
lines = string.split(text, '\n') |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
222 |
if dedent>0: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
223 |
templines = _trimEmptyLines(lines) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
224 |
lines = [] |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
225 |
for line in templines: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
226 |
line = string.rstrip(line[dedent:]) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
227 |
lines.append(line) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
228 |
else: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
229 |
lines = _trimEmptyLines(lines) |
445 | 230 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
231 |
return lines |
442 | 232 |
|
253 | 233 |
class Preformatted(Flowable): |
1683 | 234 |
"""This is like the HTML <PRE> tag. |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
235 |
It attempts to display text exactly as you typed it in a fixed width "typewriter" font. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
236 |
The line breaks are exactly where you put |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
237 |
them, and it will not be wrapped.""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
238 |
def __init__(self, text, style, bulletText = None, dedent=0): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
239 |
"""text is the text to display. If dedent is set then common leading space |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
240 |
will be chopped off the front (for example if the entire text is indented |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
241 |
6 spaces or more then each line will have 6 spaces removed from the front). |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
242 |
""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
243 |
self.style = style |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
244 |
self.bulletText = bulletText |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
245 |
self.lines = _dedenter(text,dedent) |
253 | 246 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
247 |
def __repr__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
248 |
bT = self.bulletText |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
249 |
H = "Preformatted(" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
250 |
if bT is not None: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
251 |
H = "Preformatted(bulletText=%s," % repr(bT) |
2023 | 252 |
return "%s'''\\ \n%s''')" % (H, string.join(self.lines,'\n')) |
541
33de80b3655c
added __repr__s and enhanced exception messages for debugging
aaron_watters
parents:
512
diff
changeset
|
253 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
254 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
255 |
self.width = availWidth |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
256 |
self.height = self.style.leading*len(self.lines) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
257 |
return (self.width, self.height) |
253 | 258 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
259 |
def split(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
260 |
#returns two Preformatted objects |
253 | 261 |
|
1683 | 262 |
#not sure why they can be called with a negative height |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
263 |
if availHeight < self.style.leading: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
264 |
return [] |
1683 | 265 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
266 |
linesThatFit = int(availHeight * 1.0 / self.style.leading) |
1683 | 267 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
268 |
text1 = string.join(self.lines[0:linesThatFit], '\n') |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
269 |
text2 = string.join(self.lines[linesThatFit:], '\n') |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
270 |
style = self.style |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
271 |
if style.firstLineIndent != 0: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
272 |
style = deepcopy(style) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
273 |
style.firstLineIndent = 0 |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
274 |
return [Preformatted(text1, self.style), Preformatted(text2, style)] |
1683 | 275 |
|
253 | 276 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
277 |
def draw(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
278 |
#call another method for historical reasons. Besides, I |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
279 |
#suspect I will be playing with alternate drawing routines |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
280 |
#so not doing it here makes it easier to switch. |
253 | 281 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
282 |
cur_x = self.style.leftIndent |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
283 |
cur_y = self.height - self.style.fontSize |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
284 |
self.canv.addLiteral('%PreformattedPara') |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
285 |
if self.style.textColor: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
286 |
self.canv.setFillColor(self.style.textColor) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
287 |
tx = self.canv.beginText(cur_x, cur_y) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
288 |
#set up the font etc. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
289 |
tx.setFont( self.style.fontName, |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
290 |
self.style.fontSize, |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
291 |
self.style.leading) |
253 | 292 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
293 |
for text in self.lines: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
294 |
tx.textLine(text) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
295 |
self.canv.drawText(tx) |
253 | 296 |
|
297 |
class Image(Flowable): |
|
2045 | 298 |
"""an image (digital picture). Formats supported by PIL/Java 1.4 (the Python/Java Imaging Library |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
299 |
are supported. At the present time images as flowables are always centered horozontally |
2080 | 300 |
in the frame. We allow for two kinds of lazyness to allow for many images in a document |
301 |
which could lead to file handle starvation. |
|
302 |
lazy=1 don't open image until required. |
|
303 |
lazy=2 open image when required then shut it. |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
304 |
""" |
1917 | 305 |
_fixedWidth = 1 |
306 |
_fixedHeight = 1 |
|
2080 | 307 |
def __init__(self, filename, width=None, height=None, kind='direct', mask="auto", lazy=1): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
308 |
"""If size to draw at not specified, get it from the image.""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
309 |
self.hAlign = 'CENTER' |
1880 | 310 |
self._mask = mask |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
311 |
# if it is a JPEG, will be inlined within the file - |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
312 |
# but we still need to know its size now |
2080 | 313 |
fp = hasattr(filename,'read') |
314 |
if fp: |
|
2237 | 315 |
self._file = filename |
2080 | 316 |
self.filename = `filename` |
317 |
else: |
|
2237 | 318 |
self._file = self.filename = filename |
2080 | 319 |
if not fp and os.path.splitext(filename)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']: |
2241 | 320 |
from reportlab.lib.utils import open_for_read |
321 |
f = open_for_read(filename, 'b') |
|
2080 | 322 |
info = pdfutils.readJPEGInfo(f) |
323 |
f.close() |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
324 |
self.imageWidth = info[0] |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
325 |
self.imageHeight = info[1] |
2404
8bc8743cc5ec
flowables.py: fix no PIL jpg problem reported by Martin.Zohlhuber@tttech.com
rgbecker
parents:
2403
diff
changeset
|
326 |
self._img = None |
2080 | 327 |
self._setup(width,height,kind,0) |
328 |
elif fp: |
|
329 |
self._setup(width,height,kind,0) |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
330 |
else: |
2080 | 331 |
self._setup(width,height,kind,lazy) |
1477 | 332 |
|
2080 | 333 |
def _setup(self,width,height,kind,lazy): |
334 |
self._lazy = lazy |
|
335 |
self._width = width |
|
336 |
self._height = height |
|
337 |
self._kind = kind |
|
338 |
if lazy<=0: self._setup_inner() |
|
339 |
||
340 |
def _setup_inner(self): |
|
341 |
width = self._width |
|
342 |
height = self._height |
|
343 |
kind = self._kind |
|
2404
8bc8743cc5ec
flowables.py: fix no PIL jpg problem reported by Martin.Zohlhuber@tttech.com
rgbecker
parents:
2403
diff
changeset
|
344 |
img = self._img |
8bc8743cc5ec
flowables.py: fix no PIL jpg problem reported by Martin.Zohlhuber@tttech.com
rgbecker
parents:
2403
diff
changeset
|
345 |
if img: self.imageWidth, self.imageHeight = img.getSize() |
2080 | 346 |
if self._lazy>=2: del self._img |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
347 |
if kind in ['direct','absolute']: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
348 |
self.drawWidth = width or self.imageWidth |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
349 |
self.drawHeight = height or self.imageHeight |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
350 |
elif kind in ['percentage','%']: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
351 |
self.drawWidth = self.imageWidth*width*0.01 |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
352 |
self.drawHeight = self.imageHeight*height*0.01 |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
353 |
elif kind in ['bound','proportional']: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
354 |
factor = min(float(width)/self.imageWidth,float(height)/self.imageHeight) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
355 |
self.drawWidth = self.imageWidth*factor |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
356 |
self.drawHeight = self.imageHeight*factor |
253 | 357 |
|
2080 | 358 |
def __getattr__(self,a): |
359 |
if a=='_img': |
|
360 |
from reportlab.lib.utils import ImageReader #this may raise an error |
|
2237 | 361 |
self._img = ImageReader(self._file) |
362 |
del self._file |
|
2080 | 363 |
return self._img |
364 |
elif a in ('drawWidth','drawHeight','imageWidth','imageHeight'): |
|
365 |
self._setup_inner() |
|
366 |
return self.__dict__[a] |
|
367 |
raise AttributeError(a) |
|
368 |
||
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
369 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
370 |
#the caller may decide it does not fit. |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
371 |
return (self.drawWidth, self.drawHeight) |
253 | 372 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
373 |
def draw(self): |
2080 | 374 |
lazy = self._lazy |
375 |
if lazy>=2: self._lazy = 1 |
|
376 |
self.canv.drawImage( self._img or self.filename, |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
377 |
0, |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
378 |
0, |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
379 |
self.drawWidth, |
1880 | 380 |
self.drawHeight, |
1882 | 381 |
mask=self._mask, |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
382 |
) |
2080 | 383 |
if lazy>=2: |
384 |
self._img = None |
|
385 |
self._lazy = lazy |
|
1103 | 386 |
|
2192 | 387 |
def identity(self,maxLen=None): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
388 |
r = Flowable.identity(self,maxLen) |
2080 | 389 |
if r[-4:]=='>...' and type(self.filename) is StringType: |
390 |
r = "%s filename=%s>" % (r[:-4],self.filename) |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
391 |
return r |
1103 | 392 |
|
253 | 393 |
class Spacer(Flowable): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
394 |
"""A spacer just takes up space and doesn't draw anything - it guarantees |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
395 |
a gap between objects.""" |
1917 | 396 |
_fixedWidth = 1 |
397 |
_fixedHeight = 1 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
398 |
def __init__(self, width, height): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
399 |
self.width = width |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
400 |
self.height = height |
253 | 401 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
402 |
def __repr__(self): |
2387 | 403 |
return "%s(%s, %s)" % (self.__class__.__name__,self.width, self.height) |
253 | 404 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
405 |
def draw(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
406 |
pass |
253 | 407 |
|
2535 | 408 |
class UseUpSpace(Flowable): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
409 |
def __init__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
410 |
pass |
1683 | 411 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
412 |
def __repr__(self): |
2535 | 413 |
return "%s()" % self.__class__.__name__ |
253 | 414 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
415 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
416 |
self.width = availWidth |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
417 |
self.height = availHeight |
2575 | 418 |
return (availWidth,availHeight-1e-8) #step back a point |
253 | 419 |
|
2535 | 420 |
def draw(self): |
421 |
pass |
|
422 |
||
423 |
class PageBreak(UseUpSpace): |
|
424 |
"""Move on to the next page in the document. |
|
425 |
This works by consuming all remaining space in the frame!""" |
|
426 |
||
2408 | 427 |
class SlowPageBreak(PageBreak): |
428 |
pass |
|
429 |
||
307 | 430 |
class CondPageBreak(Spacer): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
431 |
"""Throw a page if not enough vertical space""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
432 |
def __init__(self, height): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
433 |
self.height = height |
1683 | 434 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
435 |
def __repr__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
436 |
return "CondPageBreak(%s)" %(self.height,) |
307 | 437 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
438 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
439 |
if availHeight<self.height: |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
440 |
return (availWidth, availHeight) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
441 |
return (0, 0) |
253 | 442 |
|
2575 | 443 |
def _listWrapOn(F,availWidth,canv,mergeSpace=1,obj=None,dims=None): |
2375 | 444 |
'''return max width, required height for a list of flowables F''' |
445 |
W = 0 |
|
446 |
H = 0 |
|
447 |
pS = 0 |
|
2525 | 448 |
atTop = 1 |
449 |
for f in F: |
|
2375 | 450 |
w,h = f.wrapOn(canv,availWidth,0xfffffff) |
2575 | 451 |
if dims is not None: dims.append((w,h)) |
2525 | 452 |
if w<=_FUZZ or h<=_FUZZ: continue |
2375 | 453 |
W = max(W,w) |
2525 | 454 |
H += h |
455 |
if not atTop: |
|
2387 | 456 |
h = f.getSpaceBefore() |
2561 | 457 |
if mergeSpace: h = max(h-pS,0) |
458 |
H += h |
|
2525 | 459 |
else: |
460 |
if obj is not None: obj._spaceBefore = f.getSpaceBefore() |
|
461 |
atTop = 0 |
|
462 |
pS = f.getSpaceAfter() |
|
463 |
H += pS |
|
464 |
if obj is not None: obj._spaceAfter = pS |
|
465 |
return W, H-pS |
|
2375 | 466 |
|
2449
47b15f941325
platypus: attempt to make KeepTogether/keepWithNext more robust
rgbecker
parents:
2408
diff
changeset
|
467 |
def _flowableSublist(V): |
2392 | 468 |
"if it isn't a list or tuple, wrap it in a list" |
2375 | 469 |
if type(V) not in (ListType, TupleType): V = V is not None and [V] or [] |
2450 | 470 |
from doctemplate import LCActionFlowable |
2451 | 471 |
assert not [x for x in V if isinstance(x,LCActionFlowable)],'LCActionFlowables not allowed in sublists' |
2375 | 472 |
return V |
558 | 473 |
|
2575 | 474 |
class _ContainerSpace: #Abstract some common container like behaviour |
475 |
def getSpaceBefore(self): |
|
476 |
for c in self._content: |
|
477 |
if not hasattr(c,'frameAction'): |
|
478 |
return c.getSpaceBefore() |
|
479 |
return 0 |
|
480 |
||
481 |
def getSpaceAfter(self,content=None): |
|
482 |
#this needs 2.4 |
|
483 |
#for c in reversed(content or self._content): |
|
484 |
reverseContent = (content or self._content)[:] |
|
485 |
reverseContent.reverse() |
|
486 |
for c in reverseContent: |
|
487 |
if not hasattr(c,'frameAction'): |
|
488 |
return c.getSpaceAfter() |
|
489 |
return 0 |
|
490 |
||
491 |
class KeepTogether(_ContainerSpace,Flowable): |
|
1994 | 492 |
def __init__(self,flowables,maxHeight=None): |
2575 | 493 |
self._content = _flowableSublist(flowables) |
1994 | 494 |
self._maxHeight = maxHeight |
367 | 495 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
496 |
def __repr__(self): |
2575 | 497 |
f = self._content |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
498 |
L = map(repr,f) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
499 |
import string |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
500 |
L = "\n"+string.join(L, "\n") |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
501 |
L = string.replace(L, "\n", "\n ") |
1994 | 502 |
return "KeepTogether(%s,maxHeight=%s) # end KeepTogether" % (L,self._maxHeight) |
541
33de80b3655c
added __repr__s and enhanced exception messages for debugging
aaron_watters
parents:
512
diff
changeset
|
503 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
504 |
def wrap(self, aW, aH): |
2575 | 505 |
dims = [] |
506 |
W,H = _listWrapOn(self._content,aW,self.canv,dims=dims) |
|
507 |
self._H = H |
|
508 |
self._H0 = dims and dims[0][1] or 0 |
|
509 |
self._wrapInfo = aW,aH |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
510 |
return W, 0xffffff # force a split |
367 | 511 |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
512 |
def split(self, aW, aH): |
2575 | 513 |
if getattr(self,'_wrapInfo',None)!=(aW,aH): self.wrap(aW,aH) |
514 |
S = self._content[:] |
|
515 |
C0 = self._H>aH and (not self._maxHeight or aH>self._maxHeight) |
|
516 |
C1 = self._H0>aH |
|
517 |
if C0 or C1: |
|
518 |
if C0: |
|
519 |
from doctemplate import FrameBreak |
|
520 |
A = FrameBreak |
|
521 |
else: |
|
522 |
from doctemplate import NullActionFlowable |
|
523 |
A = NullActionFlowable |
|
524 |
S.insert(0,A()) |
|
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
525 |
return S |
367 | 526 |
|
2575 | 527 |
def identity(self, maxLen=None): |
528 |
msg = "<KeepTogether at %s%s> containing :%s" % (hex(id(self)),self._frameName(),"\n".join([f.identity() for f in self._content])) |
|
529 |
if maxLen: |
|
530 |
return msg[0:maxLen] |
|
531 |
else: |
|
532 |
return msg |
|
2461 | 533 |
|
253 | 534 |
class Macro(Flowable): |
1677
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
535 |
"""This is not actually drawn (i.e. it has zero height) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
536 |
but is executed when it would fit in the frame. Allows direct |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
537 |
access to the canvas through the object 'canvas'""" |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
538 |
def __init__(self, command): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
539 |
self.command = command |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
540 |
def __repr__(self): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
541 |
return "Macro(%s)" % repr(self.command) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
542 |
def wrap(self, availWidth, availHeight): |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
543 |
return (0,0) |
1450177dd19e
Exterminated all tab characters and added a test to make sure
andy_robinson
parents:
1554
diff
changeset
|
544 |
def draw(self): |
1768 | 545 |
exec self.command in globals(), {'canvas':self.canv} |
546 |
||
2403 | 547 |
class CallerMacro(Flowable): |
548 |
''' |
|
549 |
like Macro, but with callable command(s) |
|
550 |
drawCallable(self) |
|
551 |
wrapCallable(self,aW,aH) |
|
552 |
''' |
|
553 |
def __init__(self, drawCallable=None, wrapCallable=None): |
|
554 |
_ = lambda *args: None |
|
555 |
self._drawCallable = drawCallable or _ |
|
556 |
self._wrapCallable = wrapCallable or _ |
|
557 |
def __repr__(self): |
|
558 |
return "CallerMacro(%s)" % repr(self.command) |
|
559 |
def wrap(self, aW, aH): |
|
560 |
self._wrapCallable(self,aW,aH) |
|
561 |
return (0,0) |
|
562 |
def draw(self): |
|
563 |
self._drawCallable(self) |
|
564 |
||
1768 | 565 |
class ParagraphAndImage(Flowable): |
566 |
'''combine a Paragraph and an Image''' |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
567 |
def __init__(self,P,I,xpad=3,ypad=3,side='right'): |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
568 |
self.P = P |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
569 |
self.I = I |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
570 |
self.xpad = xpad |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
571 |
self.ypad = ypad |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
572 |
self._side = side |
1768 | 573 |
|
2575 | 574 |
def getSpaceBefore(self): |
575 |
return max(self.P.getSpaceBefore(),self.I.getSpaceBefore()) |
|
576 |
||
577 |
def getSpaceAfter(self): |
|
578 |
return max(self.P.getSpaceAfter(),self.I.getSpaceAfter()) |
|
579 |
||
1768 | 580 |
def wrap(self,availWidth,availHeight): |
581 |
wI, hI = self.I.wrap(availWidth,availHeight) |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
582 |
self.wI = wI |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
583 |
self.hI = hI |
1768 | 584 |
# work out widths array for breaking |
585 |
self.width = availWidth |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
586 |
P = self.P |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
587 |
style = P.style |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
588 |
xpad = self.xpad |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
589 |
ypad = self.ypad |
1768 | 590 |
leading = style.leading |
591 |
leftIndent = style.leftIndent |
|
592 |
later_widths = availWidth - leftIndent - style.rightIndent |
|
593 |
intermediate_widths = later_widths - xpad - wI |
|
594 |
first_line_width = intermediate_widths - style.firstLineIndent |
|
595 |
P.width = 0 |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
596 |
nIW = int((hI+ypad)/leading) |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
597 |
P.blPara = P.breakLines([first_line_width] + nIW*[intermediate_widths]+[later_widths]) |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
598 |
if self._side=='left': |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
599 |
self._offsets = [wI+xpad]*(1+nIW)+[0] |
1768 | 600 |
P.height = len(P.blPara.lines)*leading |
601 |
self.height = max(hI,P.height) |
|
602 |
return (self.width, self.height) |
|
603 |
||
604 |
def split(self,availWidth, availHeight): |
|
605 |
P, wI, hI, ypad = self.P, self.wI, self.hI, self.ypad |
|
606 |
if hI+ypad>availHeight or len(P.frags)<=0: return [] |
|
607 |
S = P.split(availWidth,availHeight) |
|
608 |
if not S: return S |
|
609 |
P = self.P = S[0] |
|
610 |
del S[0] |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
611 |
style = P.style |
1768 | 612 |
P.height = len(self.P.blPara.lines)*style.leading |
613 |
self.height = max(hI,P.height) |
|
614 |
return [self]+S |
|
615 |
||
616 |
def draw(self): |
|
617 |
canv = self.canv |
|
2557
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
618 |
if self._side=='left': |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
619 |
self.I.drawOn(canv,0,self.height-self.hI) |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
620 |
self.P._offsets = self._offsets |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
621 |
try: |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
622 |
self.P.drawOn(canv,0,0) |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
623 |
finally: |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
624 |
del self.P._offsets |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
625 |
else: |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
626 |
self.I.drawOn(canv,self.width-self.wI-self.xpad,self.height-self.hI) |
d84c18fb377f
added _offsets handling to paragraph for flow around on left side
rgbecker
parents:
2542
diff
changeset
|
627 |
self.P.drawOn(canv,0,0) |
2113
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
628 |
|
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
629 |
class FailOnWrap(Flowable): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
630 |
def wrap(self, availWidth, availHeight): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
631 |
raise ValueError("FailOnWrap flowable wrapped and failing as ordered!") |
2200
be0cfccc662a
Fixed up tabs and whitespace in all source files
andy_robinson
parents:
2192
diff
changeset
|
632 |
|
2113
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
633 |
def draw(self): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
634 |
pass |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
635 |
|
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
636 |
class FailOnDraw(Flowable): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
637 |
def wrap(self, availWidth, availHeight): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
638 |
return (0,0) |
2200
be0cfccc662a
Fixed up tabs and whitespace in all source files
andy_robinson
parents:
2192
diff
changeset
|
639 |
|
2113
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
640 |
def draw(self): |
e82d8b3880d8
Preliminary support for tracing through doc builds
andy_robinson
parents:
2080
diff
changeset
|
641 |
raise ValueError("FailOnDraw flowable drawn, and failing as ordered!") |
2276 | 642 |
|
643 |
class HRFlowable(Flowable): |
|
644 |
'''Like the hr tag''' |
|
645 |
def __init__(self, |
|
646 |
width="80%", |
|
647 |
thickness=1, |
|
648 |
lineCap='round', |
|
649 |
color=lightgrey, |
|
650 |
spaceBefore=1, spaceAfter=1, |
|
2523
0473810aff11
platypus: fix up None defaults for table line commands, add HRFlowable dash arg
rgbecker
parents:
2461
diff
changeset
|
651 |
hAlign='CENTER', vAlign='BOTTOM', |
0473810aff11
platypus: fix up None defaults for table line commands, add HRFlowable dash arg
rgbecker
parents:
2461
diff
changeset
|
652 |
dash=None): |
2276 | 653 |
Flowable.__init__(self) |
654 |
self.width = width |
|
655 |
self.lineWidth = thickness |
|
656 |
self.lineCap=lineCap |
|
657 |
self.spaceBefore = spaceBefore |
|
658 |
self.spaceAfter = spaceAfter |
|
659 |
self.color = color |
|
660 |
self.hAlign = hAlign |
|
661 |
self.vAlign = vAlign |
|
2523
0473810aff11
platypus: fix up None defaults for table line commands, add HRFlowable dash arg
rgbecker
parents:
2461
diff
changeset
|
662 |
self.dash = dash |
2276 | 663 |
|
664 |
def __repr__(self): |
|
2284 | 665 |
return "HRFlowable(width=%s, height=%s)" % (self.width, self.height) |
2276 | 666 |
|
667 |
def wrap(self, availWidth, availHeight): |
|
668 |
w = self.width |
|
669 |
if type(w) is type(''): |
|
670 |
w = w.strip() |
|
671 |
if w.endswith('%'): w = availWidth*float(w[:-1])*0.01 |
|
672 |
else: w = float(w) |
|
673 |
w = min(w,availWidth) |
|
674 |
self._width = w |
|
675 |
return w, self.lineWidth |
|
676 |
||
677 |
def draw(self): |
|
678 |
canv = self.canv |
|
679 |
canv.saveState() |
|
680 |
canv.setLineWidth(self.lineWidth) |
|
681 |
canv.setLineCap({'butt':0,'round':1, 'square': 2}[self.lineCap.lower()]) |
|
682 |
canv.setStrokeColor(self.color) |
|
2523
0473810aff11
platypus: fix up None defaults for table line commands, add HRFlowable dash arg
rgbecker
parents:
2461
diff
changeset
|
683 |
if self.dash: canv.setDash(self.dash) |
2276 | 684 |
canv.line(0, 0, self._width, self.height) |
685 |
canv.restoreState() |
|
2375 | 686 |
|
2387 | 687 |
class _PTOInfo: |
688 |
def __init__(self,trailer,header): |
|
2449
47b15f941325
platypus: attempt to make KeepTogether/keepWithNext more robust
rgbecker
parents:
2408
diff
changeset
|
689 |
self.trailer = _flowableSublist(trailer) |
47b15f941325
platypus: attempt to make KeepTogether/keepWithNext more robust
rgbecker
parents:
2408
diff
changeset
|
690 |
self.header = _flowableSublist(header) |
2387 | 691 |
|
2575 | 692 |
class _Container(_ContainerSpace): #Abstract some common container like behaviour |
2561 | 693 |
def drawOn(self, canv, x, y, _sW=0, scale=1.0, content=None, aW=None): |
2525 | 694 |
'''we simulate being added to a frame''' |
695 |
pS = 0 |
|
2561 | 696 |
if aW is None: aW = self.width |
697 |
aW = scale*(aW+_sW) |
|
698 |
if content is None: |
|
699 |
content = self._content |
|
2525 | 700 |
y += self.height*scale |
2561 | 701 |
for c in content: |
2525 | 702 |
w, h = c.wrapOn(canv,aW,0xfffffff) |
703 |
if w<_FUZZ or h<_FUZZ: continue |
|
2561 | 704 |
if c is not content[0]: h += max(c.getSpaceBefore()-pS,0) |
2525 | 705 |
y -= h |
706 |
c.drawOn(canv,x,y,_sW=aW-w) |
|
2561 | 707 |
if c is not content[-1]: |
2525 | 708 |
pS = c.getSpaceAfter() |
709 |
y -= pS |
|
710 |
||
711 |
class PTOContainer(_Container,Flowable): |
|
2375 | 712 |
'''PTOContainer(contentList,trailerList,headerList) |
713 |
||
714 |
A container for flowables decorated with trailer & header lists. |
|
715 |
If the split operation would be called then the trailer and header |
|
716 |
lists are injected before and after the split. This allows specialist |
|
717 |
"please turn over" and "continued from previous" like behaviours.''' |
|
718 |
def __init__(self,content,trailer=None,header=None): |
|
2387 | 719 |
I = _PTOInfo(trailer,header) |
720 |
self._content = C = [] |
|
2449
47b15f941325
platypus: attempt to make KeepTogether/keepWithNext more robust
rgbecker
parents:
2408
diff
changeset
|
721 |
for _ in _flowableSublist(content): |
2387 | 722 |
if isinstance(_,PTOContainer): |
723 |
C.extend(_._content) |
|
724 |
else: |
|
725 |
C.append(_) |
|
726 |
if not hasattr(_,'_ptoinfo'): _._ptoinfo = I |
|
2375 | 727 |
|
728 |
def wrap(self,availWidth,availHeight): |
|
729 |
self.width, self.height = _listWrapOn(self._content,availWidth,self.canv) |
|
2384 | 730 |
return self.width,self.height |
2375 | 731 |
|
732 |
def split(self, availWidth, availHeight): |
|
2535 | 733 |
if availHeight<0: return [] |
2375 | 734 |
canv = self.canv |
735 |
C = self._content |
|
2535 | 736 |
x = i = H = pS = hx = 0 |
2387 | 737 |
n = len(C) |
738 |
I2W = {} |
|
739 |
for x in xrange(n): |
|
740 |
c = C[x] |
|
741 |
I = c._ptoinfo |
|
2388 | 742 |
if I not in I2W.keys(): |
2387 | 743 |
T = I.trailer |
744 |
Hdr = I.header |
|
745 |
tW, tH = _listWrapOn(T, availWidth, self.canv) |
|
746 |
tSB = T[0].getSpaceBefore() |
|
747 |
I2W[I] = T,tW,tH,tSB |
|
748 |
else: |
|
749 |
T,tW,tH,tSB = I2W[I] |
|
2384 | 750 |
_, h = c.wrapOn(canv,availWidth,0xfffffff) |
2535 | 751 |
if x: |
752 |
hx = max(c.getSpaceBefore()-pS,0) |
|
753 |
h += hx |
|
2384 | 754 |
pS = c.getSpaceAfter() |
755 |
H += h+pS |
|
2535 | 756 |
tHS = tH+max(tSB,pS) |
757 |
if H+tHS>=availHeight-_FUZZ: break |
|
2384 | 758 |
i += 1 |
2375 | 759 |
|
760 |
#first retract last thing we tried |
|
2384 | 761 |
H -= (h+pS) |
2375 | 762 |
|
763 |
#attempt a sub split on the last one we have |
|
2535 | 764 |
aH = (availHeight-H-tHS-hx)*0.99999 |
2375 | 765 |
if aH>=0.05*availHeight: |
2387 | 766 |
SS = c.splitOn(canv,availWidth,aH) |
2375 | 767 |
else: |
768 |
SS = [] |
|
2535 | 769 |
F = [UseUpSpace()] |
2376 | 770 |
|
2535 | 771 |
if len(SS)>1: |
2387 | 772 |
R1 = C[:i] + SS[:1] + T + F |
2376 | 773 |
R2 = Hdr + SS[1:]+C[i+1:] |
2375 | 774 |
elif not i: |
775 |
return [] |
|
776 |
else: |
|
2535 | 777 |
R1 = C[:i]+T+F |
2376 | 778 |
R2 = Hdr + C[i:] |
2535 | 779 |
T = R1 + [PTOContainer(R2,deepcopy(I.trailer),deepcopy(I.header))] |
780 |
return T |
|
2375 | 781 |
|
2528 | 782 |
#utility functions used by KeepInFrame |
2525 | 783 |
def _hmodel(s0,s1,h0,h1): |
784 |
# calculate the parameters in the model |
|
785 |
# h = a/s**2 + b/s |
|
786 |
a11 = 1./s0**2 |
|
787 |
a12 = 1./s0 |
|
788 |
a21 = 1./s1**2 |
|
789 |
a22 = 1./s1 |
|
790 |
det = a11*a22-a12*a21 |
|
791 |
b11 = a22/det |
|
792 |
b12 = -a12/det |
|
793 |
b21 = -a21/det |
|
794 |
b22 = a11/det |
|
795 |
a = b11*h0+b12*h1 |
|
796 |
b = b21*h0+b22*h1 |
|
797 |
return a,b |
|
798 |
||
799 |
def _qsolve(h,(a,b)): |
|
800 |
'''solve the model v = a/s**2 + b/s for an s which gives us v==h''' |
|
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
801 |
if abs(a)<=_FUZZ: |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
802 |
return b/h |
2525 | 803 |
t = 0.5*b/a |
804 |
from math import sqrt |
|
805 |
f = -h/a |
|
806 |
r = t*t-f |
|
807 |
if r<0: return None |
|
808 |
r = sqrt(r) |
|
809 |
if t>=0: |
|
810 |
s1 = -t - r |
|
811 |
else: |
|
812 |
s1 = -t + r |
|
813 |
s2 = f/s1 |
|
814 |
return max(1./s1, 1./s2) |
|
815 |
||
2528 | 816 |
class KeepInFrame(_Container,Flowable): |
2531 | 817 |
def __init__(self, maxWidth, maxHeight, content=[], mergeSpace=1, mode='shrink', name=''): |
2525 | 818 |
'''mode describes the action to take when overflowing |
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
819 |
error raise an error in the normal way |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
820 |
continue ignore ie just draw it and report maxWidth, maxHeight |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
821 |
shrink shrinkToFit |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
822 |
truncate fit as much as possible |
2525 | 823 |
''' |
2531 | 824 |
self.name = name |
2525 | 825 |
self.maxWidth = maxWidth |
826 |
self.maxHeight = maxHeight |
|
827 |
self.mode = mode |
|
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
828 |
assert mode in ('error','overflow','shrink','truncate'), '%s invalid mode value %s' % (self.identity(),mode) |
2528 | 829 |
assert maxHeight>=0, '%s invalid maxHeight value %s' % (self.identity(),maxHeight) |
2525 | 830 |
if mergeSpace is None: mergeSpace = overlapAttachedSpace |
831 |
self.mergespace = mergeSpace |
|
832 |
self._content = content |
|
833 |
||
834 |
def _getAvailableWidth(self): |
|
835 |
return self.maxWidth - self._leftExtraIndent - self._rightExtraIndent |
|
836 |
||
837 |
def identity(self, maxLen=None): |
|
2575 | 838 |
return "<%s at %s%s%s> size=%sx%s" % (self.__class__.__name__, hex(id(self)), self._frameName(), |
839 |
getattr(self,'name','') and (' name="%s"'% getattr(self,'name','')) or '', |
|
840 |
getattr(self,'maxWidth','') and (' maxWidth=%s'%fp_str(getattr(self,'maxWidth',0))) or '', |
|
841 |
getattr(self,'maxHeight','')and (' maxHeight=%s' % fp_str(getattr(self,'maxHeight')))or '') |
|
2525 | 842 |
|
843 |
def wrap(self,availWidth,availHeight): |
|
2534 | 844 |
from doctemplate import LayoutError |
2525 | 845 |
mode = self.mode |
2526
eaaa012dffd9
flowables.py: minor fixes revealed by rml2pdf use
rgbecker
parents:
2525
diff
changeset
|
846 |
maxWidth = float(self.maxWidth or availWidth) |
2528 | 847 |
maxHeight = float(self.maxHeight or availHeight) |
2525 | 848 |
W, H = _listWrapOn(self._content,availWidth,self.canv) |
2534 | 849 |
if (mode=='error' and (W>availWidth+_FUZZ or H>availHeight+_FUZZ)): |
850 |
ident = 'content %sx%s too large for %s' % (W,H,self.identity(30)) |
|
851 |
#leave to keep apart from the raise |
|
852 |
raise LayoutError(ident) |
|
853 |
elif W<=availWidth+_FUZZ and H<=availHeight+_FUZZ: |
|
854 |
self.width = W-_FUZZ #we take what we get |
|
855 |
self.height = H-_FUZZ |
|
856 |
elif (maxWidth>=availWidth+_FUZZ or maxHeight>=availHeight+_FUZZ): |
|
857 |
ident = 'Specified size too large for available space %sx%s in %s' % (availWidth,availHeight,self.identity(30)) |
|
858 |
#leave to keep apart from the raise |
|
859 |
raise LayoutError(ident) |
|
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
860 |
elif mode in ('overflow','truncate'): #we lie |
2525 | 861 |
self.width = min(maxWidth,W)-_FUZZ |
862 |
self.height = min(maxHeight,H)-_FUZZ |
|
863 |
else: |
|
864 |
def func(x): |
|
865 |
W, H = _listWrapOn(self._content,x*availWidth,self.canv) |
|
866 |
W /= x |
|
867 |
H /= x |
|
868 |
return W, H |
|
869 |
W0 = W |
|
870 |
H0 = H |
|
871 |
s0 = 1 |
|
2527
edff938c5e89
flowablse.py: minor fix and hack to FrameFlowable
rgbecker
parents:
2526
diff
changeset
|
872 |
if W>maxWidth+_FUZZ: |
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
873 |
#squeeze out the excess width and or Height |
2525 | 874 |
s1 = W/maxWidth |
875 |
W, H = func(s1) |
|
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
876 |
if H<=maxHeight+_FUZZ: |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
877 |
self.width = W-_FUZZ |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
878 |
self.height = H-_FUZZ |
2525 | 879 |
self._scale = s1 |
880 |
return W,H |
|
881 |
s0 = s1 |
|
882 |
H0 = H |
|
883 |
W0 = W |
|
884 |
s1 = H/maxHeight |
|
885 |
W, H = func(s1) |
|
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
886 |
self.width = W-_FUZZ |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
887 |
self.height = H-_FUZZ |
2525 | 888 |
self._scale = s1 |
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
889 |
if H<min(0.95*maxHeight,maxHeight-10) or H>=maxHeight+_FUZZ: |
2525 | 890 |
#the standard case W should be OK, H is short we want |
891 |
#to find the smallest s with H<=maxHeight |
|
892 |
H1 = H |
|
893 |
for f in 0, 0.01, 0.05, 0.10, 0.15: |
|
894 |
#apply the quadratic model |
|
895 |
s = _qsolve(maxHeight*(1-f),_hmodel(s0,s1,H0,H1)) |
|
896 |
W, H = func(s) |
|
2569
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
897 |
if H<=maxHeight+_FUZZ and W<=maxWidth+_FUZZ: |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
898 |
self.width = W-_FUZZ |
f7ac857ad20d
flowables.py: attempt to fix up keepInFrame for one more case
rgbecker
parents:
2562
diff
changeset
|
899 |
self.height = H-_FUZZ |
2525 | 900 |
self._scale = s |
901 |
break |
|
902 |
||
903 |
return self.width, self.height |
|
904 |
||
2375 | 905 |
def drawOn(self, canv, x, y, _sW=0): |
2525 | 906 |
scale = getattr(self,'_scale',1.0) |
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
907 |
truncate = self.mode=='truncate' |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
908 |
ss = scale!=1.0 or truncate |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
909 |
if ss: |
2525 | 910 |
canv.saveState() |
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
911 |
if truncate: |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
912 |
p = canv.beginPath() |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
913 |
p.rect(x, y, self.width,self.height) |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
914 |
canv.clipPath(p,stroke=0) |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
915 |
else: |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
916 |
canv.translate(x,y) |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
917 |
x=y=0 |
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
918 |
canv.scale(1.0/scale, 1.0/scale) |
2525 | 919 |
_Container.drawOn(self, canv, x, y, _sW=_sW, scale=scale) |
2529
dced304f8584
flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd
rgbecker
parents:
2528
diff
changeset
|
920 |
if ss: canv.restoreState() |
2561 | 921 |
|
2562 | 922 |
class ImageAndFlowables(_Container,Flowable): |
2561 | 923 |
'''combine a list of flowables and an Image''' |
2562 | 924 |
def __init__(self,I,F,imageLeftPadding=0,imageRightPadding=3,imageTopPadding=0,imageBottomPadding=3, |
925 |
imageSide='right'): |
|
2561 | 926 |
self._content = _flowableSublist(F) |
927 |
self._I = I |
|
2562 | 928 |
self._irpad = imageRightPadding |
929 |
self._ilpad = imageLeftPadding |
|
930 |
self._ibpad = imageBottomPadding |
|
931 |
self._itpad = imageTopPadding |
|
932 |
self._side = imageSide |
|
2561 | 933 |
|
934 |
def getSpaceAfter(self): |
|
935 |
if hasattr(self,'_C1'): |
|
936 |
C = self._C1 |
|
937 |
elif hasattr(self,'_C0'): |
|
938 |
C = self._C0 |
|
939 |
else: |
|
940 |
C = self._content |
|
941 |
return _Container.getSpaceAfter(self,C) |
|
942 |
||
943 |
def getSpaceBefore(self): |
|
944 |
return max(self._I.getSpaceBefore(),_Container.getSpaceBefore(self)) |
|
945 |
||
946 |
def _reset(self): |
|
947 |
for a in ('_wrapArgs','_C0','_C1'): |
|
948 |
try: |
|
949 |
delattr(self,a) |
|
950 |
except: |
|
951 |
pass |
|
952 |
||
953 |
def wrap(self,availWidth,availHeight): |
|
954 |
canv = self.canv |
|
955 |
if hasattr(self,'_wrapArgs'): |
|
956 |
if self._wrapArgs==(availWidth,availHeight): |
|
957 |
return self.width,self.height |
|
958 |
self._reset() |
|
959 |
self._wrapArgs = availWidth, availHeight |
|
960 |
wI, hI = self._I.wrap(availWidth,availHeight) |
|
961 |
self._wI = wI |
|
962 |
self._hI = hI |
|
2562 | 963 |
ilpad = self._ilpad |
964 |
irpad = self._irpad |
|
965 |
ibpad = self._ibpad |
|
966 |
itpad = self._itpad |
|
967 |
self._iW = availWidth - irpad - wI - ilpad |
|
968 |
aH = itpad + hI + ibpad |
|
2561 | 969 |
W,H0,self._C0,self._C1 = self._findSplit(canv,self._iW,aH) |
970 |
self.width = availWidth |
|
971 |
aH = self._aH = max(aH,H0) |
|
972 |
if not self._C1: |
|
973 |
self.height = aH |
|
974 |
else: |
|
975 |
W1,H1 = _listWrapOn(self._C1,availWidth,canv) |
|
976 |
self.height = aH+H1 |
|
977 |
return self.width, self.height |
|
978 |
||
979 |
def split(self,availWidth, availHeight): |
|
980 |
if hasattr(self,'_wrapArgs'): |
|
981 |
if self._wrapArgs!=(availWidth,availHeight): |
|
982 |
self._reset() |
|
983 |
W,H=self.wrap(availWidth,availHeight) |
|
984 |
if self._aH>availHeight: return [] |
|
985 |
C1 = self._C1 |
|
986 |
if C1: |
|
987 |
c0 = C1[0] |
|
988 |
S = c0.split(availWidth,availHeight-self._aH) |
|
989 |
if not S: |
|
990 |
self._C1 = [] |
|
991 |
self.height = self._aH |
|
992 |
else: |
|
993 |
self._C1 = [S[0]] |
|
994 |
self.height = self._aH + S[0].height |
|
995 |
C1 = S[1:]+C1[1:] |
|
996 |
else: |
|
997 |
self._C1 = [] |
|
998 |
self.height = self._aH |
|
999 |
return [self]+C1 |
|
1000 |
||
1001 |
def drawOn(self, canv, x, y, _sW=0): |
|
1002 |
if self._side=='left': |
|
2562 | 1003 |
Ix = x + self._ilpad |
1004 |
Fx = Ix+ self._irpad + self._wI |
|
2561 | 1005 |
else: |
2562 | 1006 |
Ix = x + self.width-self._wI-self._irpad - self._ilpad |
2561 | 1007 |
Fx = x |
2562 | 1008 |
self._I.drawOn(canv,Ix,y+self.height-self._itpad-self._hI) |
2561 | 1009 |
_Container.drawOn(self, canv, Fx, y, content=self._C0, aW=self._iW) |
1010 |
if self._C1: |
|
1011 |
_Container.drawOn(self, canv, x, y-self._aH,content=self._C1) |
|
1012 |
||
1013 |
def _findSplit(self,canv,availWidth,availHeight,mergeSpace=1,obj=None): |
|
1014 |
'''return max width, required height for a list of flowables F''' |
|
1015 |
W = 0 |
|
1016 |
H = 0 |
|
1017 |
pS = sB = 0 |
|
1018 |
atTop = 1 |
|
1019 |
F = self._content |
|
1020 |
for i,f in enumerate(F): |
|
1021 |
w,h = f.wrapOn(canv,availWidth,0xfffffff) |
|
1022 |
if w<=_FUZZ or h<=_FUZZ: continue |
|
1023 |
W = max(W,w) |
|
1024 |
if not atTop: |
|
1025 |
s = f.getSpaceBefore() |
|
1026 |
if mergeSpace: s = max(s-pS,0) |
|
1027 |
H += s |
|
1028 |
else: |
|
1029 |
if obj is not None: obj._spaceBefore = f.getSpaceBefore() |
|
1030 |
atTop = 0 |
|
1031 |
if H>=availHeight: |
|
1032 |
return W, availHeight, F[:i],F[i:] |
|
1033 |
H += h |
|
1034 |
if H>availHeight: |
|
1035 |
from paragraph import Paragraph |
|
1036 |
aH = availHeight-(H-h) |
|
1037 |
if isinstance(f,(Paragraph,Preformatted)): |
|
1038 |
leading = f.style.leading |
|
1039 |
nH = leading*int(aH/float(leading))+_FUZZ |
|
1040 |
if nH<aH: nH += leading |
|
1041 |
availHeight += nH-aH |
|
1042 |
aH = nH |
|
1043 |
S = deepcopy(f).split(availWidth,aH) |
|
1044 |
if not S: |
|
1045 |
return W, availHeight, F[:i],F[i:] |
|
1046 |
else: |
|
1047 |
return W,availHeight,F[:i]+S[:1],S[1:]+F[i+1:] |
|
1048 |
pS = f.getSpaceAfter() |
|
1049 |
H += pS |
|
1050 |
if obj is not None: obj._spaceAfter = pS |
|
1051 |
return W, H-pS, F, [] |