1import struct
2class _BUILDER:
3	'''Virtual base helper class for structured file scanning'''
4	def _get_struct_fmt(self,info):
5		fmt = '<'
6		for f, _, _ in info:
7			fmt += f
8		return fmt
9
10	def _scan_from_file(self,f,info):
11		fmt = self._get_struct_fmt(info)
12		size = struct.calcsize(fmt)
13		T = struct.unpack(fmt,f.read(size))
14		i = 0
15		for _, n, _ in info:
16			setattr(self,n,T[i])
17			i = i + 1
18
19	def _dump(self,A):
20		for a in A:
21			print(a, getattr(self,a))
22
23	def _attr_names(self,*I):
24		A = []
25		for i in I:
26			if isinstance(i,str):
27				A.append(i)
28			else:
29				A.extend([x[1] for x in i])
30		return A
31
32	def _scanZTStr(self,f,loc):
33		'''scan a zero terminated string from the file'''
34		f.seek(loc)
35		s = ''
36		while 1:
37			c = f.read(1)
38			if c=='\000': break
39			s = s+c
40		return s
41
42	def _scanN(self,N,fmt,f,loc):
43		if not loc: return None
44		fmt = len(fmt)==1 and ("<%d%c" % (N,fmt)) or ("<"+N*fmt)
45		f.seek(loc)
46		size = struct.calcsize(fmt)
47		return struct.unpack(fmt,f.read(size))
48
49	def _scanNT(self,T,N,fmt,f,loc):
50		if not loc: return None
51		n = len(fmt)
52		X = []
53		i = 0
54		S = []
55		for x in self._scanN(N,fmt,f,loc):
56			S.append(x)
57			i = i + 1
58			if i==n:
59				X.append(S)
60				i = 0
61				S = []
62		return list(map(lambda x,T=T: T(*x),X))
63
64class KernPair:
65	'''hold info about a possible kerning pair'''
66	def __init__(self,first,second,amount):
67		self.first = first
68		self.scond = second
69		self.amount = amount
70
71class KernTrack:
72	def __init__(self,degree,minSize,minAmount,maxSize,maxAmount):
73		'''
74		degree	amount to change the character spacing. Negative values mean closer together,p
75				ositive values mean farther apart.
76		minSize minimum font height (in device units) for which to use linear track kerning.
77		minAmount track kerning amount to use for font heights less or equal ktMinSize.
78		maxSize maximum font height (in device units) for which to use linear track kerning.f
79				For font heights between ktMinSize and ktMaxSize the track kerning amount has
80				to increase linearily from ktMinAmount to ktMaxAmount.
81		maxAmount track kerning amount to use for font heights greater or equal ktMaxSize.
82		'''
83		self.degree = degree
84		self.minSize = minSize
85		self.minAmount = minAmount
86		self.maxSize = maxSize
87		self.maxAmount = maxAmount
88
89class PFM(_BUILDER):
90	def __init__(self,fn=None):
91		if fn:
92			if isinstance(fn,str):
93				f = open(fn,'rb')
94			else:
95				f = fn
96			self.scan_from_file(f)
97			if f is not fn: f.close()
98
99	'''Class to hold information scanned from a type-1 .pfm file'''
100	def scan_from_file(self,f):
101		self._scan_from_file(f,self._header_struct_info)
102		if self.dfType!=0x81: raise ValueError("Not a Type-1 Font description")
103		else: self.WidthTable = None
104		self._scan_from_file(f,self._extension_struct_info)
105		if not self.dfExtentTable: raise ValueError('dfExtentTable is zero')
106		if not self.dfExtMetricsOffset: raise ValueError('dfExtMetricsOffset is zero')
107		if self.dfDevice: self.DeviceName = self._scanZTStr(f,self.dfDevice)
108		else: self.DeviceName = None
109		if self.dfFace: self.FaceName = self._scanZTStr(f,self.dfFace)
110		else: self.FaceName = None
111		f.seek(self.dfExtMetricsOffset)
112		self._scan_from_file(f, self._extTextMetrics_struct_info)
113		N = self.dfLastChar - self.dfFirstChar + 1
114		self.ExtentTable = self._scanN(N,'H',f,self.dfExtentTable)
115		if self.dfDriverInfo: self.DriverInfo = self._scanZTStr(f,self.dfDriverInfo)
116		else: self.DriverInfo = None
117		if self.dfPairKernTable: self.KerningPairs = self._scanNT(KernPair,self.dfPairKernTable,'BBh',f,self.etmKernPairs)
118		else: self.KerningPairs = []
119		if self.dfTrackKernTable: self.KerningTracks = self._scanNT(KernTrack,self.dfTrackKernTable,'hhhhh',f,self.etmKernTracks)
120		else: self.KerningTracks = []
121
122	def dump(self):
123		self._dump(
124			self._attr_names(
125			self._header_struct_info,'WidthTable',
126			self._extension_struct_info,
127			'DeviceName',
128			'FaceName',
129			self._extTextMetrics_struct_info,
130			'DriverInfo',
131			))
132
133	_header_struct_info = (('H','dfVersion',
134'''This field contains the version of the PFM file.
135For PFM files that conform to this description
136(namely PFM files for Type-1 fonts) the
137value of this field is always 0x0100.'''),
138
139('i','dfSize',
140'''This field contains the total size of the PFM file in bytes.
141Some drivers check this field and compare its value with the size of the PFM
142file, and if these two values don't match the font is ignored
143(I know this happens e.g. with Adobe PostScript printer drivers). '''),
144
145('60s','dfCopyright',
146'''This field contains a null-terminated copyright
147string, often from the application that created the
148PFM file (this normally isn't the
149copyright string for the font file itself).
150The unused bytes in this field should be set to zero. '''),
151
152('H','dfType',
153'''This field contains the font type. The low-order
154byte is a combination of the following values
155(only the values being of interest in PFM
156files are given):
157
1580x00 (PF_RASTER_TYPE): font is a raster font
1590x01 (PF_VECTOR_TYPE): font is a vector font
1600x80 (PF_DEVICE_REALIZED): font realized by the device driver
161
162The high-order byte is never used in PFM files, it is always zero.
163
164In PFM files for Type-1 fonts the value in this field is always 0x0081. '''),
165
166('H','dfPoints',
167'''This field contains the point size at which this font
168looks best. Since this is not relevant for scalable fonts
169the field is ignored. The value
170of this field should be set to 0x000a (10 pt). '''),
171
172('H','dfVertRes',
173'''This field contains the vertical resolution at which the
174font was digitized (the value is in dots per inch).
175The value of this field should be
176set to 0x012C (300 dpi). '''),
177
178('H','dfHorizRes',
179'''This field contains the horizontal resolution at which
180the font was digitized (the value is in dots per inch).
181The value of this field should
182be set to 0x012C (300 dpi). '''),
183
184('H','dfAscent',
185'''This field contains the distance from the top of a
186character definition cell to the baseline of the
187typographical font. It is useful for aligning the
188baseline of fonts of different heights. '''),
189
190('H','dfInternalLeading',
191'''This field contains the amount of leading inside
192the bounds set by the dfPixHeight field in the PFMHEADER
193structure. Accent marks may occur in this area. '''),
194
195('H','dfExternalLeading',
196'''This field contains the amount of extra leading that the
197designer requests the application to add between rows. Since this area is
198outside the character definition cells, it contains no marks and will not be altered by text outputs. '''),
199
200('B','dfItalic',
201'''This field specifies whether this font is an italic
202(or oblique) font. The low-order bit is 1 if the flag
203is set, all other bits are zero. '''),
204
205('B','dfUnderline',
206'''This field specifies whether this font is an underlined
207font. The low-order bit is 1 if the flag is set, all other
208bits are zero. '''),
209
210('B','dfStrikeOut',
211'''This field specifies whether this font is a striked-out font.
212The low-order bit is 1 if the flag is set, all other bits are zero. '''),
213
214('H','dfWeight',
215'''This field contains the weight of the characters in this font.
216The value is on a scale from 0 through 1000, increments are in
217steps of 100 each. The values roughly give the number of black
218pixel from every 1000 pixels. Typical values are:
219
2200 (FW_DONTCARE): unknown or no information
221300 (FW_LIGHT): light font
222400 (FW_NORMAL): normal font
223700 (FW_BOLD): bold font '''),
224
225('B','dfCharSet',
226'''This field specifies the character set used in this font.
227It can be one of the following values (probably other values
228may be used here as well):
229
2300x00 (ANSI_CHARSET): the font uses the ANSI character set;
231this means that the font implements all characters needed for the
232current Windows code page (e.g. 1252). In case of a Type-1 font
233this font has been created with the encoding StandardEncoding
234Note that the code page number itself is not stored in the PFM file.
235
2360x02 (SYMBOL_CHARSET): the font uses a font-specific encoding
237which will be used unchanged in displaying an printing text
238using this font. In case of a Type-1 font this font has been
239created with a font-specific encoding vector. Typical examples are
240the Symbol and the ZapfDingbats fonts.
241
2420xFF (OEM_CHARSET): the font uses the OEM character set; this
243means that the font implements all characters needed for the
244code page 437 used in e.g. MS-DOS command line mode (at least
245in some versions of Windows, others might use code page
246850 instead). In case of a Type-1 font this font has been created with a font-specific encoding vector. '''),
247
248('H','dfPixWidth',
249'''This field contains the width of all characters in the font.
250For raster fonts this field contains the width in pixels of every
251character bitmap if the font is fixed-pitch, otherwise this field
252is zero and the character's widths are specified in the WidthTable
253table. For vector fonts this field contains the width of the grid
254on which the font was digitized. The value is ignored by PostScript
255printer drivers. '''),
256
257('H','dfPixHeight',
258'''This field contains the height of all characters in the font.
259For raster fonts this field contains the height in scan lines of
260every character bitmap. For vector fonts this field contains the
261height of the grid on which the font was digitized. The value is
262ignored by PostScript printer drivers. '''),
263
264('B','dfPitchAndFamily',
265'''This field specifies the font pitch and the font family. The
266font pitch specifies whether all characters in the font have the
267same pitch (this is called fixed pitch too) or variable pitch.
268The font family indicates, in a rather general way, the look of a font.
269
270The least significant bit in this field contains the pitch flag.
271If the bit is set the font is variable pitch, otherwise it's fixed pitch. For
272Type-1 fonts this flag is set always, even if the Type-1 font is fixed pitch.
273
274The most significant bits of this field specify the font family.
275These bits may have one of the following values:
276
2770x00 (FF_DONTCARE): no information
2780x10 (FF_ROMAN): serif font, variable pitch
2790x20 (FF_SWISS): sans serif font, variable pitch
2800x30 (FF_MODERN): fixed pitch, serif or sans serif font
2810x40 (FF_SCRIPT): cursive or handwriting font
2820x50 (FF_DECORATIVE): novelty fonts '''),
283
284('H','dfAvgWidth',
285'''This field contains the average width of the characters in the font.
286For a fixed pitch font this is the same as dfPixWidth in the
287PFMHEADER structure. For a variable pitch font this is the width
288of the character 'X'. '''),
289
290('H','dfMaxWidth',
291'''This field contains the maximum width of the characters in the font.
292For a fixed pitch font this value is identical to dfAvgWidth in the
293PFMHEADER structure. '''),
294
295('B','dfFirstChar',
296'''This field specifies the first character code defined by this font.
297Width definitions are stored only for the characters actually present
298in a font, so this field must be used when calculating indexes into the
299WidthTable or the ExtentTable tables. For text fonts this field is
300normally set to 0x20 (character space). '''),
301
302('B','dfLastChar',
303'''This field specifies the last character code defined by this font.
304Together with the dfFirstChar field in the PFMHEADER structure this
305field specifies the valid character range for this font. There must
306be an entry in the WidthTable or the ExtentTable tables for every
307character between these two values (including these values themselves).
308For text fonts this field is normally set to 0xFF (maximum
309possible value). '''),
310
311('B','dfDefaultChar',
312'''This field specifies the default character to be used whenever a
313character is used that is outside the range of the dfFirstChar through
314dfLastChar fields in the PFMHEADER structure. The character is given
315relative to dfFirstChar so that the actual value of the default
316character is the sum of dfFirstChar and dfDefaultChar. Ideally, the
317default character should be a visible character in the current font,
318e.g. a period ('.'). For text fonts this field is normally set to
319either 0x00 (character space) or 0x75 (bullet). '''),
320
321('B','dfBreakChar',
322'''This field specifies the word-break character. Applications
323use this character to separate words when wrapping or justifying lines of
324text. The character is given relative to dfFirstChar in the PFMHEADER
325structure so that the actual value of the word-break character
326is the sum of dfFirstChar and dfBreakChar. For text fonts this
327field is normally set to 0x00 (character space). '''),
328
329('H','dfWidthBytes',
330'''This field contains the number of bytes in every row of the
331font bitmap. The value is always an even quantity so that rows of the
332bitmap start on 16 bit boundaries. This field is not used for vector
333fonts, it is therefore zero in e.g. PFM files for Type-1 fonts. '''),
334
335('i','dfDevice',
336'''This field contains the offset from the beginning of the PFM file
337to the DeviceName character buffer. The DeviceName is always
338present in PFM files for Type-1 fonts, this field is therefore never zero.'''),
339
340('i','dfFace',
341'''This field contains the offset from the beginning of the PFM file
342to the FaceName character buffer. The FaceName is always present
343in PFM files for Type-1 fonts, this field is therefore never zero. '''),
344
345('i','dfBitsPointer',
346'''This field is not used in PFM files, it must be set to zero. '''),
347
348('i','dfBitsOffset',
349'''This field is not used in PFM files, it must be set to zero. '''),
350)
351
352#'H','WidthTable[]'
353#This section is present in a PFM file only when this PFM file describes a
354#variable pitch raster font. Since Type-1 fonts aren't raster fonts this
355#section never exists in PFM files for Type-1 fonts.'''
356#The WidthTable table consists of (dfLastChar - dfFirstChar + 2) entries of type WORD (dfFirstChar and dfLastChar can be found in the
357#PFMHEADER structure). Every entry contains the width of the corresponding character, the last entry in this table is extra, it is set to zero.
358
359	_extension_struct_info=(
360('H','dfSizeFields',
361'''This field contains the size (in bytes) of the
362PFMEXTENSION structure. The value is always 0x001e. '''),
363
364('I','dfExtMetricsOffset',
365'''This field contains the offset from the beginning
366of the PFM file to the ExtTextMetrics section.
367The ExtTextMetrics section is always present in PFM
368files for Type-1 fonts, this field is therefore never
369zero. '''),
370
371('I','dfExtentTable',
372'''This field contains the offset from the beginning
373of the PFM file to the ExtentTable table. This table
374is always present in PFM files for Type-1 fonts, this
375field is therefore never zero. '''),
376
377('I','dfOriginTable',
378'''This field contains the offset from the beginning
379of the PFM file to a table containing origin coordinates
380for screen fonts. This table is not present in PFM files
381for Type-1 fonts, the field must therefore be set to zero. '''),
382
383('I','dfPairKernTable',
384'''This field contains the offset from the beginning of
385the PFM file to the KerningPairs table. The value must
386be zero if the PFM file doesn't contain a KerningPairs
387table. '''),
388
389('I','dfTrackKernTable',
390'''This field contains the offset from the beginning of
391the PFM file to the KerningTracks table. The value must
392be zero if the PFM file doesn't contain a kerningTracks
393table. '''),
394('I','dfDriverInfo',
395'''This field contains the offset from the beginning of
396the PFM file to the DriverInfo section. This section is
397always present in PFM files for Type-1 fonts, this field
398is therefore never zero. '''),
399
400('I','dfReserved',
401'''This field must be set to zero. '''),
402)
403
404#char DeviceName[]
405#The DeviceName character buffer is a null-terminated string
406#containing the name of the printer driver family. PFM files
407#for Type-1 fonts have the string 'PostScript', PFM files for
408#PCL fonts have the string 'PCL/HP LaserJet'.
409#char FaceName[]
410#The FaceName character buffer is a null-terminated string
411#containing the name of the font face. In PFM files for Type-1
412#fonts this is normally
413#the PostScript name of the font without suffixes like
414#'-Bold', '-Italic' etc.
415	_extTextMetrics_struct_info = (('h','etmSize',
416'''This field contains the size (in bytes) of the
417EXTTEXTMETRIC structure. The value is always 0x0034. '''),
418
419('h','etmPointSize',
420'''This field contains the nominal point size of the font
421in twips (this is a twentieth of a point or 1/1440 inch).
422This is the intended graphics art size of the font, the
423actual size may differ slightly depending on the resolution
424of the output device. In PFM files for Type-1 fonts this value
425should be set to 0x00f0 (240 twips or 12 pt). '''),
426
427('h','etmOrientation',
428'''This field contains the orientation of the font.
429This value refers to the ability of the font to be
430imaged on a page of a given orientation. It
431can be one of the following values:
432
4330x0000: any orientation
4340x0001: portrait (page width is smaller that its height)
4350x0002: landscape (page width is greater than its height)
436
437In PFM files for Type-1 fonts this field is always 0x0000
438since a Type-1 font can be arbitrarily rotated. '''),
439
440('h','etmMasterHeight',
441'''This field contains the font size in device units for
442which the values in the ExtentTable table are exact. Since
443Type-1 fonts are by convention defined in a box of 1000 x 1000
444units, PFM files for Type-1 fonts have the value 0x03E8 (1000,
445the number of units per em) in this field. '''),
446
447('h','etmMinScale',
448'''This field contains the minimum valid size for the font in
449device units. The minimum valid point size can then be calculated
450as follows:
451(etmMinScale * points-per-inch) / dfVertRes
452The value for 'points-per-inch' is normally 72, the dfVertRes
453field can be found in the PFMHEADER structure, it contains the
454vertical resolution at which the font was digitized (this
455value is in dots per inch).
456
457In PFM files for Type-1 fonts the value should be set to 0x0003. '''),
458
459('h','etmMaxScale',
460'''This field contains the maximum valid size for the font in
461device units. The maximum valid point size can then be calculated
462as follows:
463(etmMaxScale * points-per-inch) / dfVertRes
464(see also above etmMinScale).
465
466In PFM files for Type-1 fonts the value should be set to 0x03E8 (1000). '''),
467
468('h','etmMasterUnits',
469'''This field contains the integer number of units per em
470where an em equals etmMasterHeight in the EXTTEXTMETRIC structure.
471In other words, the etmMasterHeight value is expressed in font
472units rather than device units.
473
474In PFM files for Type-1 fonts the value should be set to
4750x03E8 (1000). '''),
476
477('h','etmCapHeight',
478'''This field contains the height for uppercase characters
479in the font (the value is in font units). Typically, the
480character 'H' is used for measurement purposes.
481
482For Type-1 fonts you may find this value in the AFM file. '''),
483
484('h','etmXHeight',
485'''This field contains the height for lowercase characters
486in the font (the value is in font units). Typically, the
487character 'x' is used for measurement purposes.
488
489For Type-1 fonts you may find this value in the AFM file. '''),
490
491('h','etmLowerCaseAscent',
492'''This field contains the distance (in font units) that
493the ascender of lowercase letters extends above the baseline.
494This distance is typically specified for a lowercase character 'd'.
495
496For Type-1 fonts you may find this value in the AFM file. '''),
497
498('h','etmLowerCaseDescent',
499'''This field contains the distance (in font units) that
500the descender of lowercase letters extends below the baseline.
501This distance is typically specified for a lowercase character 'p'.
502
503For Type-1 fonts you may find this value in the AFM file. '''),
504
505('h','etmSlant',
506'''This field contains the angle in tenth of degrees clockwise
507from the upright version of the font. The value is typically not zero only for
508an italic or oblique font.
509
510For Type-1 fonts you may find this value in the AFM file
511(search for the entry 'ItalicAngle' and multiply it by 10). '''),
512
513('h','etmSuperScript',
514'''This field contains the recommended amount (in font units)
515to offset superscript characters from the baseline. This amount
516is typically specified by a negative offset. '''),
517
518('h','etmSubScript',
519'''This field contains the recommended amount (in font units)
520to offset subscript characters from the baseline. This amount
521is typically specified by a positive offset. '''),
522
523('h','etmSuperScriptSize',
524'''This field contains the recommended size (in font units)
525for superscript characters in the font. '''),
526
527('h','etmSubScriptSize',
528'''This field contains the recommended size (in font units)
529for subscript characters in the font. '''),
530
531('h','etmUnderlineOffset',
532'''This field contains the offset (in font units) downward
533from the baseline where the top of a single underline bar
534should appear.
535
536For Type-1 fonts you may find this value in the AFM file. '''),
537
538('h','etmUnderlineWidth',
539'''This field contains the thickness (in font units) of the underline bar.
540For Type-1 fonts you may find this value in the AFM file. '''),
541
542('h','etmDoubleUpperUnderlineOffset',
543'''This field contains the offset (in font units) downward from
544the baseline where the top of the upper, double underline bar should
545appear. '''),
546
547('h','etmDoubleLowerUnderlineOffset',
548'''This field contains the offset (in font units) downward
549from the baseline where the top of the lower, double underline
550bar should appear. '''),
551
552('h','etmDoubleUpperUnderlineWidth',
553'''This field contains the thickness (in font units) of the
554upper, double underline bar. '''),
555
556('h','etmDoubleLowerUnderlineWidth',
557'''This field contains the thickness (in font units) of the
558lower, double underline bar. '''),
559
560('h','etmStrikeOutOffset',
561'''This field contains the offset (in font units) upward from
562the baseline where the top of a strikeout bar should appear. '''),
563
564('h','etmStrikeOutWidth',
565'''This field contains the thickness (in font units) of the
566strikeout bar. '''),
567
568('H','etmKernPairs',
569'''This field contains the number of kerning pairs defined
570in the KerningPairs table in this PFM file. The number (and
571therefore the table) may not be greater than 512. If the PFM
572file doesn't contain a KerningPairs table the value is zero. '''),
573
574('H','etmKernTracks',
575'''This field contains the number of kerning tracks defined in
576the KerningTracks table in this PFM file. The number (and therefore the
577table) may not be greater than 16. If the PFM file doesn't contain
578a KerningTracks table the value is zero. '''),
579)
580
581#'H','ExtentTable[]'
582#The ExtentTable table must be present in a PFM file for a Type-1 font,
583#it contains the unscaled widths (in 1/1000's of an em) of the characters
584#in the font. The table consists of (dfLastChar - dfFirstChar + 1) entries
585#of type WORD (dfFirstChar and dfLastChar can be found in the PFMHEADER
586#structure).  For Type-1 fonts these widths can be found in the AFM file.
587
588#DRIVERINFO DriverInfo
589#The DriverInfo section must be present in a PFM file for a Type-1 font,
590#in this case it consists of a null-terminated string containing the
591#PostScript name of the font.
592
593#PAIRKERN KerningPairs[]
594#The KerningPairs table need not be present in a PFM file for a Type-1
595#font, if it exists it contains etmKernPairs (from the EXTTEXTMETRIC
596#structure) entries. Each of these entries looks as follows:
597#B kpFirst This field contains the first (left) character of the kerning pair.
598#B kpSecond This field contains the second (right) character of the kerning pair.
599#h kpKernAmount This field contains the kerning amount in font units, the value
600#  is mostly negative.
601
602#KERNTRACK KerningTracks[]
603#The KerningTracks table need not be present in a PFM file for a Type-1 font, if it exists it contains etmKernTracks (from the EXTTEXTMETRIC structure) entries. Each of these entries looks as follows:
604#h ktDegree This field contains the amount to change the character spacing. Negative values mean closer together, positive values mean farther apart.
605#h ktMinSize This field contains the minimum font height (in device units) for which to use linear track kerning.
606#h ktMinAmount This field contains the track kerning amount to use for font heights less or equal ktMinSize.
607#h ktMaxSize This field contains the maximum font height (in device units) for which to use linear track kerning. For font heights between ktMinSize and ktMaxSize the track kerning amount has to increase linearily from ktMinAmount to ktMaxAmount.
608#h ktMaxAmount This field contains the track kerning amount to use for font heights greater or equal ktMaxSize.
609
610if __name__=='__main__':
611	from glob import glob
612	for f in glob('/Program Files/Adobe/Acrobat 4.0/resource/font/pfm/*.pfm'):
613		print(f)
614		p=PFM(f)
615		p.dump()
616