# # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) # # Jockey is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any # later version. # # Jockey is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # import sys import pychart_util import string import re import math import theme import os import basecanvas from scaling import * try: import zlib _zlib_available_p = 1 except: _zlib_available_p = 0 class pdf_stream(object): def __init__(self, fp): self.fp = fp self.off = 0 def write(self, str): self.fp.write(str) self.off += len(str) def tell(self): return self.off def to_radian(deg): return deg*2*math.pi / 360.0 class T(basecanvas.T): def __init__(self, fname, compress_p_): basecanvas.T.__init__(self) self.__out_fname = fname self.__reset_context() self.__next_obj_id = 1 self.__next_font_id = 1 self.__obj_offsets = {} self.__registered_fonts = {} self.__lines = [] self.__nr_gsave = 0 if compress_p_ and not _zlib_available_p: pychart_util.warn("Zlib not available. Compression request ignored.\n") compress_p_ = 0 self.__compress_p = compress_p_ def __intern_font(self, name): "Assign an ID to the font NAME. Return its ID." if not self.__registered_fonts.has_key(name): self.__registered_fonts[name] = self.__next_font_id self.__next_font_id += 1 return self.__registered_fonts[name] def __define_obj(self, fp, str): obj_id = self.__next_obj_id self.__next_obj_id += 1 self.__obj_offsets[obj_id] = fp.tell() fp.write("%d 0 obj\n%s\nendobj\n" % (obj_id, str)) return obj_id def __define_stream_obj(self, fp, s): if self.__compress_p: p = zlib.compress(s) return self.__define_obj(fp, "<>\nstream\n%sendstream" % (len(p), p)) else: return self.__define_obj(fp, "<>\nstream\n%s\nendstream" % (len(s), s)) def __define_font_obj(self, fp, name, font_id): obj_id = self.__define_obj(fp, """<>""" % (font_id, name)) return obj_id def __reset_context(self): self.__font_name = None self.__font_size = -1 self.__line_style = None self.__fill_color = None self.__stroke_color = None self.__mtx_pushed = 0 def newpath(self): pass def set_fill_color(self, color): if self.__fill_color == color: return if color.r == color.g and color.r == color.b: self.__write("%f g\n" % (color.r)) self.__write("%f G\n" % (color.r)) else: self.__write("%f %f %f rg\n" % (color.r, color.g, color.b)) self.__write("%f %f %f RG\n" % (color.r, color.g, color.b)) self.__fill_color = color def set_stroke_color(self, color): self.set_fill_color(color) return def __arcsub(self, x, y, radius, start, theta): xcos = math.cos(to_radian(theta)) xsin = math.sin(to_radian(theta)) x0 = radius * xcos y0 = radius * xsin x1 = radius * (4-xcos)/3.0 y1 = radius * (1-xcos)*(xcos-3)/(3*xsin) xx0, xy0 = pychart_util.rotate(x0, y0, start+theta) xx1, xy1 = pychart_util.rotate(x1, -y1, start+theta) xx2, xy2 = pychart_util.rotate(x1, y1, start+theta) self.__write("%f %f %f %f %f %f c\n" % (x+xx1, y+xy1, x+xx2, y+xy2, x+xx0, y+xy0)) def path_arc(self, x, y, radius, ratio, start, end): self.comment("PATHARC %f %f %f %f %f %f\n" % (x, y, radius, ratio, start, end)) step = 10 if radius < 10: step = 20 if radius < 5: step = 30 if ratio != 1.0: self.push_transformation((x, y), (1, ratio), None) deg = start while deg < end: theta = min(step, end-deg) self.__arcsub(x, y, radius, deg, theta/2) deg += theta self.pop_transformation() else: deg = start while deg < end: theta = min(step, end-deg) self.__arcsub(x, y, radius, deg, theta/2) deg += theta self.comment("end PATHARC\n") def text_begin(self): self.__write("BT ") self.__font_name = None self.__font_size = None def text_end(self): self.__write("ET\n") def text_moveto(self, x, y, angle): if angle != None: xcos = math.cos(to_radian(angle)) xsin = math.sin(to_radian(angle)) self.__write("%f %f %f %f %f %f Tm " % (xcos, xsin, -xsin, xcos, x, y)) else: self.__write("1 0 0 1 %f %f Tm " % (x, y)) def text_show(self, font_name, font_size, color, str): if self.__font_name != font_name or self.__font_size != font_size: font_id = self.__intern_font(font_name) self.__write("/F%d %d Tf " % (font_id, font_size)) self.__font_name = font_name self.__font_size = font_size self.set_fill_color(color) self.__write("(%s) Tj " % (str)) def push_transformation(self, baseloc, scale, angle, in_text = 0): if in_text: op = "Tm" else: op = "cm" self.gsave() if baseloc == None: baseloc = (0,0) if angle != None: radian = to_radian(angle) self.__write("%f %f %f %f %f %f %s\n" % (math.cos(radian), math.sin(radian), -math.sin(radian), math.cos(radian), baseloc[0], baseloc[1], op)) if scale != None: self.__write("%f 0 0 %f %f %f %s\n" % (scale[0], scale[1], baseloc[0], baseloc[1], op)) def pop_transformation(self, in_text = 0): if not in_text: self.grestore() def closepath(self): self.__write("h\n") def clip_sub(self): self.__write("W n\n") def fill(self): self.__write("f n\n") def gsave(self): self.__write("q\n") def grestore(self): self.__write("Q\n") self.__reset_context() def moveto(self, x, y): self.__write('%f %f m ' % (x, y)) def lineto(self, x, y): self.__write("%f %f l\n" % (x, y)) def stroke(self): self.__write("S\n") def set_line_style(self, style): if (self.__line_style == style): pass else: self.set_stroke_color(style.color) self.__write("%f w " % nscale(style.width)) if style.dash != None: self.__write("[%s] 0 d\n" % " ".join(map(str, nscale_seq(style.dash)))) else: self.__write("[] 0 d\n") self.__write("%d j %d J\n" % (style.cap_style, style.join_style)) self.__line_style = style def comment(self, str): if not self.__compress_p: self.__write("%%" + str + "\n") def verbatim(self, str): self.__write(str) def __write(self, str): self.__lines.append(str) # def setbb(self, xmin, ymin, xmax, ymax): # self.__xmin = xmin # self.__ymin = ymin # self.__xmax = xmax # self.__ymax = ymax def close(self): basecanvas.T.close(self) if self.__lines == []: return _fp, need_close = self.open_output(self.__out_fname) fp = pdf_stream(_fp) fp.write("%PDF-1.2\n") stream_obj_id = self.__define_stream_obj(fp, " ".join(self.__lines)) fontstr = "" for font_name, font_id in self.__registered_fonts.items(): obj_id = self.__define_font_obj(fp, font_name, font_id) fontstr += "/F%d %d 0 R " % (font_id, obj_id) pages_obj_id = self.__define_obj(fp, " <>" % (self.__next_obj_id + 1)) bbox = theme.adjust_bounding_box([xscale(self.__xmin), yscale(self.__ymin), xscale(self.__xmax), yscale(self.__ymax)]) page_obj_id = self.__define_obj(fp, """ <> >> >>""" % (pages_obj_id, stream_obj_id, bbox[0], bbox[1], bbox[2], bbox[3], fontstr)) info_str = "/Producer (%s)\n/CreationDate (%s)" % (self.creator, self.creation_date) if self.title: info_str += "\n/Title (%s)" % (self.title, ) if self.author: info_str += "\n/Author (%s)" % (self.author, ) info_obj_id = self.__define_obj(fp, """<<%s>>""" % info_str) catalog_obj_id = self.__define_obj(fp, """ <>""" % (pages_obj_id)) xref_offset = fp.tell() fp.write("xref\n0 %d\n" % (len(self.__obj_offsets)+1)) fp.write("0000000000 65535 f \n") id = 1 while id <= len(self.__obj_offsets): fp.write("%010d 00000 n \n" % (self.__obj_offsets[id])) id += 1 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)) fp.write("startxref\n%d\n%%%%EOF\n" % xref_offset) if need_close: _fp.close()