Spaces:
Sleeping
Sleeping
| class CharInfoWord(object): | |
| def __init__(self, word): | |
| b1, b2, b3, b4 = (word >> 24, | |
| (word & 0xff0000) >> 16, | |
| (word & 0xff00) >> 8, | |
| word & 0xff) | |
| self.width_index = b1 | |
| self.height_index = b2 >> 4 | |
| self.depth_index = b2 & 0x0f | |
| self.italic_index = (b3 & 0b11111100) >> 2 | |
| self.tag = b3 & 0b11 | |
| self.remainder = b4 | |
| def has_ligkern(self): | |
| return self.tag == 1 | |
| def ligkern_start(self): | |
| return self.remainder | |
| class LigKernProgram(object): | |
| def __init__(self, program): | |
| self.program = program | |
| def execute(self, start, next_char): | |
| curr_instruction = start | |
| while True: | |
| instruction = self.program[curr_instruction] | |
| (skip, inst_next_char, op, remainder) = instruction | |
| if inst_next_char == next_char: | |
| if op < 128: | |
| # Don't worry about ligatures for now, we only need kerns | |
| return None | |
| else: | |
| return 256 * (op - 128) + remainder | |
| elif skip >= 128: | |
| return None | |
| else: | |
| curr_instruction += 1 + skip | |
| class TfmCharMetrics(object): | |
| def __init__(self, width, height, depth, italic, kern_table): | |
| self.width = width | |
| self.height = height | |
| self.depth = depth | |
| self.italic_correction = italic | |
| self.kern_table = kern_table | |
| class TfmFile(object): | |
| def __init__(self, start_char, end_char, char_info, width_table, | |
| height_table, depth_table, italic_table, ligkern_table, | |
| kern_table): | |
| self.start_char = start_char | |
| self.end_char = end_char | |
| self.char_info = char_info | |
| self.width_table = width_table | |
| self.height_table = height_table | |
| self.depth_table = depth_table | |
| self.italic_table = italic_table | |
| self.ligkern_program = LigKernProgram(ligkern_table) | |
| self.kern_table = kern_table | |
| def get_char_metrics(self, char_num, fix_rsfs=False): | |
| """Return glyph metrics for a unicode code point. | |
| Arguments: | |
| char_num: a unicode code point | |
| fix_rsfs: adjust for rsfs10.tfm's different indexing system | |
| """ | |
| if char_num < self.start_char or char_num > self.end_char: | |
| raise RuntimeError("Invalid character number") | |
| if fix_rsfs: | |
| # all of the char_nums contained start from zero in rsfs10.tfm | |
| info = self.char_info[char_num - self.start_char] | |
| else: | |
| info = self.char_info[char_num + self.start_char] | |
| char_kern_table = {} | |
| if info.has_ligkern(): | |
| for char in range(self.start_char, self.end_char + 1): | |
| kern = self.ligkern_program.execute(info.ligkern_start(), char) | |
| if kern: | |
| char_kern_table[char] = self.kern_table[kern] | |
| return TfmCharMetrics( | |
| self.width_table[info.width_index], | |
| self.height_table[info.height_index], | |
| self.depth_table[info.depth_index], | |
| self.italic_table[info.italic_index], | |
| char_kern_table) | |
| class TfmReader(object): | |
| def __init__(self, f): | |
| self.f = f | |
| def read_byte(self): | |
| return ord(self.f.read(1)) | |
| def read_halfword(self): | |
| b1 = self.read_byte() | |
| b2 = self.read_byte() | |
| return (b1 << 8) | b2 | |
| def read_word(self): | |
| b1 = self.read_byte() | |
| b2 = self.read_byte() | |
| b3 = self.read_byte() | |
| b4 = self.read_byte() | |
| return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 | |
| def read_fixword(self): | |
| word = self.read_word() | |
| neg = False | |
| if word & 0x80000000: | |
| neg = True | |
| word = (-word & 0xffffffff) | |
| return (-1 if neg else 1) * word / float(1 << 20) | |
| def read_bcpl(self, length): | |
| str_length = self.read_byte() | |
| data = self.f.read(length - 1) | |
| return data[:str_length] | |
| def read_tfm_file(file_name): | |
| with open(file_name, 'rb') as f: | |
| reader = TfmReader(f) | |
| # file_size | |
| reader.read_halfword() | |
| header_size = reader.read_halfword() | |
| start_char = reader.read_halfword() | |
| end_char = reader.read_halfword() | |
| width_table_size = reader.read_halfword() | |
| height_table_size = reader.read_halfword() | |
| depth_table_size = reader.read_halfword() | |
| italic_table_size = reader.read_halfword() | |
| ligkern_table_size = reader.read_halfword() | |
| kern_table_size = reader.read_halfword() | |
| # extensible_table_size | |
| reader.read_halfword() | |
| # parameter_table_size | |
| reader.read_halfword() | |
| # checksum | |
| reader.read_word() | |
| # design_size | |
| reader.read_fixword() | |
| if header_size > 2: | |
| # coding_scheme | |
| reader.read_bcpl(40) | |
| if header_size > 12: | |
| # font_family | |
| reader.read_bcpl(20) | |
| for i in range(header_size - 17): | |
| reader.read_word() | |
| char_info = [] | |
| for i in range(start_char, end_char + 1): | |
| char_info.append(CharInfoWord(reader.read_word())) | |
| width_table = [] | |
| for i in range(width_table_size): | |
| width_table.append(reader.read_fixword()) | |
| height_table = [] | |
| for i in range(height_table_size): | |
| height_table.append(reader.read_fixword()) | |
| depth_table = [] | |
| for i in range(depth_table_size): | |
| depth_table.append(reader.read_fixword()) | |
| italic_table = [] | |
| for i in range(italic_table_size): | |
| italic_table.append(reader.read_fixword()) | |
| ligkern_table = [] | |
| for i in range(ligkern_table_size): | |
| skip = reader.read_byte() | |
| next_char = reader.read_byte() | |
| op = reader.read_byte() | |
| remainder = reader.read_byte() | |
| ligkern_table.append((skip, next_char, op, remainder)) | |
| kern_table = [] | |
| for i in range(kern_table_size): | |
| kern_table.append(reader.read_fixword()) | |
| # There is more information, like the ligkern, kern, extensible, and | |
| # param table, but we don't need these for now | |
| return TfmFile(start_char, end_char, char_info, width_table, | |
| height_table, depth_table, italic_table, | |
| ligkern_table, kern_table) | |