1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
5 # Jockey is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2, or (at your option) any
10 # Jockey is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
31 class pdf_stream(object):
32 def __init__(self, fp):
42 return deg*2*math.pi / 360.0
44 class T(basecanvas.T):
45 def __init__(self, fname, compress_p_):
46 basecanvas.T.__init__(self)
47 self.__out_fname = fname
48 self.__reset_context()
49 self.__next_obj_id = 1
50 self.__next_font_id = 1
51 self.__obj_offsets = {}
52 self.__registered_fonts = {}
56 if compress_p_ and not _zlib_available_p:
57 pychart_util.warn("Zlib not available. Compression request ignored.\n")
59 self.__compress_p = compress_p_
61 def __intern_font(self, name):
62 "Assign an ID to the font NAME. Return its ID."
63 if not self.__registered_fonts.has_key(name):
64 self.__registered_fonts[name] = self.__next_font_id
65 self.__next_font_id += 1
66 return self.__registered_fonts[name]
68 def __define_obj(self, fp, str):
69 obj_id = self.__next_obj_id
70 self.__next_obj_id += 1
71 self.__obj_offsets[obj_id] = fp.tell()
72 fp.write("%d 0 obj\n%s\nendobj\n" % (obj_id, str))
75 def __define_stream_obj(self, fp, s):
78 return self.__define_obj(fp, "<</Length %d/Filter/FlateDecode>>\nstream\n%sendstream"
81 return self.__define_obj(fp, "<</Length %d\n>>\nstream\n%s\nendstream"
84 def __define_font_obj(self, fp, name, font_id):
85 obj_id = self.__define_obj(fp, """<</Type/Font /Subtype/Type1 /Name/F%d /BaseFont/%s /Encoding/MacRomanEncoding>>""" % (font_id, name))
88 def __reset_context(self):
89 self.__font_name = None
91 self.__line_style = None
92 self.__fill_color = None
93 self.__stroke_color = None
98 def set_fill_color(self, color):
99 if self.__fill_color == color:
101 if color.r == color.g and color.r == color.b:
102 self.__write("%f g\n" % (color.r))
103 self.__write("%f G\n" % (color.r))
105 self.__write("%f %f %f rg\n" % (color.r, color.g, color.b))
106 self.__write("%f %f %f RG\n" % (color.r, color.g, color.b))
107 self.__fill_color = color
108 def set_stroke_color(self, color):
109 self.set_fill_color(color)
112 def __arcsub(self, x, y, radius, start, theta):
113 xcos = math.cos(to_radian(theta))
114 xsin = math.sin(to_radian(theta))
117 x1 = radius * (4-xcos)/3.0
118 y1 = radius * (1-xcos)*(xcos-3)/(3*xsin)
120 xx0, xy0 = pychart_util.rotate(x0, y0, start+theta)
121 xx1, xy1 = pychart_util.rotate(x1, -y1, start+theta)
122 xx2, xy2 = pychart_util.rotate(x1, y1, start+theta)
123 self.__write("%f %f %f %f %f %f c\n" %
124 (x+xx1, y+xy1, x+xx2, y+xy2, x+xx0, y+xy0))
125 def path_arc(self, x, y, radius, ratio, start, end):
126 self.comment("PATHARC %f %f %f %f %f %f\n"
127 % (x, y, radius, ratio, start, end))
134 self.push_transformation((x, y), (1, ratio), None)
137 theta = min(step, end-deg)
138 self.__arcsub(x, y, radius, deg, theta/2)
140 self.pop_transformation()
144 theta = min(step, end-deg)
145 self.__arcsub(x, y, radius, deg, theta/2)
147 self.comment("end PATHARC\n")
149 def text_begin(self):
151 self.__font_name = None
152 self.__font_size = None
156 def text_moveto(self, x, y, angle):
158 xcos = math.cos(to_radian(angle))
159 xsin = math.sin(to_radian(angle))
160 self.__write("%f %f %f %f %f %f Tm " % (xcos, xsin, -xsin, xcos, x, y))
162 self.__write("1 0 0 1 %f %f Tm " % (x, y))
164 def text_show(self, font_name, font_size, color, str):
165 if self.__font_name != font_name or self.__font_size != font_size:
166 font_id = self.__intern_font(font_name)
167 self.__write("/F%d %d Tf " % (font_id, font_size))
168 self.__font_name = font_name
169 self.__font_size = font_size
170 self.set_fill_color(color)
171 self.__write("(%s) Tj " % (str))
173 def push_transformation(self, baseloc, scale, angle, in_text = 0):
184 radian = to_radian(angle)
185 self.__write("%f %f %f %f %f %f %s\n" %
186 (math.cos(radian), math.sin(radian),
187 -math.sin(radian), math.cos(radian),
188 baseloc[0], baseloc[1], op))
190 self.__write("%f 0 0 %f %f %f %s\n" % (scale[0], scale[1],
194 def pop_transformation(self, in_text = 0):
200 self.__write("W n\n")
202 self.__write("f n\n")
207 self.__reset_context()
209 def moveto(self, x, y):
210 self.__write('%f %f m ' % (x, y))
211 def lineto(self, x, y):
212 self.__write("%f %f l\n" % (x, y))
216 def set_line_style(self, style):
217 if (self.__line_style == style):
220 self.set_stroke_color(style.color)
221 self.__write("%f w " % nscale(style.width))
222 if style.dash != None:
223 self.__write("[%s] 0 d\n" %
224 " ".join(map(str, nscale_seq(style.dash))))
226 self.__write("[] 0 d\n")
227 self.__write("%d j %d J\n" % (style.cap_style, style.join_style))
228 self.__line_style = style
230 def comment(self, str):
231 if not self.__compress_p:
232 self.__write("%%" + str + "\n")
234 def verbatim(self, str):
237 def __write(self, str):
238 self.__lines.append(str)
240 # def setbb(self, xmin, ymin, xmax, ymax):
247 basecanvas.T.close(self)
248 if self.__lines == []:
251 _fp, need_close = self.open_output(self.__out_fname)
254 fp.write("%PDF-1.2\n")
256 stream_obj_id = self.__define_stream_obj(fp, " ".join(self.__lines))
259 for font_name, font_id in self.__registered_fonts.items():
260 obj_id = self.__define_font_obj(fp, font_name, font_id)
261 fontstr += "/F%d %d 0 R " % (font_id, obj_id)
263 pages_obj_id = self.__define_obj(fp, " <</Type/Pages /Kids [%d 0 R] /Count 1 >>" % (self.__next_obj_id + 1))
265 bbox = theme.adjust_bounding_box([xscale(self.__xmin), yscale(self.__ymin),
266 xscale(self.__xmax), yscale(self.__ymax)])
268 page_obj_id = self.__define_obj(fp, """ <</Type/Page
271 \t/MediaBox [%d %d %d %d]
272 \t/Resources << /ProcSet [/PDF /Text]
274 >> >>""" % (pages_obj_id, stream_obj_id,
275 bbox[0], bbox[1], bbox[2], bbox[3], fontstr))
277 info_str = "/Producer (%s)\n/CreationDate (%s)" % (self.creator, self.creation_date)
280 info_str += "\n/Title (%s)" % (self.title, )
282 info_str += "\n/Author (%s)" % (self.author, )
284 info_obj_id = self.__define_obj(fp, """<<%s>>""" % info_str)
285 catalog_obj_id = self.__define_obj(fp, """ <</Type/Catalog/Pages %d 0 R>>""" % (pages_obj_id))
287 xref_offset = fp.tell()
288 fp.write("xref\n0 %d\n" % (len(self.__obj_offsets)+1))
289 fp.write("0000000000 65535 f \n")
291 while id <= len(self.__obj_offsets):
292 fp.write("%010d 00000 n \n" % (self.__obj_offsets[id]))
294 fp.write("trailer << /Size %d /Root %d 0 R /Info %d 0 R\n>>\n" % (len(self.__obj_offsets)+1, catalog_obj_id, info_obj_id))
295 fp.write("startxref\n%d\n%%%%EOF\n" % xref_offset)