Add /OpenFontFormat

This commit is contained in:
germax26 2024-05-03 21:02:58 +10:00
parent 38987a04c6
commit 9b509e6306
Signed by: germax26
SSH Key Fingerprint: SHA256:N3w+8798IMWBt7SYH8G1C0iJlIa2HIIcRCXwILT5FvM
5 changed files with 6058 additions and 0 deletions

2
OpenFontFormat/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
examples
mypy.ini

5960
OpenFontFormat/OpenFont.py Normal file

File diff suppressed because it is too large Load Diff

21
OpenFontFormat/abcde.py Normal file
View File

@ -0,0 +1,21 @@
class ABD:
"""
#### Abstract Base Dataclass
This will only work on dataclasses
"""
def __post_init__(self, *args, **kwargs):
if ABD in self.__class__.__bases__ or ABD == self.__class__:
msg = f"Cannot instantiate an Abstract Base Dataclass: {self.__class__.__name__}"
raise TypeError(msg)
class ABE:
"""
#### Abstract Base Enum
This is for classes that will have an Enum subclass them
"""
def __init__(self, *args, **kwargs):
if ABE in self.__class__.__bases__ or ABE == self:
msg = f"Cannot instantiate an Abstract Base Enum: {self.__class__.__name__}"
raise TypeError(msg)

49
OpenFontFormat/name.py Normal file
View File

@ -0,0 +1,49 @@
import os
from OpenFont import FontSpecificNameID, NameID, NameTable_Format_0, OpenFont, PredefinedNameID, TrueTypeOutlines, parse_file
def search_names(font: OpenFont, nameID: NameID) -> str:
assert isinstance(font.naming_table, NameTable_Format_0)
for nameRecord in font.naming_table.nameRecord:
if nameRecord.nameID == nameID:
return nameRecord.string
assert False, f"Name not found: {nameID}"
def print_font(font: OpenFont):
assert isinstance(font.naming_table, NameTable_Format_0)
assert isinstance(font.outlines, TrueTypeOutlines)
name = search_names(font, PredefinedNameID.FULL_NAME)
print(name, f"({font.maximum_profile.numGlyphs} glyphs, {font.naming_table.count} names)")
if font.font_variations:
axis_names = [search_names(font, FontSpecificNameID(axis.axisNameID)) for axis in font.font_variations.font_variations.axes]
num_instances = font.font_variations.font_variations.instanceCount
print(f"\tAxes: [{', '.join(axis_names)}] ({num_instances} instances)")
path = "examples/"
# path = "examples/android_fonts/api_level/30"
# path = "examples/android_fonts/api_level/30/NotoNaskhArabic-Bold.ttf"
path = "examples/JetBrainsMono-2.304/"
if os.path.isfile(path):
try:
font = parse_file(path)
except AssertionError as err:
print("Failed:", path)
raise err
print_font(font)
else:
for root, dirs, files in os.walk(path):
for file in files:
_, ext = os.path.splitext(file)
match ext:
case '.ttf':
file_path = os.path.join(root, file)
try:
font = parse_file(file_path)
except AssertionError as err:
print("Failed:", file_path)
raise err
print_font(font)

View File

@ -0,0 +1,26 @@
from typing import BinaryIO
def read_int(f: BinaryIO, number: int, signed:bool=False) -> int: return int.from_bytes(f.read(number), 'big', signed=signed)
def read_u64(f: BinaryIO) -> int: return read_int(f, 8)
def read_u32(f: BinaryIO) -> int: return read_int(f, 4)
def read_u24(f: BinaryIO) -> int: return read_int(f, 3)
def read_u16(f: BinaryIO) -> int: return read_int(f, 2)
def read_u8(f: BinaryIO) -> int: return read_int(f, 1)
def read_i32(f: BinaryIO) -> int: return read_int(f, 4, signed=True)
def read_i16(f: BinaryIO) -> int: return read_int(f, 2, signed=True)
def read_i8(f: BinaryIO) -> int: return read_int(f, 1, signed=True)
def read_utf8(f: BinaryIO, number: int) -> str: return f.read(number).decode(encoding='utf8')
def read_ascii(f: BinaryIO, number: int) -> str: return f.read(number).decode(encoding='ascii')
def read_fixed_point(f: BinaryIO, preradix_bits: int, postradix_bits:int, *, signed:bool=True) -> float:
assert (preradix_bits+postradix_bits)%8 == 0
raw = read_int(f, (preradix_bits+postradix_bits)//8, signed=signed)
return raw/(1<<(postradix_bits))
def read_pascal_string(f: BinaryIO) -> str:
string_size = read_int(f, 1)
pascal_string = read_ascii(f, string_size)
return pascal_string