mirror of
https://github.com/NaitLee/Cat-Printer.git
synced 2025-05-16 07:10:30 -07:00
136 lines
4.0 KiB
Python
136 lines
4.0 KiB
Python
''' 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] << 24) +
|
|
(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()
|