mirror of https://github.com/VLSIDA/OpenRAM.git
499 lines
17 KiB
Python
499 lines
17 KiB
Python
# -*- coding: ISO-8859-1 -*-
|
|
#
|
|
#
|
|
# Copyright (C) 2005-2006 Jörg Lehmann <joergl@users.sourceforge.net>
|
|
# Copyright (C) 2005-2006 André Wobst <wobsta@users.sourceforge.net>
|
|
#
|
|
# This file is part of PyX (http://pyx.sourceforge.net/).
|
|
#
|
|
# PyX 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 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# PyX 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.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with PyX; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
import cStringIO, copy, warnings, time
|
|
try:
|
|
import zlib
|
|
haszlib = 1
|
|
except:
|
|
haszlib = 0
|
|
|
|
import bbox, unit, style, type1font, version
|
|
|
|
try:
|
|
enumerate([])
|
|
except NameError:
|
|
# fallback implementation for Python 2.2 and below
|
|
def enumerate(list):
|
|
return zip(xrange(len(list)), list)
|
|
|
|
try:
|
|
dict([])
|
|
except NameError:
|
|
# fallback implementation for Python 2.1
|
|
def dict(list):
|
|
result = {}
|
|
for key, value in list:
|
|
result[key] = value
|
|
return result
|
|
|
|
|
|
class PDFregistry:
|
|
|
|
def __init__(self):
|
|
self.types = {}
|
|
# we want to keep the order of the resources
|
|
self.objects = []
|
|
self.resources = {}
|
|
self.procsets = {"PDF": 1}
|
|
self.merged = None
|
|
|
|
def add(self, object):
|
|
""" register object, merging it with an already registered object of the same type and id """
|
|
sameobjects = self.types.setdefault(object.type, {})
|
|
if sameobjects.has_key(object.id):
|
|
sameobjects[object.id].merge(object)
|
|
else:
|
|
self.objects.append(object)
|
|
sameobjects[object.id] = object
|
|
|
|
def getrefno(self, object):
|
|
if self.merged:
|
|
return self.merged.getrefno(object)
|
|
else:
|
|
return self.types[object.type][object.id].refno
|
|
|
|
def mergeregistry(self, registry):
|
|
for object in registry.objects:
|
|
self.add(object)
|
|
registry.merged = self
|
|
|
|
def write(self, file, writer, catalog):
|
|
# first we set all refnos
|
|
refno = 1
|
|
for object in self.objects:
|
|
object.refno = refno
|
|
refno += 1
|
|
|
|
# second, all objects are written, keeping the positions in the output file
|
|
fileposes = []
|
|
for object in self.objects:
|
|
fileposes.append(file.tell())
|
|
file.write("%i 0 obj\n" % object.refno)
|
|
object.write(file, writer, self)
|
|
file.write("endobj\n")
|
|
|
|
# xref
|
|
xrefpos = file.tell()
|
|
file.write("xref\n"
|
|
"0 %d\n"
|
|
"0000000000 65535 f \n" % refno)
|
|
|
|
for filepos in fileposes:
|
|
file.write("%010i 00000 n \n" % filepos)
|
|
|
|
# trailer
|
|
file.write("trailer\n"
|
|
"<<\n"
|
|
"/Size %i\n" % refno)
|
|
file.write("/Root %i 0 R\n" % self.getrefno(catalog))
|
|
file.write("/Info %i 0 R\n" % self.getrefno(catalog.PDFinfo))
|
|
file.write(">>\n"
|
|
"startxref\n"
|
|
"%i\n" % xrefpos)
|
|
file.write("%%EOF\n")
|
|
|
|
def addresource(self, resourcetype, resourcename, object, procset=None):
|
|
self.resources.setdefault(resourcetype, {})[resourcename] = object
|
|
if procset:
|
|
self.procsets[procset] = 1
|
|
|
|
def writeresources(self, file):
|
|
file.write("/Resources <<\n")
|
|
file.write("/ProcSet [ %s ]\n" % " ".join(["/%s" % p for p in self.procsets.keys()]))
|
|
if self.resources:
|
|
for resourcetype, resources in self.resources.items():
|
|
file.write("/%s <<\n%s\n>>\n" % (resourcetype, "\n".join(["/%s %i 0 R" % (name, self.getrefno(object))
|
|
for name, object in resources.items()])))
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFobject:
|
|
|
|
def __init__(self, type, _id=None):
|
|
"""create a PDFobject
|
|
- type has to be a string describing the type of the object
|
|
- _id is a unique identification used for the object if it is not None.
|
|
Otherwise id(self) is used
|
|
"""
|
|
self.type = type
|
|
if _id is None:
|
|
self.id = id(self)
|
|
else:
|
|
self.id = _id
|
|
|
|
def merge(self, other):
|
|
pass
|
|
|
|
def write(self, file, writer, registry):
|
|
raise NotImplementedError("write method has to be provided by PDFobject subclass")
|
|
|
|
|
|
class PDFcatalog(PDFobject):
|
|
|
|
def __init__(self, document, writer, registry):
|
|
PDFobject.__init__(self, "catalog")
|
|
self.PDFpages = PDFpages(document, writer, registry)
|
|
registry.add(self.PDFpages)
|
|
self.PDFinfo = PDFinfo()
|
|
registry.add(self.PDFinfo)
|
|
|
|
def write(self, file, writer, registry):
|
|
file.write("<<\n"
|
|
"/Type /Catalog\n"
|
|
"/Pages %i 0 R\n" % registry.getrefno(self.PDFpages))
|
|
if writer.fullscreen:
|
|
file.write("/PageMode /FullScreen\n")
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFinfo(PDFobject):
|
|
|
|
def __init__(self):
|
|
PDFobject.__init__(self, "info")
|
|
|
|
def write(self, file, writer, registry):
|
|
if time.timezone < 0:
|
|
# divmod on positive numbers, otherwise the minutes have a different sign from the hours
|
|
timezone = "-%02i'%02i'" % divmod(-time.timezone/60, 60)
|
|
elif time.timezone > 0:
|
|
timezone = "+%02i'%02i'" % divmod(time.timezone/60, 60)
|
|
else:
|
|
timezone = "Z00'00'"
|
|
|
|
def pdfstring(s):
|
|
r = ""
|
|
for c in s:
|
|
if 32 <= ord(c) <= 127 and c not in "()[]<>\\":
|
|
r += c
|
|
else:
|
|
r += "\\%03o" % ord(c)
|
|
return r
|
|
|
|
file.write("<<\n")
|
|
if writer.title:
|
|
file.write("/Title (%s)\n" % pdfstring(writer.title))
|
|
if writer.author:
|
|
file.write("/Author (%s)\n" % pdfstring(writer.author))
|
|
if writer.subject:
|
|
file.write("/Subject (%s)\n" % pdfstring(writer.subject))
|
|
if writer.keywords:
|
|
file.write("/Keywords (%s)\n" % pdfstring(writer.keywords))
|
|
file.write("/Creator (PyX %s)\n" % version.version)
|
|
file.write("/CreationDate (D:%s%s)\n" % (time.strftime("%Y%m%d%H%M"), timezone))
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFpages(PDFobject):
|
|
|
|
def __init__(self, document, writer, registry):
|
|
PDFobject.__init__(self, "pages")
|
|
self.PDFpagelist = []
|
|
for pageno, page in enumerate(document.pages):
|
|
page = PDFpage(page, pageno, self, writer, registry)
|
|
registry.add(page)
|
|
self.PDFpagelist.append(page)
|
|
|
|
def write(self, file, writer, registry):
|
|
file.write("<<\n"
|
|
"/Type /Pages\n"
|
|
"/Kids [%s]\n"
|
|
"/Count %i\n"
|
|
">>\n" % (" ".join(["%i 0 R" % registry.getrefno(page)
|
|
for page in self.PDFpagelist]),
|
|
len(self.PDFpagelist)))
|
|
|
|
|
|
class PDFpage(PDFobject):
|
|
|
|
def __init__(self, page, pageno, PDFpages, writer, registry):
|
|
PDFobject.__init__(self, "page")
|
|
self.PDFpages = PDFpages
|
|
self.page = page
|
|
|
|
# every page uses its own registry in order to find out which
|
|
# resources are used within the page. However, the
|
|
# pageregistry is also merged in the global registry
|
|
self.pageregistry = PDFregistry()
|
|
|
|
self.PDFcontent = PDFcontent(page, writer, self.pageregistry)
|
|
self.pageregistry.add(self.PDFcontent)
|
|
registry.mergeregistry(self.pageregistry)
|
|
|
|
def write(self, file, writer, registry):
|
|
file.write("<<\n"
|
|
"/Type /Page\n"
|
|
"/Parent %i 0 R\n" % registry.getrefno(self.PDFpages))
|
|
paperformat = self.page.paperformat
|
|
if paperformat:
|
|
file.write("/MediaBox [0 0 %f %f]\n" % (unit.topt(paperformat.width), unit.topt(paperformat.height)))
|
|
else:
|
|
file.write("/MediaBox [%f %f %f %f]\n" % self.PDFcontent.bbox.highrestuple_pt())
|
|
if self.PDFcontent.bbox and writer.writebbox:
|
|
file.write("/CropBox [%f %f %f %f]\n" % self.PDFcontent.bbox.highrestuple_pt())
|
|
if self.page.rotated:
|
|
file.write("/Rotate 90\n")
|
|
file.write("/Contents %i 0 R\n" % registry.getrefno(self.PDFcontent))
|
|
self.pageregistry.writeresources(file)
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFcontent(PDFobject):
|
|
|
|
def __init__(self, page, writer, registry):
|
|
PDFobject.__init__(self, registry, "content")
|
|
contentfile = cStringIO.StringIO()
|
|
self.bbox = bbox.empty()
|
|
acontext = context()
|
|
page.processPDF(contentfile, writer, acontext, registry, self.bbox)
|
|
self.content = contentfile.getvalue()
|
|
contentfile.close()
|
|
|
|
def write(self, file, writer, registry):
|
|
if writer.compress:
|
|
content = zlib.compress(self.content)
|
|
else:
|
|
content = self.content
|
|
file.write("<<\n"
|
|
"/Length %i\n" % len(content))
|
|
if writer.compress:
|
|
file.write("/Filter /FlateDecode\n")
|
|
file.write(">>\n"
|
|
"stream\n")
|
|
file.write(content)
|
|
file.write("endstream\n")
|
|
|
|
|
|
class PDFfont(PDFobject):
|
|
|
|
def __init__(self, font, chars, writer, registry):
|
|
PDFobject.__init__(self, "font", font.name)
|
|
registry.addresource("Font", font.name, self, procset="Text")
|
|
|
|
self.fontdescriptor = PDFfontdescriptor(font, chars, writer, registry)
|
|
registry.add(self.fontdescriptor)
|
|
|
|
if font.encoding:
|
|
self.encoding = PDFencoding(font.encoding, writer, registry)
|
|
registry.add(self.encoding)
|
|
else:
|
|
self.encoding = None
|
|
|
|
self.name = font.name
|
|
self.basefontname = font.basefontname
|
|
self.metric = font.metric
|
|
|
|
def write(self, file, writer, registry):
|
|
file.write("<<\n"
|
|
"/Type /Font\n"
|
|
"/Subtype /Type1\n")
|
|
file.write("/Name /%s\n" % self.name)
|
|
file.write("/BaseFont /%s\n" % self.basefontname)
|
|
if self.fontdescriptor.fontfile is not None and self.fontdescriptor.fontfile.usedchars is not None:
|
|
usedchars = self.fontdescriptor.fontfile.usedchars
|
|
firstchar = min(usedchars.keys())
|
|
lastchar = max(usedchars.keys())
|
|
file.write("/FirstChar %d\n" % firstchar)
|
|
file.write("/LastChar %d\n" % lastchar)
|
|
file.write("/Widths\n"
|
|
"[")
|
|
for i in range(firstchar, lastchar+1):
|
|
if i and not (i % 8):
|
|
file.write("\n")
|
|
else:
|
|
file.write(" ")
|
|
if usedchars.has_key(i):
|
|
file.write("%f" % self.metric.getwidth_ds(i))
|
|
else:
|
|
file.write("0")
|
|
file.write(" ]\n")
|
|
else:
|
|
file.write("/FirstChar 0\n"
|
|
"/LastChar 255\n"
|
|
"/Widths\n"
|
|
"[")
|
|
for i in range(256):
|
|
if i and not (i % 8):
|
|
file.write("\n")
|
|
else:
|
|
file.write(" ")
|
|
try:
|
|
width = self.metric.getwidth_ds(i)
|
|
except (IndexError, AttributeError):
|
|
width = 0
|
|
file.write("%f" % width)
|
|
file.write(" ]\n")
|
|
file.write("/FontDescriptor %d 0 R\n" % registry.getrefno(self.fontdescriptor))
|
|
if self.encoding:
|
|
file.write("/Encoding %d 0 R\n" % registry.getrefno(self.encoding))
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFfontdescriptor(PDFobject):
|
|
|
|
def __init__(self, font, chars, writer, registry):
|
|
PDFobject.__init__(self, "fontdescriptor", font.basefontname)
|
|
|
|
if font.filename is None:
|
|
self.fontfile = None
|
|
else:
|
|
self.fontfile = PDFfontfile(font.basefontname, font.filename, font.encoding, chars, writer, registry)
|
|
registry.add(self.fontfile)
|
|
|
|
self.name = font.basefontname
|
|
self.fontinfo = font.metric.fontinfo()
|
|
|
|
def write(self, file, writer, registry):
|
|
file.write("<<\n"
|
|
"/Type /FontDescriptor\n"
|
|
"/FontName /%s\n" % self.name)
|
|
if self.fontfile is None:
|
|
file.write("/Flags 32\n")
|
|
else:
|
|
file.write("/Flags %d\n" % self.fontfile.getflags())
|
|
file.write("/FontBBox [%d %d %d %d]\n" % self.fontinfo.fontbbox)
|
|
file.write("/ItalicAngle %d\n" % self.fontinfo.italicangle)
|
|
file.write("/Ascent %d\n" % self.fontinfo.ascent)
|
|
file.write("/Descent %d\n" % self.fontinfo.descent)
|
|
file.write("/CapHeight %d\n" % self.fontinfo.capheight)
|
|
file.write("/StemV %d\n" % self.fontinfo.vstem)
|
|
if self.fontfile is not None:
|
|
file.write("/FontFile %d 0 R\n" % registry.getrefno(self.fontfile))
|
|
file.write(">>\n")
|
|
|
|
|
|
class PDFfontfile(PDFobject):
|
|
|
|
def __init__(self, name, filename, encoding, chars, writer, registry):
|
|
PDFobject.__init__(self, "fontfile", filename)
|
|
self.name = name
|
|
self.filename = filename
|
|
if encoding is None:
|
|
self.encodingfilename = None
|
|
else:
|
|
self.encodingfilename = encoding.filename
|
|
self.usedchars = {}
|
|
for char in chars:
|
|
self.usedchars[char] = 1
|
|
|
|
self.strip = 1
|
|
self.font = None
|
|
|
|
def merge(self, other):
|
|
if self.encodingfilename == other.encodingfilename:
|
|
self.usedchars.update(other.usedchars)
|
|
else:
|
|
# TODO: need to resolve the encoding when several encodings are in the play
|
|
self.strip = 0
|
|
|
|
def mkfontfile(self):
|
|
import font.t1font
|
|
self.font = font.t1font.T1pfbfont(self.filename)
|
|
|
|
def getflags(self):
|
|
if self.font is None:
|
|
self.mkfontfile()
|
|
return self.font.getflags()
|
|
|
|
def write(self, file, writer, registry):
|
|
if self.font is None:
|
|
self.mkfontfile()
|
|
if self.strip:
|
|
# XXX: access to the encoding file
|
|
if self.encodingfilename:
|
|
encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename)
|
|
usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()])
|
|
else:
|
|
self.font._encoding()
|
|
usedglyphs = dict([(self.font.encoding.decode(char), 1) for char in self.usedchars.keys()])
|
|
strippedfont = self.font.getstrippedfont(usedglyphs)
|
|
else:
|
|
strippedfont = self.font
|
|
strippedfont.outputPDF(file, writer)
|
|
|
|
|
|
class PDFencoding(PDFobject):
|
|
|
|
def __init__(self, encoding, writer, registry):
|
|
PDFobject.__init__(self, "encoding", encoding.name)
|
|
self.encoding = encoding
|
|
|
|
def write(self, file, writer, registry):
|
|
encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename)
|
|
encodingfile.outputPDF(file, writer)
|
|
|
|
|
|
class PDFwriter:
|
|
|
|
def __init__(self, document, file,
|
|
title=None, author=None, subject=None, keywords=None,
|
|
fullscreen=0, writebbox=0, compress=1, compresslevel=6):
|
|
try:
|
|
file.write("")
|
|
except:
|
|
filename = file
|
|
if not filename.endswith(".pdf"):
|
|
filename += ".pdf"
|
|
try:
|
|
file = open(filename, "wb")
|
|
except IOError:
|
|
raise IOError("cannot open output file")
|
|
|
|
self.title = title
|
|
self.author = author
|
|
self.subject = subject
|
|
self.keywords = keywords
|
|
self.fullscreen = fullscreen
|
|
self.writebbox = writebbox
|
|
if compress and not haszlib:
|
|
compress = 0
|
|
warnings.warn("compression disabled due to missing zlib module")
|
|
self.compress = compress
|
|
self.compresslevel = compresslevel
|
|
|
|
# the PDFcatalog class automatically builds up the pdfobjects from a document
|
|
registry = PDFregistry()
|
|
catalog = PDFcatalog(document, self, registry)
|
|
registry.add(catalog)
|
|
|
|
file.write("%%PDF-1.4\n%%%s%s%s%s\n" % (chr(195), chr(182), chr(195), chr(169)))
|
|
registry.write(file, self, catalog)
|
|
file.close()
|
|
|
|
|
|
class context:
|
|
|
|
def __init__(self):
|
|
self.linewidth_pt = None
|
|
# XXX there are both stroke and fill color spaces
|
|
self.colorspace = None
|
|
self.strokeattr = 1
|
|
self.fillattr = 1
|
|
self.font = None
|
|
self.textregion = 0
|
|
|
|
def __call__(self, **kwargs):
|
|
newcontext = copy.copy(self)
|
|
for key, value in kwargs.items():
|
|
setattr(newcontext, key, value)
|
|
return newcontext
|