298 lines
9.8 KiB
Python
298 lines
9.8 KiB
Python
#
|
|
# 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, "<</Length %d/Filter/FlateDecode>>\nstream\n%sendstream"
|
|
% (len(p), p))
|
|
else:
|
|
return self.__define_obj(fp, "<</Length %d\n>>\nstream\n%s\nendstream"
|
|
% (len(s), s))
|
|
|
|
def __define_font_obj(self, fp, name, font_id):
|
|
obj_id = self.__define_obj(fp, """<</Type/Font /Subtype/Type1 /Name/F%d /BaseFont/%s /Encoding/MacRomanEncoding>>""" % (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, " <</Type/Pages /Kids [%d 0 R] /Count 1 >>" % (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, """ <</Type/Page
|
|
\t/Parent %d 0 R
|
|
\t/Contents %d 0 R
|
|
\t/MediaBox [%d %d %d %d]
|
|
\t/Resources << /ProcSet [/PDF /Text]
|
|
\t\t/Font << %s >>
|
|
>> >>""" % (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, """ <</Type/Catalog/Pages %d 0 R>>""" % (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()
|