~ubuntu-branches/debian/sid/pyx/sid

« back to all changes in this revision

Viewing changes to pyx/pswriter.py

  • Committer: Bazaar Package Importer
  • Author(s): Stuart Prescott
  • Date: 2011-05-20 00:13:52 UTC
  • mto: (9.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20110520001352-odcuqpdezuusbbw1
Tags: upstream-0.11.1
Import upstream version 0.11.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: ISO-8859-1 -*-
2
 
#
3
 
#
4
 
# Copyright (C) 2005-2006 J�rg Lehmann <joergl@users.sourceforge.net>
5
 
# Copyright (C) 2005-2006 Andr� Wobst <wobsta@users.sourceforge.net>
 
1
# -*- encoding: utf-8 -*-
 
2
#
 
3
#
 
4
# Copyright (C) 2005-2011 Jörg Lehmann <joergl@users.sourceforge.net>
 
5
# Copyright (C) 2005-2011 André Wobst <wobsta@users.sourceforge.net>
6
6
#
7
7
# This file is part of PyX (http://pyx.sourceforge.net/).
8
8
#
21
21
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22
22
 
23
23
import cStringIO, copy, time, math
24
 
import bbox, style, version, type1font, unit, trafo
25
 
 
26
 
try:
27
 
    enumerate([])
28
 
except NameError:
29
 
    # fallback implementation for Python 2.2 and below
30
 
    def enumerate(list):
31
 
        return zip(xrange(len(list)), list)
32
 
 
33
 
try:
34
 
    dict([])
35
 
except NameError:
36
 
    # fallback implementation for Python 2.1
37
 
    def dict(list):
38
 
        result = {}
39
 
        for key, value in list:
40
 
            result[key] = value
41
 
        return result
 
24
import bbox, config, style, version, unit, trafo
42
25
 
43
26
 
44
27
class PSregistry:
91
74
    def output(self, file, writer, registry):
92
75
        raise NotImplementedError("output not implemented for %s" % repr(self))
93
76
 
94
 
#
95
 
# Different variants of prolog items
96
 
#
97
 
 
98
77
class PSdefinition(PSresource):
99
78
 
100
79
    """ PostScript function definition included in the prolog """
105
84
        self.body = body
106
85
 
107
86
    def output(self, file, writer, registry):
108
 
        file.write("%%%%BeginRessource: %s\n" % self.id)
 
87
        file.write("%%%%BeginResource: %s\n" % self.id)
109
88
        file.write("%(body)s /%(id)s exch def\n" % self.__dict__)
110
 
        file.write("%%EndRessource\n")
111
 
 
112
 
 
113
 
class PSfont:
114
 
 
115
 
    def __init__(self, font, chars, registry):
116
 
        if font.filename:
117
 
            registry.add(PSfontfile(font.basefontname,
118
 
                                    font.filename,
119
 
                                    font.encoding,
120
 
                                    chars))
121
 
        if font.encoding and font.slant:
122
 
            assert font.encname
123
 
            # do first the reencoding and then the slanting:
124
 
            enc_basename, enc_finalname = font.basefontname, font.encname
125
 
            slt_basename, slt_finalname = tfont.encname, font.name
126
 
        elif font.encoding:
127
 
            enc_basename, enc_finalname = font.basefontname, font.name
128
 
        elif font.slant:
129
 
            slt_basename, slt_finalname = font.basefontname, font.name
130
 
 
131
 
        if font.encoding:
132
 
            registry.add(_ReEncodeFont)
133
 
            registry.add(PSfontencoding(font.encoding))
134
 
            registry.add(PSfontreencoding(enc_finalname, enc_basename, font.encoding.name))
135
 
 
136
 
        if font.slant:
137
 
            # we need the current fontmatrix in order to manipulate it:
138
 
            # for this we need to re-read the fontfile as below in
139
 
            # PSfontfile.ouput:
140
 
            # XXX Is there a better way to do this?
141
 
            t = trafo.trafo_pt(matrix=((1, font.slant), (0, 1)))
142
 
            if font.filename:
143
 
                # for the builtin fonts, we assume a trivial fontmatrix
144
 
                import font.t1font as t1fontmodule
145
 
                t1font = t1fontmodule.T1pfbfont(font.filename)
146
 
                m = t1font.fontmatrixpattern.search(t1font.data1)
147
 
                m11, m12, m21, m22, v1, v2 = map(float, m.groups()[:6])
148
 
                t *= trafo.trafo_pt(matrix=((m11, m12), (m21, m22)), vector=(v1, v2))
149
 
            else:
150
 
                raise NotImplementedError(
151
 
                "cannot slant unembedded fonts -- try to include \"download35.map\" in fontmaps")
152
 
            registry.add(PSfontslanting(slt_finalname, slt_basename, t.__str__()))
153
 
 
154
 
 
155
 
class PSfontfile(PSresource):
156
 
 
157
 
    """ PostScript font definition included in the prolog """
158
 
 
159
 
    def __init__(self, name, filename, encoding, chars):
160
 
        """ include type 1 font defined by the following parameters
161
 
 
162
 
        - name:        name of the PostScript font
163
 
        - filename:    name (without path) of file containing the font definition
164
 
        - encfilename: name (without path) of file containing used encoding of font
165
 
                       or None (if no encoding file used)
166
 
        - chars:       character list to fill usedchars
167
 
 
168
 
        """
169
 
 
170
 
        # Note that here we only need the encoding for selecting the used glyphs!
171
 
 
172
 
        self.type = "fontfile"
173
 
        self.id = self.name = name
174
 
        self.filename = filename
175
 
        if encoding is None:
176
 
            self.encodingfilename = None
177
 
        else:
178
 
            self.encodingfilename = encoding.filename
179
 
        self.usedchars = {}
180
 
        for char in chars:
181
 
            self.usedchars[char] = 1
182
 
 
183
 
        self.strip = 1
184
 
 
185
 
    def merge(self, other):
186
 
        if self.encodingfilename == other.encodingfilename:
187
 
            self.usedchars.update(other.usedchars)
188
 
        else:
189
 
            # TODO: need to resolve the encoding when several encodings are in the play
190
 
            self.strip = 0
191
 
 
192
 
    def output(self, file, writer, registry):
193
 
        import font.t1font
194
 
        font = font.t1font.T1pfbfont(self.filename)
195
 
 
196
 
        file.write("%%%%BeginFont: %s\n" % self.name)
197
 
        # file.write("%%Included glyphs: %s\n" % " ".join(usedglyphs))
198
 
        if self.strip:
199
 
            # XXX: access to the encoding file
200
 
            if self.encodingfilename:
201
 
                encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename)
202
 
                usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()])
203
 
            else:
204
 
                font._encoding()
205
 
                usedglyphs = dict([(font.encoding.decode(char), 1) for char in self.usedchars.keys()])
206
 
            strippedfont = font.getstrippedfont(usedglyphs)
207
 
        else:
208
 
            strippedfont = font
209
 
        strippedfont.outputPS(file, writer)
210
 
        file.write("\n%%EndFont\n")
211
 
 
212
 
 
213
 
class PSfontencoding(PSresource):
214
 
 
215
 
    """ PostScript font encoding vector included in the prolog """
216
 
 
217
 
    def __init__(self, encoding):
218
 
        """ include font encoding vector specified by encoding """
219
 
 
220
 
        self.type = "fontencoding"
221
 
        self.id = encoding.name
222
 
        self.encoding = encoding
223
 
 
224
 
    def output(self, file, writer, registry):
225
 
        encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename)
226
 
        encodingfile.outputPS(file, writer)
227
 
 
228
 
 
229
 
class PSfontslanting(PSresource):
230
 
 
231
 
    """ PostScript font slanting directive included in the prolog """
232
 
 
233
 
    def __init__(self, fontname, basefontname, matrixstring):
234
 
        """ include transformed font directive specified by
235
 
 
236
 
        - fontname:     PostScript FontName of the new slanted font
237
 
        - basefontname: PostScript FontName of the original font
238
 
        - slant:        the value of slanting
239
 
        """
240
 
 
241
 
        self.type = "fontslanting"
242
 
        self.id = self.fontname = fontname
243
 
        self.basefontname = basefontname
244
 
        self.matrixstring = matrixstring
245
 
 
246
 
    def output(self, file, writer, registry):
247
 
        file.write("%%%%BeginProcSet: %s\n" % self.fontname)
248
 
        file.write("/%s findfont\n" % self.basefontname)
249
 
        file.write("dup length dict begin\n")
250
 
        file.write("{ 1 index /FID ne {def} {pop pop} ifelse } forall\n")
251
 
        file.write("/FontMatrix %s readonly def\n" % self.matrixstring)
252
 
        file.write("currentdict\n")
253
 
        file.write("end\n")
254
 
        file.write("/%s exch definefont pop\n" % self.fontname)
255
 
        file.write("%%EndProcSet\n")
256
 
 
257
 
class PSfontreencoding(PSresource):
258
 
 
259
 
    """ PostScript font re-encoding directive included in the prolog """
260
 
 
261
 
    def __init__(self, fontname, basefontname, encodingname):
262
 
        """ include font re-encoding directive specified by
263
 
 
264
 
        - fontname:     PostScript FontName of the new reencoded font
265
 
        - basefontname: PostScript FontName of the original font
266
 
        - encname:      name of the encoding
267
 
 
268
 
        Before being able to reencode a font, you have to include the
269
 
        encoding via a fontencoding prolog item with name=encname
270
 
 
271
 
        """
272
 
 
273
 
        self.type = "fontreencoding"
274
 
        self.id = self.fontname = fontname
275
 
        self.basefontname = basefontname
276
 
        self.encodingname = encodingname
277
 
 
278
 
    def output(self, file, writer, registry):
279
 
        file.write("%%%%BeginProcSet: %s\n" % self.fontname)
280
 
        file.write("/%s /%s %s ReEncodeFont\n" % (self.basefontname, self.fontname, self.encodingname))
281
 
        file.write("%%EndProcSet\n")
282
 
 
283
 
 
284
 
_ReEncodeFont = PSdefinition("ReEncodeFont", """{
285
 
  5 dict
286
 
  begin
287
 
    /newencoding exch def
288
 
    /newfontname exch def
289
 
    /basefontname exch def
290
 
    /basefontdict basefontname findfont def
291
 
    /newfontdict basefontdict maxlength dict def
292
 
    basefontdict {
293
 
      exch dup dup /FID ne exch /Encoding ne and
294
 
      { exch newfontdict 3 1 roll put }
295
 
      { pop pop }
296
 
      ifelse
297
 
    } forall
298
 
    newfontdict /FontName newfontname put
299
 
    newfontdict /Encoding newencoding put
300
 
    newfontname newfontdict definefont pop
301
 
  end
302
 
}""")
303
 
 
304
 
 
305
 
class epswriter:
306
 
 
307
 
    def __init__(self, document, file):
 
89
        file.write("%%EndResource\n")
 
90
 
 
91
#
 
92
# Writers
 
93
#
 
94
 
 
95
class _PSwriter:
 
96
 
 
97
    def __init__(self, title=None, strip_fonts=True, text_as_path=False, mesh_as_bitmap=False, mesh_as_bitmap_resolution=300):
 
98
        self._fontmap = None
 
99
        self.title = title
 
100
        self.strip_fonts = strip_fonts
 
101
        self.text_as_path = text_as_path
 
102
        self.mesh_as_bitmap = mesh_as_bitmap
 
103
        self.mesh_as_bitmap_resolution = mesh_as_bitmap_resolution
 
104
 
 
105
        # dictionary mapping font names to dictionaries mapping encoding names to encodings
 
106
        # encodings themselves are mappings from glyphnames to codepoints
 
107
        self.encodings = {}
 
108
 
 
109
    def writeinfo(self, file):
 
110
        file.write("%%%%Creator: PyX %s\n" % version.version)
 
111
        if self.title is not None:
 
112
            file.write("%%%%Title: %s\n" % self.title)
 
113
        file.write("%%%%CreationDate: %s\n" %
 
114
                   time.asctime(time.localtime(time.time())))
 
115
 
 
116
    def getfontmap(self):
 
117
        if self._fontmap is None:
 
118
            # late import due to cyclic dependency
 
119
            from pyx.dvi import mapfile
 
120
            fontmapfiles = config.getlist("text", "psfontmaps", ["psfonts.map"])
 
121
            self._fontmap = mapfile.readfontmap(fontmapfiles)
 
122
        return self._fontmap
 
123
 
 
124
 
 
125
class EPSwriter(_PSwriter):
 
126
 
 
127
    def __init__(self, document, file, **kwargs):
 
128
        _PSwriter.__init__(self, **kwargs)
 
129
 
308
130
        if len(document.pages) != 1:
309
131
            raise ValueError("EPS file can be constructed out of a single page document only")
310
132
        page = document.pages[0]
311
133
        canvas = page.canvas
312
134
 
313
 
        try:
314
 
            file.write("")
315
 
        except:
316
 
            filename = file
317
 
            if not filename.endswith(".eps"):
318
 
                filename += ".eps"
319
 
            try:
320
 
                file = open(filename, "w")
321
 
            except IOError:
322
 
                raise IOError("cannot open output file")
323
 
        else:
324
 
            filename = "stream"
325
 
 
326
135
        pagefile = cStringIO.StringIO()
327
136
        registry = PSregistry()
328
137
        acontext = context()
334
143
        if pagebbox:
335
144
            file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
336
145
            file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
337
 
        file.write("%%%%Creator: PyX %s\n" % version.version)
338
 
        file.write("%%%%Title: %s\n" % filename)
339
 
        file.write("%%%%CreationDate: %s\n" %
340
 
                   time.asctime(time.localtime(time.time())))
 
146
        self.writeinfo(file)
341
147
        file.write("%%EndComments\n")
342
148
 
343
149
        file.write("%%BeginProlog\n")
352
158
        file.write("%%EOF\n")
353
159
 
354
160
 
355
 
class pswriter:
 
161
class PSwriter(_PSwriter):
356
162
 
357
 
    def __init__(self, document, file, writebbox=0):
358
 
        try:
359
 
            file.write("")
360
 
        except:
361
 
            filename = file
362
 
            if not filename.endswith(".ps"):
363
 
                filename += ".ps"
364
 
            try:
365
 
                file = open(filename, "w")
366
 
            except IOError:
367
 
                raise IOError("cannot open output file")
368
 
        else:
369
 
            filename = "stream"
 
163
    def __init__(self, document, file, writebbox=False, **kwargs):
 
164
        _PSwriter.__init__(self, **kwargs)
370
165
 
371
166
        # We first have to process the content of the pages, writing them into the stream pagesfile
372
167
        # Doing so, we fill the registry and also calculate the page bounding boxes, which are
408
203
        if documentbbox and writebbox:
409
204
            file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
410
205
            file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
411
 
        file.write("%%%%Creator: PyX %s\n" % version.version)
412
 
        file.write("%%%%Title: %s\n" % filename)
413
 
        file.write("%%%%CreationDate: %s\n" %
414
 
                   time.asctime(time.localtime(time.time())))
 
206
        self.writeinfo(file)
415
207
 
416
208
        # required paper formats
417
209
        paperformats = {}
455
247
        file.write("%%Trailer\n")
456
248
        file.write("%%EOF\n")
457
249
 
 
250
 
458
251
class context:
459
252
 
460
253
    def __init__(self):
461
254
        self.linewidth_pt = None
462
255
        self.colorspace = None
463
 
        self.font = None
 
256
        self.selectedfont = None
464
257
 
465
258
    def __call__(self, **kwargs):
466
259
        newcontext = copy.copy(self)