pdfwriter.py :  » PDF » PyX » PyX-0.10 » pyx » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » PDF » PyX 
PyX » PyX 0.10 » pyx » pdfwriter.py
# -*- coding: ISO-8859-1 -*-
#
#
# Copyright (C) 2005-2006 Jrg 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
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.