author | aaron_watters |
Sat, 15 Apr 2000 14:58:32 +0000 | |
changeset 136 | 9fa42a32d2f7 |
parent 127 | 44912033ce1b |
child 139 | 6369f06cf280 |
permissions | -rwxr-xr-x |
6 | 1 |
############################################################################### |
2 |
# |
|
3 |
# ReportLab Public License Version 1.0 |
|
4 |
# |
|
5 |
# Except for the change of names the spirit and intention of this |
|
6 |
# license is the same as that of Python |
|
7 |
# |
|
8 |
# (C) Copyright ReportLab Inc. 1998-2000. |
|
9 |
# |
|
10 |
# |
|
11 |
# All Rights Reserved |
|
12 |
# |
|
13 |
# Permission to use, copy, modify, and distribute this software and its |
|
14 |
# documentation for any purpose and without fee is hereby granted, provided |
|
15 |
# that the above copyright notice appear in all copies and that both that |
|
16 |
# copyright notice and this permission notice appear in supporting |
|
7 | 17 |
# documentation, and that the name of ReportLab not be used |
6 | 18 |
# in advertising or publicity pertaining to distribution of the software |
19 |
# without specific, written prior permission. |
|
20 |
# |
|
21 |
# |
|
22 |
# Disclaimer |
|
23 |
# |
|
24 |
# ReportLab Inc. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS |
|
25 |
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, |
|
26 |
# IN NO EVENT SHALL ReportLab BE LIABLE FOR ANY SPECIAL, INDIRECT |
|
27 |
# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
|
28 |
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
|
29 |
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
|
30 |
# PERFORMANCE OF THIS SOFTWARE. |
|
31 |
# |
|
32 |
############################################################################### |
|
33 |
# $Log: canvas.py,v $ |
|
136 | 34 |
# Revision 1.28 2000/04/15 14:58:32 aaron_watters |
35 |
# Completed addOutlineEntry0 api |
|
36 |
# |
|
127
44912033ce1b
Removed illegal append statement usage in canvas.grid()
andy_robinson
parents:
117
diff
changeset
|
37 |
# Revision 1.27 2000/04/14 11:28:32 andy_robinson |
44912033ce1b
Removed illegal append statement usage in canvas.grid()
andy_robinson
parents:
117
diff
changeset
|
38 |
# Removed illegal append statement usage in canvas.grid() |
136 | 39 |
# |
117 | 40 |
# Revision 1.26 2000/04/12 16:26:51 rgbecker |
41 |
# XML Tagged Paragraph parser changes |
|
127
44912033ce1b
Removed illegal append statement usage in canvas.grid()
andy_robinson
parents:
117
diff
changeset
|
42 |
# |
100
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
43 |
# Revision 1.25 2000/04/10 09:21:21 andy_robinson |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
44 |
# Color methods in textobject and canvas now synchronised. |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
45 |
# Added 'verbosity' keyword to allow hiding of 'save myfile.pdf' messages. |
117 | 46 |
# |
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
47 |
# Revision 1.24 2000/04/06 09:52:02 andy_robinson |
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
48 |
# Removed some old comments; tweaks to experimental Outline methods. |
100
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
49 |
# |
70 | 50 |
# Revision 1.23 2000/04/05 16:26:36 rgbecker |
51 |
# Fixes to setFill/StrokeColor |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
52 |
# |
69 | 53 |
# Revision 1.22 2000/04/05 16:21:02 rgbecker |
54 |
# Added _SeqTypes for efficiency |
|
70 | 55 |
# |
68 | 56 |
# Revision 1.21 2000/04/05 16:15:11 rgbecker |
57 |
# Made setFill/StrokeColor polymorphic |
|
69 | 58 |
# |
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
59 |
# Revision 1.20 2000/04/03 09:36:15 andy_robinson |
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
60 |
# Using trailing zero convention for new form and link API |
68 | 61 |
# |
60 | 62 |
# Revision 1.19 2000/04/02 02:53:49 aaron_watters |
63 |
# added support for outline trees |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
64 |
# |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
65 |
# Revision 1.18 2000/03/26 20:45:01 aaron_watters |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
66 |
# added beginForm..endForm and fixed some naming convention issues. |
60 | 67 |
# |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
68 |
# Revision 1.17 2000/03/24 21:02:21 aaron_watters |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
69 |
# added support for destinations, forms, linkages |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
70 |
# |
45 | 71 |
# Revision 1.15 2000/03/10 21:46:04 andy_robinson |
72 |
# fixed typo in setDash |
|
48 | 73 |
# |
43
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
74 |
# Revision 1.14 2000/03/08 13:40:03 andy_robinson |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
75 |
# Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor) |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
76 |
# which accepts color objects directly. |
45 | 77 |
# |
36 | 78 |
# Revision 1.13 2000/03/06 20:06:36 rgbecker |
79 |
# Typo self._currentPageHasImages = 1 |
|
43
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
80 |
# |
33 | 81 |
# Revision 1.12 2000/03/02 12:58:58 rgbecker |
82 |
# Remove over officious import checks Imag/zlib |
|
36 | 83 |
# |
28 | 84 |
# Revision 1.11 2000/03/02 10:28:54 rgbecker |
85 |
# [].extend illegal in 1.5.1 |
|
33 | 86 |
# |
27
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
87 |
# Revision 1.10 2000/02/24 17:28:13 andy_robinson |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
88 |
# Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1 |
28 | 89 |
# |
26
ee96702d3008
Removed some constants which are no longer used.
andy_robinson
parents:
21
diff
changeset
|
90 |
# Revision 1.9 2000/02/24 09:12:55 andy_robinson |
27
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
91 |
# |
26
ee96702d3008
Removed some constants which are no longer used.
andy_robinson
parents:
21
diff
changeset
|
92 |
# Removed some constants which are no longer used. |
27
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
93 |
# |
21 | 94 |
# Revision 1.8 2000/02/20 14:43:27 rgbecker |
95 |
# _currentPageHasImages = 0 in init |
|
26
ee96702d3008
Removed some constants which are no longer used.
andy_robinson
parents:
21
diff
changeset
|
96 |
# |
20 | 97 |
# Revision 1.7 2000/02/20 11:08:56 rgbecker |
98 |
# Canvas.setPageSize fix |
|
21 | 99 |
# |
18 | 100 |
# Revision 1.6 2000/02/17 15:26:28 rgbecker |
101 |
# Change page compression default |
|
20 | 102 |
# |
16 | 103 |
# Revision 1.5 2000/02/17 02:08:04 rgbecker |
104 |
# Docstring & other fixes |
|
18 | 105 |
# |
10 | 106 |
# Revision 1.4 2000/02/16 09:42:50 rgbecker |
107 |
# Conversion to reportlab package |
|
16 | 108 |
# |
7 | 109 |
# Revision 1.3 2000/02/15 17:55:59 rgbecker |
110 |
# License text fixes |
|
10 | 111 |
# |
6 | 112 |
# Revision 1.2 2000/02/15 15:47:09 rgbecker |
113 |
# Added license, __version__ and Logi comment |
|
7 | 114 |
# |
136 | 115 |
__version__=''' $Id: canvas.py,v 1.28 2000/04/15 14:58:32 aaron_watters Exp $ ''' |
16 | 116 |
__doc__=""" |
0 | 117 |
PDFgen is a library to generate PDF files containing text and graphics. It is the |
118 |
foundation for a complete reporting solution in Python. It is also the |
|
119 |
foundation for piddlePDF, the PDF back end for PIDDLE. |
|
120 |
||
121 |
Documentation is a little slim right now; run then look at testpdfgen.py |
|
122 |
to get a clue. |
|
123 |
||
6 | 124 |
Progress Reports: |
125 |
8.83, 2000-01-13, gmcm: |
|
126 |
Packagizing: |
|
127 |
renamed from pdfgen.py to canvas.py |
|
128 |
broke out PDFTextObject to textobject.py |
|
129 |
broke out PDFPathObject to pathobject.py |
|
0 | 130 |
placed all three in a package directory named pdfgen |
131 |
0.82, 1999-10-27, AR: |
|
132 |
Fixed some bugs on printing to Postscript. Added 'Text Object' |
|
133 |
analogous to Path Object to control entry and exit from text mode. |
|
134 |
Much simpler clipping API. All verified to export Postscript and |
|
135 |
redistill. |
|
136 |
One limitation still - clipping to text paths is fine in Acrobat |
|
137 |
but not in Postscript (any level) |
|
138 |
||
139 |
0.81,1999-10-13, AR: |
|
140 |
Adding RoundRect; changed all format strings to use %0.2f instead of %s, |
|
141 |
so we don't get exponentials in the output. |
|
142 |
0.8,1999-10-07, AR: all changed! |
|
143 |
""" |
|
144 |
## 0.81 1999-10-13: |
|
145 |
## |
|
146 |
## |
|
147 |
## |
|
148 |
import os |
|
149 |
import sys |
|
150 |
import string |
|
151 |
import time |
|
152 |
import tempfile |
|
153 |
import cStringIO |
|
154 |
from types import * |
|
155 |
from math import sin, cos, tan, pi, ceil |
|
156 |
||
10 | 157 |
from reportlab.pdfbase import pdfutils |
158 |
from reportlab.pdfbase import pdfdoc |
|
159 |
from reportlab.pdfbase import pdfmetrics |
|
16 | 160 |
from reportlab.pdfgen import pdfgeom, pathobject, textobject |
68 | 161 |
from reportlab.lib.colors import ColorType |
0 | 162 |
|
69 | 163 |
_SeqTypes=(TupleType,ListType) |
164 |
||
0 | 165 |
# Robert Kern |
166 |
# Constants for closing paths. |
|
167 |
# May be useful if one changes 'arc' and 'rect' to take a |
|
168 |
# default argument that tells how to close the path. |
|
169 |
# That way we can draw filled shapes. |
|
170 |
||
171 |
FILL_EVEN_ODD = 0 |
|
172 |
FILL_NON_ZERO = 1 |
|
173 |
#this is used by path-closing routines. |
|
174 |
#map stroke, fill, fillmode -> operator |
|
175 |
# fillmode: 1 = non-Zero (obviously), 0 = evenOdd |
|
176 |
PATH_OPS = {(0, 0, FILL_EVEN_ODD) : 'n', #no op |
|
177 |
(0, 0, FILL_NON_ZERO) : 'n', #no op |
|
178 |
(1, 0, FILL_EVEN_ODD) : 'S', #stroke only |
|
179 |
(1, 0, FILL_NON_ZERO) : 'S', #stroke only |
|
180 |
(0, 1, FILL_EVEN_ODD) : 'f*', #Fill only |
|
181 |
(0, 1, FILL_NON_ZERO) : 'f', #Fill only |
|
182 |
(1, 1, FILL_EVEN_ODD) : 'B*', #Stroke and Fill |
|
183 |
(1, 1, FILL_NON_ZERO) : 'B', #Stroke and Fill |
|
184 |
} |
|
185 |
||
186 |
class Canvas: |
|
187 |
"""This is a low-level interface to the PDF file format. The plan is to |
|
188 |
expose the whole pdfgen API through this. Its drawing functions should have a |
|
189 |
one-to-one correspondence with PDF functionality. Unlike PIDDLE, it thinks |
|
190 |
in terms of RGB values, Postscript font names, paths, and a 'current graphics |
|
191 |
state'. Just started development at 5/9/99, not in use yet. |
|
192 |
||
193 |
""" |
|
100
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
194 |
def __init__(self,filename, |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
195 |
pagesize=(595.27,841.89), |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
196 |
bottomup = 1, |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
197 |
pageCompression=0, |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
198 |
verbosity=1): |
0 | 199 |
"""Most of the attributes are private - we will use set/get methods |
200 |
as the preferred interface. Default page size is A4.""" |
|
201 |
self._filename = filename |
|
202 |
self._doc = pdfdoc.PDFDocument() |
|
100
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
203 |
|
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
204 |
#this only controls whether it prints 'saved ...' - 0 disables |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
205 |
self._verbosity = verbosity |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
206 |
|
0 | 207 |
self._pagesize = pagesize |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
208 |
#self._currentPageHasImages = 0 |
0 | 209 |
self._pageTransitionString = '' |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
210 |
self._destinations = {} # dictionary of destinations for cross indexing. |
0 | 211 |
|
21 | 212 |
self._pageCompression = pageCompression #off by default - turn on when we're happy! |
0 | 213 |
self._pageNumber = 1 # keep a count |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
214 |
#self._code = [] #where the current page's marking operators accumulate |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
215 |
self._restartAccumulators() # restart all accumulation state (generalized, arw) |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
216 |
self._annotationCount = 0 |
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
217 |
|
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
218 |
self._outlines = [] # list for a name tree |
0 | 219 |
|
220 |
#PostScript has the origin at bottom left. It is easy to achieve a top- |
|
221 |
#down coord system by translating to the top of the page and setting y |
|
222 |
#scale to -1, but then text is inverted. So self.bottomup is used |
|
223 |
#to also set the text matrix accordingly. You can now choose your |
|
224 |
#drawing coordinates. |
|
225 |
self.bottomup = bottomup |
|
20 | 226 |
self._make_preamble() |
0 | 227 |
|
228 |
#initial graphics state |
|
229 |
self._x = 0 |
|
230 |
self._y = 0 |
|
231 |
self._fontname = 'Times-Roman' |
|
232 |
self._fontsize = 12 |
|
233 |
self._textMode = 0 #track if between BT/ET |
|
234 |
self._leading = 14.4 |
|
235 |
self._currentMatrix = (1., 0., 0., 1., 0., 0.) |
|
236 |
self._fillMode = 0 #even-odd |
|
237 |
||
238 |
#text state |
|
239 |
self._charSpace = 0 |
|
240 |
self._wordSpace = 0 |
|
241 |
self._horizScale = 100 |
|
242 |
self._textRenderMode = 0 |
|
243 |
self._rise = 0 |
|
244 |
self._textLineMatrix = (1., 0., 0., 1., 0., 0.) |
|
245 |
self._textMatrix = (1., 0., 0., 1., 0., 0.) |
|
246 |
||
247 |
# line drawing |
|
248 |
self._lineCap = 0 |
|
249 |
self._lineJoin = 0 |
|
250 |
self._lineDash = None #not done |
|
251 |
self._lineWidth = 0 |
|
252 |
self._mitreLimit = 0 |
|
253 |
||
254 |
self._fillColorRGB = (0,0,0) |
|
255 |
self._strokeColorRGB = (0,0,0) |
|
256 |
||
20 | 257 |
def _make_preamble(self): |
258 |
if self.bottomup: |
|
259 |
#set initial font |
|
260 |
#self._preamble = 'BT /F9 12 Tf 14.4 TL ET' |
|
261 |
self._preamble = '1 0 0 1 0 0 cm BT /F9 12 Tf 14.4 TL ET' |
|
262 |
else: |
|
263 |
#switch coordinates, flip text and set font |
|
264 |
#self._preamble = '1 0 0 -1 0 %0.2f cm BT /F9 12 Tf 14.4 TL ET' % self._pagesize[1] |
|
265 |
self._preamble = '1 0 0 -1 0 %0.2f cm BT /F9 12 Tf 14.4 TL ET' % self._pagesize[1] |
|
266 |
||
0 | 267 |
def _escape(self, s): |
268 |
"""PDF escapes are like Python ones, but brackets need slashes before them too. |
|
269 |
Use Python's repr function and chop off the quotes first""" |
|
270 |
s = repr(s)[1:-1] |
|
271 |
s = string.replace(s, '(','\(') |
|
272 |
s = string.replace(s, ')','\)') |
|
273 |
return s |
|
274 |
||
275 |
#info functions - non-standard |
|
276 |
def setAuthor(self, author): |
|
277 |
self._doc.setAuthor(author) |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
278 |
|
136 | 279 |
def addOutlineEntry0(self, title, key, level=0): |
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
280 |
"""Adds a new entry to the outline. If not specified, |
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
281 |
this gos at the top level. If specified, it must be |
136 | 282 |
no more than 1 greater than the current outline level. |
283 |
||
284 |
Example |
|
285 |
c.addOutlineEntry0("first section", "section1") |
|
286 |
c.addOutlineEntry0("introduction", "s1s1", 1) |
|
287 |
c.addOutlineEntry0("body", "s1s2", 1) |
|
288 |
c.addOutlineEntry0("detail1", "s1s2s1", 2) |
|
289 |
c.addOutlineEntry0("detail2", "s1s2s2", 2) |
|
290 |
c.addOutlineEntry0("conclusion", "s1s3", 1) |
|
291 |
c.addOutlineEntry0("further reading", "s1s3s1", 2) |
|
292 |
c.addOutlineEntry0("second section", "section1") |
|
293 |
c.addOutlineEntry0("introduction", "s2s1", 1) |
|
294 |
c.addOutlineEntry0("body", "s2s2", 1) |
|
295 |
c.addOutlineEntry0("detail1", "s2s2s1", 2) |
|
296 |
c.addOutlineEntry0("detail2", "s2s2s2", 2) |
|
297 |
c.addOutlineEntry0("conclusion", "s2s3", 1) |
|
298 |
c.addOutlineEntry0("further reading", "s2s3s1", 2) |
|
299 |
||
300 |
note that you can jump from level 5 to level 3 but not |
|
301 |
from 3 to 5: instead you need to provide all intervening |
|
302 |
levels going down (4 in this case). Note that titles can |
|
303 |
collide but keys cannot. |
|
304 |
||
305 |
""" |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
306 |
#to be completed |
136 | 307 |
#self._outlines.append(title) |
308 |
self._doc.outline.addOutlineEntry(key, level, title) |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
309 |
|
0 | 310 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
311 |
def setOutlineNames0(self, *nametree): |
60 | 312 |
"""nametree should can be a recursive tree like so |
313 |
c.setOutlineNames( |
|
314 |
"chapter1dest", |
|
315 |
("chapter2dest", |
|
316 |
["chapter2section1dest", |
|
317 |
"chapter2section2dest", |
|
318 |
"chapter2conclusiondest"] |
|
319 |
), # end of chapter2 description |
|
320 |
"chapter3dest", |
|
321 |
("chapter4dest", ["c4s1", "c4s2"]) |
|
322 |
) |
|
323 |
each of the string names inside must be bound to a bookmark |
|
324 |
before the document is generated. |
|
325 |
""" |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
326 |
#print nametree |
60 | 327 |
apply(self._doc.outline.setNames, (self,)+nametree) |
328 |
||
0 | 329 |
def setTitle(self, title): |
330 |
self._doc.setTitle(title) |
|
331 |
||
332 |
def setSubject(self, subject): |
|
333 |
self._doc.setSubject(subject) |
|
334 |
||
335 |
def pageHasData(self): |
|
336 |
"Info function - app can call it after showPage to see if it needs a save" |
|
337 |
return len(self._code) == 0 |
|
60 | 338 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
339 |
def showOutline0(self): |
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
340 |
"Specify that Acrobat Reader should start with the outline tree visible" |
60 | 341 |
self._doc._catalog.showOutline() |
0 | 342 |
|
343 |
def showPage(self): |
|
344 |
"""This is where the fun happens""" |
|
345 |
page = pdfdoc.PDFPage() |
|
346 |
page.pagewidth = self._pagesize[0] |
|
347 |
page.pageheight = self._pagesize[1] |
|
348 |
page.hasImages = self._currentPageHasImages |
|
349 |
page.pageTransitionString = self._pageTransitionString |
|
350 |
page.setCompression(self._pageCompression) |
|
351 |
#print stream |
|
352 |
page.setStream([self._preamble] + self._code) |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
353 |
self._setXObjects(page) |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
354 |
self._setAnnotations(page) |
0 | 355 |
self._doc.addPage(page) |
356 |
||
357 |
#now get ready for the next one |
|
358 |
self._pageNumber = self._pageNumber + 1 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
359 |
self._restartAccumulators() |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
360 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
361 |
def _setAnnotations(self,page): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
362 |
page.Annots = self._annotationrefs |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
363 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
364 |
def _setXObjects(self, thing): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
365 |
"""for pages and forms, define the XObject dictionary for resources, if needed""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
366 |
forms = self._formsinuse |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
367 |
if forms: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
368 |
xobjectsdict = self._doc.xobjDict(forms) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
369 |
thing.XObjects = xobjectsdict |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
370 |
else: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
371 |
thing.XObjects = None |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
372 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
373 |
def _bookmarkReference(self, name): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
374 |
"""get a reference to a (possibly undefined, possibly unbound) bookmark""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
375 |
d = self._destinations |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
376 |
try: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
377 |
return d[name] |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
378 |
except: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
379 |
result = d[name] = pdfdoc.Destination(name) # newly defined, unbound |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
380 |
return result |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
381 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
382 |
def bookmarkPage0(self, name): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
383 |
"""bind a bookmark (destination) to the current page""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
384 |
# XXXX there are a lot of other ways a bookmark destination can be bound: should be implemented. |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
385 |
# XXXX the other ways require tracking of the graphics state.... |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
386 |
dest = self._bookmarkReference(name) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
387 |
pageref = self._doc.thisPageRef() |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
388 |
dest.fit() |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
389 |
dest.setPageRef(pageref) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
390 |
return dest |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
391 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
392 |
def bookmarkHorizontalAbsolute0(self, name, yhorizontal): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
393 |
"""bind a bookmark (destination to the current page at a horizontal position""" |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
394 |
dest = self._bookmarkReference(name) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
395 |
pageref = self._doc.thisPageRef() |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
396 |
dest.fith(yhorizontal) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
397 |
dest.setPageRef(pageref) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
398 |
return dest |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
399 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
400 |
def _inPage0(self): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
401 |
"""declare a page, enable page features""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
402 |
self._doc.inPage() |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
403 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
404 |
def _inForm0(self): |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
405 |
"deprecated in favore of beginForm...endForm" |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
406 |
self._doc.inForm() |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
407 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
408 |
def doForm0(self, name): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
409 |
"""use a form XObj in current operation stream""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
410 |
internalname = self._doc.hasForm(name) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
411 |
if not internalname: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
412 |
raise ValueError, "form is not defined %s" % name |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
413 |
self._code.append("/%s Do" % internalname) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
414 |
self._formsinuse.append(name) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
415 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
416 |
def _restartAccumulators(self): |
0 | 417 |
self._code = [] # ready for more... |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
418 |
self._currentPageHasImages = 1 # for safety... |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
419 |
self._formsinuse = [] |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
420 |
self._annotationrefs = [] |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
421 |
self._formData = None |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
422 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
423 |
def beginForm0(self, name, lowerx=0, lowery=0, upperx=None, uppery=None): |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
424 |
"declare the current graphics stream to be a form" |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
425 |
self._formData = (name, lowerx, lowery, upperx, uppery) |
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
426 |
self._inForm0() |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
427 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
428 |
def endForm0(self): |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
429 |
(name, lowerx, lowery, upperx, uppery) = self._formData |
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
430 |
self.makeForm0(name, lowerx, lowery, upperx, uppery) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
431 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
432 |
def makeForm0(self, name, lowerx=0, lowery=0, upperx=None, uppery=None): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
433 |
"""Like showpage, but make a form using accumulated operations instead""" |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
434 |
# deprecated in favor or beginForm(...)... endForm() |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
435 |
(w,h) = self._pagesize |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
436 |
if upperx is None: upperx=w |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
437 |
if uppery is None: uppery=h |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
438 |
form = pdfdoc.PDFFormXObject(lowerx=lowerx, lowery=lowery, upperx=upperx, uppery=uppery) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
439 |
form.compression = self._pageCompression |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
440 |
form.setStreamList([self._preamble] + self._code) # ??? minus preamble (seems to be needed!) |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
441 |
self._setXObjects(form) |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
442 |
self._setAnnotations(form) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
443 |
self._doc.addForm(name, form) |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
444 |
self._restartAccumulators() |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
445 |
|
136 | 446 |
def forceCodeInsert0(self, code): |
447 |
"""I know a whole lot about PDF and I want to add a bunch of code I know will work...""" |
|
448 |
self._code.append(code) |
|
449 |
||
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
450 |
def textAnnotation0(self, contents, Rect=None, addtopage=1, name=None, **kw): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
451 |
if not Rect: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
452 |
(w,h) = self._pagesize# default to whole page (?) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
453 |
Rect = (0,0,w,h) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
454 |
annotation = apply(pdfdoc.TextAnnotation, (Rect, contents), kw) |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
455 |
self._addAnnotation(annotation, name, addtopage) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
456 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
457 |
def inkAnnotation0(self, contents, InkList=None, Rect=None, addtopage=1, name=None, **kw): |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
458 |
"not working?" |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
459 |
(w,h) = self._pagesize |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
460 |
if not Rect: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
461 |
Rect = (0,0,w,h) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
462 |
if not InkList: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
463 |
InkList = ( (100,100,100,h-100,w-100,h-100,w-100,100), ) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
464 |
annotation = apply(pdfdoc.InkAnnotation, (Rect, contents, InkList), kw) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
465 |
self.addAnnotation(annotation, name, addtopage) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
466 |
|
64
568049c4a12f
Using trailing zero convention for new form and link API
andy_robinson
parents:
60
diff
changeset
|
467 |
def linkAbsolute0(self, contents, destinationname, Rect=None, addtopage=1, name=None, **kw): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
468 |
"""link annotation positioned wrt the default user space""" |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
469 |
destination = self._bookmarkReference(destinationname) # permitted to be undefined... must bind later... |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
470 |
(w,h) = self._pagesize |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
471 |
if not Rect: |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
472 |
Rect = (0,0,w,h) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
473 |
kw["Rect"] = Rect |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
474 |
kw["Contents"] = contents |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
475 |
kw["Destination"] = destination |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
476 |
annotation = apply(pdfdoc.LinkAnnotation, (), kw) |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
477 |
self._addAnnotation(annotation, name, addtopage) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
478 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
479 |
def _addAnnotation(self, annotation, name=None, addtopage=1): |
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
480 |
count = self._annotationCount = self._annotationCount+1 |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
481 |
if not name: name="NUMBER"+repr(count) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
482 |
self._doc.addAnnotation(name, annotation) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
483 |
if addtopage: |
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
484 |
self._annotatePage(name) |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
485 |
|
54
9f1cadbf9728
added beginForm..endForm and fixed some naming convention issues.
aaron_watters
parents:
52
diff
changeset
|
486 |
def _annotatePage(self, name): |
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
487 |
ref = self._doc.refAnnotation(name) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
488 |
self._annotationrefs.append(ref) |
0 | 489 |
|
490 |
def getPageNumber(self): |
|
491 |
return self._pageNumber |
|
492 |
||
493 |
def save(self): |
|
494 |
"""Saves the file. If holding data, do |
|
495 |
a showPage() to save them having to.""" |
|
496 |
if len(self._code): |
|
497 |
self.showPage() |
|
77
a5d452cb7a5d
Removed some old comments; tweaks to experimental Outline methods.
andy_robinson
parents:
70
diff
changeset
|
498 |
|
136 | 499 |
self._doc.SaveToFile(self._filename, self) |
100
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
500 |
if self._verbosity > 0: |
b98a1487daad
Color methods in textobject and canvas now synchronised.
andy_robinson
parents:
77
diff
changeset
|
501 |
print 'saved', self._filename |
0 | 502 |
|
503 |
def setPageSize(self, size): |
|
504 |
"""accepts a 2-tuple in points for paper size for this |
|
505 |
and subsequent pages""" |
|
506 |
self._pagesize = size |
|
20 | 507 |
self._make_preamble() |
0 | 508 |
|
509 |
def addLiteral(self, s, escaped=1): |
|
510 |
if escaped==0: |
|
511 |
s = self._escape(s) |
|
512 |
self._code.append(s) |
|
513 |
||
514 |
||
515 |
###################################################################### |
|
516 |
# |
|
517 |
# coordinate transformations |
|
518 |
# |
|
519 |
###################################################################### |
|
520 |
||
521 |
||
522 |
def transform(self, a,b,c,d,e,f): |
|
523 |
"""How can Python track this?""" |
|
524 |
a0,b0,c0,d0,e0,f0 = self._currentMatrix |
|
525 |
self._currentMatrix = (a0*a+c0*b, b0*a+d0*b, |
|
526 |
a0*c+c0*d, b0*c+d0*d, |
|
527 |
a0*e+c0*f+e0, b0*e+d0*f+f0) |
|
528 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f cm' % (a,b,c,d,e,f)) |
|
529 |
||
530 |
def translate(self, dx, dy): |
|
531 |
self.transform(1,0,0,1,dx,dy) |
|
532 |
||
533 |
def scale(self, x, y): |
|
534 |
self.transform(x,0,0,y,0,0) |
|
535 |
||
536 |
def rotate(self, theta): |
|
537 |
"""Canvas.rotate(theta) |
|
538 |
||
539 |
theta is in degrees.""" |
|
540 |
c = cos(theta * pi / 180) |
|
541 |
s = sin(theta * pi / 180) |
|
542 |
self.transform(c, s, -s, c, 0, 0) |
|
543 |
||
544 |
def skew(self, alpha, beta): |
|
545 |
tanAlpha = tan(alpha * pi / 180) |
|
546 |
tanBeta = tan(beta * pi / 180) |
|
547 |
self.transform(1, tanAlpha, tanBeta, 1, 0, 0) |
|
548 |
||
549 |
###################################################################### |
|
550 |
# |
|
551 |
# graphics state management |
|
552 |
# |
|
553 |
###################################################################### |
|
554 |
||
555 |
||
556 |
||
557 |
def saveState(self): |
|
558 |
"""These need expanding to save/restore Python's state tracking too""" |
|
559 |
self._code.append('q') |
|
560 |
||
561 |
def restoreState(self): |
|
562 |
"""These need expanding to save/restore Python's state tracking too""" |
|
563 |
self._code.append('Q') |
|
564 |
||
565 |
############################################################### |
|
566 |
# |
|
567 |
# Drawing methods. These draw things directly without |
|
568 |
# fiddling around with Path objects. We can add any geometry |
|
569 |
# methods we wish as long as their meaning is precise and |
|
570 |
# they are of general use. |
|
571 |
# |
|
572 |
# In general there are two patterns. Closed shapes |
|
573 |
# have the pattern shape(self, args, stroke=1, fill=0); |
|
574 |
# by default they draw an outline only. Line segments come |
|
575 |
# in three flavours: line, bezier, arc (which is a segment |
|
576 |
# of an elliptical arc, approximated by up to four bezier |
|
577 |
# curves, one for each quadrant. |
|
578 |
# |
|
579 |
# In the case of lines, we provide a 'plural' to unroll |
|
580 |
# the inner loop; it is useful for drawing big grids |
|
581 |
################################################################ |
|
582 |
||
583 |
||
584 |
#--------first the line drawing methods----------------------- |
|
585 |
def line(self, x1,y1, x2,y2): |
|
586 |
"As it says" |
|
587 |
self._code.append('n %0.2f %0.2f m %0.2f %0.2f l S' % (x1, y1, x2, y2)) |
|
588 |
||
589 |
def lines(self, linelist): |
|
590 |
"""As line(), but slightly more efficient for lots of them - |
|
591 |
one stroke operation and one less function call""" |
|
592 |
self._code.append('n') |
|
593 |
for (x1,y1,x2,y2) in linelist: |
|
594 |
self._code.append('%0.2f %0.2f m %0.2f %0.2f l' % (x1, y1, x2, y2)) |
|
595 |
self._code.append('S') |
|
596 |
||
597 |
def grid(self, xlist, ylist): |
|
598 |
"""Lays out a grid in current line style. Suuply list of |
|
599 |
x an y positions.""" |
|
600 |
assert len(xlist) > 1, "x coordinate list must have 2+ items" |
|
601 |
assert len(ylist) > 1, "y coordinate list must have 2+ items" |
|
602 |
lines = [] |
|
603 |
y0, y1 = ylist[0], ylist[-1] |
|
604 |
x0, x1 = xlist[0], xlist[-1] |
|
605 |
for x in xlist: |
|
127
44912033ce1b
Removed illegal append statement usage in canvas.grid()
andy_robinson
parents:
117
diff
changeset
|
606 |
lines.append((x,y0,x,y1)) |
0 | 607 |
for y in ylist: |
127
44912033ce1b
Removed illegal append statement usage in canvas.grid()
andy_robinson
parents:
117
diff
changeset
|
608 |
lines.append((x0,y,x1,y)) |
0 | 609 |
self.lines(lines) |
610 |
||
611 |
def bezier(self, x1, y1, x2, y2, x3, y3, x4, y4): |
|
612 |
"Bezier curve with the four given control points" |
|
613 |
self._code.append('n %0.2f %0.2f m %0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c S' % |
|
614 |
(x1, y1, x2, y2, x3, y3, x4, y4) |
|
615 |
) |
|
616 |
def arc(self, x1,y1, x2,y2, startAng=0, extent=90): |
|
617 |
"""Contributed to piddlePDF by Robert Kern, 28/7/99. |
|
618 |
Trimmed down by AR to remove color stuff for pdfgen.canvas and |
|
619 |
revert to positive coordinates. |
|
620 |
||
621 |
Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, |
|
622 |
starting at startAng degrees and covering extent degrees. Angles |
|
623 |
start with 0 to the right (+x) and increase counter-clockwise. |
|
624 |
These should have x1<x2 and y1<y2. |
|
625 |
||
626 |
The algorithm is an elliptical generalization of the formulae in |
|
627 |
Jim Fitzsimmon's TeX tutorial <URL: http://www.tinaja.com/bezarc1.pdf>.""" |
|
628 |
||
629 |
pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) |
|
630 |
#move to first point |
|
631 |
self._code.append('n %0.2f %0.2f m' % pointList[0][:2]) |
|
632 |
for curve in pointList: |
|
633 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:]) |
|
634 |
# stroke |
|
635 |
self._code.append('S') |
|
636 |
||
637 |
#--------now the shape drawing methods----------------------- |
|
638 |
def rect(self, x, y, width, height, stroke=1, fill=0): |
|
639 |
"draws a rectangle" |
|
640 |
self._code.append('n %0.2f %0.2f %0.2f %0.2f re ' % (x, y, width, height) |
|
641 |
+ PATH_OPS[stroke, fill, self._fillMode]) |
|
642 |
||
643 |
||
644 |
def ellipse(self, x1, y1, x2, y2, stroke=1, fill=0): |
|
645 |
"""Uses bezierArc, which conveniently handles 360 degrees - |
|
646 |
nice touch Robert""" |
|
647 |
pointList = pdfgeom.bezierArc(x1,y1, x2,y2, 0, 360) |
|
648 |
#move to first point |
|
649 |
self._code.append('n %0.2f %0.2f m' % pointList[0][:2]) |
|
650 |
for curve in pointList: |
|
651 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:]) |
|
652 |
#finish |
|
653 |
self._code.append(PATH_OPS[stroke, fill, self._fillMode]) |
|
654 |
||
655 |
||
656 |
def wedge(self, x1,y1, x2,y2, startAng, extent, stroke=1, fill=0): |
|
657 |
"""Like arc, but connects to the centre of the ellipse. |
|
658 |
Most useful for pie charts and PacMan!""" |
|
659 |
||
660 |
x_cen = (x1+x2)/2. |
|
661 |
y_cen = (y1+y2)/2. |
|
662 |
pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) |
|
663 |
||
664 |
self._code.append('n %0.2f %0.2f m' % (x_cen, y_cen)) |
|
665 |
# Move the pen to the center of the rectangle |
|
666 |
self._code.append('%0.2f %0.2f l' % pointList[0][:2]) |
|
667 |
for curve in pointList: |
|
668 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:]) |
|
669 |
# finish the wedge |
|
670 |
self._code.append('%0.2f %0.2f l ' % (x_cen, y_cen)) |
|
671 |
# final operator |
|
672 |
self._code.append(PATH_OPS[stroke, fill, self._fillMode]) |
|
673 |
||
674 |
def circle(self, x_cen, y_cen, r, stroke=1, fill=0): |
|
675 |
"""special case of ellipse""" |
|
676 |
||
677 |
x1 = x_cen - r |
|
678 |
x2 = x_cen + r |
|
679 |
y1 = y_cen - r |
|
680 |
y2 = y_cen + r |
|
681 |
self.ellipse(x1, y1, x2, y2, stroke, fill) |
|
682 |
||
683 |
def roundRect(self, x, y, width, height, radius, stroke=1, fill=0): |
|
684 |
"""Draws a rectangle with rounded corners. The corners are |
|
685 |
approximately quadrants of a circle, with the given radius.""" |
|
686 |
#use a precomputed set of factors for the bezier approximation |
|
687 |
#to a circle. There are six relevant points on the x axis and y axis. |
|
688 |
#sketch them and it should all make sense! |
|
689 |
t = 0.4472 * radius |
|
690 |
||
691 |
x0 = x |
|
692 |
x1 = x0 + t |
|
693 |
x2 = x0 + radius |
|
694 |
x3 = x0 + width - radius |
|
695 |
x4 = x0 + width - t |
|
696 |
x5 = x0 + width |
|
697 |
||
698 |
y0 = y |
|
699 |
y1 = y0 + t |
|
700 |
y2 = y0 + radius |
|
701 |
y3 = y0 + height - radius |
|
702 |
y4 = y0 + height - t |
|
703 |
y5 = y0 + height |
|
704 |
||
705 |
self._code.append('n %0.2f %0.2f m' % (x2, y0)) |
|
706 |
self._code.append('%0.2f %0.2f l' % (x3, y0)) # bottom row |
|
707 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' |
|
708 |
% (x4, y0, x5, y1, x5, y2)) # bottom right |
|
709 |
||
710 |
self._code.append('%0.2f %0.2f l' % (x5, y3)) # right edge |
|
711 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' |
|
712 |
% (x5, y4, x4, y5, x3, y5)) # top right |
|
713 |
||
714 |
self._code.append('%0.2f %0.2f l' % (x2, y5)) # top row |
|
715 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' |
|
716 |
% (x1, y5, x0, y4, x0, y3)) # top left |
|
717 |
||
718 |
self._code.append('%0.2f %0.2f l' % (x0, y2)) # left edge |
|
719 |
self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' |
|
720 |
% (x0, y1, x1, y0, x2, y0)) # bottom left |
|
721 |
||
722 |
self._code.append('h') #close off, although it should be where it started anyway |
|
723 |
||
724 |
||
725 |
self._code.append(PATH_OPS[stroke, fill, self._fillMode]) |
|
726 |
################################################## |
|
727 |
# |
|
728 |
# Text methods |
|
729 |
# |
|
730 |
# As with graphics, a separate object ensures that |
|
731 |
# everything is bracketed between text operators. |
|
732 |
# The methods below are a high-level convenience. |
|
733 |
# use PDFTextObject for multi-line text. |
|
734 |
################################################## |
|
735 |
||
52
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
736 |
|
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
737 |
def setFillColorCMYK(self, c, m, y, k): |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
738 |
"""Takes 4 arguments between 0.0 and 1.0""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
739 |
self._fillColorCMYK = (c, m, y, k) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
740 |
self._code.append('%0.2f %0.2f %0.2f %0.2f k' % (c, m, y, k)) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
741 |
|
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
742 |
def setStrokeColorCMYK(self, c, m, y, k): |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
743 |
"""Takes 4 arguments between 0.0 and 1.0""" |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
744 |
self._strokeColorCMYK = (c, m, y, k) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
745 |
self._code.append('%0.2f %0.2f %0.2f %0.2f K' % (c, m, y, k)) |
3bbe0067a2dc
added support for destinations, forms, linkages
aaron_watters
parents:
48
diff
changeset
|
746 |
|
0 | 747 |
def drawString(self, x, y, text): |
748 |
"""Draws a string in the current text styles.""" |
|
749 |
#we could inline this for speed if needed |
|
750 |
t = self.beginText(x, y) |
|
751 |
t.textLine(text) |
|
752 |
self.drawText(t) |
|
753 |
||
754 |
def drawRightString(self, x, y, text): |
|
755 |
"""Draws a string right-aligned with the y coordinate""" |
|
756 |
width = self.stringWidth(text, self._fontname, self._fontsize) |
|
757 |
t = self.beginText(x - width, y) |
|
758 |
t.textLine(text) |
|
759 |
self.drawText(t) |
|
760 |
||
761 |
def drawCentredString(self, x, y, text): |
|
762 |
"""Draws a string right-aligned with the y coordinate. I |
|
763 |
am British so the spelling is correct, OK?""" |
|
764 |
width = self.stringWidth(text, self._fontname, self._fontsize) |
|
765 |
t = self.beginText(x - 0.5*width, y) |
|
766 |
t.textLine(text) |
|
767 |
self.drawText(t) |
|
768 |
||
769 |
def getAvailableFonts(self): |
|
770 |
"""Returns the list of PostScript font names available. |
|
771 |
Standard set now, but may grow in future with font embedding.""" |
|
772 |
fontnames = self._doc.getAvailableFonts() |
|
773 |
fontnames.sort() |
|
774 |
return fontnames |
|
775 |
||
776 |
def setFont(self, psfontname, size, leading = None): |
|
777 |
"""Sets the font. If leading not specified, defaults to 1.2 x |
|
778 |
font size. Raises a readable exception if an illegal font |
|
779 |
is supplied. Font names are case-sensitive! Keeps track |
|
780 |
of font anme and size for metrics.""" |
|
781 |
self._fontname = psfontname |
|
782 |
self._fontsize = size |
|
783 |
pdffontname = self._doc.getInternalFontName(psfontname) |
|
784 |
if leading is None: |
|
785 |
leading = size * 1.2 |
|
786 |
self._leading = leading |
|
787 |
self._code.append('BT %s %0.1f Tf %0.1f TL ET' % (pdffontname, size, leading)) |
|
788 |
||
117 | 789 |
def stringWidth(self, text, fontName, fontSize): |
0 | 790 |
"gets width of a string in the given font and size" |
117 | 791 |
return pdfmetrics.stringWidth(text, fontName, fontSize) |
0 | 792 |
|
793 |
# basic graphics modes |
|
794 |
def setLineWidth(self, width): |
|
795 |
self._lineWidth = width |
|
796 |
self._code.append('%0.2f w' % width) |
|
797 |
||
798 |
def setLineCap(self, mode): |
|
799 |
"""0=butt,1=round,2=square""" |
|
800 |
assert mode in (0,1,2), "Line caps allowed: 0=butt,1=round,2=square" |
|
801 |
self._lineCap = mode |
|
802 |
self._code.append('%d J' % mode) |
|
803 |
||
804 |
def setLineJoin(self, mode): |
|
805 |
"""0=mitre, 1=round, 2=bevel""" |
|
806 |
assert mode in (0,1,2), "Line Joins allowed: 0=mitre, 1=round, 2=bevel" |
|
807 |
self._lineJoin = mode |
|
808 |
self._code.append('%d j' % mode) |
|
809 |
||
810 |
def setMiterLimit(self, limit): |
|
811 |
self._miterLimit = limit |
|
812 |
self._code.append('%0.2f M' % limit) |
|
813 |
||
814 |
def setDash(self, array=[], phase=0): |
|
815 |
"""Two notations. pass two numbers, or an array and phase""" |
|
816 |
if type(array) == IntType or type(array) == FloatType: |
|
817 |
self._code.append('[%s %s] 0 d' % (array, phase)) |
|
45 | 818 |
elif type(array) == ListType or type(array) == TupleType: |
0 | 819 |
assert phase <= len(array), "setDash phase must be l.t.e. length of array" |
820 |
textarray = string.join(map(str, array)) |
|
821 |
self._code.append('[%s] %s d' % (textarray, phase)) |
|
822 |
||
823 |
def setFillColorRGB(self, r, g, b): |
|
43
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
824 |
"""Takes 3 arguments between 0.0 and 1.0""" |
0 | 825 |
self._fillColorRGB = (r, g, b) |
826 |
self._code.append('%0.2f %0.2f %0.2f rg' % (r,g,b)) |
|
827 |
||
828 |
def setStrokeColorRGB(self, r, g, b): |
|
43
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
829 |
"""Takes 3 arguments between 0.0 and 1.0""" |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
830 |
self._strokeColorRGB = (r, g, b) |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
831 |
self._code.append('%0.2f %0.2f %0.2f RG' % (r,g,b)) |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
832 |
|
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
833 |
def setFillColor(self, aColor): |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
834 |
"""Takes a color object, allowing colors to be referred to by name""" |
68 | 835 |
if type(aColor) == ColorType: |
836 |
rgb = (aColor.red, aColor.green, aColor.blue) |
|
70 | 837 |
self._fillColorRGB = rgb |
68 | 838 |
self._code.append('%0.2f %0.2f %0.2f rg' % rgb ) |
69 | 839 |
elif type(aColor) in _SeqTypes: |
68 | 840 |
l = len(aColor) |
841 |
if l==3: |
|
70 | 842 |
self._fillColorRGB = aColor |
68 | 843 |
self._code.append('%0.2f %0.2f %0.2f rg' % aColor ) |
844 |
elif l==4: |
|
845 |
self.setFillColorCMYK(self, aColor[0], aColor[1], aColor[2], aColor[3]) |
|
846 |
else: |
|
847 |
raise 'Unknown color', str(aColor) |
|
848 |
else: |
|
849 |
raise 'Unknown color', str(aColor) |
|
850 |
||
851 |
||
43
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
852 |
def setStrokeColor(self, aColor): |
71baccf1c57f
Canvas has two methods setFillColor(aColor) and setStrokeColor(aColor)
andy_robinson
parents:
36
diff
changeset
|
853 |
"""Takes a color object, allowing colors to be referred to by name""" |
68 | 854 |
if type(aColor) == ColorType: |
855 |
rgb = (aColor.red, aColor.green, aColor.blue) |
|
856 |
self._strokeColorRGB = rgb |
|
857 |
self._code.append('%0.2f %0.2f %0.2f RG' % rgb ) |
|
69 | 858 |
elif type(aColor) in _SeqTypes: |
68 | 859 |
l = len(aColor) |
860 |
if l==3: |
|
861 |
self._strokeColorRGB = aColor |
|
862 |
self._code.append('%0.2f %0.2f %0.2f RG' % aColor ) |
|
863 |
elif l==4: |
|
864 |
self.setStrokeColorCMYK(self, aColor[0], aColor[1], aColor[2], aColor[3]) |
|
865 |
else: |
|
866 |
raise 'Unknown color', str(aColor) |
|
867 |
else: |
|
868 |
raise 'Unknown color', str(aColor) |
|
27
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
869 |
|
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
870 |
def setFillGray(self, gray): |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
871 |
"""Sets the gray level; 0.0=black, 1.0=white""" |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
872 |
self._fillColorRGB = (gray, gray, gray) |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
873 |
self._code.append('%0.2f g' % gray) |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
874 |
|
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
875 |
def setStrokeGray(self, gray): |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
876 |
"""Sets the gray level; 0.0=black, 1.0=white""" |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
877 |
self._strokeColorRGB = (gray, gray, gray) |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
878 |
self._code.append('%0.2f G' % gray) |
684657a34422
Added methods setFillGray(g), setStrokeGray(g) where 0 <= g <= 1
andy_robinson
parents:
26
diff
changeset
|
879 |
|
0 | 880 |
|
881 |
# path stuff - the separate path object builds it |
|
882 |
def beginPath(self): |
|
883 |
"""Returns a fresh path object""" |
|
884 |
return pathobject.PDFPathObject() |
|
885 |
||
886 |
def drawPath(self, aPath, stroke=1, fill=0): |
|
887 |
"Draw in the mode indicated" |
|
888 |
op = PATH_OPS[stroke, fill, self._fillMode] |
|
889 |
self._code.append(aPath.getCode() + ' ' + op) |
|
890 |
||
891 |
def clipPath(self, aPath, stroke=1, fill=0): |
|
892 |
"clip as well as drawing" |
|
893 |
op = PATH_OPS[stroke, fill, self._fillMode] |
|
894 |
self._code.append(aPath.getCode() + ' W ' + op) |
|
895 |
||
896 |
def beginText(self, x=0, y=0): |
|
897 |
"""Returns a fresh text object""" |
|
898 |
return textobject.PDFTextObject(self, x, y) |
|
899 |
||
900 |
def drawText(self, aTextObject): |
|
901 |
"""Draws a text object""" |
|
902 |
self._code.append(aTextObject.getCode()) |
|
903 |
||
904 |
###################################################### |
|
905 |
# |
|
906 |
# Image routines |
|
907 |
# |
|
908 |
###################################################### |
|
909 |
def drawInlineImage(self, image, x,y, width=None,height=None): |
|
33 | 910 |
"""Draw an Image into the specified rectangle. If width and |
0 | 911 |
height are omitted, they are calculated from the image size. |
912 |
Also allow file names as well as images. This allows a |
|
913 |
caching mechanism""" |
|
914 |
||
36 | 915 |
self._currentPageHasImages = 1 |
0 | 916 |
|
917 |
if type(image) == StringType: |
|
918 |
if os.path.splitext(image)[1] in ['.jpg', '.JPG']: |
|
33 | 919 |
#directly process JPEG files |
920 |
#open file, needs some error handling!! |
|
0 | 921 |
imageFile = open(image, 'rb') |
922 |
info = self.readJPEGInfo(imageFile) |
|
923 |
imgwidth, imgheight = info[0], info[1] |
|
924 |
if info[2] == 1: |
|
925 |
colorSpace = 'DeviceGray' |
|
926 |
elif info[2] == 3: |
|
927 |
colorSpace = 'DeviceRGB' |
|
928 |
else: #maybe should generate an error, is this right for CMYK? |
|
929 |
colorSpace = 'DeviceCMYK' |
|
930 |
imageFile.seek(0) #reset file pointer |
|
931 |
imagedata = [] |
|
932 |
imagedata.append('BI') # begin image |
|
933 |
# this describes what is in the image itself |
|
934 |
imagedata.append('/Width %0.2f /Height %0.2f' %(info[0], info[1])) |
|
935 |
imagedata.append('/BitsPerComponent 8') |
|
936 |
imagedata.append('/ColorSpace /%s' % colorSpace) |
|
937 |
imagedata.append('/Filter [ /ASCII85Decode /DCTDecode]') |
|
938 |
imagedata.append('ID') |
|
939 |
#write in blocks of (??) 60 characters per line to a list |
|
940 |
compressed = imageFile.read() |
|
941 |
encoded = pdfutils._AsciiBase85Encode(compressed) |
|
942 |
outstream = cStringIO.StringIO(encoded) |
|
943 |
dataline = outstream.read(60) |
|
944 |
while dataline <> "": |
|
945 |
imagedata.append(dataline) |
|
946 |
dataline = outstream.read(60) |
|
947 |
imagedata.append('EI') |
|
948 |
else: |
|
949 |
if not pdfutils.cachedImageExists(image): |
|
33 | 950 |
try: |
951 |
import Image |
|
952 |
except ImportError: |
|
953 |
print 'Python Imaging Library not available' |
|
954 |
return |
|
955 |
try: |
|
956 |
import zlib |
|
957 |
except ImportError: |
|
958 |
print 'zlib not available' |
|
959 |
return |
|
0 | 960 |
pdfutils.cacheImageFile(image) |
33 | 961 |
|
0 | 962 |
#now we have one cached, slurp it in |
963 |
cachedname = os.path.splitext(image)[0] + '.a85' |
|
964 |
imagedata = open(cachedname,'rb').readlines() |
|
965 |
#trim off newlines... |
|
966 |
imagedata = map(string.strip, imagedata) |
|
967 |
||
968 |
#parse line two for width, height |
|
969 |
words = string.split(imagedata[1]) |
|
970 |
imgwidth = string.atoi(words[1]) |
|
971 |
imgheight = string.atoi(words[3]) |
|
972 |
else: |
|
973 |
#PIL Image |
|
974 |
#work out all dimensions |
|
33 | 975 |
try: |
976 |
import zlib |
|
977 |
except ImportError: |
|
978 |
print 'zlib not available' |
|
979 |
return |
|
0 | 980 |
myimage = image.convert('RGB') |
981 |
imgwidth, imgheight = myimage.size |
|
982 |
imagedata = [] |
|
983 |
imagedata.append('BI') # begin image |
|
984 |
||
985 |
# this describes what is in the image itself |
|
986 |
imagedata.append('/W %0.2f /H %0.2f /BPC 8 /CS /RGB /F [/A85 /Fl]' % (imgwidth, imgheight)) |
|
987 |
imagedata.append('ID') |
|
988 |
||
989 |
#use a flate filter and Ascii Base 85 to compress |
|
990 |
raw = myimage.tostring() |
|
991 |
assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image") |
|
992 |
compressed = zlib.compress(raw) #this bit is very fast... |
|
993 |
encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this isn't |
|
994 |
||
995 |
#write in blocks of (??) 60 characters per line to a list |
|
996 |
outstream = cStringIO.StringIO(encoded) |
|
997 |
dataline = outstream.read(60) |
|
998 |
while dataline <> "": |
|
999 |
imagedata.append(dataline) |
|
1000 |
dataline = outstream.read(60) |
|
1001 |
imagedata.append('EI') |
|
1002 |
||
1003 |
#now build the PDF for the image. |
|
1004 |
if not width: |
|
1005 |
width = imgwidth |
|
1006 |
if not height: |
|
1007 |
height = imgheight |
|
1008 |
||
1009 |
# this says where and how big to draw it |
|
1010 |
#self._code.append('ET') |
|
1011 |
#self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y+height)) |
|
1012 |
if self.bottomup: |
|
1013 |
self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y)) |
|
1014 |
else: |
|
1015 |
self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y+height)) |
|
28 | 1016 |
|
68 | 1017 |
# self._code.extend(imagedata) if >=python-1.5.2 |
28 | 1018 |
for line in imagedata: |
68 | 1019 |
self._code.append(line) |
1020 |
||
0 | 1021 |
self._code.append('Q') |
1022 |
#self._code.append('BT') |
|
1023 |
||
1024 |
######################################################################### |
|
1025 |
# |
|
1026 |
# JPEG processing code - contributed by Eric Johnson |
|
1027 |
# |
|
1028 |
######################################################################### |
|
1029 |
||
1030 |
# Read data from the JPEG file. We should probably be using PIL to |
|
1031 |
# get this information for us -- but this way is more fun! |
|
1032 |
# Returns (width, height, color components) as a triple |
|
1033 |
# This is based on Thomas Merz's code from GhostScript (viewjpeg.ps) |
|
1034 |
def readJPEGInfo(self, image): |
|
1035 |
"Read width, height and number of components from JPEG file" |
|
68 | 1036 |
import struct |
0 | 1037 |
|
68 | 1038 |
#Acceptable JPEG Markers: |
1039 |
# SROF0=baseline, SOF1=extended sequential or SOF2=progressive |
|
1040 |
validMarkers = [0xC0, 0xC1, 0xC2] |
|
0 | 1041 |
|
68 | 1042 |
#JPEG markers without additional parameters |
1043 |
noParamMarkers = \ |
|
1044 |
[ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01 ] |
|
0 | 1045 |
|
68 | 1046 |
#Unsupported JPEG Markers |
1047 |
unsupportedMarkers = \ |
|
1048 |
[ 0xC3, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF ] |
|
0 | 1049 |
|
68 | 1050 |
#read JPEG marker segments until we find SOFn marker or EOF |
1051 |
done = 0 |
|
1052 |
while not done: |
|
1053 |
x = struct.unpack('B', image.read(1)) |
|
1054 |
if x[0] == 0xFF: #found marker |
|
1055 |
x = struct.unpack('B', image.read(1)) |
|
1056 |
#print "Marker: ", '%0.2x' % x[0] |
|
1057 |
#check marker type is acceptable and process it |
|
1058 |
if x[0] in validMarkers: |
|
1059 |
image.seek(2, 1) #skip segment length |
|
1060 |
x = struct.unpack('B', image.read(1)) #data precision |
|
1061 |
if x[0] != 8: |
|
1062 |
raise 'PDFError', ' JPEG must have 8 bits per component' |
|
1063 |
y = struct.unpack('BB', image.read(2)) |
|
1064 |
height = (y[0] << 8) + y[1] |
|
1065 |
y = struct.unpack('BB', image.read(2)) |
|
1066 |
width = (y[0] << 8) + y[1] |
|
1067 |
y = struct.unpack('B', image.read(1)) |
|
1068 |
color = y[0] |
|
1069 |
return width, height, color |
|
1070 |
done = 1 |
|
1071 |
elif x[0] in unsupportedMarkers: |
|
1072 |
raise 'PDFError', ' Unsupported JPEG marker: %0.2x' % x[0] |
|
1073 |
elif x[0] not in noParamMarkers: |
|
1074 |
#skip segments with parameters |
|
1075 |
#read length and skip the data |
|
1076 |
x = struct.unpack('BB', image.read(2)) |
|
1077 |
image.seek( (x[0] << 8) + x[1] - 2, 1) |
|
0 | 1078 |
|
1079 |
def setPageCompression(self, onoff=1): |
|
1080 |
"""Possible values 1 or 0 (1 for 'on' is the default). |
|
1081 |
If on, the page data will be compressed, leading to much |
|
1082 |
smaller files, but takes a little longer to create the files. |
|
1083 |
This applies to all subsequent pages, or until setPageCompression() |
|
1084 |
is next called.""" |
|
1085 |
self._pageCompression = onoff |
|
1086 |
||
1087 |
||
1088 |
def setPageTransition(self, effectname=None, duration=1, |
|
1089 |
direction=0,dimension='H',motion='I'): |
|
1090 |
"""PDF allows page transition effects for use when giving |
|
1091 |
presentations. There are six possible effects. You can |
|
1092 |
just guive the effect name, or supply more advanced options |
|
1093 |
to refine the way it works. There are three types of extra |
|
1094 |
argument permitted, and here are the allowed values: |
|
1095 |
direction_arg = [0,90,180,270] |
|
1096 |
dimension_arg = ['H', 'V'] |
|
1097 |
motion_arg = ['I','O'] (start at inside or outside) |
|
1098 |
||
1099 |
This table says which ones take which arguments: |
|
1100 |
||
1101 |
PageTransitionEffects = { |
|
1102 |
'Split': [direction_arg, motion_arg], |
|
1103 |
'Blinds': [dimension_arg], |
|
1104 |
'Box': [motion_arg], |
|
1105 |
'Wipe' : [direction_arg], |
|
1106 |
'Dissolve' : [], |
|
1107 |
'Glitter':[direction_arg] |
|
1108 |
} |
|
1109 |
Have fun! |
|
1110 |
""" |
|
1111 |
if not effectname: |
|
1112 |
self._pageTransitionString = '' |
|
1113 |
return |
|
1114 |
||
1115 |
#first check each optional argument has an allowed value |
|
1116 |
if direction in [0,90,180,270]: |
|
1117 |
direction_arg = '/Di /%d' % direction |
|
1118 |
else: |
|
1119 |
raise 'PDFError', ' directions allowed are 0,90,180,270' |
|
1120 |
||
1121 |
if dimension in ['H', 'V']: |
|
1122 |
dimension_arg = '/Dm /%s' % dimension |
|
1123 |
else: |
|
1124 |
raise'PDFError','dimension values allowed are H and V' |
|
1125 |
||
1126 |
if motion in ['I','O']: |
|
1127 |
motion_arg = '/M /%s' % motion |
|
1128 |
else: |
|
1129 |
raise'PDFError','motion values allowed are I and O' |
|
1130 |
||
1131 |
||
1132 |
# this says which effects require which argument types from above |
|
1133 |
PageTransitionEffects = { |
|
1134 |
'Split': [direction_arg, motion_arg], |
|
1135 |
'Blinds': [dimension_arg], |
|
1136 |
'Box': [motion_arg], |
|
1137 |
'Wipe' : [direction_arg], |
|
1138 |
'Dissolve' : [], |
|
1139 |
'Glitter':[direction_arg] |
|
1140 |
} |
|
1141 |
||
1142 |
try: |
|
1143 |
args = PageTransitionEffects[effectname] |
|
1144 |
except KeyError: |
|
1145 |
raise 'PDFError', 'Unknown Effect Name "%s"' % effectname |
|
1146 |
self._pageTransitionString = '' |
|
1147 |
return |
|
1148 |
||
1149 |
||
1150 |
self._pageTransitionString = (('/Trans <</D %d /S /%s ' % (duration, effectname)) + |
|
1151 |
string.join(args, ' ') + ' >>') |
|
1152 |
||
1153 |
if __name__ == '__main__': |
|
1154 |
print 'For test scripts, run testpdfgen.py' |