1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2007-2008 by Igor Novikov
4
# Copyright (C) 2000, 2001, 2002 by Bernhard Herzog
6
# This library is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU Library General Public
8
# License as published by the Free Software Foundation; either
9
# version 2 of the License, or (at your option) any later version.
11
# This library is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
# Library General Public License for more details.
16
# You should have received a copy of the GNU Library General Public
17
# License along with this library; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
#tk_file_type = ("Portable Document Format (PDF)", '.pdf')
24
#format_name = 'PDF-Reportlab'
28
from math import atan2, pi
30
from app import _,Bezier, EmptyPattern, Rotation, Translation, _sketch
31
from app.Graphics.curveop import arrow_trafos
32
import reportlab.pdfgen.canvas
36
def make_pdf_path(pdfpath, paths):
38
for i in range(path.len):
39
type, control, p, cont = path.Segment(i)
42
pdfpath.curveTo(p1.x, p1.y, p2.x, p2.y, p.x, p.y)
45
pdfpath.lineTo(p.x, p.y)
47
pdfpath.moveTo(p.x, p.y)
54
has_axial_gradient = 0
55
has_radial_gradient = 0
56
has_conical_gradient = 0
59
def __init__(self, pdf):
65
def Concat(self, trafo):
66
apply(self.pdf.transform, trafo.coeff())
68
def Translate(self, x, y = None):
71
self.pdf.translate(x, y)
73
def Rotate(self, angle):
74
self.pdf.rotate(angle)
76
def Scale(self, scale):
77
self.pdf.scale(scale, scale)
80
self.pdf.restoreState()
85
def SetFillColor(self, color):
86
self.pdf.setFillColor(tuple(color))
88
def SetLineColor(self, color):
89
self.pdf.setStrokeColor(tuple(color))
91
def SetLineAttributes(self, width, cap = 1, join = 0, dashes = ()):
92
self.pdf.setLineWidth(width)
93
self.pdf.setLineCap(cap - 1)
94
self.pdf.setLineJoin(join)
100
for i in range(len(dashes)):
101
dashes[i] = w * dashes[i]
102
self.pdf.setDash(dashes)
104
def DrawLine(self, start, end):
105
self.pdf.line(start.x, start.y, end.x, end.y)
107
def DrawLineXY(self, x1, y1, x2, y2):
108
self.pdf.line(x1, y1, x2, y2)
110
def DrawRectangle(self, start, end):
111
self.pdf.rectangle(start.x, start.y, end.x - start.x, end.y - start.y,
114
def FillRectangle(self, left, bottom, right, top):
115
self.pdf.rect(left, bottom, right - left, top - bottom, 0, 1)
117
def DrawCircle(self, center, radius):
118
self.pdf.circle(center.x, center.y, radius, 1, 0)
120
def FillCircle(self, center, radius):
121
self.pdf.circle(center.x, center.y, radius, 0, 1)
123
def FillPolygon(self, pts):
124
path = self.pdf.beginPath()
125
apply(path.moveTo, pts[0])
129
self.pdf.drawPath(path, 0, 1)
131
def DrawBezierPath(self, path, rect = None):
132
self.pdf.drawPath(make_pdf_path(self.pdf.beginPath(), (path,)), 1, 0)
134
def FillBezierPath(self, path, rect = None):
135
self.pdf.drawPath(make_pdf_path(self.pdf.beginPath(), (path,)), 0, 1)
141
def __init__(self, file, filename, document, options):
143
self.filename = filename
144
self.document = document
145
self.options = options
147
# if there's a pdfgen_canvas option assume it's an instance of
148
# reportlab.pdfgen.canvas.Canvas that we should render on. This
149
# allows multiple documents to be rendered into the same PDF
150
# file or to have other python code outside of Sketch such as
151
# reportlab itself (more precisely one of its other components
152
# besides pdfgen) render into to too.
154
# The code here assumes that the canvas is already setup
156
if options.has_key("pdfgen_canvas"):
157
self.pdf = options["pdfgen_canvas"]
159
self.pdf = reportlab.pdfgen.canvas.Canvas(file)
160
self.pdf.setPageSize(document.PageSize())
163
if not self.options.has_key("pdfgen_canvas"):
166
def set_properties(self, properties, bounding_rect = None):
167
pattern = properties.line_pattern
168
if not pattern.is_Empty:
170
c, m, y, k =pattern.Color().getCMYK()
171
self.pdf.setStrokeColorCMYK(c, m, y, k)
172
self.pdf.setLineWidth(properties.line_width)
173
self.pdf.setLineJoin(properties.line_join)
174
self.pdf.setLineCap(properties.line_cap - 1)
175
dashes = properties.line_dashes
177
dashes = list(dashes)
178
w = properties.line_width
181
for i in range(len(dashes)):
182
dashes[i] = w * dashes[i]
183
self.pdf.setDash(dashes)
185
pattern = properties.fill_pattern
186
if not pattern.is_Empty:
188
c, m, y, k =pattern.Color().getCMYK()
189
self.pdf.setFillColorCMYK(c, m, y, k)
190
elif pattern.is_Tiled:
192
elif pattern.is_AxialGradient:
193
active_fill = self.axial_gradient
195
active_fill = self.execute_pattern
198
def axial_gradient(self, properties, rect):
199
pattern = properties.fill_pattern
200
vx, vy = pattern.Direction()
201
angle = atan2(vy, vx) - pi / 2
202
center = rect.center()
203
rot = Rotation(angle, center)
204
left, bottom, right, top = rot(rect)
205
trafo = rot(Translation(center))
206
image = PIL.Image.new('RGB', (1, 200))
207
border = int(round(100 * pattern.Border()))
208
_sketch.fill_axial_gradient(image.im, pattern.Gradient().Colors(),
209
0, border, 0, 200 - border)
211
apply(self.pdf.transform, trafo.coeff())
212
self.pdf.drawInlineImage(image, (left - right) / 2, (bottom - top) / 2,
213
right - left, top - bottom)
214
self.pdf.restoreState()
216
def execute_pattern(self, properties, rect):
217
device = PDFDevice(self.pdf)
218
properties.fill_pattern.Execute(device, rect)
220
def make_pdf_path(self, paths):
221
return make_pdf_path(self.pdf.beginPath(), paths)
223
def polybezier(self, paths, properties, bounding_rect, clip = 0):
224
pdfpath = self.make_pdf_path(paths)
225
active_fill = self.set_properties(properties, bounding_rect)
229
self.pdf.clipPath(pdfpath, 0, 0)
230
active_fill(properties, bounding_rect)
232
self.pdf.restoreState()
233
if properties.HasLine():
234
self.pdf.drawPath(pdfpath, 1, 0)
237
method = self.pdf.clipPath
239
method = self.pdf.drawPath
240
method(self.make_pdf_path(paths), properties.HasLine(),
241
properties.HasFill())
243
if properties.HasLine():
244
# Set the pdf fill color to the line color to make sure that
245
# arrows that are filled are filled with the line color of
246
# the object. Since lines are always drawn last, this
247
# shouldn't interfere with the object's fill.
248
c, m, y, k = properties.line_pattern.Color().getCMYK()
249
self.pdf.setFillColorCMYK(c, m, y, k)
250
arrow1 = properties.line_arrow1
251
arrow2 = properties.line_arrow2
254
t1, t2 = arrow_trafos(path, properties)
255
if arrow1 and t1 is not None:
256
self.draw_arrow(arrow1, t1)
257
if arrow2 and t2 is not None:
258
self.draw_arrow(arrow2, t2)
260
def draw_arrow(self, arrow, trafo):
261
path = arrow.Paths()[0].Duplicate()
262
path.Transform(trafo)
263
pdfpath = self.make_pdf_path((path,))
265
self.pdf.drawPath(pdfpath, 0, 1)
267
self.pdf.drawPath(pdfpath, 1, 0)
269
def mask_group(self, object):
271
if not mask.has_properties:
272
# XXX implement this case (raster images)
276
prop = mask.Properties().Duplicate()
277
prop.SetProperty(line_pattern = EmptyPattern)
278
self.polybezier(mask.Paths(), prop, mask.bounding_rect, clip = 1)
279
self.save_objects(object.MaskedObjects())
280
if mask.has_line and mask.Properties().HasLine():
281
prop = mask.Properties().Duplicate()
282
prop.SetProperty(fill_pattern = EmptyPattern)
283
self.polybezier(mask.Paths(), prop, mask.bounding_rect,
285
self.pdf.restoreState()
287
def raster_image(self, object):
289
apply(self.pdf.transform, object.Trafo().coeff())
290
self.pdf.drawInlineImage(object.Data().Image(), 0, 0)
291
self.pdf.restoreState()
293
def simple_text(self, object, clip = 0):
294
properties = object.Properties()
295
active_fill = self.set_properties(properties, object.bounding_rect)
296
fontname = properties.font.PostScriptName()
297
if fontname not in self.pdf.getAvailableFonts():
298
fontname = 'Times-Roman'
300
if active_fill and not clip:
302
pdftext = self.pdf.beginText()
304
pdftext.setTextRenderMode(7)
306
pdftext.setTextRenderMode(4)
307
pdftext.setFont(fontname, properties.font_size)
308
apply(pdftext.setTextTransform, object.FullTrafo().coeff())
309
pdftext.textOut(object.Text())
310
self.pdf.drawText(pdftext)
312
active_fill(properties, object.bounding_rect)
314
self.pdf.restoreState()
316
def path_text(self, object, clip = 0):
317
properties = object.Properties()
318
active_fill = self.set_properties(properties, object.bounding_rect)
319
fontname = properties.font.PostScriptName()
320
if fontname not in self.pdf.getAvailableFonts():
321
fontname = 'Times-Roman'
323
if active_fill and not clip:
325
pdftext = self.pdf.beginText()
327
pdftext.setTextRenderMode(7)
329
pdftext.setTextRenderMode(4)
330
pdftext.setFont(fontname, properties.font_size)
331
trafos = object.CharacterTransformations()
333
for i in range(len(trafos)):
334
apply(pdftext.setTextTransform, trafos[i].coeff())
335
pdftext.textOut(text[i])
336
self.pdf.drawText(pdftext)
338
active_fill(properties, object.bounding_rect)
340
self.pdf.restoreState()
343
self.document.updateActivePage()
344
masters=self.document.getMasterLayers()
346
pagenum=len(self.document.pages)
347
interval=int(97/pagenum)
348
for page in self.document.pages:
350
app.updateInfo(inf2=_('Composing page %u of %u')%(count,pagenum),inf3=count*interval)
353
if not layer.is_SpecialLayer and layer.Printable():
354
self.save_objects(layer.GetObjects())
357
def save_objects(self, objects):
358
for object in objects:
359
if object.is_Compound:
360
if object.is_MaskGroup:
361
self.mask_group(object)
363
self.save_objects(object.GetObjects())
364
elif object.is_SimpleText:
365
# self.simple_text(object)
366
obj=object.AsBezier()
367
self.polybezier(obj.Paths(), obj.Properties(), obj.bounding_rect)
368
elif object.is_PathTextText:
369
self.path_text(object)
370
elif object.is_Image:
371
self.raster_image(object)
372
elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse:
373
self.polybezier(object.Paths(), object.Properties(), object.bounding_rect)
377
def save(document, file, filename, options = {}):
378
app.updateInfo(inf1=_('PDF generation.'),inf2=_('Start document composing'),inf3=3)
379
saver = PDFGenSaver(file, filename, document, options)
382
app.updateInfo(inf2=_('Document generation is finished'),inf3=100)