[MERGE] Remove the embedded pychart library, and use the online version
[odoo/odoo.git] /
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
4
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
8 # later version.
9 #
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
13 # for more details.
14 #
15 import sys
16 import pychart_util
17 import string
18 import re
19 import math
20 import theme
21 import os
22 import basecanvas
23 from scaling import *
24
25 try:
26     import zlib
27     _zlib_available_p = 1
28 except:
29     _zlib_available_p = 0
30
31 class pdf_stream(object):
32     def __init__(self, fp):
33         self.fp = fp
34         self.off = 0
35     def write(self, str):
36         self.fp.write(str)
37         self.off += len(str)
38     def tell(self):
39         return self.off
40         
41 def to_radian(deg):
42     return deg*2*math.pi / 360.0
43
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 = {}
53         self.__lines = []
54         self.__nr_gsave = 0
55
56         if compress_p_ and not _zlib_available_p:
57             pychart_util.warn("Zlib not available. Compression request ignored.\n")
58             compress_p_ = 0
59         self.__compress_p = compress_p_
60
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]
67
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))
73         return obj_id
74
75     def __define_stream_obj(self, fp, s):
76         if self.__compress_p:
77             p = zlib.compress(s)
78             return self.__define_obj(fp, "<</Length %d/Filter/FlateDecode>>\nstream\n%sendstream"
79                               % (len(p), p))
80         else:            
81             return self.__define_obj(fp, "<</Length %d\n>>\nstream\n%s\nendstream"
82                               % (len(s), s))
83
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))
86         return obj_id
87
88     def __reset_context(self):
89         self.__font_name = None
90         self.__font_size = -1
91         self.__line_style = None
92         self.__fill_color = None
93         self.__stroke_color = None
94         self.__mtx_pushed = 0
95
96     def newpath(self):
97         pass
98     def set_fill_color(self, color):
99         if self.__fill_color == color:
100             return
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))
104         else:
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)
110         return
111                 
112     def __arcsub(self, x, y, radius, start, theta):
113         xcos = math.cos(to_radian(theta))
114         xsin = math.sin(to_radian(theta))
115         x0 = radius * xcos
116         y0 = radius * xsin
117         x1 = radius * (4-xcos)/3.0
118         y1 = radius * (1-xcos)*(xcos-3)/(3*xsin)
119
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))
128         step = 10
129         if radius < 10:
130             step = 20
131         if radius < 5:
132             step = 30
133         if ratio != 1.0:
134             self.push_transformation((x, y), (1, ratio), None)
135             deg = start
136             while deg < end:
137                 theta = min(step, end-deg)
138                 self.__arcsub(x, y, radius, deg, theta/2)
139                 deg += theta
140             self.pop_transformation()
141         else:
142             deg = start
143             while deg < end:
144                 theta = min(step, end-deg)
145                 self.__arcsub(x, y, radius, deg, theta/2)
146                 deg += theta
147         self.comment("end PATHARC\n")
148
149     def text_begin(self):
150         self.__write("BT ")
151         self.__font_name = None
152         self.__font_size = None
153         
154     def text_end(self):
155         self.__write("ET\n")
156     def text_moveto(self, x, y, angle):
157         if angle != None:
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))
161         else:
162             self.__write("1 0 0 1 %f %f Tm " % (x, y))
163
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))
172
173     def push_transformation(self, baseloc, scale, angle, in_text = 0):
174         if in_text:
175             op = "Tm"
176         else:
177             op = "cm"
178             self.gsave()
179             
180         if baseloc == None:
181             baseloc = (0,0)
182
183         if angle != None:
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))
189         if scale != None:
190             self.__write("%f 0 0 %f %f %f %s\n" % (scale[0], scale[1],
191                                                    baseloc[0],
192                                                    baseloc[1], op))
193         
194     def pop_transformation(self, in_text = 0):
195         if not in_text:
196             self.grestore()
197     def closepath(self):
198         self.__write("h\n")
199     def clip_sub(self):
200         self.__write("W n\n")
201     def fill(self):
202         self.__write("f n\n")
203     def gsave(self):
204         self.__write("q\n")
205     def grestore(self):
206         self.__write("Q\n")
207         self.__reset_context()
208         
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))
213     def stroke(self):
214         self.__write("S\n")
215
216     def set_line_style(self, style):
217         if (self.__line_style == style):
218             pass
219         else:
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))))
225             else:
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        
229     
230     def comment(self, str):
231         if not self.__compress_p:
232             self.__write("%%" + str + "\n")
233
234     def verbatim(self, str):
235         self.__write(str)
236
237     def __write(self, str):
238         self.__lines.append(str)
239         
240 #    def setbb(self, xmin, ymin, xmax, ymax):
241 #        self.__xmin = xmin
242 #        self.__ymin = ymin
243 #        self.__xmax = xmax
244 #        self.__ymax = ymax
245         
246     def close(self):
247         basecanvas.T.close(self)
248         if self.__lines == []:
249             return
250
251         _fp, need_close = self.open_output(self.__out_fname)
252         fp = pdf_stream(_fp)
253
254         fp.write("%PDF-1.2\n")
255
256         stream_obj_id = self.__define_stream_obj(fp, " ".join(self.__lines))
257
258         fontstr = ""
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)
262         
263         pages_obj_id = self.__define_obj(fp, " <</Type/Pages /Kids [%d 0 R] /Count 1 >>" % (self.__next_obj_id + 1))
264
265         bbox = theme.adjust_bounding_box([xscale(self.__xmin), yscale(self.__ymin),
266                                           xscale(self.__xmax), yscale(self.__ymax)])
267         
268         page_obj_id = self.__define_obj(fp, """  <</Type/Page
269 \t/Parent %d 0 R
270 \t/Contents %d 0 R
271 \t/MediaBox [%d %d %d %d]
272 \t/Resources << /ProcSet [/PDF /Text]
273 \t\t/Font << %s >>
274 >> >>""" % (pages_obj_id, stream_obj_id, 
275             bbox[0], bbox[1], bbox[2], bbox[3], fontstr))
276
277         info_str = "/Producer (%s)\n/CreationDate (%s)" % (self.creator, self.creation_date)
278         
279         if self.title:
280             info_str += "\n/Title (%s)" % (self.title, )
281         if self.author:
282             info_str += "\n/Author (%s)" % (self.author, )
283             
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))
286
287         xref_offset = fp.tell()
288         fp.write("xref\n0 %d\n" % (len(self.__obj_offsets)+1))
289         fp.write("0000000000 65535 f \n")
290         id = 1
291         while id <= len(self.__obj_offsets):
292             fp.write("%010d 00000 n \n" % (self.__obj_offsets[id]))
293             id += 1
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)
296
297         if need_close:
298             _fp.close()