1*e1fe3e4aSElliott Hughes"""Module for reading TFM (TeX Font Metrics) files. 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott HughesThe TFM format is described in the TFtoPL WEB source code, whose typeset form 4*e1fe3e4aSElliott Hughescan be found on `CTAN <http://mirrors.ctan.org/info/knuth-pdf/texware/tftopl.pdf>`_. 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott Hughes >>> from fontTools.tfmLib import TFM 7*e1fe3e4aSElliott Hughes >>> tfm = TFM("Tests/tfmLib/data/cmr10.tfm") 8*e1fe3e4aSElliott Hughes >>> 9*e1fe3e4aSElliott Hughes >>> # Accessing an attribute gets you metadata. 10*e1fe3e4aSElliott Hughes >>> tfm.checksum 11*e1fe3e4aSElliott Hughes 1274110073 12*e1fe3e4aSElliott Hughes >>> tfm.designsize 13*e1fe3e4aSElliott Hughes 10.0 14*e1fe3e4aSElliott Hughes >>> tfm.codingscheme 15*e1fe3e4aSElliott Hughes 'TeX text' 16*e1fe3e4aSElliott Hughes >>> tfm.family 17*e1fe3e4aSElliott Hughes 'CMR' 18*e1fe3e4aSElliott Hughes >>> tfm.seven_bit_safe_flag 19*e1fe3e4aSElliott Hughes False 20*e1fe3e4aSElliott Hughes >>> tfm.face 21*e1fe3e4aSElliott Hughes 234 22*e1fe3e4aSElliott Hughes >>> tfm.extraheader 23*e1fe3e4aSElliott Hughes {} 24*e1fe3e4aSElliott Hughes >>> tfm.fontdimens 25*e1fe3e4aSElliott Hughes {'SLANT': 0.0, 'SPACE': 0.33333396911621094, 'STRETCH': 0.16666698455810547, 'SHRINK': 0.11111164093017578, 'XHEIGHT': 0.4305553436279297, 'QUAD': 1.0000028610229492, 'EXTRASPACE': 0.11111164093017578} 26*e1fe3e4aSElliott Hughes >>> # Accessing a character gets you its metrics. 27*e1fe3e4aSElliott Hughes >>> # “width” is always available, other metrics are available only when 28*e1fe3e4aSElliott Hughes >>> # applicable. All values are relative to “designsize”. 29*e1fe3e4aSElliott Hughes >>> tfm.chars[ord("g")] 30*e1fe3e4aSElliott Hughes {'width': 0.5000019073486328, 'height': 0.4305553436279297, 'depth': 0.1944446563720703, 'italic': 0.013888359069824219} 31*e1fe3e4aSElliott Hughes >>> # Kerning and ligature can be accessed as well. 32*e1fe3e4aSElliott Hughes >>> tfm.kerning[ord("c")] 33*e1fe3e4aSElliott Hughes {104: -0.02777862548828125, 107: -0.02777862548828125} 34*e1fe3e4aSElliott Hughes >>> tfm.ligatures[ord("f")] 35*e1fe3e4aSElliott Hughes {105: ('LIG', 12), 102: ('LIG', 11), 108: ('LIG', 13)} 36*e1fe3e4aSElliott Hughes""" 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughesfrom types import SimpleNamespace 39*e1fe3e4aSElliott Hughes 40*e1fe3e4aSElliott Hughesfrom fontTools.misc.sstruct import calcsize, unpack, unpack2 41*e1fe3e4aSElliott Hughes 42*e1fe3e4aSElliott HughesSIZES_FORMAT = """ 43*e1fe3e4aSElliott Hughes > 44*e1fe3e4aSElliott Hughes lf: h # length of the entire file, in words 45*e1fe3e4aSElliott Hughes lh: h # length of the header data, in words 46*e1fe3e4aSElliott Hughes bc: h # smallest character code in the font 47*e1fe3e4aSElliott Hughes ec: h # largest character code in the font 48*e1fe3e4aSElliott Hughes nw: h # number of words in the width table 49*e1fe3e4aSElliott Hughes nh: h # number of words in the height table 50*e1fe3e4aSElliott Hughes nd: h # number of words in the depth table 51*e1fe3e4aSElliott Hughes ni: h # number of words in the italic correction table 52*e1fe3e4aSElliott Hughes nl: h # number of words in the ligature/kern table 53*e1fe3e4aSElliott Hughes nk: h # number of words in the kern table 54*e1fe3e4aSElliott Hughes ne: h # number of words in the extensible character table 55*e1fe3e4aSElliott Hughes np: h # number of font parameter words 56*e1fe3e4aSElliott Hughes""" 57*e1fe3e4aSElliott Hughes 58*e1fe3e4aSElliott HughesSIZES_SIZE = calcsize(SIZES_FORMAT) 59*e1fe3e4aSElliott Hughes 60*e1fe3e4aSElliott HughesFIXED_FORMAT = "12.20F" 61*e1fe3e4aSElliott Hughes 62*e1fe3e4aSElliott HughesHEADER_FORMAT1 = f""" 63*e1fe3e4aSElliott Hughes > 64*e1fe3e4aSElliott Hughes checksum: L 65*e1fe3e4aSElliott Hughes designsize: {FIXED_FORMAT} 66*e1fe3e4aSElliott Hughes""" 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott HughesHEADER_FORMAT2 = f""" 69*e1fe3e4aSElliott Hughes {HEADER_FORMAT1} 70*e1fe3e4aSElliott Hughes codingscheme: 40p 71*e1fe3e4aSElliott Hughes""" 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott HughesHEADER_FORMAT3 = f""" 74*e1fe3e4aSElliott Hughes {HEADER_FORMAT2} 75*e1fe3e4aSElliott Hughes family: 20p 76*e1fe3e4aSElliott Hughes""" 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott HughesHEADER_FORMAT4 = f""" 79*e1fe3e4aSElliott Hughes {HEADER_FORMAT3} 80*e1fe3e4aSElliott Hughes seven_bit_safe_flag: ? 81*e1fe3e4aSElliott Hughes ignored: x 82*e1fe3e4aSElliott Hughes ignored: x 83*e1fe3e4aSElliott Hughes face: B 84*e1fe3e4aSElliott Hughes""" 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott HughesHEADER_SIZE1 = calcsize(HEADER_FORMAT1) 87*e1fe3e4aSElliott HughesHEADER_SIZE2 = calcsize(HEADER_FORMAT2) 88*e1fe3e4aSElliott HughesHEADER_SIZE3 = calcsize(HEADER_FORMAT3) 89*e1fe3e4aSElliott HughesHEADER_SIZE4 = calcsize(HEADER_FORMAT4) 90*e1fe3e4aSElliott Hughes 91*e1fe3e4aSElliott HughesLIG_KERN_COMMAND = """ 92*e1fe3e4aSElliott Hughes > 93*e1fe3e4aSElliott Hughes skip_byte: B 94*e1fe3e4aSElliott Hughes next_char: B 95*e1fe3e4aSElliott Hughes op_byte: B 96*e1fe3e4aSElliott Hughes remainder: B 97*e1fe3e4aSElliott Hughes""" 98*e1fe3e4aSElliott Hughes 99*e1fe3e4aSElliott HughesBASE_PARAMS = [ 100*e1fe3e4aSElliott Hughes "SLANT", 101*e1fe3e4aSElliott Hughes "SPACE", 102*e1fe3e4aSElliott Hughes "STRETCH", 103*e1fe3e4aSElliott Hughes "SHRINK", 104*e1fe3e4aSElliott Hughes "XHEIGHT", 105*e1fe3e4aSElliott Hughes "QUAD", 106*e1fe3e4aSElliott Hughes "EXTRASPACE", 107*e1fe3e4aSElliott Hughes] 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott HughesMATHSY_PARAMS = [ 110*e1fe3e4aSElliott Hughes "NUM1", 111*e1fe3e4aSElliott Hughes "NUM2", 112*e1fe3e4aSElliott Hughes "NUM3", 113*e1fe3e4aSElliott Hughes "DENOM1", 114*e1fe3e4aSElliott Hughes "DENOM2", 115*e1fe3e4aSElliott Hughes "SUP1", 116*e1fe3e4aSElliott Hughes "SUP2", 117*e1fe3e4aSElliott Hughes "SUP3", 118*e1fe3e4aSElliott Hughes "SUB1", 119*e1fe3e4aSElliott Hughes "SUB2", 120*e1fe3e4aSElliott Hughes "SUPDROP", 121*e1fe3e4aSElliott Hughes "SUBDROP", 122*e1fe3e4aSElliott Hughes "DELIM1", 123*e1fe3e4aSElliott Hughes "DELIM2", 124*e1fe3e4aSElliott Hughes "AXISHEIGHT", 125*e1fe3e4aSElliott Hughes] 126*e1fe3e4aSElliott Hughes 127*e1fe3e4aSElliott HughesMATHEX_PARAMS = [ 128*e1fe3e4aSElliott Hughes "DEFAULTRULETHICKNESS", 129*e1fe3e4aSElliott Hughes "BIGOPSPACING1", 130*e1fe3e4aSElliott Hughes "BIGOPSPACING2", 131*e1fe3e4aSElliott Hughes "BIGOPSPACING3", 132*e1fe3e4aSElliott Hughes "BIGOPSPACING4", 133*e1fe3e4aSElliott Hughes "BIGOPSPACING5", 134*e1fe3e4aSElliott Hughes] 135*e1fe3e4aSElliott Hughes 136*e1fe3e4aSElliott HughesVANILLA = 0 137*e1fe3e4aSElliott HughesMATHSY = 1 138*e1fe3e4aSElliott HughesMATHEX = 2 139*e1fe3e4aSElliott Hughes 140*e1fe3e4aSElliott HughesUNREACHABLE = 0 141*e1fe3e4aSElliott HughesPASSTHROUGH = 1 142*e1fe3e4aSElliott HughesACCESSABLE = 2 143*e1fe3e4aSElliott Hughes 144*e1fe3e4aSElliott HughesNO_TAG = 0 145*e1fe3e4aSElliott HughesLIG_TAG = 1 146*e1fe3e4aSElliott HughesLIST_TAG = 2 147*e1fe3e4aSElliott HughesEXT_TAG = 3 148*e1fe3e4aSElliott Hughes 149*e1fe3e4aSElliott HughesSTOP_FLAG = 128 150*e1fe3e4aSElliott HughesKERN_FLAG = 128 151*e1fe3e4aSElliott Hughes 152*e1fe3e4aSElliott Hughes 153*e1fe3e4aSElliott Hughesclass TFMException(Exception): 154*e1fe3e4aSElliott Hughes def __init__(self, message): 155*e1fe3e4aSElliott Hughes super().__init__(message) 156*e1fe3e4aSElliott Hughes 157*e1fe3e4aSElliott Hughes 158*e1fe3e4aSElliott Hughesclass TFM: 159*e1fe3e4aSElliott Hughes def __init__(self, file): 160*e1fe3e4aSElliott Hughes self._read(file) 161*e1fe3e4aSElliott Hughes 162*e1fe3e4aSElliott Hughes def __repr__(self): 163*e1fe3e4aSElliott Hughes return ( 164*e1fe3e4aSElliott Hughes f"<TFM" 165*e1fe3e4aSElliott Hughes f" for {self.family}" 166*e1fe3e4aSElliott Hughes f" in {self.codingscheme}" 167*e1fe3e4aSElliott Hughes f" at {self.designsize:g}pt>" 168*e1fe3e4aSElliott Hughes ) 169*e1fe3e4aSElliott Hughes 170*e1fe3e4aSElliott Hughes def _read(self, file): 171*e1fe3e4aSElliott Hughes if hasattr(file, "read"): 172*e1fe3e4aSElliott Hughes data = file.read() 173*e1fe3e4aSElliott Hughes else: 174*e1fe3e4aSElliott Hughes with open(file, "rb") as fp: 175*e1fe3e4aSElliott Hughes data = fp.read() 176*e1fe3e4aSElliott Hughes 177*e1fe3e4aSElliott Hughes self._data = data 178*e1fe3e4aSElliott Hughes 179*e1fe3e4aSElliott Hughes if len(data) < SIZES_SIZE: 180*e1fe3e4aSElliott Hughes raise TFMException("Too short input file") 181*e1fe3e4aSElliott Hughes 182*e1fe3e4aSElliott Hughes sizes = SimpleNamespace() 183*e1fe3e4aSElliott Hughes unpack2(SIZES_FORMAT, data, sizes) 184*e1fe3e4aSElliott Hughes 185*e1fe3e4aSElliott Hughes # Do some file structure sanity checks. 186*e1fe3e4aSElliott Hughes # TeX and TFtoPL do additional functional checks and might even correct 187*e1fe3e4aSElliott Hughes # “errors” in the input file, but we instead try to output the file as 188*e1fe3e4aSElliott Hughes # it is as long as it is parsable, even if the data make no sense. 189*e1fe3e4aSElliott Hughes 190*e1fe3e4aSElliott Hughes if sizes.lf < 0: 191*e1fe3e4aSElliott Hughes raise TFMException("The file claims to have negative or zero length!") 192*e1fe3e4aSElliott Hughes 193*e1fe3e4aSElliott Hughes if len(data) < sizes.lf * 4: 194*e1fe3e4aSElliott Hughes raise TFMException("The file has fewer bytes than it claims!") 195*e1fe3e4aSElliott Hughes 196*e1fe3e4aSElliott Hughes for name, length in vars(sizes).items(): 197*e1fe3e4aSElliott Hughes if length < 0: 198*e1fe3e4aSElliott Hughes raise TFMException("The subfile size: '{name}' is negative!") 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes if sizes.lh < 2: 201*e1fe3e4aSElliott Hughes raise TFMException(f"The header length is only {sizes.lh}!") 202*e1fe3e4aSElliott Hughes 203*e1fe3e4aSElliott Hughes if sizes.bc > sizes.ec + 1 or sizes.ec > 255: 204*e1fe3e4aSElliott Hughes raise TFMException( 205*e1fe3e4aSElliott Hughes f"The character code range {sizes.bc}..{sizes.ec} is illegal!" 206*e1fe3e4aSElliott Hughes ) 207*e1fe3e4aSElliott Hughes 208*e1fe3e4aSElliott Hughes if sizes.nw == 0 or sizes.nh == 0 or sizes.nd == 0 or sizes.ni == 0: 209*e1fe3e4aSElliott Hughes raise TFMException("Incomplete subfiles for character dimensions!") 210*e1fe3e4aSElliott Hughes 211*e1fe3e4aSElliott Hughes if sizes.ne > 256: 212*e1fe3e4aSElliott Hughes raise TFMException(f"There are {ne} extensible recipes!") 213*e1fe3e4aSElliott Hughes 214*e1fe3e4aSElliott Hughes if sizes.lf != ( 215*e1fe3e4aSElliott Hughes 6 216*e1fe3e4aSElliott Hughes + sizes.lh 217*e1fe3e4aSElliott Hughes + (sizes.ec - sizes.bc + 1) 218*e1fe3e4aSElliott Hughes + sizes.nw 219*e1fe3e4aSElliott Hughes + sizes.nh 220*e1fe3e4aSElliott Hughes + sizes.nd 221*e1fe3e4aSElliott Hughes + sizes.ni 222*e1fe3e4aSElliott Hughes + sizes.nl 223*e1fe3e4aSElliott Hughes + sizes.nk 224*e1fe3e4aSElliott Hughes + sizes.ne 225*e1fe3e4aSElliott Hughes + sizes.np 226*e1fe3e4aSElliott Hughes ): 227*e1fe3e4aSElliott Hughes raise TFMException("Subfile sizes don’t add up to the stated total") 228*e1fe3e4aSElliott Hughes 229*e1fe3e4aSElliott Hughes # Subfile offsets, used in the helper function below. These all are 230*e1fe3e4aSElliott Hughes # 32-bit word offsets not 8-bit byte offsets. 231*e1fe3e4aSElliott Hughes char_base = 6 + sizes.lh - sizes.bc 232*e1fe3e4aSElliott Hughes width_base = char_base + sizes.ec + 1 233*e1fe3e4aSElliott Hughes height_base = width_base + sizes.nw 234*e1fe3e4aSElliott Hughes depth_base = height_base + sizes.nh 235*e1fe3e4aSElliott Hughes italic_base = depth_base + sizes.nd 236*e1fe3e4aSElliott Hughes lig_kern_base = italic_base + sizes.ni 237*e1fe3e4aSElliott Hughes kern_base = lig_kern_base + sizes.nl 238*e1fe3e4aSElliott Hughes exten_base = kern_base + sizes.nk 239*e1fe3e4aSElliott Hughes param_base = exten_base + sizes.ne 240*e1fe3e4aSElliott Hughes 241*e1fe3e4aSElliott Hughes # Helper functions for accessing individual data. If this looks 242*e1fe3e4aSElliott Hughes # nonidiomatic Python, I blame the effect of reading the literate WEB 243*e1fe3e4aSElliott Hughes # documentation of TFtoPL. 244*e1fe3e4aSElliott Hughes def char_info(c): 245*e1fe3e4aSElliott Hughes return 4 * (char_base + c) 246*e1fe3e4aSElliott Hughes 247*e1fe3e4aSElliott Hughes def width_index(c): 248*e1fe3e4aSElliott Hughes return data[char_info(c)] 249*e1fe3e4aSElliott Hughes 250*e1fe3e4aSElliott Hughes def noneexistent(c): 251*e1fe3e4aSElliott Hughes return c < sizes.bc or c > sizes.ec or width_index(c) == 0 252*e1fe3e4aSElliott Hughes 253*e1fe3e4aSElliott Hughes def height_index(c): 254*e1fe3e4aSElliott Hughes return data[char_info(c) + 1] // 16 255*e1fe3e4aSElliott Hughes 256*e1fe3e4aSElliott Hughes def depth_index(c): 257*e1fe3e4aSElliott Hughes return data[char_info(c) + 1] % 16 258*e1fe3e4aSElliott Hughes 259*e1fe3e4aSElliott Hughes def italic_index(c): 260*e1fe3e4aSElliott Hughes return data[char_info(c) + 2] // 4 261*e1fe3e4aSElliott Hughes 262*e1fe3e4aSElliott Hughes def tag(c): 263*e1fe3e4aSElliott Hughes return data[char_info(c) + 2] % 4 264*e1fe3e4aSElliott Hughes 265*e1fe3e4aSElliott Hughes def remainder(c): 266*e1fe3e4aSElliott Hughes return data[char_info(c) + 3] 267*e1fe3e4aSElliott Hughes 268*e1fe3e4aSElliott Hughes def width(c): 269*e1fe3e4aSElliott Hughes r = 4 * (width_base + width_index(c)) 270*e1fe3e4aSElliott Hughes return read_fixed(r, "v")["v"] 271*e1fe3e4aSElliott Hughes 272*e1fe3e4aSElliott Hughes def height(c): 273*e1fe3e4aSElliott Hughes r = 4 * (height_base + height_index(c)) 274*e1fe3e4aSElliott Hughes return read_fixed(r, "v")["v"] 275*e1fe3e4aSElliott Hughes 276*e1fe3e4aSElliott Hughes def depth(c): 277*e1fe3e4aSElliott Hughes r = 4 * (depth_base + depth_index(c)) 278*e1fe3e4aSElliott Hughes return read_fixed(r, "v")["v"] 279*e1fe3e4aSElliott Hughes 280*e1fe3e4aSElliott Hughes def italic(c): 281*e1fe3e4aSElliott Hughes r = 4 * (italic_base + italic_index(c)) 282*e1fe3e4aSElliott Hughes return read_fixed(r, "v")["v"] 283*e1fe3e4aSElliott Hughes 284*e1fe3e4aSElliott Hughes def exten(c): 285*e1fe3e4aSElliott Hughes return 4 * (exten_base + remainder(c)) 286*e1fe3e4aSElliott Hughes 287*e1fe3e4aSElliott Hughes def lig_step(i): 288*e1fe3e4aSElliott Hughes return 4 * (lig_kern_base + i) 289*e1fe3e4aSElliott Hughes 290*e1fe3e4aSElliott Hughes def lig_kern_command(i): 291*e1fe3e4aSElliott Hughes command = SimpleNamespace() 292*e1fe3e4aSElliott Hughes unpack2(LIG_KERN_COMMAND, data[i:], command) 293*e1fe3e4aSElliott Hughes return command 294*e1fe3e4aSElliott Hughes 295*e1fe3e4aSElliott Hughes def kern(i): 296*e1fe3e4aSElliott Hughes r = 4 * (kern_base + i) 297*e1fe3e4aSElliott Hughes return read_fixed(r, "v")["v"] 298*e1fe3e4aSElliott Hughes 299*e1fe3e4aSElliott Hughes def param(i): 300*e1fe3e4aSElliott Hughes return 4 * (param_base + i) 301*e1fe3e4aSElliott Hughes 302*e1fe3e4aSElliott Hughes def read_fixed(index, key, obj=None): 303*e1fe3e4aSElliott Hughes ret = unpack2(f">;{key}:{FIXED_FORMAT}", data[index:], obj) 304*e1fe3e4aSElliott Hughes return ret[0] 305*e1fe3e4aSElliott Hughes 306*e1fe3e4aSElliott Hughes # Set all attributes to empty values regardless of the header size. 307*e1fe3e4aSElliott Hughes unpack(HEADER_FORMAT4, [0] * HEADER_SIZE4, self) 308*e1fe3e4aSElliott Hughes 309*e1fe3e4aSElliott Hughes offset = 24 310*e1fe3e4aSElliott Hughes length = sizes.lh * 4 311*e1fe3e4aSElliott Hughes self.extraheader = {} 312*e1fe3e4aSElliott Hughes if length >= HEADER_SIZE4: 313*e1fe3e4aSElliott Hughes rest = unpack2(HEADER_FORMAT4, data[offset:], self)[1] 314*e1fe3e4aSElliott Hughes if self.face < 18: 315*e1fe3e4aSElliott Hughes s = self.face % 2 316*e1fe3e4aSElliott Hughes b = self.face // 2 317*e1fe3e4aSElliott Hughes self.face = "MBL"[b % 3] + "RI"[s] + "RCE"[b // 3] 318*e1fe3e4aSElliott Hughes for i in range(sizes.lh - HEADER_SIZE4 // 4): 319*e1fe3e4aSElliott Hughes rest = unpack2(f">;HEADER{i + 18}:l", rest, self.extraheader)[1] 320*e1fe3e4aSElliott Hughes elif length >= HEADER_SIZE3: 321*e1fe3e4aSElliott Hughes unpack2(HEADER_FORMAT3, data[offset:], self) 322*e1fe3e4aSElliott Hughes elif length >= HEADER_SIZE2: 323*e1fe3e4aSElliott Hughes unpack2(HEADER_FORMAT2, data[offset:], self) 324*e1fe3e4aSElliott Hughes elif length >= HEADER_SIZE1: 325*e1fe3e4aSElliott Hughes unpack2(HEADER_FORMAT1, data[offset:], self) 326*e1fe3e4aSElliott Hughes 327*e1fe3e4aSElliott Hughes self.fonttype = VANILLA 328*e1fe3e4aSElliott Hughes scheme = self.codingscheme.upper() 329*e1fe3e4aSElliott Hughes if scheme.startswith("TEX MATH SY"): 330*e1fe3e4aSElliott Hughes self.fonttype = MATHSY 331*e1fe3e4aSElliott Hughes elif scheme.startswith("TEX MATH EX"): 332*e1fe3e4aSElliott Hughes self.fonttype = MATHEX 333*e1fe3e4aSElliott Hughes 334*e1fe3e4aSElliott Hughes self.fontdimens = {} 335*e1fe3e4aSElliott Hughes for i in range(sizes.np): 336*e1fe3e4aSElliott Hughes name = f"PARAMETER{i+1}" 337*e1fe3e4aSElliott Hughes if i <= 6: 338*e1fe3e4aSElliott Hughes name = BASE_PARAMS[i] 339*e1fe3e4aSElliott Hughes elif self.fonttype == MATHSY and i <= 21: 340*e1fe3e4aSElliott Hughes name = MATHSY_PARAMS[i - 7] 341*e1fe3e4aSElliott Hughes elif self.fonttype == MATHEX and i <= 12: 342*e1fe3e4aSElliott Hughes name = MATHEX_PARAMS[i - 7] 343*e1fe3e4aSElliott Hughes read_fixed(param(i), name, self.fontdimens) 344*e1fe3e4aSElliott Hughes 345*e1fe3e4aSElliott Hughes lig_kern_map = {} 346*e1fe3e4aSElliott Hughes self.right_boundary_char = None 347*e1fe3e4aSElliott Hughes self.left_boundary_char = None 348*e1fe3e4aSElliott Hughes if sizes.nl > 0: 349*e1fe3e4aSElliott Hughes cmd = lig_kern_command(lig_step(0)) 350*e1fe3e4aSElliott Hughes if cmd.skip_byte == 255: 351*e1fe3e4aSElliott Hughes self.right_boundary_char = cmd.next_char 352*e1fe3e4aSElliott Hughes 353*e1fe3e4aSElliott Hughes cmd = lig_kern_command(lig_step((sizes.nl - 1))) 354*e1fe3e4aSElliott Hughes if cmd.skip_byte == 255: 355*e1fe3e4aSElliott Hughes self.left_boundary_char = 256 356*e1fe3e4aSElliott Hughes r = 256 * cmd.op_byte + cmd.remainder 357*e1fe3e4aSElliott Hughes lig_kern_map[self.left_boundary_char] = r 358*e1fe3e4aSElliott Hughes 359*e1fe3e4aSElliott Hughes self.chars = {} 360*e1fe3e4aSElliott Hughes for c in range(sizes.bc, sizes.ec + 1): 361*e1fe3e4aSElliott Hughes if width_index(c) > 0: 362*e1fe3e4aSElliott Hughes self.chars[c] = info = {} 363*e1fe3e4aSElliott Hughes info["width"] = width(c) 364*e1fe3e4aSElliott Hughes if height_index(c) > 0: 365*e1fe3e4aSElliott Hughes info["height"] = height(c) 366*e1fe3e4aSElliott Hughes if depth_index(c) > 0: 367*e1fe3e4aSElliott Hughes info["depth"] = depth(c) 368*e1fe3e4aSElliott Hughes if italic_index(c) > 0: 369*e1fe3e4aSElliott Hughes info["italic"] = italic(c) 370*e1fe3e4aSElliott Hughes char_tag = tag(c) 371*e1fe3e4aSElliott Hughes if char_tag == NO_TAG: 372*e1fe3e4aSElliott Hughes pass 373*e1fe3e4aSElliott Hughes elif char_tag == LIG_TAG: 374*e1fe3e4aSElliott Hughes lig_kern_map[c] = remainder(c) 375*e1fe3e4aSElliott Hughes elif char_tag == LIST_TAG: 376*e1fe3e4aSElliott Hughes info["nextlarger"] = remainder(c) 377*e1fe3e4aSElliott Hughes elif char_tag == EXT_TAG: 378*e1fe3e4aSElliott Hughes info["varchar"] = varchar = {} 379*e1fe3e4aSElliott Hughes for i in range(4): 380*e1fe3e4aSElliott Hughes part = data[exten(c) + i] 381*e1fe3e4aSElliott Hughes if i == 3 or part > 0: 382*e1fe3e4aSElliott Hughes name = "rep" 383*e1fe3e4aSElliott Hughes if i == 0: 384*e1fe3e4aSElliott Hughes name = "top" 385*e1fe3e4aSElliott Hughes elif i == 1: 386*e1fe3e4aSElliott Hughes name = "mid" 387*e1fe3e4aSElliott Hughes elif i == 2: 388*e1fe3e4aSElliott Hughes name = "bot" 389*e1fe3e4aSElliott Hughes if noneexistent(part): 390*e1fe3e4aSElliott Hughes varchar[name] = c 391*e1fe3e4aSElliott Hughes else: 392*e1fe3e4aSElliott Hughes varchar[name] = part 393*e1fe3e4aSElliott Hughes 394*e1fe3e4aSElliott Hughes self.ligatures = {} 395*e1fe3e4aSElliott Hughes self.kerning = {} 396*e1fe3e4aSElliott Hughes for c, i in sorted(lig_kern_map.items()): 397*e1fe3e4aSElliott Hughes cmd = lig_kern_command(lig_step(i)) 398*e1fe3e4aSElliott Hughes if cmd.skip_byte > STOP_FLAG: 399*e1fe3e4aSElliott Hughes i = 256 * cmd.op_byte + cmd.remainder 400*e1fe3e4aSElliott Hughes 401*e1fe3e4aSElliott Hughes while i < sizes.nl: 402*e1fe3e4aSElliott Hughes cmd = lig_kern_command(lig_step(i)) 403*e1fe3e4aSElliott Hughes if cmd.skip_byte > STOP_FLAG: 404*e1fe3e4aSElliott Hughes pass 405*e1fe3e4aSElliott Hughes else: 406*e1fe3e4aSElliott Hughes if cmd.op_byte >= KERN_FLAG: 407*e1fe3e4aSElliott Hughes r = 256 * (cmd.op_byte - KERN_FLAG) + cmd.remainder 408*e1fe3e4aSElliott Hughes self.kerning.setdefault(c, {})[cmd.next_char] = kern(r) 409*e1fe3e4aSElliott Hughes else: 410*e1fe3e4aSElliott Hughes r = cmd.op_byte 411*e1fe3e4aSElliott Hughes if r == 4 or (r > 7 and r != 11): 412*e1fe3e4aSElliott Hughes # Ligature step with nonstandard code, we output 413*e1fe3e4aSElliott Hughes # the code verbatim. 414*e1fe3e4aSElliott Hughes lig = r 415*e1fe3e4aSElliott Hughes else: 416*e1fe3e4aSElliott Hughes lig = "" 417*e1fe3e4aSElliott Hughes if r % 4 > 1: 418*e1fe3e4aSElliott Hughes lig += "/" 419*e1fe3e4aSElliott Hughes lig += "LIG" 420*e1fe3e4aSElliott Hughes if r % 2 != 0: 421*e1fe3e4aSElliott Hughes lig += "/" 422*e1fe3e4aSElliott Hughes while r > 3: 423*e1fe3e4aSElliott Hughes lig += ">" 424*e1fe3e4aSElliott Hughes r -= 4 425*e1fe3e4aSElliott Hughes self.ligatures.setdefault(c, {})[cmd.next_char] = ( 426*e1fe3e4aSElliott Hughes lig, 427*e1fe3e4aSElliott Hughes cmd.remainder, 428*e1fe3e4aSElliott Hughes ) 429*e1fe3e4aSElliott Hughes 430*e1fe3e4aSElliott Hughes if cmd.skip_byte >= STOP_FLAG: 431*e1fe3e4aSElliott Hughes break 432*e1fe3e4aSElliott Hughes i += cmd.skip_byte + 1 433*e1fe3e4aSElliott Hughes 434*e1fe3e4aSElliott Hughes 435*e1fe3e4aSElliott Hughesif __name__ == "__main__": 436*e1fe3e4aSElliott Hughes import sys 437*e1fe3e4aSElliott Hughes 438*e1fe3e4aSElliott Hughes tfm = TFM(sys.argv[1]) 439*e1fe3e4aSElliott Hughes print( 440*e1fe3e4aSElliott Hughes "\n".join( 441*e1fe3e4aSElliott Hughes x 442*e1fe3e4aSElliott Hughes for x in [ 443*e1fe3e4aSElliott Hughes f"tfm.checksum={tfm.checksum}", 444*e1fe3e4aSElliott Hughes f"tfm.designsize={tfm.designsize}", 445*e1fe3e4aSElliott Hughes f"tfm.codingscheme={tfm.codingscheme}", 446*e1fe3e4aSElliott Hughes f"tfm.fonttype={tfm.fonttype}", 447*e1fe3e4aSElliott Hughes f"tfm.family={tfm.family}", 448*e1fe3e4aSElliott Hughes f"tfm.seven_bit_safe_flag={tfm.seven_bit_safe_flag}", 449*e1fe3e4aSElliott Hughes f"tfm.face={tfm.face}", 450*e1fe3e4aSElliott Hughes f"tfm.extraheader={tfm.extraheader}", 451*e1fe3e4aSElliott Hughes f"tfm.fontdimens={tfm.fontdimens}", 452*e1fe3e4aSElliott Hughes f"tfm.right_boundary_char={tfm.right_boundary_char}", 453*e1fe3e4aSElliott Hughes f"tfm.left_boundary_char={tfm.left_boundary_char}", 454*e1fe3e4aSElliott Hughes f"tfm.kerning={tfm.kerning}", 455*e1fe3e4aSElliott Hughes f"tfm.ligatures={tfm.ligatures}", 456*e1fe3e4aSElliott Hughes f"tfm.chars={tfm.chars}", 457*e1fe3e4aSElliott Hughes ] 458*e1fe3e4aSElliott Hughes ) 459*e1fe3e4aSElliott Hughes ) 460*e1fe3e4aSElliott Hughes print(tfm) 461