ReportLab (PDF generation)
Hello World
Source code:
1 #copyright ReportLab Inc. 2000
2 #see license.txt for license details
3 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/pdfgen/test/test_hello.py?cvsroot=reportlab
4 #$Header: /cvsroot/reportlab/reportlab/test/test_hello.py,v 1.3 2002/07/24 19:56:38 andy_robinson Exp $
5 __version__=''' $Id'''
6 __doc__="""most basic test possible that makes a PDF.
7
8 Useful if you want to test that a really minimal PDF is healthy,
9 since the output is about the smallest thing we can make."""
10
11 from reportlab.test import unittest
12 from reportlab.test.utils import makeSuiteForClasses
13 from reportlab.pdfgen.canvas import Canvas
14
15 import sys
16
17 class HelloTestCase(unittest.TestCase):
18 "Simplest test that makes PDF"
19
20 def test(self):
21 c = Canvas('sys$output')
22 c.setFont('Helvetica-Bold', 36)
23 c.drawString(100,700, 'Hello World')
24 print 'Content-Type: application/pdf\n'
25 sys.stdout.flush()
26 c.save()
27 sys.stdout.flush()
28
29 def makeSuite():
30 return makeSuiteForClasses(HelloTestCase)
31
32
33 #noruntests
34 unittest.TextTestRunner().run(makeSuite())
Result:
General demonstration
Source code:
1 #copyright ReportLab Inc. 2000
2 #see license.txt for license details
3 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/pdfgen/test/testpdfgen.py?cvsroot=reportlab
4 #$Header: /cvsroot/reportlab/reportlab/test/test_pdfgen_general.py,v 1.19 2003/09/08 16:09:51 rgbecker Exp $
5 __version__=''' $Id: test_pdfgen_general.py,v 1.19 2003/09/08 16:09:51 rgbecker Exp $ '''
6 __doc__='testscript for reportlab.pdfgen'
7 #tests and documents new low-level canvas
8
9 import string
10
11 from reportlab.test import unittest
12 from reportlab.test.utils import makeSuiteForClasses
13
14 from reportlab.pdfgen import canvas # gmcm 2000/10/13, pdfgen now a package
15 from reportlab.lib.units import inch, cm
16 from reportlab.lib import colors
17 from reportlab.lib.utils import haveImages
18
19 import sys
20
21 #################################################################
22 #
23 # first some drawing utilities
24 #
25 #
26 ################################################################
27
28 BASEFONT = ('Times-Roman', 10)
29 def framePageForm(c):
30 c.beginForm("frame")
31 c.saveState()
32 # forms can't do non-constant operations
33 #canvas.setFont('Times-BoldItalic',20)
34 #canvas.drawString(inch, 10.5 * inch, title)
35
36 #c.setFont('Times-Roman',10)
37 #c.drawCentredString(4.135 * inch, 0.75 * inch,
38 # 'Page %d' % c.getPageNumber())
39
40 #draw a border
41 c.setFillColor(colors.ReportLabBlue)
42 c.rect(0.3*inch, inch, 0.5*inch, 10*inch, fill=1)
43 from reportlab.lib import corp
44 c.translate(0.8*inch, 9.6*inch)
45 c.rotate(90)
46 logo = corp.ReportLabLogo(width=1.3*inch, height=0.5*inch, powered_by=1)
47 c.setFillColorRGB(1,1,1)
48 c.setStrokeColorRGB(1,1,1)
49 logo.draw(c)
50 #c.setStrokeColorRGB(1,0,0)
51 #c.setLineWidth(5)
52 #c.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch)
53 #reset carefully afterwards
54 #canvas.setLineWidth(1)
55 #canvas.setStrokeColorRGB(0,0,0)\
56 c.restoreState()
57 c.endForm()
58
59 def framePage(canvas, title):
60 global closeit
61 titlelist.append(title)
62 #canvas._inPage0() # do we need this at all? would be good to eliminate it
63 canvas.saveState()
64 canvas.setFont('Times-BoldItalic',20)
65
66 canvas.drawString(inch, 10.5 * inch, title)
67 canvas.bookmarkHorizontalAbsolute(title, 10.8*inch)
68 #newsection(title)
69 canvas.addOutlineEntry(title+" section", title, level=0, closed=closeit)
70 closeit = not closeit # close every other one
71 canvas.setFont('Times-Roman',10)
72 canvas.drawCentredString(4.135 * inch, 0.75 * inch,
73 'Page %d' % canvas.getPageNumber())
74 canvas.restoreState()
75 canvas.doForm("frame")
76
77
78 def makesubsection(canvas, title, horizontal):
79 canvas.bookmarkHorizontalAbsolute(title, horizontal)
80 #newsubsection(title)
81 canvas.addOutlineEntry(title+" subsection", title, level=1)
82
83
84 # outline helpers
85 #outlinenametree = []
86 #def newsection(name):
87 # outlinenametree.append(name)
88
89
90 #def newsubsection(name):
91 # from types import TupleType
92 # thissection = outlinenametree[-1]
93 # if type(thissection) is not TupleType:
94 # subsectionlist = []
95 # thissection = outlinenametree[-1] = (thissection, subsectionlist)
96 # else:
97 # (sectionname, subsectionlist) = thissection
98 # subsectionlist.append(name)
99
100
101 class DocBlock:
102 """A DocBlock has a chunk of commentary and a chunk of code.
103 It prints the code and commentary, then executes the code,
104 which is presumed to draw in a region reserved for it.
105 """
106 def __init__(self):
107 self.comment1 = "A doc block"
108 self.code = "canvas.setTextOrigin(cm, cm)\ncanvas.textOut('Hello World')"
109 self.comment2 = "That was a doc block"
110 self.drawHeight = 0
111
112 def _getHeight(self):
113 "splits into lines"
114 self.comment1lines = string.split(self.comment1, '\n')
115 self.codelines = string.split(self.code, '\n')
116 self.comment2lines = string.split(self.comment2, '\n')
117 textheight = (len(self.comment1lines) +
118 len(self.code) +
119 len(self.comment2lines) +
120 18)
121 return max(textheight, self.drawHeight)
122
123 def draw(self, canvas, x, y):
124 #specifies top left corner
125 canvas.saveState()
126 height = self._getHeight()
127 canvas.rect(x, y-height, 6*inch, height)
128 #first draw the text
129 canvas.setTextOrigin(x + 3 * inch, y - 12)
130 canvas.setFont('Times-Roman',10)
131 canvas.textLines(self.comment1)
132 drawCode(canvas, self.code)
133 canvas.textLines(self.comment2)
134
135 #now a box for the drawing, slightly within rect
136 canvas.rect(x + 9, y - height + 9, 198, height - 18)
137 #boundary:
138 self.namespace = {'canvas':canvas,'cm': cm,'inch':inch}
139 canvas.translate(x+9, y - height + 9)
140 codeObj = compile(self.code, '<sample>','exec')
141 exec codeObj in self.namespace
142
143 canvas.restoreState()
144
145
146 def drawAxes(canvas, label):
147 """draws a couple of little rulers showing the coords -
148 uses points as units so you get an imperial ruler
149 one inch on each side"""
150 #y axis
151 canvas.line(0,0,0,72)
152 for y in range(9):
153 tenths = (y+1) * 7.2
154 canvas.line(-6,tenths,0,tenths)
155 canvas.line(-6, 66, 0, 72) #arrow...
156 canvas.line(6, 66, 0, 72) #arrow...
157
158 canvas.line(0,0,72,0)
159 for x in range(9):
160 tenths = (x+1) * 7.2
161 canvas.line(tenths,-6,tenths, 0)
162 canvas.line(66, -6, 72, 0) #arrow...
163 canvas.line(66, +6, 72, 0) #arrow...
164
165 canvas.drawString(18, 30, label)
166
167
168 def drawCrossHairs(canvas, x, y):
169 """just a marker for checking text metrics - blue for fun"""
170
171 canvas.saveState()
172 canvas.setStrokeColorRGB(0,1,0)
173 canvas.line(x-6,y,x+6,y)
174 canvas.line(x,y-6,x,y+6)
175 canvas.restoreState()
176
177
178 def drawCode(canvas, code):
179 """Draws a block of text at current point, indented and in Courier"""
180 canvas.addLiteral('36 0 Td')
181 canvas.setFillColor(colors.blue)
182 canvas.setFont('Courier',10)
183
184 t = canvas.beginText()
185 t.textLines(code)
186 c.drawText(t)
187
188 canvas.setFillColor(colors.black)
189 canvas.addLiteral('-36 0 Td')
190 canvas.setFont('Times-Roman',10)
191
192
193 def makeDocument(filename, pageCallBack=None):
194 #the extra arg is a hack added later, so other
195 #tests can get hold of the canvas just before it is
196 #saved
197 global titlelist, closeit
198 titlelist = []
199 closeit = 0
200
201 c = canvas.Canvas(filename)
202 c.setPageCompression(0)
203 c.setPageCallBack(pageCallBack)
204 framePageForm(c) # define the frame form
205 c.showOutline()
206
207 framePage(c, 'PDFgen graphics API test script')
208 makesubsection(c, "PDFgen", 10*inch)
209
210 t = c.beginText(inch, 10*inch)
211 t.setFont('Times-Roman', 10)
212 drawCrossHairs(c, t.getX(),t.getY())
213 t.textLines("""
214 The ReportLab library permits you to create PDF documents directly from
215 your Python code. The "pdfgen" subpackage is the lowest level exposed
216 to the user and lets you directly position test and graphics on the
217 page, with access to almost the full range of PDF features.
218 The API is intended to closely mirror the PDF / Postscript imaging
219 model. There is an almost one to one correspondence between commands
220 and PDF operators. However, where PDF provides several ways to do a job,
221 we have generally only picked one.
222 The test script attempts to use all of the methods exposed by the Canvas
223 class, defined in reportlab/pdfgen/canvas.py
224 First, let's look at text output. There are some basic commands
225 to draw strings:
226 - canvas.setFont(fontname, fontsize [, leading])
227 - canvas.drawString(x, y, text)
228 - canvas.drawRightString(x, y, text)
229 - canvas.drawCentredString(x, y, text)
230
231 The coordinates are in points starting at the bottom left corner of the
232 page. When setting a font, the leading (i.e. inter-line spacing)
233 defaults to 1.2 * fontsize if the fontsize is not provided.
234
235 For more sophisticated operations, you can create a Text Object, defined
236 in reportlab/pdfgen/testobject.py. Text objects produce tighter PDF, run
237 faster and have many methods for precise control of spacing and position.
238 Basic usage goes as follows:
239 - tx = canvas.beginText(x, y)
240 - tx.textOut('Hello') # this moves the cursor to the right
241 - tx.textLine('Hello again') # prints a line and moves down
242 - y = tx.getY() # getX, getY and getCursor track position
243 - canvas.drawText(tx) # all gets drawn at the end
244
245 The green crosshairs below test whether the text cursor is working
246 properly. They should appear at the bottom left of each relevant
247 substring.
248 """)
249
250 t.setFillColorRGB(1,0,0)
251 t.setTextOrigin(inch, 4*inch)
252 drawCrossHairs(c, t.getX(),t.getY())
253 t.textOut('textOut moves across:')
254 drawCrossHairs(c, t.getX(),t.getY())
255 t.textOut('textOut moves across:')
256 drawCrossHairs(c, t.getX(),t.getY())
257 t.textOut('textOut moves across:')
258 drawCrossHairs(c, t.getX(),t.getY())
259 t.textLine('')
260 drawCrossHairs(c, t.getX(),t.getY())
261 t.textLine('textLine moves down')
262 drawCrossHairs(c, t.getX(),t.getY())
263 t.textLine('textLine moves down')
264 drawCrossHairs(c, t.getX(),t.getY())
265 t.textLine('textLine moves down')
266 drawCrossHairs(c, t.getX(),t.getY())
267
268 t.setTextOrigin(4*inch,3.25*inch)
269 drawCrossHairs(c, t.getX(),t.getY())
270 t.textLines('This is a multi-line\nstring with embedded newlines\ndrawn with textLines().\n')
271 drawCrossHairs(c, t.getX(),t.getY())
272 t.textLines(['This is a list of strings',
273 'drawn with textLines().'])
274 c.drawText(t)
275
276 t = c.beginText(2*inch,2*inch)
277 t.setFont('Times-Roman',10)
278 drawCrossHairs(c, t.getX(),t.getY())
279 t.textOut('Small text.')
280 drawCrossHairs(c, t.getX(),t.getY())
281 t.setFont('Courier',14)
282 t.textOut('Bigger fixed width text.')
283 drawCrossHairs(c, t.getX(),t.getY())
284 t.setFont('Times-Roman',10)
285 t.textOut('Small text again.')
286 drawCrossHairs(c, t.getX(),t.getY())
287 c.drawText(t)
288
289 #mark the cursor where it stopped
290 c.showPage()
291
292
293 ##############################################################
294 #
295 # page 2 - line styles
296 #
297 ###############################################################
298
299 #page 2 - lines and styles
300 framePage(c, 'Line Drawing Styles')
301
302
303
304 # three line ends, lines drawn the hard way
305 #firt make some vertical end markers
306 c.setDash(4,4)
307 c.setLineWidth(0)
308 c.line(inch,9.2*inch,inch, 7.8*inch)
309 c.line(3*inch,9.2*inch,3*inch, 7.8*inch)
310 c.setDash() #clears it
311
312 c.setLineWidth(5)
313 c.setLineCap(0)
314 p = c.beginPath()
315 p.moveTo(inch, 9*inch)
316 p.lineTo(3*inch, 9*inch)
317 c.drawPath(p)
318 c.drawString(4*inch, 9*inch, 'the default - butt caps project half a width')
319 makesubsection(c, "caps and joins", 8.5*inch)
320
321 c.setLineCap(1)
322 p = c.beginPath()
323 p.moveTo(inch, 8.5*inch)
324 p.lineTo(3*inch, 8.5*inch)
325 c.drawPath(p)
326 c.drawString(4*inch, 8.5*inch, 'round caps')
327
328 c.setLineCap(2)
329 p = c.beginPath()
330 p.moveTo(inch, 8*inch)
331 p.lineTo(3*inch, 8*inch)
332 c.drawPath(p)
333 c.drawString(4*inch, 8*inch, 'square caps')
334
335 c.setLineCap(0)
336
337 # three line joins
338 c.setLineJoin(0)
339 p = c.beginPath()
340 p.moveTo(inch, 7*inch)
341 p.lineTo(2*inch, 7*inch)
342 p.lineTo(inch, 6.7*inch)
343 c.drawPath(p)
344 c.drawString(4*inch, 6.8*inch, 'Default - mitered join')
345
346 c.setLineJoin(1)
347 p = c.beginPath()
348 p.moveTo(inch, 6.5*inch)
349 p.lineTo(2*inch, 6.5*inch)
350 p.lineTo(inch, 6.2*inch)
351 c.drawPath(p)
352 c.drawString(4*inch, 6.3*inch, 'round join')
353
354 c.setLineJoin(2)
355 p = c.beginPath()
356 p.moveTo(inch, 6*inch)
357 p.lineTo(2*inch, 6*inch)
358 p.lineTo(inch, 5.7*inch)
359 c.drawPath(p)
360 c.drawString(4*inch, 5.8*inch, 'bevel join')
361
362 c.setDash(6,6)
363 p = c.beginPath()
364 p.moveTo(inch, 5*inch)
365 p.lineTo(3*inch, 5*inch)
366 c.drawPath(p)
367 c.drawString(4*inch, 5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(0)')
368 makesubsection(c, "dash patterns", 5*inch)
369
370 c.setLineCap(1)
371 p = c.beginPath()
372 p.moveTo(inch, 4.5*inch)
373 p.lineTo(3*inch, 4.5*inch)
374 c.drawPath(p)
375 c.drawString(4*inch, 4.5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(1)')
376
377 c.setLineCap(0)
378 c.setDash([1,2,3,4,5,6],0)
379 p = c.beginPath()
380 p.moveTo(inch, 4.0*inch)
381 p.lineTo(3*inch, 4.0*inch)
382 c.drawPath(p)
383 c.drawString(4*inch, 4*inch, 'dash growing - setDash([1,2,3,4,5,6],0) setLineCap(0)')
384
385 c.setLineCap(1)
386 c.setLineJoin(1)
387 c.setDash(32,12)
388 p = c.beginPath()
389 p.moveTo(inch, 3.0*inch)
390 p.lineTo(2.5*inch, 3.0*inch)
391 p.lineTo(inch, 2*inch)
392 c.drawPath(p)
393 c.drawString(4*inch, 3*inch, 'dash pattern, join and cap style interacting - ')
394 c.drawString(4*inch, 3*inch - 12, 'round join & miter results in sausages')
395
396 c.showPage()
397
398
399 ##############################################################
400 #
401 # higher level shapes
402 #
403 ###############################################################
404 framePage(c, 'Shape Drawing Routines')
405
406 t = c.beginText(inch, 10*inch)
407 t.textLines("""
408 Rather than making your own paths, you have access to a range of shape routines.
409 These are built in pdfgen out of lines and bezier curves, but use the most compact
410 set of operators possible. We can add any new ones that are of general use at no
411 cost to performance.""")
412 t.textLine()
413
414 #line demo
415 makesubsection(c, "lines", 10*inch)
416 c.line(inch, 8*inch, 3*inch, 8*inch)
417 t.setTextOrigin(4*inch, 8*inch)
418 t.textLine('canvas.line(x1, y1, x2, y2)')
419
420 #bezier demo - show control points
421 makesubsection(c, "bezier curves", 7.5*inch)
422 (x1, y1, x2, y2, x3, y3, x4, y4) = (
423 inch, 6.5*inch,
424 1.2*inch, 7.5 * inch,
425 3*inch, 7.5 * inch,
426 3.5*inch, 6.75 * inch
427 )
428 c.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
429 c.setDash(3,3)
430 c.line(x1,y1,x2,y2)
431 c.line(x3,y3,x4,y4)
432 c.setDash()
433 t.setTextOrigin(4*inch, 7 * inch)
434 t.textLine('canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)')
435
436 #rectangle
437 makesubsection(c, "rectangles", 7*inch)
438 c.rect(inch, 5.25 * inch, 2 * inch, 0.75 * inch)
439 t.setTextOrigin(4*inch, 5.5 * inch)
440 t.textLine('canvas.rect(x, y, width, height) - x,y is lower left')
441
442 #wedge
443 makesubsection(c, "wedges", 5*inch)
444 c.wedge(inch, 5*inch, 3*inch, 4*inch, 0, 315)
445 t.setTextOrigin(4*inch, 4.5 * inch)
446 t.textLine('canvas.wedge(x1, y1, x2, y2, startDeg, extentDeg)')
447 t.textLine('Note that this is an elliptical arc, not just circular!')
448
449 #wedge the other way
450 c.wedge(inch, 4*inch, 3*inch, 3*inch, 0, -45)
451 t.setTextOrigin(4*inch, 3.5 * inch)
452 t.textLine('Use a negative extent to go clockwise')
453
454 #circle
455 makesubsection(c, "circles", 3.5*inch)
456 c.circle(1.5*inch, 2*inch, 0.5 * inch)
457 c.circle(3*inch, 2*inch, 0.5 * inch)
458 t.setTextOrigin(4*inch, 2 * inch)
459 t.textLine('canvas.circle(x, y, radius)')
460 c.drawText(t)
461
462 c.showPage()
463
464 ##############################################################
465 #
466 # Page 4 - fonts
467 #
468 ###############################################################
469 framePage(c, "Font Control")
470
471 c.drawString(inch, 10*inch, 'Listing available fonts...')
472
473 y = 9.5*inch
474 for fontname in c.getAvailableFonts():
475 c.setFont(fontname,24)
476 c.drawString(inch, y, 'This should be %s' % fontname)
477 y = y - 28
478 makesubsection(c, "fonts and colors", 4*inch)
479
480 c.setFont('Times-Roman', 12)
481 t = c.beginText(inch, 4*inch)
482 t.textLines("""Now we'll look at the color functions and how they interact
483 with the text. In theory, a word is just a shape; so setFillColorRGB()
484 determines most of what you see. If you specify other text rendering
485 modes, an outline color could be defined by setStrokeColorRGB() too""")
486 c.drawText(t)
487
488 t = c.beginText(inch, 2.75 * inch)
489 t.setFont('Times-Bold',36)
490 t.setFillColor(colors.green) #green
491 t.textLine('Green fill, no stroke')
492
493 #t.setStrokeColorRGB(1,0,0) #ou can do this in a text object, or the canvas.
494 t.setStrokeColor(colors.red) #ou can do this in a text object, or the canvas.
495 t.setTextRenderMode(2) # fill and stroke
496 t.textLine('Green fill, red stroke - yuk!')
497
498 t.setTextRenderMode(0) # back to default - fill only
499 t.setFillColorRGB(0,0,0) #back to default
500 t.setStrokeColorRGB(0,0,0) #ditto
501 c.drawText(t)
502 c.showPage()
503
504 #########################################################################
505 #
506 # Page 5 - coord transforms
507 #
508 #########################################################################
509 framePage(c, "Coordinate Transforms")
510 c.setFont('Times-Roman', 12)
511 t = c.beginText(inch, 10 * inch)
512 t.textLines("""This shows coordinate transformations. We draw a set of axes,
513 moving down the page and transforming space before each one.
514 You can use saveState() and restoreState() to unroll transformations.
515 Note that functions which track the text cursor give the cursor position
516 in the current coordinate system; so if you set up a 6 inch high frame
517 2 inches down the page to draw text in, and move the origin to its top
518 left, you should stop writing text after six inches and not eight.""")
519 c.drawText(t)
520
521 drawAxes(c, "0. at origin")
522 c.addLiteral('%about to translate space')
523 c.translate(2*inch, 7 * inch)
524 drawAxes(c, '1. translate near top of page')
525
526 c.saveState()
527 c.translate(1*inch, -2 * inch)
528 drawAxes(c, '2. down 2 inches, across 1')
529 c.restoreState()
530
531 c.saveState()
532 c.translate(0, -3 * inch)
533 c.scale(2, -1)
534 drawAxes(c, '3. down 3 from top, scale (2, -1)')
535 c.restoreState()
536
537 c.saveState()
538 c.translate(0, -5 * inch)
539 c.rotate(-30)
540 drawAxes(c, "4. down 5, rotate 30' anticlockwise")
541 c.restoreState()
542
543 c.saveState()
544 c.translate(3 * inch, -5 * inch)
545 c.skew(0,30)
546 drawAxes(c, "5. down 5, 3 across, skew beta 30")
547 c.restoreState()
548
549 c.showPage()
550
551 #########################################################################
552 #
553 # Page 6 - clipping
554 #
555 #########################################################################
556 framePage(c, "Clipping")
557 c.setFont('Times-Roman', 12)
558 t = c.beginText(inch, 10 * inch)
559 t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
560 into a path object, and clip it. This then forms a mask which limits the region of
561 the page on which one can draw. This paragraph was drawn after setting the clipping
562 path, and so you should only see part of the text.""")
563 c.drawText(t)
564
565 c.saveState()
566 #c.setFillColorRGB(0,0,1)
567 p = c.beginPath()
568 #make a chesboard effect, 1 cm squares
569 for i in range(14):
570 x0 = (3 + i) * cm
571 for j in range(7):
572 y0 = (16 + j) * cm
573 p.rect(x0, y0, 0.85*cm, 0.85*cm)
574 c.addLiteral('%Begin clip path')
575 c.clipPath(p)
576 c.addLiteral('%End clip path')
577 t = c.beginText(3 * cm, 22.5 * cm)
578 t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
579 into a path object, and clip it. This then forms a mask which limits the region of
580 the page on which one can draw. This paragraph was drawn after setting the clipping
581 path, and so you should only see part of the text.
582 This shows clipping at work. We draw a chequerboard of rectangles
583 into a path object, and clip it. This then forms a mask which limits the region of
584 the page on which one can draw. This paragraph was drawn after setting the clipping
585 path, and so you should only see part of the text.
586 This shows clipping at work. We draw a chequerboard of rectangles
587 into a path object, and clip it. This then forms a mask which limits the region of
588 the page on which one can draw. This paragraph was drawn after setting the clipping
589 path, and so you should only see part of the text.""")
590 c.drawText(t)
591
592 c.restoreState()
593
594 t = c.beginText(inch, 5 * inch)
595 t.textLines("""You can also use text as an outline for clipping with the text render mode.
596 The API is not particularly clean on this and one has to follow the right sequence;
597 this can be optimized shortly.""")
598 c.drawText(t)
599
600 #first the outline
601 c.saveState()
602 t = c.beginText(inch, 3.0 * inch)
603 t.setFont('Helvetica-BoldOblique',108)
604 t.setTextRenderMode(5) #stroke and add to path
605 t.textLine('Python!')
606 t.setTextRenderMode(0)
607 c.drawText(t) #this will make a clipping mask
608
609 #now some small stuff which wil be drawn into the current clip mask
610 t = c.beginText(inch, 4 * inch)
611 t.setFont('Times-Roman',6)
612 t.textLines((('spam ' * 40) + '\n') * 15)
613 c.drawText(t)
614
615 #now reset canvas to get rid of the clipping mask
616 c.restoreState()
617
618 c.showPage()
619
620
621 #########################################################################
622 #
623 # Page 7 - images
624 #
625 #########################################################################
626 framePage(c, "Images")
627 c.setFont('Times-Roman', 12)
628 t = c.beginText(inch, 10 * inch)
629 if not haveImages:
630 c.drawString(inch, 11*inch,
631 "Python or Java Imaging Library not found! Below you see rectangles instead of images.")
632
633 t.textLines("""PDFgen uses the Python Imaging Library (or, under Jython, java.awt.image and javax.imageio)
634 to process a very wide variety of image formats.
635 This page shows image capabilities. If I've done things right, the bitmap should have
636 its bottom left corner aligned with the crosshairs.
637 There are two methods for drawing images. The recommended use is to call drawImage.
638 This produces the smallest PDFs and the fastest generation times as each image's binary data is
639 only embedded once in the file. Also you can use advanced features like transparency masks.
640 You can also use drawInlineImage, which puts images in the page stream directly.
641 This is slightly faster for Acrobat to render or for very small images, but wastes
642 space if you use images more than once.""")
643
644 c.drawText(t)
645
646 if haveImages:
647 c.drawInlineImage('pythonpowered.gif',2*inch, 7*inch)
648 else:
649 c.rect(2*inch, 7*inch, 110, 44)
650
651 c.line(1.5*inch, 7*inch, 4*inch, 7*inch)
652 c.line(2*inch, 6.5*inch, 2*inch, 8*inch)
653 c.drawString(4.5 * inch, 7.25*inch, 'inline image drawn at natural size')
654
655 if haveImages:
656 c.drawInlineImage('pythonpowered.gif',2*inch, 5*inch, inch, inch)
657 else:
658 c.rect(2*inch, 5*inch, inch, inch)
659
660 c.line(1.5*inch, 5*inch, 4*inch, 5*inch)
661 c.line(2*inch, 4.5*inch, 2*inch, 6*inch)
662 c.drawString(4.5 * inch, 5.25*inch, 'inline image distorted to fit box')
663
664 c.drawString(1.5 * inch, 4*inch, 'Image XObjects can be defined once in the file and drawn many times.')
665 c.drawString(1.5 * inch, 3.75*inch, 'This results in faster generation and much smaller files.')
666
667 for i in range(5):
668 if haveImages:
669 (w, h) = c.drawImage('pythonpowered.gif', (1.5 + i)*inch, 3*inch)
670 else:
671 c.rect((1.5 + i)*inch, 3*inch, 110, 44)
672
673 myMask = [254,255,222,223,0,1]
674 c.drawString(1.5 * inch, 2.5*inch, "The optional 'mask' parameter lets you define transparent colors. We used a color picker")
675 c.drawString(1.5 * inch, 2.3*inch, "to determine that the yellow in the image above is RGB=(225,223,0). We then define a mask")
676 c.drawString(1.5 * inch, 2.1*inch, "spanning these RGB values: %s. The background vanishes!!" % myMask)
677 c.drawString(2.5*inch, 1.2*inch, 'This would normally be obscured')
678 if haveImages:
679 c.drawImage('pythonpowered.gif', 3*inch, 1.2*inch, w, h, mask=myMask)
680 else:
681 c.rect(3*inch, 1.2*inch, 110, 44)
682
683 c.showPage()
684
685
686 #########################################################################
687 #
688 # Page 8 - Forms and simple links
689 #
690 #########################################################################
691 framePage(c, "Forms and Links")
692 c.setFont('Times-Roman', 12)
693 t = c.beginText(inch, 10 * inch)
694 t.textLines("""Forms are sequences of text or graphics operations
695 which are stored only once in a PDF file and used as many times
696 as desired. The blue logo bar to the left is an example of a form
697 in this document. See the function framePageForm in this demo script
698 for an example of how to use canvas.beginForm(name, ...) ... canvas.endForm().
699
700 Documents can also contain cross references where (for example) a rectangle
701 on a page may be bound to a position on another page. If the user clicks
702 on the rectangle the PDF viewer moves to the bound position on the other
703 page. There are many other types of annotations and links supported by PDF.
704
705 For example, there is a bookmark to each page in this document and below
706 is a browsable index that jumps to those pages. In addition we show two
707 URL hyperlinks; for these, you specify a rectangle but must draw the contents
708 or any surrounding rectangle yourself.
709 """)
710 c.drawText(t)
711
712 nentries = len(titlelist)
713 xmargin = 3*inch
714 xmax = 7*inch
715 ystart = 6.54*inch
716 ydelta = 0.4*inch
717 for i in range(nentries):
718 yposition = ystart - i*ydelta
719 title = titlelist[i]
720 c.drawString(xmargin, yposition, title)
721 c.linkAbsolute(title, title, (xmargin-ydelta/4, yposition-ydelta/4, xmax, yposition+ydelta/2))
722
723
724 # test URLs
725 r1 = (inch, 3*inch, 5*inch, 3.25*inch) # this is x1,y1,x2,y2
726 c.linkURL('http://www.reportlab.com/', r1, thickness=1, color=colors.green)
727 c.drawString(inch+3, 3*inch+6, 'Hyperlink to www.reportlab.com, with green border')
728
729 r1 = (inch, 2.5*inch, 5*inch, 2.75*inch) # this is x1,y1,x2,y2
730 c.linkURL('mailto:reportlab-users@egroups.com', r1) #, border=0)
731 c.drawString(inch+3, 2.5*inch+6, 'mailto: hyperlink, without border')
732
733 r1 = (inch, 2*inch, 5*inch, 2.25*inch) # this is x1,y1,x2,y2
734 c.linkURL('http://www.reportlab.com/', r1,
735 thickness=2,
736 dashArray=[2,4],
737 color=colors.magenta)
738 c.drawString(inch+3, 2*inch+6, 'Hyperlink with custom border style')
739
740 ### now do stuff for the outline
741 #for x in outlinenametree: print x
742 #stop
743 #apply(c.setOutlineNames0, tuple(outlinenametree))
744 return c
745
746
747 def run(filename):
748 c = makeDocument(filename)
749 data = c.getpdfdata()
750 print 'Content-Type: application/pdf\n'
751 sys.stdout.flush()
752 sys.stdout.write(data)
753 sys.stdout.flush()
754
755 def pageShapes(c):
756 """Demonstrates the basic lines and shapes"""
757
758 c.showPage()
759 framePage(c, "Basic line and shape routines""")
760 c.setTextOrigin(inch, 10 * inch)
761 c.setFont('Times-Roman', 12)
762 c.textLines("""pdfgen provides some basic routines for drawing straight and curved lines,
763 and also for solid shapes.""")
764
765 y = 9 * inch
766 d = DocBlock()
767 d.comment1 = 'Lesson one'
768 d.code = "canvas.textOut('hello, world')"
769 print d.code
770
771 d.comment2 = 'Lesson two'
772
773 d.draw(c, inch, 9 * inch)
774
775
776 class PdfgenTestCase(unittest.TestCase):
777 "Make documents with lots of Pdfgen features"
778
779 def test0(self):
780 "Make a PDFgen document with most graphics features"
781 run('test_pdfgen_general.pdf')
782
783
784 def makeSuite():
785 return makeSuiteForClasses(PdfgenTestCase)
786
787
788 #noruntests
789 unittest.TextTestRunner().run(makeSuite())
Result: pdfgen_general.pdf
