author | rgbecker |
Thu, 12 Feb 2009 18:01:35 +0000 | |
changeset 3108 | 1e67b49dbf22 |
parent 3075 | 5175c093a0dd |
child 3129 | 171184fe71d2 |
permissions | -rw-r--r-- |
2963 | 1 |
#Copyright ReportLab Europe Ltd. 2000-2004 |
2 |
#see license.txt for license details |
|
3 |
__version__=''' $Id$ ''' |
|
4 |
||
5 |
#tests and documents Page Layout API |
|
6 |
__doc__="""This is not obvious so here's a brief explanation. This module is both |
|
7 |
the test script and user guide for layout. Each page has two frames on it: |
|
8 |
one for commentary, and one for demonstration objects which may be drawn in |
|
9 |
various esoteric ways. The two functions getCommentary() and getExamples() |
|
10 |
return the 'story' for each. The run() function gets the stories, then |
|
11 |
builds a special "document model" in which the frames are added to each page |
|
12 |
and drawn into. |
|
13 |
""" |
|
2987 | 14 |
from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, outputfile, printLocation |
2984 | 15 |
setOutDir(__name__) |
2963 | 16 |
import string, copy, sys, os |
17 |
from reportlab.pdfgen import canvas |
|
18 |
from reportlab import platypus |
|
19 |
from reportlab.platypus import BaseDocTemplate, PageTemplate, Flowable, FrameBreak |
|
20 |
from reportlab.platypus import Paragraph, Preformatted |
|
21 |
from reportlab.lib.units import inch, cm |
|
22 |
from reportlab.lib.styles import PropertySet, getSampleStyleSheet, ParagraphStyle |
|
23 |
from reportlab.lib import colors |
|
24 |
from reportlab.rl_config import defaultPageSize |
|
25 |
from reportlab.lib.utils import haveImages, _RL_DIR, rl_isfile, open_for_read, fileName2Utf8 |
|
2966 | 26 |
import unittest |
2987 | 27 |
from reportlab.lib.testutils import testsFolder |
2963 | 28 |
if haveImages: |
2966 | 29 |
_GIF = os.path.join(testsFolder,'pythonpowered.gif') |
2963 | 30 |
if not rl_isfile(_GIF): _GIF = None |
31 |
else: |
|
32 |
_GIF = None |
|
33 |
if _GIF: _GIFUTF8=fileName2Utf8(_GIF) |
|
34 |
||
2966 | 35 |
_JPG = os.path.join(testsFolder,'..','docs','images','lj8100.jpg') |
2963 | 36 |
if not rl_isfile(_JPG): _JPG = None |
37 |
||
38 |
def getFurl(fn): |
|
39 |
furl = fn.replace(os.sep,'/') |
|
40 |
if sys.platform=='win32' and furl[1]==':': furl = furl[0]+'|'+furl[2:] |
|
41 |
if furl[0]!='/': furl = '/'+furl |
|
42 |
return 'file://'+furl |
|
43 |
||
44 |
PAGE_HEIGHT = defaultPageSize[1] |
|
45 |
||
46 |
################################################################# |
|
47 |
# |
|
48 |
# first some drawing utilities |
|
49 |
# |
|
50 |
# |
|
51 |
################################################################ |
|
52 |
||
53 |
BASEFONT = ('Times-Roman', 10) |
|
54 |
||
55 |
def framePage(canvas,doc): |
|
56 |
#canvas.drawImage("snkanim.gif", 36, 36) |
|
57 |
canvas.saveState() |
|
58 |
canvas.setStrokeColorRGB(1,0,0) |
|
59 |
canvas.setLineWidth(5) |
|
60 |
canvas.line(66,72,66,PAGE_HEIGHT-72) |
|
61 |
||
62 |
canvas.setFont('Times-Italic',12) |
|
63 |
canvas.drawRightString(523, PAGE_HEIGHT - 56, "Platypus User Guide and Test Script") |
|
64 |
||
65 |
canvas.setFont('Times-Roman',12) |
|
66 |
canvas.drawString(4 * inch, 0.75 * inch, |
|
67 |
"Page %d" % canvas.getPageNumber()) |
|
68 |
canvas.restoreState() |
|
69 |
||
70 |
def getParagraphs(textBlock): |
|
71 |
"""Within the script, it is useful to whack out a page in triple |
|
72 |
quotes containing separate paragraphs. This breaks one into its |
|
73 |
constituent paragraphs, using blank lines as the delimiter.""" |
|
74 |
lines = string.split(textBlock, '\n') |
|
75 |
paras = [] |
|
76 |
currentPara = [] |
|
77 |
for line in lines: |
|
78 |
if len(string.strip(line)) == 0: |
|
79 |
#blank, add it |
|
80 |
if currentPara <> []: |
|
81 |
paras.append(string.join(currentPara, '\n')) |
|
82 |
currentPara = [] |
|
83 |
else: |
|
84 |
currentPara.append(line) |
|
85 |
#...and the last one |
|
86 |
if currentPara <> []: |
|
87 |
paras.append(string.join(currentPara, '\n')) |
|
88 |
||
89 |
return paras |
|
90 |
||
91 |
def getCommentary(): |
|
92 |
"""Returns the story for the commentary - all the paragraphs.""" |
|
93 |
||
94 |
styleSheet = getSampleStyleSheet() |
|
95 |
||
96 |
story = [] |
|
97 |
story.append(Paragraph(""" |
|
98 |
PLATYPUS User Guide and Test Script |
|
99 |
""", styleSheet['Heading1'])) |
|
100 |
||
101 |
||
102 |
spam = """ |
|
103 |
Welcome to PLATYPUS! |
|
104 |
||
105 |
Platypus stands for "Page Layout and Typography Using Scripts". It is a high |
|
106 |
level page layout library which lets you programmatically create complex |
|
107 |
documents with a minimum of effort. |
|
108 |
||
109 |
This document is both the user guide & the output of the test script. |
|
110 |
In other words, a script used platypus to create the document you are now |
|
111 |
reading, and the fact that you are reading it proves that it works. Or |
|
112 |
rather, that it worked for this script anyway. It is a first release! |
|
113 |
||
114 |
Platypus is built 'on top of' PDFgen, the Python library for creating PDF |
|
115 |
documents. To learn about PDFgen, read the document testpdfgen.pdf. |
|
116 |
||
117 |
""" |
|
118 |
||
119 |
for text in getParagraphs(spam): |
|
120 |
story.append(Paragraph(text, styleSheet['BodyText'])) |
|
121 |
||
122 |
story.append(Paragraph(""" |
|
123 |
What concepts does PLATYPUS deal with? |
|
124 |
""", styleSheet['Heading2'])) |
|
125 |
story.append(Paragraph(""" |
|
126 |
The central concepts in PLATYPUS are Flowable Objects, Frames, Flow |
|
127 |
Management, Styles and Style Sheets, Paragraphs and Tables. This is |
|
128 |
best explained in contrast to PDFgen, the layer underneath PLATYPUS. |
|
129 |
PDFgen is a graphics library, and has primitive commans to draw lines |
|
130 |
and strings. There is nothing in it to manage the flow of text down |
|
131 |
the page. PLATYPUS works at the conceptual level fo a desktop publishing |
|
132 |
package; you can write programs which deal intelligently with graphic |
|
133 |
objects and fit them onto the page. |
|
134 |
""", styleSheet['BodyText'])) |
|
135 |
||
136 |
story.append(Paragraph(""" |
|
137 |
How is this document organized? |
|
138 |
""", styleSheet['Heading2'])) |
|
139 |
||
140 |
story.append(Paragraph(""" |
|
141 |
Since this is a test script, we'll just note how it is organized. |
|
142 |
the top of each page contains commentary. The bottom half contains |
|
143 |
example drawings and graphic elements to whicht he commentary will |
|
144 |
relate. Down below, you can see the outline of a text frame, and |
|
145 |
various bits and pieces within it. We'll explain how they work |
|
146 |
on the next page. |
|
147 |
""", styleSheet['BodyText'])) |
|
148 |
||
149 |
story.append(FrameBreak()) |
|
150 |
####################################################################### |
|
151 |
# Commentary Page 2 |
|
152 |
####################################################################### |
|
153 |
||
154 |
story.append(Paragraph(""" |
|
155 |
Flowable Objects |
|
156 |
""", styleSheet['Heading2'])) |
|
157 |
spam = """ |
|
158 |
The first and most fundamental concept is that of a 'Flowable Object'. |
|
159 |
In PDFgen, you draw stuff by calling methods of the canvas to set up |
|
160 |
the colors, fonts and line styles, and draw the graphics primitives. |
|
161 |
If you set the pen color to blue, everything you draw after will be |
|
162 |
blue until you change it again. And you have to handle all of the X-Y |
|
163 |
coordinates yourself. |
|
164 |
||
165 |
A 'Flowable object' is exactly what it says. It knows how to draw itself |
|
166 |
on the canvas, and the way it does so is totally independent of what |
|
167 |
you drew before or after. Furthermore, it draws itself at the location |
|
168 |
on the page you specify. |
|
169 |
||
170 |
The most fundamental Flowable Objects in most documents are likely to be |
|
171 |
paragraphs, tables, diagrams/charts and images - but there is no |
|
172 |
restriction. You can write your own easily, and I hope that people |
|
173 |
will start to contribute them. PINGO users - we provide a "PINGO flowable" object to let |
|
174 |
you insert platform-independent graphics into the flow of a document. |
|
175 |
||
176 |
When you write a flowable object, you inherit from Flowable and |
|
177 |
must implement two methods. object.wrap(availWidth, availHeight) will be called by other parts of |
|
178 |
the system, and tells you how much space you have. You should return |
|
179 |
how much space you are going to use. For a fixed-size object, this |
|
180 |
is trivial, but it is critical - PLATYPUS needs to figure out if things |
|
181 |
will fit on the page before drawing them. For other objects such as paragraphs, |
|
182 |
the height is obviously determined by the available width. |
|
183 |
||
184 |
||
185 |
The second method is object.draw(). Here, you do whatever you want. |
|
186 |
The Flowable base class sets things up so that you have an origin of |
|
187 |
(0,0) for your drawing, and everything will fit nicely if you got the |
|
188 |
height and width right. It also saves and restores the graphics state |
|
189 |
around your calls, so you don;t have to reset all the properties you |
|
190 |
changed. |
|
191 |
||
192 |
Programs which actually draw a Flowable don't |
|
193 |
call draw() this directly - they call object.drawOn(canvas, x, y). |
|
194 |
So you can write code in your own coordinate system, and things |
|
195 |
can be drawn anywhere on the page (possibly even scaled or rotated). |
|
196 |
""" |
|
197 |
for text in getParagraphs(spam): |
|
198 |
story.append(Paragraph(text, styleSheet['BodyText'])) |
|
199 |
||
200 |
story.append(FrameBreak()) |
|
201 |
####################################################################### |
|
202 |
# Commentary Page 3 |
|
203 |
####################################################################### |
|
204 |
||
205 |
story.append(Paragraph(""" |
|
206 |
Available Flowable Objects |
|
207 |
""", styleSheet['Heading2'])) |
|
208 |
||
209 |
story.append(Paragraph(""" |
|
210 |
Platypus comes with a basic set of flowable objects. Here we list their |
|
211 |
class names and tell you what they do: |
|
212 |
""", styleSheet['BodyText'])) |
|
213 |
#we can use the bullet feature to do a definition list |
|
214 |
story.append(Paragraph(""" |
|
3075 | 215 |
<para color=green bcolor=red bg=pink>This is a <font bgcolor=yellow color=red>contrived</font> object to give an example of a Flowable - |
2963 | 216 |
just a fixed-size box with an X through it and a centred string.</para>""", |
217 |
styleSheet['Definition'], |
|
218 |
bulletText='XBox ' #hack - spot the extra space after |
|
219 |
)) |
|
220 |
||
221 |
story.append(Paragraph(""" |
|
222 |
This is the basic unit of a document. Paragraphs can be finely |
|
223 |
tuned and offer a host of properties through their associated |
|
224 |
ParagraphStyle.""", |
|
225 |
styleSheet['Definition'], |
|
226 |
bulletText='Paragraph ' #hack - spot the extra space after |
|
227 |
)) |
|
228 |
||
229 |
story.append(Paragraph(""" |
|
230 |
This is used for printing code and other preformatted text. |
|
231 |
There is no wrapping, and line breaks are taken where they occur. |
|
232 |
Many paragraph style properties do not apply. You may supply |
|
233 |
an optional 'dedent' parameter to trim a number of characters |
|
234 |
off the front of each line.""", |
|
235 |
styleSheet['Definition'], |
|
236 |
bulletText='Preformatted ' #hack - spot the extra space after |
|
237 |
)) |
|
238 |
story.append(Paragraph(""" |
|
239 |
This is a straight wrapper around an external image file. By default |
|
240 |
the image will be drawn at a scale of one pixel equals one point, and |
|
241 |
centred in the frame. You may supply an optional width and height.""", |
|
242 |
styleSheet['Definition'], |
|
243 |
bulletText='Image ' #hack - spot the extra space after |
|
244 |
)) |
|
245 |
||
246 |
story.append(Paragraph(""" |
|
247 |
This is a table drawing class; it is intended to be simpler |
|
248 |
than a full HTML table model yet be able to draw attractive output, |
|
249 |
and behave intelligently when the numbers of rows and columns vary. |
|
250 |
Still need to add the cell properties (shading, alignment, font etc.)""", |
|
251 |
styleSheet['Definition'], |
|
252 |
bulletText='Table ' #hack - spot the extra space after |
|
253 |
)) |
|
254 |
||
255 |
story.append(Paragraph(""" |
|
256 |
This is a 'null object' which merely takes up space on the page. |
|
257 |
Use it when you want some extra padding betweene elements.""", |
|
258 |
styleSheet['Definition'], |
|
259 |
bulletText='Spacer ' #hack - spot the extra space after |
|
260 |
)) |
|
261 |
||
262 |
story.append(Paragraph(""" |
|
263 |
A FrameBreak causes the document to call its handle_frameEnd method.""", |
|
264 |
styleSheet['Definition'], |
|
265 |
bulletText='FrameBreak ' #hack - spot the extra space after |
|
266 |
)) |
|
267 |
||
268 |
story.append(Paragraph(""" |
|
269 |
This is in progress, but a macro is basically a chunk of Python code to |
|
270 |
be evaluated when it is drawn. It could do lots of neat things.""", |
|
271 |
styleSheet['Definition'], |
|
272 |
bulletText='Macro ' #hack - spot the extra space after |
|
273 |
)) |
|
274 |
||
275 |
story.append(FrameBreak()) |
|
276 |
||
277 |
story.append(Paragraph( |
|
278 |
"The next example uses a custom font", |
|
279 |
styleSheet['Italic'])) |
|
280 |
def code(txt,story=story,styleSheet=styleSheet): |
|
281 |
story.append(Preformatted(txt,styleSheet['Code'])) |
|
282 |
code('''import reportlab.rl_config |
|
283 |
reportlab.rl_config.warnOnMissingFontGlyphs = 0 |
|
284 |
||
285 |
from reportlab.pdfbase import pdfmetrics |
|
2966 | 286 |
fontDir = os.path.join(_RL_DIR,'fonts') |
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
287 |
face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'DarkGardenMK.afm'), |
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
288 |
os.path.join(fontDir,'DarkGardenMK.pfb')) |
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
289 |
faceName = face.name # should be 'DarkGardenMK' |
2963 | 290 |
pdfmetrics.registerTypeFace(face) |
291 |
font = pdfmetrics.Font(faceName, faceName, 'WinAnsiEncoding') |
|
292 |
pdfmetrics.registerFont(font) |
|
293 |
||
294 |
||
295 |
# put it inside a paragraph. |
|
296 |
story.append(Paragraph( |
|
297 |
"""This is an ordinary paragraph, which happens to contain |
|
298 |
text in an embedded font: |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
299 |
<font name="DarkGardenMK">DarkGardenMK</font>. |
2963 | 300 |
Now for the real challenge...""", styleSheet['Normal'])) |
301 |
||
302 |
||
303 |
styRobot = ParagraphStyle('Robot', styleSheet['Normal']) |
|
304 |
styRobot.fontSize = 16 |
|
305 |
styRobot.leading = 20 |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
306 |
styRobot.fontName = 'DarkGardenMK' |
2963 | 307 |
|
308 |
story.append(Paragraph( |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
309 |
"This whole paragraph is 16-point DarkGardenMK.", |
2963 | 310 |
styRobot))''') |
311 |
||
312 |
story.append(FrameBreak()) |
|
313 |
if _GIF: |
|
314 |
story.append(Paragraph("""We can use images via the file name""", styleSheet['BodyText'])) |
|
315 |
code(''' story.append(platypus.Image('%s'))'''%_GIFUTF8) |
|
316 |
code(''' story.append(platypus.Image(fileName2Utf8('%s')))''' % _GIFUTF8) |
|
317 |
story.append(Paragraph("""They can also be used with a file URI or from an open python file!""", styleSheet['BodyText'])) |
|
318 |
code(''' story.append(platypus.Image('%s'))'''% getFurl(_GIFUTF8)) |
|
319 |
code(''' story.append(platypus.Image(open_for_read('%s','b')))''' % _GIFUTF8) |
|
320 |
story.append(FrameBreak()) |
|
321 |
story.append(Paragraph("""Images can even be obtained from the internet.""", styleSheet['BodyText'])) |
|
322 |
code(''' img = platypus.Image('http://www.reportlab.com/rsrc/encryption.gif') |
|
323 |
story.append(img)''') |
|
324 |
story.append(FrameBreak()) |
|
325 |
||
326 |
if _JPG: |
|
327 |
story.append(Paragraph("""JPEGs are a native PDF image format. They should be available even if PIL cannot be used.""", styleSheet['BodyText'])) |
|
328 |
story.append(FrameBreak()) |
|
329 |
return story |
|
330 |
||
331 |
def getExamples(): |
|
332 |
"""Returns all the example flowable objects""" |
|
333 |
styleSheet = getSampleStyleSheet() |
|
334 |
||
335 |
story = [] |
|
336 |
||
337 |
#make a style with indents and spacing |
|
338 |
sty = ParagraphStyle('obvious', None) |
|
339 |
sty.leftIndent = 18 |
|
340 |
sty.rightIndent = 18 |
|
341 |
sty.firstLineIndent = 18 |
|
342 |
sty.spaceBefore = 6 |
|
343 |
sty.spaceAfter = 6 |
|
344 |
story.append(Paragraph("""Now for some demo stuff - we need some on this page, |
|
345 |
even before we explain the concepts fully""", styleSheet['BodyText'])) |
|
346 |
p = Paragraph(""" |
|
347 |
Platypus is all about fitting objects into frames on the page. You |
|
348 |
are looking at a fairly simple Platypus paragraph in Debug mode. |
|
349 |
It has some gridlines drawn around it to show the left and right indents, |
|
350 |
and the space before and after, all of which are attributes set in |
|
351 |
the style sheet. To be specific, this paragraph has left and |
|
352 |
right indents of 18 points, a first line indent of 36 points, |
|
353 |
and 6 points of space before and after itself. A paragraph |
|
354 |
object fills the width of the enclosing frame, as you would expect.""", sty) |
|
355 |
||
356 |
p.debug = 1 #show me the borders |
|
357 |
story.append(p) |
|
358 |
||
359 |
story.append(Paragraph("""Same but with justification 1.5 extra leading and green text.""", styleSheet['BodyText'])) |
|
360 |
p = Paragraph(""" |
|
361 |
<para align=justify leading=+1.5 fg=green><font color=red>Platypus</font> is all about fitting objects into frames on the page. You |
|
362 |
are looking at a fairly simple Platypus paragraph in Debug mode. |
|
363 |
It has some gridlines drawn around it to show the left and right indents, |
|
364 |
and the space before and after, all of which are attributes set in |
|
365 |
the style sheet. To be specific, this paragraph has left and |
|
366 |
right indents of 18 points, a first line indent of 36 points, |
|
367 |
and 6 points of space before and after itself. A paragraph |
|
368 |
object fills the width of the enclosing frame, as you would expect.</para>""", sty) |
|
369 |
||
370 |
p.debug = 1 #show me the borders |
|
371 |
story.append(p) |
|
372 |
||
373 |
story.append(platypus.XBox(4*inch, 0.75*inch, |
|
374 |
'This is a box with a fixed size')) |
|
375 |
||
376 |
story.append(Paragraph(""" |
|
377 |
All of this is being drawn within a text frame which was defined |
|
378 |
on the page. This frame is in 'debug' mode so you can see the border, |
|
379 |
and also see the margins which it reserves. A frame does not have |
|
380 |
to have margins, but they have been set to 6 points each to create |
|
381 |
a little space around the contents. |
|
382 |
""", styleSheet['BodyText'])) |
|
383 |
||
384 |
story.append(FrameBreak()) |
|
385 |
||
386 |
####################################################################### |
|
387 |
# Examples Page 2 |
|
388 |
####################################################################### |
|
389 |
||
390 |
story.append(Paragraph(""" |
|
391 |
Here's the base class for Flowable... |
|
392 |
""", styleSheet['Italic'])) |
|
393 |
||
394 |
code = '''class Flowable: |
|
395 |
"""Abstract base class for things to be drawn. Key concepts: |
|
396 |
1. It knows its size |
|
397 |
2. It draws in its own coordinate system (this requires the |
|
398 |
base API to provide a translate() function. |
|
399 |
""" |
|
400 |
def __init__(self): |
|
401 |
self.width = 0 |
|
402 |
self.height = 0 |
|
403 |
self.wrapped = 0 |
|
404 |
||
405 |
def drawOn(self, canvas, x, y): |
|
406 |
"Tell it to draw itself on the canvas. Do not override" |
|
407 |
self.canv = canvas |
|
408 |
self.canv.saveState() |
|
409 |
self.canv.translate(x, y) |
|
410 |
||
411 |
self.draw() #this is the bit you overload |
|
412 |
||
413 |
self.canv.restoreState() |
|
414 |
del self.canv |
|
415 |
||
416 |
def wrap(self, availWidth, availHeight): |
|
417 |
"""This will be called by the enclosing frame before objects |
|
418 |
are asked their size, drawn or whatever. It returns the |
|
419 |
size actually used.""" |
|
420 |
return (self.width, self.height) |
|
421 |
''' |
|
422 |
||
423 |
story.append(Preformatted(code, styleSheet['Code'], dedent=4)) |
|
424 |
story.append(FrameBreak()) |
|
425 |
####################################################################### |
|
426 |
# Examples Page 3 |
|
427 |
####################################################################### |
|
428 |
||
429 |
story.append(Paragraph( |
|
430 |
"Here are some examples of the remaining objects above.", |
|
431 |
styleSheet['Italic'])) |
|
432 |
||
433 |
story.append(Paragraph("This is a bullet point", styleSheet['Bullet'], bulletText='O')) |
|
434 |
story.append(Paragraph("Another bullet point", styleSheet['Bullet'], bulletText='O')) |
|
435 |
||
436 |
||
437 |
story.append(Paragraph("""Here is a Table, which takes all kinds of formatting options...""", |
|
438 |
styleSheet['Italic'])) |
|
439 |
story.append(platypus.Spacer(0, 12)) |
|
440 |
||
441 |
g = platypus.Table( |
|
442 |
(('','North','South','East','West'), |
|
443 |
('Quarter 1',100,200,300,400), |
|
444 |
('Quarter 2',100,200,300,400), |
|
445 |
('Total',200,400,600,800)), |
|
446 |
(72,36,36,36,36), |
|
447 |
(24, 16,16,18) |
|
448 |
) |
|
449 |
style = platypus.TableStyle([('ALIGN', (1,1), (-1,-1), 'RIGHT'), |
|
450 |
('ALIGN', (0,0), (-1,0), 'CENTRE'), |
|
451 |
('GRID', (0,0), (-1,-1), 0.25, colors.black), |
|
452 |
('LINEBELOW', (0,0), (-1,0), 2, colors.black), |
|
453 |
('LINEBELOW',(1,-1), (-1, -1), 2, (0.5, 0.5, 0.5)), |
|
454 |
('TEXTCOLOR', (0,1), (0,-1), colors.black), |
|
455 |
('BACKGROUND', (0,0), (-1,0), (0,0.7,0.7)) |
|
456 |
]) |
|
457 |
g.setStyle(style) |
|
458 |
story.append(g) |
|
459 |
story.append(FrameBreak()) |
|
460 |
||
461 |
####################################################################### |
|
462 |
# Examples Page 4 - custom fonts |
|
463 |
####################################################################### |
|
464 |
# custom font with LettError-Robot font |
|
465 |
import reportlab.rl_config |
|
466 |
reportlab.rl_config.warnOnMissingFontGlyphs = 0 |
|
467 |
||
468 |
from reportlab.pdfbase import pdfmetrics |
|
2966 | 469 |
fontDir = os.path.join(_RL_DIR,'fonts') |
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
470 |
face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'DarkGardenMK.afm'),os.path.join(fontDir,'DarkGardenMK.pfb')) |
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
471 |
faceName = face.name # should be 'DarkGardenMK' |
2963 | 472 |
pdfmetrics.registerTypeFace(face) |
473 |
font = pdfmetrics.Font(faceName, faceName, 'WinAnsiEncoding') |
|
474 |
pdfmetrics.registerFont(font) |
|
475 |
||
476 |
||
477 |
# put it inside a paragraph. |
|
478 |
story.append(Paragraph( |
|
479 |
"""This is an ordinary paragraph, which happens to contain |
|
480 |
text in an embedded font: |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
481 |
<font name="DarkGardenMK">DarkGardenMK</font>. |
2963 | 482 |
Now for the real challenge...""", styleSheet['Normal'])) |
483 |
||
484 |
||
485 |
styRobot = ParagraphStyle('Robot', styleSheet['Normal']) |
|
486 |
styRobot.fontSize = 16 |
|
487 |
styRobot.leading = 20 |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
488 |
styRobot.fontName = 'DarkGardenMK' |
2963 | 489 |
|
490 |
story.append(Paragraph( |
|
2977
beca8d75f400
reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents:
2967
diff
changeset
|
491 |
"This whole paragraph is 16-point DarkGardenMK.", |
2963 | 492 |
styRobot)) |
493 |
story.append(FrameBreak()) |
|
494 |
||
495 |
if _GIF: |
|
496 |
story.append(Paragraph("Here is an Image flowable obtained from a string filename.",styleSheet['Italic'])) |
|
497 |
story.append(platypus.Image(_GIF)) |
|
498 |
story.append(Paragraph( "Here is an Image flowable obtained from a utf8 filename.", styleSheet['Italic'])) |
|
499 |
#story.append(platypus.Image(fileName2Utf8(_GIF))) |
|
500 |
story.append(Paragraph("Here is an Image flowable obtained from a string file url.",styleSheet['Italic'])) |
|
501 |
story.append(platypus.Image(getFurl(_GIF))) |
|
502 |
story.append(Paragraph("Here is an Image flowable obtained from an open file.",styleSheet['Italic'])) |
|
503 |
story.append(platypus.Image(open_for_read(_GIF,'b'))) |
|
504 |
story.append(FrameBreak()) |
|
505 |
try: |
|
506 |
img = platypus.Image('http://www.reportlab.com/rsrc/encryption.gif') |
|
507 |
story.append(Paragraph("Here is an Image flowable obtained from a string http url.",styleSheet['Italic'])) |
|
508 |
story.append(img) |
|
509 |
except: |
|
510 |
story.append(Paragraph("The image could not be obtained from a string http url.",styleSheet['Italic'])) |
|
511 |
story.append(FrameBreak()) |
|
512 |
||
513 |
if _JPG: |
|
514 |
img = platypus.Image(_JPG) |
|
515 |
story.append(Paragraph("Here is an JPEG Image flowable obtained from a filename.",styleSheet['Italic'])) |
|
516 |
story.append(img) |
|
517 |
story.append(Paragraph("Here is an JPEG Image flowable obtained from an open file.",styleSheet['Italic'])) |
|
518 |
img = platypus.Image(open_for_read(_JPG,'b')) |
|
519 |
story.append(img) |
|
520 |
story.append(FrameBreak()) |
|
521 |
||
522 |
||
523 |
return story |
|
524 |
||
525 |
class AndyTemplate(BaseDocTemplate): |
|
526 |
_invalidInitArgs = ('pageTemplates',) |
|
527 |
def __init__(self, filename, **kw): |
|
528 |
frame1 = platypus.Frame(inch, 5.6*inch, 6*inch, 5.2*inch,id='F1') |
|
529 |
frame2 = platypus.Frame(inch, inch, 6*inch, 4.5*inch, showBoundary=1,id='F2') |
|
530 |
self.allowSplitting = 0 |
|
531 |
apply(BaseDocTemplate.__init__,(self,filename),kw) |
|
532 |
self.addPageTemplates(PageTemplate('normal',[frame1,frame2],framePage)) |
|
533 |
||
534 |
def fillFrame(self,flowables): |
|
535 |
f = self.frame |
|
536 |
while len(flowables)>0 and f is self.frame: |
|
537 |
self.handle_flowable(flowables) |
|
538 |
||
539 |
def build(self, flowables1, flowables2): |
|
540 |
assert filter(lambda x: not isinstance(x,Flowable), flowables1)==[], "flowables1 argument error" |
|
541 |
assert filter(lambda x: not isinstance(x,Flowable), flowables2)==[], "flowables2 argument error" |
|
542 |
self._startBuild() |
|
543 |
while (len(flowables1) > 0 or len(flowables1) > 0): |
|
544 |
self.clean_hanging() |
|
545 |
self.fillFrame(flowables1) |
|
546 |
self.fillFrame(flowables2) |
|
547 |
||
548 |
self._endBuild() |
|
549 |
||
550 |
def showProgress(pageNo): |
|
551 |
print 'CALLBACK SAYS: page %d' % pageNo |
|
552 |
||
553 |
||
554 |
def run(): |
|
3108 | 555 |
doc = AndyTemplate(outputfile('test_platypus_general.pdf'),subject='test0') |
2963 | 556 |
#doc.setPageCallBack(showProgress) |
557 |
commentary = getCommentary() |
|
558 |
examples = getExamples() |
|
559 |
doc.build(commentary,examples) |
|
560 |
||
561 |
||
562 |
class PlatypusTestCase(unittest.TestCase): |
|
563 |
"Make documents with lots of Platypus features" |
|
564 |
||
565 |
def test0(self): |
|
566 |
"Make a platypus document" |
|
567 |
run() |
|
568 |
||
569 |
def test1(self): |
|
570 |
#test from Wietse Jacobs |
|
571 |
from reportlab.lib.styles import ParagraphStyle |
|
572 |
from reportlab.graphics.shapes import Drawing, Rect |
|
573 |
from reportlab.platypus import SimpleDocTemplate |
|
574 |
normal = ParagraphStyle(name='Normal', fontName='Helvetica', fontSize=8.5, leading=11) |
|
575 |
header = ParagraphStyle(name='Heading1', parent=normal, fontSize=14, leading=19, |
|
576 |
spaceAfter=6, keepWithNext=1) |
|
577 |
d = Drawing(400, 200) |
|
578 |
d.add(Rect(50, 50, 300, 100)) |
|
579 |
||
580 |
story = [Paragraph("The section header", header), d, |
|
581 |
] |
|
2966 | 582 |
doc = SimpleDocTemplate(outputfile('test_drawing_keepwithnext.pdf')) |
2963 | 583 |
doc.build(story) |
584 |
||
585 |
def makeSuite(): |
|
586 |
return makeSuiteForClasses(PlatypusTestCase) |
|
587 |
||
588 |
#noruntests |
|
589 |
if __name__ == "__main__": |
|
590 |
if '-debug' in sys.argv: |
|
591 |
run() |
|
592 |
else: |
|
593 |
unittest.TextTestRunner().run(makeSuite()) |
|
594 |
printLocation() |