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

« back to all changes in this revision

Viewing changes to pyx/font/pfmfile.py

  • Committer: Package Import Robot
  • Author(s): Stuart Prescott
  • Date: 2012-12-17 13:45:12 UTC
  • mfrom: (1.1.4)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: package-import@ubuntu.com-20121217134512-u0w6lrgdowsc1sfu
Tags: 0.12.1-1
* New upstream release
* Update maintainer address.
* Update copyright format URL.
* Bump standards version to 3.9.4 (no changes required).
* Drop postinst that was needed for lenny->squeeze upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
#
 
3
#
 
4
# Copyright (C) 2007-2011 André Wobst <wobsta@users.sourceforge.net>
 
5
#
 
6
# This file is part of PyX (http://pyx.sourceforge.net/).
 
7
#
 
8
# PyX is free software; you can redistribute it and/or modify
 
9
# it under the terms of the GNU General Public License as published by
 
10
# the Free Software Foundation; either version 2 of the License, or
 
11
# (at your option) any later version.
 
12
#
 
13
# PyX is distributed in the hope that it will be useful,
 
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
# GNU General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU General Public License
 
19
# along with PyX; if not, write to the Free Software
 
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
21
 
 
22
import struct, re
 
23
import metric
 
24
 
 
25
 
 
26
ansiglyphs = {"space": 32,
 
27
              "exclam": 33,
 
28
              "quotedbl": 34,
 
29
              "numbersign": 35,
 
30
              "dollar": 36,
 
31
              "percent": 37,
 
32
              "ampersand": 38,
 
33
              "quotesingle": 39,
 
34
              "parenleft": 40,
 
35
              "parenright": 41,
 
36
              "asterisk": 42,
 
37
              "plus": 43,
 
38
              "comma": 44,
 
39
              "hyphen": 45,
 
40
              "period": 46,
 
41
              "slash": 47,
 
42
              "zero": 48,
 
43
              "one": 49,
 
44
              "two": 50,
 
45
              "three": 51,
 
46
              "four": 52,
 
47
              "five": 53,
 
48
              "six": 54,
 
49
              "seven": 55,
 
50
              "eight": 56,
 
51
              "nine": 57,
 
52
              "colon": 58,
 
53
              "semicolon": 59,
 
54
              "less": 60,
 
55
              "equal": 61,
 
56
              "greater": 62,
 
57
              "question": 63,
 
58
              "at": 64,
 
59
              "A": 65,
 
60
              "B": 66,
 
61
              "C": 67,
 
62
              "D": 68,
 
63
              "E": 69,
 
64
              "F": 70,
 
65
              "G": 71,
 
66
              "H": 72,
 
67
              "I": 73,
 
68
              "J": 74,
 
69
              "K": 75,
 
70
              "L": 76,
 
71
              "M": 77,
 
72
              "N": 78,
 
73
              "O": 79,
 
74
              "P": 80,
 
75
              "Q": 81,
 
76
              "R": 82,
 
77
              "S": 83,
 
78
              "T": 84,
 
79
              "U": 85,
 
80
              "V": 86,
 
81
              "W": 87,
 
82
              "X": 88,
 
83
              "Y": 89,
 
84
              "Z": 90,
 
85
              "bracketleft": 91,
 
86
              "backslash": 92,
 
87
              "bracketright": 93,
 
88
              "asciicircum": 94,
 
89
              "underscore": 95,
 
90
              "grave": 96,
 
91
              "a": 97,
 
92
              "b": 98,
 
93
              "c": 99,
 
94
              "d": 100,
 
95
              "e":101,
 
96
              "f":102,
 
97
              "g":103,
 
98
              "h":104,
 
99
              "i":105,
 
100
              "j":106,
 
101
              "k":107,
 
102
              "l":108,
 
103
              "m":109,
 
104
              "n":110,
 
105
              "o":111,
 
106
              "p":112,
 
107
              "q":113,
 
108
              "r":114,
 
109
              "s":115,
 
110
              "t":116,
 
111
              "u":117,
 
112
              "v":118,
 
113
              "w":119,
 
114
              "x":120,
 
115
              "y":121,
 
116
              "z":122,
 
117
              "braceleft":123,
 
118
              "bar":124,
 
119
              "braceright":125,
 
120
              "asciitilde":126,
 
121
              "bullet":127,
 
122
              "Euro":128,
 
123
              "bullet":129,
 
124
              "quotesinglbase":130,
 
125
              "florin":131,
 
126
              "quotedblbase":132,
 
127
              "ellipsis":133,
 
128
              "dagger":134,
 
129
              "daggerdbl":135,
 
130
              "circumflex":136,
 
131
              "perthousand":137,
 
132
              "Scaron":138,
 
133
              "guilsinglleft":139,
 
134
              "OE":140,
 
135
              "bullet":141,
 
136
              "Zcaron":142,
 
137
              "bullet":143,
 
138
              "bullet":144,
 
139
              "quoteleft":145,
 
140
              "quoteright":146,
 
141
              "quotedblleft":147,
 
142
              "quotedblright":148,
 
143
              "bullet":149,
 
144
              "endash":150,
 
145
              "emdash":151,
 
146
              "tilde":152,
 
147
              "trademark":153,
 
148
              "scaron":154,
 
149
              "guilsinglright":155,
 
150
              "oe":156,
 
151
              "bullet":157,
 
152
              "zcaron":158,
 
153
              "Ydieresis":159,
 
154
              "space":160,
 
155
              "exclamdown":161,
 
156
              "cent":162,
 
157
              "sterling":163,
 
158
              "currency":164,
 
159
              "yen":165,
 
160
              "brokenbar":166,
 
161
              "section":167,
 
162
              "dieresis":168,
 
163
              "copyright":169,
 
164
              "ordfeminine":170,
 
165
              "guillemotleft":171,
 
166
              "logicalnot":172,
 
167
              "hyphen":173,
 
168
              "registered":174,
 
169
              "macron":175,
 
170
              "degree":176,
 
171
              "plusminus":177,
 
172
              "twosuperior":178,
 
173
              "threesuperior":179,
 
174
              "acute":180,
 
175
              "mu":181,
 
176
              "paragraph":182,
 
177
              "periodcentered":183,
 
178
              "cedilla":184,
 
179
              "onesuperior":185,
 
180
              "ordmasculine":186,
 
181
              "guillemotright":187,
 
182
              "onequarter":188,
 
183
              "onehalf":189,
 
184
              "threequarters":190,
 
185
              "questiondown":191,
 
186
              "Agrave":192,
 
187
              "Aacute":193,
 
188
              "Acircumflex":194,
 
189
              "Atilde":195,
 
190
              "Adieresis":196,
 
191
              "Aring":197,
 
192
              "AE":198,
 
193
              "Ccedilla":199,
 
194
              "Egrave":200,
 
195
              "Eacute":201,
 
196
              "Ecircumflex":202,
 
197
              "Edieresis":203,
 
198
              "Igrave":204,
 
199
              "Iacute":205,
 
200
              "Icircumflex":206,
 
201
              "Idieresis":207,
 
202
              "Eth":208,
 
203
              "Ntilde":209,
 
204
              "Ograve":210,
 
205
              "Oacute":211,
 
206
              "Ocircumflex":212,
 
207
              "Otilde":213,
 
208
              "Odieresis":214,
 
209
              "multiply":215,
 
210
              "Oslash":216,
 
211
              "Ugrave":217,
 
212
              "Uacute":218,
 
213
              "Ucircumflex":219,
 
214
              "Udieresis":220,
 
215
              "Yacute":221,
 
216
              "Thorn":222,
 
217
              "germandbls":223,
 
218
              "agrave":224,
 
219
              "aacute":225,
 
220
              "acircumflex":226,
 
221
              "atilde":227,
 
222
              "adieresis":228,
 
223
              "aring":229,
 
224
              "ae":230,
 
225
              "ccedilla":231,
 
226
              "egrave":232,
 
227
              "eacute":233,
 
228
              "ecircumflex":234,
 
229
              "edieresis":235,
 
230
              "igrave":236,
 
231
              "iacute":237,
 
232
              "icircumflex":238,
 
233
              "idieresis":239,
 
234
              "eth":240,
 
235
              "ntilde":241,
 
236
              "ograve":242,
 
237
              "oacute":243,
 
238
              "ocircumflex":244,
 
239
              "otilde":245,
 
240
              "odieresis":246,
 
241
              "divide":247,
 
242
              "oslash":248,
 
243
              "ugrave":249,
 
244
              "uacute":250,
 
245
              "ucircumflex":251,
 
246
              "udieresis":252,
 
247
              "yacute":253,
 
248
              "thorn":254,
 
249
              "ydieresis":255}
 
250
 
 
251
 
 
252
fontbboxpattern = re.compile("/FontBBox\s*\{\s*(?P<fontbbox>(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+))\s*\}\s*(readonly\s+)?def")
 
253
 
 
254
 
 
255
def _readNullString(file):
 
256
    s = []
 
257
    c = file.read(1)
 
258
    while c and c != "\0":
 
259
        s.append(c)
 
260
        c = file.read(1)
 
261
    return "".join(s)
 
262
 
 
263
 
 
264
class PFMfile(metric.metric):
 
265
 
 
266
    def __init__(self, file, t1file):
 
267
        # pfm is rather incomplete, the t1file instance can be used to fill the gap
 
268
        (self.dfVersion, self.dfSize, self.dfCopyright, self.dfType,
 
269
         self.dfPoint, self.dfVertRes, self.dfHorizRes, self.dfAscent,
 
270
         self.dfInternalLeading, self.dfExternalLeading, self.dfItalic,
 
271
         self.dfUnderline, self.dfStrikeOut, self.dfWeight,
 
272
         self.dfCharSet, self.dfPixWidth, self.dfPixHeight,
 
273
         self.dfPitchAndFamily, self.dfAvgWidth, self.dfMaxWidth,
 
274
         self.dfFirstChar, self.dfLastChar, self.dfDefaultChar,
 
275
         self.dfBreakChar, self.dfWidthBytes, self.dfDevice, self.dfFace,
 
276
         self.dfBitsPointer, self.dfBitsOffset) = struct.unpack("<HL60s7H3BHB2HB2H4BH4L", file.read(117))
 
277
        self.dfCopyright = self.dfCopyright.split("\000", 1)[0]
 
278
        (self.dfSizeFields, self.dfExtMetricsOffset, self.dfExtentTable,
 
279
         self.dfOriginTable, self.dfPairKernTable, self.dfTrackKernTable,
 
280
         self.dfDriverInfo, self.dfReserved) = struct.unpack("<H7L", file.read(30))
 
281
        if self.dfDevice == 0:
 
282
            raise ValueError("DeviceName is required for Type1 pfm files.")
 
283
        file.seek(self.dfDevice)
 
284
        self.deviceName = _readNullString(file)
 
285
        if self.deviceName.lower() != "postscript":
 
286
            raise ValueError("Can process pfm files for PostScript fonts only.")
 
287
        if self.dfVersion != 0x100:
 
288
            raise ValueError("Invalid pfm file version.")
 
289
        if self.dfType != 0x81:
 
290
            raise ValueError("Not a Type1 pfm file.")
 
291
        if self.dfFace == 0:
 
292
            raise ValueError("FaceName is required for Type1 pfm files.")
 
293
        if self.dfExtMetricsOffset == 0:
 
294
            raise ValueError("ExtTextMetrics is required for Type1 pfm files.")
 
295
        if self.dfExtentTable == 0:
 
296
            raise ValueError("ExtentTable is required for Type1 pfm files.")
 
297
        if self.dfOriginTable != 0:
 
298
            raise ValueError("OriginTable is forbidden for Type1 pfm files.")
 
299
        if self.dfDriverInfo == 0:
 
300
            raise ValueError("DriverInfo is required for Type1 pfm files.")
 
301
        # assert self.dfReserved == 0 (must be zero according to the spec, but we don't care)
 
302
        file.seek(self.dfExtMetricsOffset)
 
303
        (etmSize, self.etmPointSize, self.etmOrientation,
 
304
         self.etmMasterHeight, self.etmMinScale, self.etmMaxScale,
 
305
         self.etmMasterUnits, self.etmCapHeight, self.etmXHeight,
 
306
         self.etmLowerCaseAscent, self.etmLowerCaseDescent, self.etmSlant,
 
307
         self.etmSuperScript, self.etmSubScript, self.etmSuperScriptSize,
 
308
         self.etmSubScriptSize, self.etmUnderlineOffset,
 
309
         self.etmUnderlineWidth, self.etmDoubleUpperUnderlineOffset,
 
310
         self.etmDoubleLowerUnderlineOffset, self.etmDoubleUpperUnderlineWidth,
 
311
         self.etmDoubleLowerUnderlineWidth, self.etmStrikeOutOffset,
 
312
         self.etmStrikeOutWidth, self.etmKernPairs, self.etmKernTracks) = struct.unpack("<24h2H", file.read(52))
 
313
        file.seek(self.dfFace)
 
314
        self.faceName = _readNullString(file)
 
315
        file.seek(self.dfDriverInfo)
 
316
        self.driverInfo = _readNullString(file)
 
317
        file.seek(self.dfExtentTable)
 
318
        count = self.dfLastChar - self.dfFirstChar + 1
 
319
        self.widths = struct.unpack("<%dH" % count, file.read(2*count))
 
320
        self.kernpairs = []
 
321
        self.kernpairsdict = {}
 
322
        if self.dfPairKernTable:
 
323
            file.seek(self.dfPairKernTable)
 
324
            pairs, = struct.unpack("<H", file.read(2))
 
325
            if pairs != self.etmKernPairs:
 
326
                raise ValueError("number of kerning pairs mismatch in pfm file.")
 
327
            for i in range(self.etmKernPairs):
 
328
                kpFirst, kpSecond, kpKernAmount = struct.unpack("<BBh", file.read(4))
 
329
                self.kernpairs.append((kpFirst, kpSecond, kpKernAmount))
 
330
                self.kernpairsdict[(kpFirst, kpSecond)] = kpKernAmount
 
331
        self.trackkerns = []
 
332
        if self.dfTrackKernTable:
 
333
            file.seek(self.dfTrackKernTable)
 
334
            items, = struct.unpack("<H", file.read(2))
 
335
            if items != self.etmKernTracks:
 
336
                raise ValueError("number of kerning tracks mismatch in pfm file.")
 
337
            for i in range(self.etmKernTracks):
 
338
                # each item consists of the tuple ktDegree, ktMinSize, ktMinAmount, ktMaxSize, ktMaxAmount
 
339
                self.trackkerns.append(struct.unpack("<hhhhh", file.read(10)))
 
340
        if self.dfCharSet:
 
341
            if not t1file.encoding:
 
342
                t1file._encoding()
 
343
            self.glyphs = dict([(glyph, i) for i, glyph in enumerate(t1file.encoding) if glyph != None])
 
344
        else:
 
345
            self.glyphs = ansiglyphs
 
346
        self.t1file = t1file
 
347
 
 
348
    def width_ds(self, glyphname):
 
349
        return self.widths[self.glyphs[glyphname]-self.dfFirstChar]
 
350
 
 
351
    def width_pt(self, glyphnames, size_pt):
 
352
        return sum([self.widths[self.glyphs[glyphname]-self.dfFirstChar] for glyphname in glyphnames])*size_pt/1000.0
 
353
 
 
354
    def height_pt(self, glyphnames, size_pt):
 
355
        return self.dfAscent*size_pt/1000.0
 
356
 
 
357
    def depth_pt(self, glyphnames, size_pt):
 
358
        return -self.etmLowerCaseDescent*size_pt/1000.0
 
359
 
 
360
    def resolvekernings(self, glyphnames, size_pt=None):
 
361
        result = [None]*(2*len(glyphnames)-1)
 
362
        for i, glyphname in enumerate(glyphnames):
 
363
            result[2*i] = glyphname
 
364
        return result
 
365
 
 
366
    def resolvekernings(self, glyphnames, size_pt=None):
 
367
        result = [None]*(2*len(glyphnames)-1)
 
368
        for i, glyphname in enumerate(glyphnames):
 
369
            result[2*i] = glyphname
 
370
            if i:
 
371
                amount = self.kernpairsdict.get((self.glyphs[glyphnames[i-1]], self.glyphs[glyphname]))
 
372
                if amount:
 
373
                    if size_pt is not None:
 
374
                        result[2*i-1] = amount*size_pt/1000.0
 
375
                    else:
 
376
                        result[2*i-1] = amount
 
377
        return result
 
378
 
 
379
    def writePDFfontinfo(self, file, seriffont=False, symbolfont=True):
 
380
        flags = 0
 
381
        if self.dfMaxWidth == self.dfAvgWidth:
 
382
            flags += 1<<0
 
383
        if seriffont:
 
384
            flags += 1<<1
 
385
        if symbolfont:
 
386
            flags += 1<<2
 
387
        else:
 
388
            flags += 1<<5
 
389
        if self.dfItalic:
 
390
            flags += 1<<6
 
391
        file.write("/Flags %d\n" % flags)
 
392
        file.write("/ItalicAngles %d\n" % (self.etmSlant/10))
 
393
        file.write("/Ascent %d\n" % self.dfAscent)
 
394
        file.write("/Descent %d\n" % -self.etmLowerCaseDescent)
 
395
        file.write("/FontBBox [%s]\n" % fontbboxpattern.search(self.t1file.data1).groups('fontbbox')[0])
 
396
        file.write("/CapHeight %d\n" % self.etmCapHeight)
 
397
        if self.dfWeight >= 600:
 
398
            stemv = 120
 
399
        else:
 
400
            stemv = 70
 
401
        file.write("/StemV %d\n" % stemv)
 
402