pswriter.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 » pswriter.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, time, math
import bbox, style, version, type1font, unit, trafo

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 PSregistry:

    def __init__(self):
        # in order to keep a consistent order of the registered resources we
        # not only store them in a hash but also keep an ordered list (up to a
        # possible merging of resources, in which case the first instance is
        # kept)
        self.resourceshash = {}
        self.resourceslist = []

    def add(self, resource):
        rkey = (resource.type, resource.id)
        if self.resourceshash.has_key(rkey):
           self.resourceshash[rkey].merge(resource)
        else:
           self.resourceshash[rkey] = resource
           self.resourceslist.append(resource)

    def mergeregistry(self, registry):
        for resource in registry.resources:
            self.add(resource)

    def output(self, file, writer):
        """ write all PostScript code of the prolog resources """
        for resource in self.resourceslist:
            resource.output(file, writer, self)

#
# Abstract base class
#

class PSresource:

    """ a PostScript resource """

    def __init__(self, type, id):
        # Every PSresource has to have a type and a unique id.
        # Resources with the same type and id will be merged
        # when they are registered in the PSregistry
        self.type = type
        self.id = id

    def merge(self, other):
        """ merge self with other, which has to be a resource of the same type and with
        the same id"""
        pass

    def output(self, file, writer, registry):
        raise NotImplementedError("output not implemented for %s" % repr(self))

#
# Different variants of prolog items
#

class PSdefinition(PSresource):

    """ PostScript function definition included in the prolog """

    def __init__(self, id, body):
        self.type = "definition"
        self.id = id
        self.body = body

    def output(self, file, writer, registry):
        file.write("%%%%BeginRessource: %s\n" % self.id)
        file.write("%(body)s /%(id)s exch def\n" % self.__dict__)
        file.write("%%EndRessource\n")


class PSfont:

    def __init__(self, font, chars, registry):
        if font.filename:
            registry.add(PSfontfile(font.basefontname,
                                    font.filename,
                                    font.encoding,
                                    chars))
        if font.encoding and font.slant:
            assert font.encname
            # do first the reencoding and then the slanting:
            enc_basename, enc_finalname = font.basefontname, font.encname
            slt_basename, slt_finalname = tfont.encname, font.name
        elif font.encoding:
            enc_basename, enc_finalname = font.basefontname, font.name
        elif font.slant:
            slt_basename, slt_finalname = font.basefontname, font.name

        if font.encoding:
            registry.add(_ReEncodeFont)
            registry.add(PSfontencoding(font.encoding))
            registry.add(PSfontreencoding(enc_finalname, enc_basename, font.encoding.name))

        if font.slant:
            # we need the current fontmatrix in order to manipulate it:
            # for this we need to re-read the fontfile as below in
            # PSfontfile.ouput:
            # XXX Is there a better way to do this?
            t = trafo.trafo_pt(matrix=((1, font.slant), (0, 1)))
            if font.filename:
                # for the builtin fonts, we assume a trivial fontmatrix
                import font.t1font as t1fontmodule
                t1font = t1fontmodule.T1pfbfont(font.filename)
                m = t1font.fontmatrixpattern.search(t1font.data1)
                m11, m12, m21, m22, v1, v2 = map(float, m.groups()[:6])
                t *= trafo.trafo_pt(matrix=((m11, m12), (m21, m22)), vector=(v1, v2))
            else:
                raise NotImplementedError(
                "cannot slant unembedded fonts -- try to include \"download35.map\" in fontmaps")
            registry.add(PSfontslanting(slt_finalname, slt_basename, t.__str__()))


class PSfontfile(PSresource):

    """ PostScript font definition included in the prolog """

    def __init__(self, name, filename, encoding, chars):
        """ include type 1 font defined by the following parameters

        - name:        name of the PostScript font
        - filename:    name (without path) of file containing the font definition
        - encfilename: name (without path) of file containing used encoding of font
                       or None (if no encoding file used)
        - chars:       character list to fill usedchars

        """

        # Note that here we only need the encoding for selecting the used glyphs!

        self.type = "fontfile"
        self.id = 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

    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 output(self, file, writer, registry):
        import font.t1font
        font = font.t1font.T1pfbfont(self.filename)

        file.write("%%%%BeginFont: %s\n" % self.name)
        # file.write("%%Included glyphs: %s\n" % " ".join(usedglyphs))
        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:
                font._encoding()
                usedglyphs = dict([(font.encoding.decode(char), 1) for char in self.usedchars.keys()])
            strippedfont = font.getstrippedfont(usedglyphs)
        else:
            strippedfont = font
        strippedfont.outputPS(file, writer)
        file.write("\n%%EndFont\n")


class PSfontencoding(PSresource):

    """ PostScript font encoding vector included in the prolog """

    def __init__(self, encoding):
        """ include font encoding vector specified by encoding """

        self.type = "fontencoding"
        self.id = encoding.name
        self.encoding = encoding

    def output(self, file, writer, registry):
        encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename)
        encodingfile.outputPS(file, writer)


class PSfontslanting(PSresource):

    """ PostScript font slanting directive included in the prolog """

    def __init__(self, fontname, basefontname, matrixstring):
        """ include transformed font directive specified by

        - fontname:     PostScript FontName of the new slanted font
        - basefontname: PostScript FontName of the original font
        - slant:        the value of slanting
        """

        self.type = "fontslanting"
        self.id = self.fontname = fontname
        self.basefontname = basefontname
        self.matrixstring = matrixstring

    def output(self, file, writer, registry):
        file.write("%%%%BeginProcSet: %s\n" % self.fontname)
        file.write("/%s findfont\n" % self.basefontname)
        file.write("dup length dict begin\n")
        file.write("{ 1 index /FID ne {def} {pop pop} ifelse } forall\n")
        file.write("/FontMatrix %s readonly def\n" % self.matrixstring)
        file.write("currentdict\n")
        file.write("end\n")
        file.write("/%s exch definefont pop\n" % self.fontname)
        file.write("%%EndProcSet\n")

class PSfontreencoding(PSresource):

    """ PostScript font re-encoding directive included in the prolog """

    def __init__(self, fontname, basefontname, encodingname):
        """ include font re-encoding directive specified by

        - fontname:     PostScript FontName of the new reencoded font
        - basefontname: PostScript FontName of the original font
        - encname:      name of the encoding

        Before being able to reencode a font, you have to include the
        encoding via a fontencoding prolog item with name=encname

        """

        self.type = "fontreencoding"
        self.id = self.fontname = fontname
        self.basefontname = basefontname
        self.encodingname = encodingname

    def output(self, file, writer, registry):
        file.write("%%%%BeginProcSet: %s\n" % self.fontname)
        file.write("/%s /%s %s ReEncodeFont\n" % (self.basefontname, self.fontname, self.encodingname))
        file.write("%%EndProcSet\n")


_ReEncodeFont = PSdefinition("ReEncodeFont", """{
  5 dict
  begin
    /newencoding exch def
    /newfontname exch def
    /basefontname exch def
    /basefontdict basefontname findfont def
    /newfontdict basefontdict maxlength dict def
    basefontdict {
      exch dup dup /FID ne exch /Encoding ne and
      { exch newfontdict 3 1 roll put }
      { pop pop }
      ifelse
    } forall
    newfontdict /FontName newfontname put
    newfontdict /Encoding newencoding put
    newfontname newfontdict definefont pop
  end
}""")


class epswriter:

    def __init__(self, document, file):
        if len(document.pages) != 1:
            raise ValueError("EPS file can be constructed out of a single page document only")
        page = document.pages[0]
        canvas = page.canvas

        try:
            file.write("")
        except:
            filename = file
            if not filename.endswith(".eps"):
                filename += ".eps"
            try:
                file = open(filename, "w")
            except IOError:
                raise IOError("cannot open output file")
        else:
            filename = "stream"

        pagefile = cStringIO.StringIO()
        registry = PSregistry()
        acontext = context()
        pagebbox = bbox.empty()

        page.processPS(pagefile, self, acontext, registry, pagebbox)

        file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
        if pagebbox:
            file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
            file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
        file.write("%%%%Creator: PyX %s\n" % version.version)
        file.write("%%%%Title: %s\n" % filename)
        file.write("%%%%CreationDate: %s\n" %
                   time.asctime(time.localtime(time.time())))
        file.write("%%EndComments\n")

        file.write("%%BeginProlog\n")
        registry.output(file, self)
        file.write("%%EndProlog\n")

        file.write(pagefile.getvalue())
        pagefile.close()

        file.write("showpage\n")
        file.write("%%Trailer\n")
        file.write("%%EOF\n")


class pswriter:

    def __init__(self, document, file, writebbox=0):
        try:
            file.write("")
        except:
            filename = file
            if not filename.endswith(".ps"):
                filename += ".ps"
            try:
                file = open(filename, "w")
            except IOError:
                raise IOError("cannot open output file")
        else:
            filename = "stream"

        # We first have to process the content of the pages, writing them into the stream pagesfile
        # Doing so, we fill the registry and also calculate the page bounding boxes, which are
        # stored in page._bbox for every page
        pagesfile = cStringIO.StringIO()
        registry = PSregistry()

        # calculated bounding boxes of the whole document
        documentbbox = bbox.empty()

        for nr, page in enumerate(document.pages):
            # process contents of page
            pagefile = cStringIO.StringIO()
            acontext = context()
            pagebbox = bbox.empty()
            page.processPS(pagefile, self, acontext, registry, pagebbox)

            documentbbox += pagebbox

            pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1))
            if page.paperformat:
                pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name)
            pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait"))
            if pagebbox and writebbox:
                pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())

            # page setup section
            pagesfile.write("%%BeginPageSetup\n")
            pagesfile.write("/pgsave save def\n")

            pagesfile.write("%%EndPageSetup\n")
            pagesfile.write(pagefile.getvalue())
            pagefile.close()
            pagesfile.write("pgsave restore\n")
            pagesfile.write("showpage\n")
            pagesfile.write("%%PageTrailer\n")

        file.write("%!PS-Adobe-3.0\n")
        if documentbbox and writebbox:
            file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
            file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
        file.write("%%%%Creator: PyX %s\n" % version.version)
        file.write("%%%%Title: %s\n" % filename)
        file.write("%%%%CreationDate: %s\n" %
                   time.asctime(time.localtime(time.time())))

        # required paper formats
        paperformats = {}
        for page in document.pages:
            if page.paperformat:
                paperformats[page.paperformat] = page.paperformat

        first = 1
        for paperformat in paperformats.values():
            if first:
                file.write("%%DocumentMedia: ")
                first = 0
            else:
                file.write("%%+ ")
            file.write("%s %d %d 75 white ()\n" % (paperformat.name,
                                                   unit.topt(paperformat.width),
                                                   unit.topt(paperformat.height)))

        # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here

        file.write("%%%%Pages: %d\n" % len(document.pages))
        file.write("%%PageOrder: Ascend\n")
        file.write("%%EndComments\n")

        # document defaults section
        #file.write("%%BeginDefaults\n")
        #file.write("%%EndDefaults\n")

        # document prolog section
        file.write("%%BeginProlog\n")
        registry.output(file, self)
        file.write("%%EndProlog\n")

        # document setup section
        #file.write("%%BeginSetup\n")
        #file.write("%%EndSetup\n")

        file.write(pagesfile.getvalue())
        pagesfile.close()

        file.write("%%Trailer\n")
        file.write("%%EOF\n")

class context:

    def __init__(self):
        self.linewidth_pt = None
        self.colorspace = None
        self.font = None

    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.