1
#Copyright ReportLab Europe Ltd. 2000-2004
2
#see license.txt for license details
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/figures.py
4
"""This includes some demos of platypus for use in the API proposal"""
5
__version__=''' $Id$ '''
9
from reportlab.lib import colors
10
from reportlab.pdfgen.canvas import Canvas
11
from reportlab.lib.styles import ParagraphStyle
12
from reportlab.lib.utils import recursiveImport
13
from reportlab.platypus import Frame
14
from reportlab.platypus import Flowable
15
from reportlab.platypus import Paragraph
16
from reportlab.lib.units import inch
17
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER
18
from reportlab.lib.validators import isColor
19
from reportlab.lib.colors import toColor
21
captionStyle = ParagraphStyle('Caption', fontName='Times-Italic', fontSize=10, alignment=TA_CENTER)
23
class Figure(Flowable):
24
def __init__(self, width, height, caption="",
25
captionFont="Times-Italic", captionSize=12,
27
captionTextColor=toColor('black'),
28
captionBackColor=None,
30
Flowable.__init__(self)
32
self.figureHeight = height
33
self.caption = caption
34
self.captionFont = captionFont
35
self.captionSize = captionSize
36
self.captionTextColor = captionTextColor
37
self.captionBackColor = captionBackColor
38
self._captionData = None
39
self.captionHeight = 0 # work out later
40
self.background = background
45
def _getCaptionPara(self):
46
caption = self.caption
47
captionFont = self.captionFont
48
captionSize = self.captionSize
49
captionTextColor = self.captionTextColor
50
captionBackColor = self.captionBackColor
51
if self._captionData!=(caption,captionFont,captionSize,captionTextColor,captionBackColor):
52
self._captionData = (caption,captionFont,captionSize,captionTextColor,captionBackColor)
53
self.captionStyle = ParagraphStyle(
57
leading=1.2*captionSize,
58
textColor = captionTextColor,
59
backColor = captionBackColor,
60
spaceBefore=captionSize * 0.5,
62
#must build paragraph now to get sequencing in synch with rest of story
63
self.captionPara = Paragraph(self.caption, self.captionStyle)
65
def wrap(self, availWidth, availHeight):
66
# try to get the caption aligned
68
self._getCaptionPara()
69
(w, h) = self.captionPara.wrap(self.width, availHeight - self.figureHeight)
70
self.captionHeight = h
71
self.height = self.captionHeight + self.figureHeight
72
if w>self.width: self.width = w
74
self.height = self.figureHeight
75
self.dx = 0.5 * (availWidth - self.width)
76
return (self.width, self.height)
79
self.canv.translate(self.dx, 0)
81
self._getCaptionPara()
83
self.canv.translate(0, self.captionHeight)
91
self.canv.rect(0, 0, self.width, self.figureHeight)
93
def _doBackground(self, color):
95
self.canv.setFillColor(self.background)
96
self.canv.rect(0, 0, self.width, self.figureHeight, fill=1)
97
self.canv.restoreState()
99
def drawBackground(self):
100
"""For use when using a figure on a differently coloured background.
101
Allows you to specify a colour to be used as a background for the figure."""
102
if isColor(self.background):
103
self._doBackground(self.background)
106
c = toColor(self.background)
107
self._doBackground(c)
111
def drawCaption(self):
112
self.captionPara.drawOn(self.canv, 0, 0)
114
def drawFigure(self):
117
def drawPage(canvas,x, y, width, height):
118
#draws something which looks like a page
119
pth = canvas.beginPath()
122
# shaded backdrop offset a little
123
canvas.setFillColorRGB(0.5,0.5,0.5)
124
canvas.rect(x + corner, y - corner, width, height, stroke=0, fill=1)
126
#'sheet of paper' in light yellow
127
canvas.setFillColorRGB(1,1,0.9)
128
canvas.setLineWidth(0)
129
canvas.rect(x, y, width, height, stroke=1, fill=1)
132
canvas.setFillColorRGB(0,0,0)
133
canvas.setStrokeColorRGB(0,0,0)
135
class PageFigure(Figure):
136
"""Shows a blank page in a frame, and draws on that. Used in
137
illustrations of how PLATYPUS works."""
138
def __init__(self, background=None):
139
Figure.__init__(self, 3*inch, 3*inch)
140
self.caption = 'Figure 1 - a blank page'
141
self.captionStyle = captionStyle
142
self.background = background
144
def drawVirtualPage(self):
147
def drawFigure(self):
148
drawPage(self.canv, 0.625*inch, 0.25*inch, 1.75*inch, 2.5*inch)
149
self.canv.translate(0.625*inch, 0.25*inch)
150
self.canv.scale(1.75/8.27, 2.5/11.69)
151
self.drawVirtualPage()
153
class PlatPropFigure1(PageFigure):
154
"""This shows a page with a frame on it"""
156
PageFigure.__init__(self)
157
self.caption = "Figure 1 - a page with a simple frame"
158
def drawVirtualPage(self):
161
class FlexFigure(Figure):
162
"""Base for a figure class with a caption. Can grow or shrink in proportion"""
163
def __init__(self, width, height, caption, background=None):
164
Figure.__init__(self, width, height, caption,
165
captionFont="Helvetica-Oblique", captionSize=8,
167
self.shrinkToFit = 1 #if set and wrap is too tight, shrinks
168
self.growToFit = 1 #if set and wrap is too tight, shrinks
169
self.scaleFactor = None
170
self.captionStyle = ParagraphStyle(
172
fontName='Times', #'Helvetica-Oblique',
177
self._scaleFactor = None
178
self.background = background
180
def _scale(self,availWidth,availHeight):
181
"Rescale to fit according to the rules, but only once"
182
if self._scaleFactor is None or self.width>availWidth or self.height>availHeight:
183
w, h = Figure.wrap(self, availWidth, availHeight)
184
captionHeight = h - self.figureHeight
185
if self.scaleFactor is None:
186
#scale factor None means auto
187
self._scaleFactor = min(availWidth/self.width,(availHeight-captionHeight)/self.figureHeight)
188
else: #they provided a factor
189
self._scaleFactor = self.scaleFactor
190
if self._scaleFactor<1 and self.shrinkToFit:
191
self.width = self.width * self._scaleFactor - 0.0001
192
self.figureHeight = self.figureHeight * self._scaleFactor
193
elif self._scaleFactor>1 and self.growToFit:
194
self.width = self.width*self._scaleFactor - 0.0001
195
self.figureHeight = self.figureHeight * self._scaleFactor
197
def wrap(self, availWidth, availHeight):
198
self._scale(availWidth,availHeight)
199
return Figure.wrap(self, availWidth, availHeight)
201
def split(self, availWidth, availHeight):
202
self._scale(availWidth,availHeight)
203
return Figure.split(self, availWidth, availHeight)
205
class ImageFigure(FlexFigure):
206
"""Image with a caption below it"""
207
def __init__(self, filename, caption, background=None):
208
assert os.path.isfile(filename), 'image file %s not found' % filename
209
from reportlab.lib.utils import ImageReader
210
w, h = ImageReader(filename).getSize()
211
self.filename = filename
212
FlexFigure.__init__(self, w, h, caption, background)
214
def drawFigure(self):
215
self.canv.drawInlineImage(self.filename,
216
0, 0,self.width, self.figureHeight)
218
class DrawingFigure(FlexFigure):
219
"""Drawing with a caption below it. Clunky, scaling fails."""
220
def __init__(self, modulename, classname, caption, baseDir=None, background=None):
221
module = recursiveImport(modulename, baseDir)
222
klass = getattr(module, classname)
223
self.drawing = klass()
224
FlexFigure.__init__(self,
231
def drawFigure(self):
232
self.canv.scale(self._scaleFactor, self._scaleFactor)
233
self.drawing.drawOn(self.canv, 0, 0)
237
from rlextra.pageCatcher.pageCatcher import restoreForms, storeForms, storeFormsInMemory, restoreFormsInMemory
242
####################################################################
244
# PageCatcher plugins
245
# These let you use our PageCatcher product to add figures
246
# to other documents easily.
247
####################################################################
248
class PageCatcherCachingMixIn:
249
"Helper functions to cache pages for figures"
251
def getFormName(self, pdfFileName, pageNo):
252
#naming scheme works within a directory only
253
dirname, filename = os.path.split(pdfFileName)
254
root, ext = os.path.splitext(filename)
255
return '%s_page%d' % (root, pageNo)
258
def needsProcessing(self, pdfFileName, pageNo):
259
"returns 1 if no forms or form is older"
260
formName = self.getFormName(pdfFileName, pageNo)
261
if os.path.exists(formName + '.frm'):
262
formModTime = os.stat(formName + '.frm')[8]
263
pdfModTime = os.stat(pdfFileName)[8]
264
return (pdfModTime > formModTime)
268
def processPDF(self, pdfFileName, pageNo):
269
formName = self.getFormName(pdfFileName, pageNo)
270
storeForms(pdfFileName, formName + '.frm',
271
prefix= formName + '_',
272
pagenumbers=[pageNo])
273
#print 'stored %s.frm' % formName
274
return formName + '.frm'
276
class cachePageCatcherFigureNonA4(FlexFigure, PageCatcherCachingMixIn):
277
"""PageCatcher page with a caption below it. Size to be supplied."""
278
# This should merge with PageFigure into one class that reuses
279
# form information to determine the page orientation...
280
def __init__(self, filename, pageNo, caption, width, height, background=None):
281
self.dirname, self.filename = os.path.split(filename)
282
if self.dirname == '':
283
self.dirname = os.curdir
285
self.formName = self.getFormName(self.filename, self.pageNo) + '_' + str(pageNo)
286
FlexFigure.__init__(self, width, height, caption, background)
288
def drawFigure(self):
289
self.canv.saveState()
290
if not self.canv.hasForm(self.formName):
291
restorePath = self.dirname + os.sep + self.filename
292
#does the form file exist? if not, generate it.
293
formFileName = self.getFormName(restorePath, self.pageNo) + '.frm'
294
if self.needsProcessing(restorePath, self.pageNo):
295
#print 'preprocessing PDF %s page %s' % (restorePath, self.pageNo)
296
self.processPDF(restorePath, self.pageNo)
297
names = restoreForms(formFileName, self.canv)
298
self.canv.scale(self._scaleFactor, self._scaleFactor)
299
self.canv.doForm(self.formName)
300
self.canv.restoreState()
302
class cachePageCatcherFigure(cachePageCatcherFigureNonA4):
303
"""PageCatcher page with a caption below it. Presumes A4, Portrait.
304
This needs our commercial PageCatcher product, or you'll get a blank."""
305
def __init__(self, filename, pageNo, caption, width=595, height=842, background=None):
306
cachePageCatcherFigureNonA4.__init__(self, filename, pageNo, caption, width, height, background=background)
308
class PageCatcherFigureNonA4(FlexFigure):
309
"""PageCatcher page with a caption below it. Size to be supplied."""
310
# This should merge with PageFigure into one class that reuses
311
# form information to determine the page orientation...
313
def __init__(self, filename, pageNo, caption, width, height, background=None, caching=None):
314
fn = self.filename = filename
316
fn = fn.replace(os.sep,'_').replace('/','_').replace('\\','_').replace('-','_').replace(':','_')
317
self.prefix = fn.replace('.','_')+'_'+str(pageNo)+'_'
318
self.formName = self.prefix + str(pageNo)
319
self.caching = caching
320
FlexFigure.__init__(self, width, height, caption, background)
322
def drawFigure(self):
323
if not self.canv.hasForm(self.formName):
324
if self.filename in self._cache:
325
f,data = self._cache[self.filename]
327
f = open(self.filename,'rb')
330
f, data = storeFormsInMemory(pdf, pagenumbers=[self.pageNo], prefix=self.prefix)
331
if self.caching=='memory':
332
self._cache[self.filename] = f, data
333
f = restoreFormsInMemory(data, self.canv)
334
self.canv.saveState()
335
self.canv.scale(self._scaleFactor, self._scaleFactor)
336
self.canv.doForm(self.formName)
337
self.canv.restoreState()
339
class PageCatcherFigure(PageCatcherFigureNonA4):
340
"""PageCatcher page with a caption below it. Presumes A4, Portrait.
341
This needs our commercial PageCatcher product, or you'll get a blank."""
342
def __init__(self, filename, pageNo, caption, width=595, height=842, background=None, caching=None):
343
PageCatcherFigureNonA4.__init__(self, filename, pageNo, caption, width, height, background=background, caching=caching)
348
4*inch, # y at bottom
351
showBoundary = 1 # helps us see what's going on
353
bodyStyle = ParagraphStyle('Body', fontName='Times-Roman', fontSize=24, leading=28, spaceBefore=6)
354
para1 = Paragraph('Spam spam spam spam. ' * 5, bodyStyle)
355
para2 = Paragraph('Eggs eggs eggs. ' * 5, bodyStyle)
356
mydata = [para1, para2]
358
#this does the packing and drawing. The frame will consume
359
#items from the front of the list as it prints them
360
frame.addFromList(mydata,canvas)
363
c = Canvas('figures.pdf')
364
f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1)
365
v = PlatPropFigure1()
366
v.captionTextColor = toColor('blue')
367
v.captionBackColor = toColor('lightyellow')
371
if __name__ == '__main__':