''' Python lib for reading PF2 font files: http://grub.gibibit.com/New_font_format I'd like to put it in Public Domain. Don't forget to see how it's used in `text_print.py` ''' import io from typing import Dict, Tuple def uint32be(b: bytes): 'Translate 4 bytes as unsigned big-endian 32-bit int' return ( (b[0] << 32) + (b[1] << 16) + (b[2] << 8) + b[3] ) def int32be(b: bytes): 'Translate 4 bytes as signed big-endian 32-bit int' u = uint32be(b) return u - ((u >> 31 & 0b1) << 32) def uint16be(b: bytes): 'Translate 2 bytes as big-endian unsigned 16-bit int' return (b[0] << 8) + b[1] def int16be(b: bytes): 'Translate 2 bytes as big-endian signed 16-bit int' u = uint16be(b) return u - ((u >> 15 & 0b1) << 16) class Character(): 'A PF2 character' width: int height: int x_offset: int y_offset: int device_width: int bitmap_data: bytes class PF2(): 'The PF2 class, for serializing a PF2 font file' is_pf2: bool 'Sets to false if the read file is not PF2 font file' missing_character_code: int in_memory: bool font_name: str family: str weight: str slant: str point_size: int max_width: int max_height: int ascent: int descent: int character_index: Dict[int, Tuple[int, int]] data_offset: int data_io: io.BufferedIOBase def __init__(self, path='font.pf2', *, read_to_mem=True, missing_character: str='?'): self.missing_character_code = ord(missing_character) self.in_memory = read_to_mem file = open(path, 'rb') if read_to_mem: self.data_io = io.BytesIO(file.read()) file.close() file = self.data_io self.is_pf2 = (file.read(12) == b'FILE\x00\x00\x00\x04PFF2') if not self.is_pf2: return while True: name = file.read(4) data_length = int32be(file.read(4)) if name == b'CHIX': self.character_index = {} for _ in range(data_length // (4 + 1 + 4)): code_point = int32be(file.read(4)) compression = file.read(1)[0] offset = int32be(file.read(4)) self.character_index[code_point] = ( compression, offset ) continue elif name == b'DATA': file.seek(0) break data = file.read(data_length) if name == b'NAME': self.font_name = data elif name == b'FAMI': self.family = data elif name == b'WEIG': self.weight = data elif name == b'SLAN': self.slant = data elif name == b'PTSZ': self.point_size = uint16be(data) elif name == b'MAXW': self.max_width = uint16be(data) elif name == b'MAXH': self.max_height = uint16be(data) elif name == b'ASCE': self.ascent = uint16be(data) elif name == b'DESC': self.descent = uint16be(data) def get_char(self, char: str): 'Get a character, returning a `Character` instance' char_point = ord(char) info = self.character_index.get(char_point) if info is None: info = self.character_index[self.missing_character_code] _compression, offset = info data = self.data_io data.seek(offset) char = Character() char.width = uint16be(data.read(2)) char.height = uint16be(data.read(2)) char.x_offset = int16be(data.read(2)) char.y_offset = int16be(data.read(2)) char.device_width = int16be(data.read(2)) char.bitmap_data = data.read( (char.width * char.height + 7) // 8 ) return char __getitem__ = get_char def __del__(self): self.data_io.close()