www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

parse_tfm.py (6524B)


      1 class CharInfoWord(object):
      2     def __init__(self, word):
      3         b1, b2, b3, b4 = (word >> 24,
      4                           (word & 0xff0000) >> 16,
      5                           (word & 0xff00) >> 8,
      6                           word & 0xff)
      7 
      8         self.width_index = b1
      9         self.height_index = b2 >> 4
     10         self.depth_index = b2 & 0x0f
     11         self.italic_index = (b3 & 0b11111100) >> 2
     12         self.tag = b3 & 0b11
     13         self.remainder = b4
     14 
     15     def has_ligkern(self):
     16         return self.tag == 1
     17 
     18     def ligkern_start(self):
     19         return self.remainder
     20 
     21 
     22 class LigKernProgram(object):
     23     def __init__(self, program):
     24         self.program = program
     25 
     26     def execute(self, start, next_char):
     27         curr_instruction = start
     28         while True:
     29             instruction = self.program[curr_instruction]
     30             (skip, inst_next_char, op, remainder) = instruction
     31 
     32             if inst_next_char == next_char:
     33                 if op < 128:
     34                     # Don't worry about ligatures for now, we only need kerns
     35                     return None
     36                 else:
     37                     return 256 * (op - 128) + remainder
     38             elif skip >= 128:
     39                 return None
     40             else:
     41                 curr_instruction += 1 + skip
     42 
     43 
     44 class TfmCharMetrics(object):
     45     def __init__(self, width, height, depth, italic, kern_table):
     46         self.width = width
     47         self.height = height
     48         self.depth = depth
     49         self.italic_correction = italic
     50         self.kern_table = kern_table
     51 
     52 
     53 class TfmFile(object):
     54     def __init__(self, start_char, end_char, char_info, width_table,
     55                  height_table, depth_table, italic_table, ligkern_table,
     56                  kern_table):
     57         self.start_char = start_char
     58         self.end_char = end_char
     59         self.char_info = char_info
     60         self.width_table = width_table
     61         self.height_table = height_table
     62         self.depth_table = depth_table
     63         self.italic_table = italic_table
     64         self.ligkern_program = LigKernProgram(ligkern_table)
     65         self.kern_table = kern_table
     66 
     67     def get_char_metrics(self, char_num, fix_rsfs=False):
     68         """Return glyph metrics for a unicode code point.
     69 
     70         Arguments:
     71             char_num: a unicode code point
     72             fix_rsfs: adjust for rsfs10.tfm's different indexing system
     73         """
     74         if char_num < self.start_char or char_num > self.end_char:
     75             raise RuntimeError("Invalid character number")
     76 
     77         if fix_rsfs:
     78             # all of the char_nums contained start from zero in rsfs10.tfm
     79             info = self.char_info[char_num - self.start_char]
     80         else:
     81             info = self.char_info[char_num + self.start_char]
     82 
     83         char_kern_table = {}
     84         if info.has_ligkern():
     85             for char in range(self.start_char, self.end_char + 1):
     86                 kern = self.ligkern_program.execute(info.ligkern_start(), char)
     87                 if kern:
     88                     char_kern_table[char] = self.kern_table[kern]
     89 
     90         return TfmCharMetrics(
     91             self.width_table[info.width_index],
     92             self.height_table[info.height_index],
     93             self.depth_table[info.depth_index],
     94             self.italic_table[info.italic_index],
     95             char_kern_table)
     96 
     97 
     98 class TfmReader(object):
     99     def __init__(self, f):
    100         self.f = f
    101 
    102     def read_byte(self):
    103         return ord(self.f.read(1))
    104 
    105     def read_halfword(self):
    106         b1 = self.read_byte()
    107         b2 = self.read_byte()
    108         return (b1 << 8) | b2
    109 
    110     def read_word(self):
    111         b1 = self.read_byte()
    112         b2 = self.read_byte()
    113         b3 = self.read_byte()
    114         b4 = self.read_byte()
    115         return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4
    116 
    117     def read_fixword(self):
    118         word = self.read_word()
    119 
    120         neg = False
    121         if word & 0x80000000:
    122             neg = True
    123             word = (-word & 0xffffffff)
    124 
    125         return (-1 if neg else 1) * word / float(1 << 20)
    126 
    127     def read_bcpl(self, length):
    128         str_length = self.read_byte()
    129         data = self.f.read(length - 1)
    130         return data[:str_length]
    131 
    132 
    133 def read_tfm_file(file_name):
    134     with open(file_name, 'rb') as f:
    135         reader = TfmReader(f)
    136 
    137         # file_size
    138         reader.read_halfword()
    139         header_size = reader.read_halfword()
    140 
    141         start_char = reader.read_halfword()
    142         end_char = reader.read_halfword()
    143 
    144         width_table_size = reader.read_halfword()
    145         height_table_size = reader.read_halfword()
    146         depth_table_size = reader.read_halfword()
    147         italic_table_size = reader.read_halfword()
    148 
    149         ligkern_table_size = reader.read_halfword()
    150         kern_table_size = reader.read_halfword()
    151 
    152         # extensible_table_size
    153         reader.read_halfword()
    154         # parameter_table_size
    155         reader.read_halfword()
    156 
    157         # checksum
    158         reader.read_word()
    159         # design_size
    160         reader.read_fixword()
    161 
    162         if header_size > 2:
    163             # coding_scheme
    164             reader.read_bcpl(40)
    165 
    166         if header_size > 12:
    167             # font_family
    168             reader.read_bcpl(20)
    169 
    170         for i in range(header_size - 17):
    171             reader.read_word()
    172 
    173         char_info = []
    174         for i in range(start_char, end_char + 1):
    175             char_info.append(CharInfoWord(reader.read_word()))
    176 
    177         width_table = []
    178         for i in range(width_table_size):
    179             width_table.append(reader.read_fixword())
    180 
    181         height_table = []
    182         for i in range(height_table_size):
    183             height_table.append(reader.read_fixword())
    184 
    185         depth_table = []
    186         for i in range(depth_table_size):
    187             depth_table.append(reader.read_fixword())
    188 
    189         italic_table = []
    190         for i in range(italic_table_size):
    191             italic_table.append(reader.read_fixword())
    192 
    193         ligkern_table = []
    194         for i in range(ligkern_table_size):
    195             skip = reader.read_byte()
    196             next_char = reader.read_byte()
    197             op = reader.read_byte()
    198             remainder = reader.read_byte()
    199 
    200             ligkern_table.append((skip, next_char, op, remainder))
    201 
    202         kern_table = []
    203         for i in range(kern_table_size):
    204             kern_table.append(reader.read_fixword())
    205 
    206         # There is more information, like the ligkern, kern, extensible, and
    207         # param table, but we don't need these for now
    208 
    209         return TfmFile(start_char, end_char, char_info, width_table,
    210                        height_table, depth_table, italic_table,
    211                        ligkern_table, kern_table)