1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2007-2011 André Wobst <wobsta@users.sourceforge.net>
6 # This file is part of PyX (http://pyx.sourceforge.net/).
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.
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.
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
26 ansiglyphs
= {"space": 32,
124 "quotesinglbase":130,
149 "guilsinglright":155,
177 "periodcentered":183,
181 "guillemotright":187,
251 def _readNullString(file):
254 while c
and c
!= "\0":
260 class PFMfile(metric
.metric
):
262 def __init__(self
, file):
263 (self
.dfVersion
, self
.dfSize
, self
.dfCopyright
, self
.dfType
,
264 self
.dfPoint
, self
.dfVertRes
, self
.dfHorizRes
, self
.dfAscent
,
265 self
.dfInternalLeading
, self
.dfExternalLeading
, self
.dfItalic
,
266 self
.dfUnderline
, self
.dfStrikeOut
, self
.dfWeight
,
267 self
.dfCharSet
, self
.dfPixWidth
, self
.dfPixHeight
,
268 self
.dfPitchAndFamily
, self
.dfAvgWidth
, self
.dfMaxWidth
,
269 self
.dfFirstChar
, self
.dfLastChar
, self
.dfDefaultChar
,
270 self
.dfBreakChar
, self
.dfWidthBytes
, self
.dfDevice
, self
.dfFace
,
271 self
.dfBitsPointer
, self
.dfBitsOffset
) = struct
.unpack("<HL60s7H3BHB2HB2H4BH4L", file.read(117))
272 self
.dfCopyright
= self
.dfCopyright
.split("\000", 1)[0]
273 (self
.dfSizeFields
, self
.dfExtMetricsOffset
, self
.dfExtentTable
,
274 self
.dfOriginTable
, self
.dfPairKernTable
, self
.dfTrackKernTable
,
275 self
.dfDriverInfo
, self
.dfReserved
) = struct
.unpack("<H7L", file.read(30))
276 if self
.dfDevice
== 0:
277 raise ValueError("DeviceName is required for Type1 pfm files.")
278 file.seek(self
.dfDevice
)
279 self
.deviceName
= _readNullString(file)
280 if self
.deviceName
.lower() != "postscript":
281 raise ValueError("Can process pfm files for PostScript fonts only.")
282 if self
.dfVersion
!= 0x100:
283 raise ValueError("Invalid pfm file version.")
284 if self
.dfType
!= 0x81:
285 raise ValueError("Not a Type1 pfm file.")
287 raise ValueError("FaceName is required for Type1 pfm files.")
288 if self
.dfExtMetricsOffset
== 0:
289 raise ValueError("ExtTextMetrics is required for Type1 pfm files.")
290 if self
.dfExtentTable
== 0:
291 raise ValueError("ExtentTable is required for Type1 pfm files.")
292 if self
.dfOriginTable
!= 0:
293 raise ValueError("OriginTable is forbidden for Type1 pfm files.")
294 if self
.dfDriverInfo
== 0:
295 raise ValueError("DriverInfo is required for Type1 pfm files.")
296 # assert self.dfReserved == 0 (must be zero according to the spec, but we don't care)
297 file.seek(self
.dfExtMetricsOffset
)
298 (etmSize
, self
.etmPointSize
, self
.etmOrientation
,
299 self
.etmMasterHeight
, self
.etmMinScale
, self
.etmMaxScale
,
300 self
.etmMasterUnits
, self
.etmCapHeight
, self
.etmXHeight
,
301 self
.etmLowerCaseAscent
, self
.etmLowerCaseDescent
, self
.etmSlant
,
302 self
.etmSuperScript
, self
.etmSubScript
, self
.etmSuperScriptSize
,
303 self
.etmSubScriptSize
, self
.etmUnderlineOffset
,
304 self
.etmUnderlineWidth
, self
.etmDoubleUpperUnderlineOffset
,
305 self
.etmDoubleLowerUnderlineOffset
, self
.etmDoubleUpperUnderlineWidth
,
306 self
.etmDoubleLowerUnderlineWidth
, self
.etmStrikeOutOffset
,
307 self
.etmStrikeOutWidth
, self
.etmKernPairs
, self
.etmKernTracks
) = struct
.unpack("<24h2H", file.read(52))
308 print self
.etmMasterHeight
, self
.etmMasterUnits
, self
.etmPointSize
, self
.etmOrientation
309 file.seek(self
.dfFace
)
310 self
.faceName
= _readNullString(file)
311 file.seek(self
.dfDriverInfo
)
312 self
.driverInfo
= _readNullString(file)
313 file.seek(self
.dfExtentTable
)
314 count
= self
.dfLastChar
- self
.dfFirstChar
+ 1
315 self
.widths
= struct
.unpack("<%dH" % count
, file.read(2*count
))
317 self
.kernpairsdict
= {}
318 if self
.dfPairKernTable
:
319 file.seek(self
.dfPairKernTable
)
320 pairs
, = struct
.unpack("<H", file.read(2))
321 if pairs
!= self
.etmKernPairs
:
322 raise ValueError("number of kerning pairs mismatch in pfm file.")
323 for i
in range(self
.etmKernPairs
):
324 kpFirst
, kpSecond
, kpKernAmount
= struct
.unpack("<BBh", file.read(4))
325 self
.kernpairs
.append((kpFirst
, kpSecond
, kpKernAmount
))
326 self
.kernpairsdict
[(kpFirst
, kpSecond
)] = kpKernAmount
328 if self
.dfTrackKernTable
:
329 file.seek(self
.dfTrackKernTable
)
330 items
, = struct
.unpack("<H", file.read(2))
331 if items
!= self
.etmKernTracks
:
332 raise ValueError("number of kerning tracks mismatch in pfm file.")
333 for i
in range(self
.etmKernTracks
):
334 # each item consists of the tuple ktDegree, ktMinSize, ktMinAmount, ktMaxSize, ktMaxAmount
335 self
.trackkerns
.append(struct
.unpack("<hhhhh", file.read(10)))
337 raise ValueError("PFM font matrices are currently supported for ansi encoded fonts only.")
339 def width_ds(self
, glyphname
):
340 return self
.widths
[ansiglyphs
[glyphname
]-self
.dfFirstChar
]
342 def width_pt(self
, glyphnames
, size_pt
):
343 return sum([self
.widths
[ansiglyphs
[glyphname
]-self
.dfFirstChar
] for glyphname
in glyphnames
])*size_pt
/1000.0
345 def height_pt(self
, glyphnames
, size_pt
):
346 return self
.dfAscent
*size_pt
/1000.0
348 def depth_pt(self
, glyphnames
, size_pt
):
349 return -self
.etmLowerCaseDescent
*size_pt
/1000.0
351 def resolvekernings(self
, glyphnames
, size_pt
=None):
352 result
= [None]*(2*len(glyphnames
)-1)
353 for i
, glyphname
in enumerate(glyphnames
):
354 result
[2*i
] = glyphname
357 def resolvekernings(self
, glyphnames
, size_pt
=None):
358 result
= [None]*(2*len(glyphnames
)-1)
359 for i
, glyphname
in enumerate(glyphnames
):
360 result
[2*i
] = glyphname
362 amount
= self
.kernpairsdict
.get((ansiglyphs
[glyphnames
[i
-1]], ansiglyphs
[glyphname
]))
364 if size_pt
is not None:
365 result
[2*i
-1] = amount
*size_pt
/1000.0
367 result
[2*i
-1] = amount
371 def writePDFfontinfo(self
, file, seriffont
=False, symbolfont
=True):
373 if self
.dfMaxWidth
== self
.dfAvgWidth
:
383 file.write("/Flags %d\n" % flags
)
384 file.write("/ItalicAngles %d\n" % (self
.etmSlant
/10))
385 file.write("/Ascent %d\n" % self
.dfAscent
)
386 file.write("/Descent %d\n" % self
.etmLowerCaseDescent
)
387 file.write("/FontBBox [0 %d 1000 %d]\n" % (self
.etmLowerCaseDescent
, self
.dfAscent
))
388 file.write("/CapHeight %d\n" % self
.dfAscent
)
389 if self
.dfWeight
>= 600:
393 file.write("/StemV %d\n" % stemv
)