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