import re
from typing import List
def to_snake_case(name: str) -> str:
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
class TTFType:
def to_rust(self) -> str:
raise NotImplementedError()
def size(self) -> int:
return 0
def print(self, offset: int) -> None:
raise NotImplementedError()
class TTF_UInt8(TTFType):
def to_rust(self) -> str:
return 'u8'
def size(self) -> int:
return 1
def print(self, offset: int) -> None:
print(f'self.data[{offset}]')
class TTF_UInt16(TTFType):
def to_rust(self) -> str:
return 'u16'
def size(self) -> int:
return 2
def print(self, offset: int) -> None:
print(f'u16::from_be_bytes([self.data[{offset}], self.data[{offset + 1}]])')
class TTF_Int16(TTFType):
def to_rust(self) -> str:
return 'i16'
def size(self) -> int:
return 2
def print(self, offset: int) -> None:
print(f'i16::from_be_bytes([self.data[{offset}], self.data[{offset + 1}]])')
class TTF_UInt24(TTFType):
def to_rust(self) -> str:
return 'u32'
def size(self) -> int:
return 3
def print(self, offset: int) -> None:
print(f'(self.data[{offset}] as u32) << 16 | (self.data[{offset + 1}] as u32) << 8 '
f'| self.data[{offset + 2}] as u32')
class TTF_UInt32(TTFType):
def to_rust(self) -> str:
return 'u32'
def size(self) -> int:
return 4
def print(self, offset: int) -> None:
print(f'u32::from_be_bytes(['
f' self.data[{offset}], self.data[{offset + 1}], self.data[{offset + 2}], self.data[{offset + 3}]'
f'])')
class TTF_FWORD(TTF_Int16):
pass
class TTF_UFWORD(TTF_UInt16):
pass
class TTF_Offset32(TTF_UInt32):
pass
class TTF_Optional_Offset32(TTF_UInt32):
def to_rust(self) -> str:
return 'Option<u32>'
def size(self) -> int:
return 4
def print(self, offset: int) -> None:
print(f'let n = u32::from_be_bytes(['
f' self.data[{offset}], self.data[{offset + 1}], self.data[{offset + 2}], self.data[{offset + 3}]'
f']);')
print('if n != 0 { Some(n) } else { None }')
class TTF_GlyphId(TTFType):
def to_rust(self) -> str:
return 'GlyphId'
def size(self) -> int:
return 2
def print(self, offset: int) -> None:
print(f'GlyphId(u16::from_be_bytes([self.data[{offset}], self.data[{offset + 1}]]))')
class TTF_Tag(TTFType):
def to_rust(self) -> str:
return '[u8; 4]'
def size(self) -> int:
return 4
def print(self, offset: int) -> None:
print('// Unwrap is safe, because an array and a slice have the same size.')
print(f'self.data[{offset}..{offset + self.size()}].try_into().unwrap()')
class TTF_Fixed(TTFType):
def size(self) -> int:
return 4
class TTF_LONGDATETIME(TTFType):
def size(self) -> int:
return 8
class TTF_Panose(TTFType):
def size(self) -> int:
return 10
class TableRow:
enable: bool
ttf_type: TTFType
name: str
def __init__(self, enable: bool, ttf_type: TTFType, name: str):
self.enable = enable
self.ttf_type = ttf_type
self.name = name
TTC_HEADER = [
TableRow(True, TTF_Tag(), 'ttcTag'),
TableRow(False, TTF_UInt16(), 'majorVersion'),
TableRow(False, TTF_UInt16(), 'minorVersion'),
TableRow(True, TTF_UInt32(), 'numFonts'),
]
TABLE_RECORD = [
TableRow(True, TTF_Tag(), 'tableTag'),
TableRow(False, TTF_UInt32(), 'checkSum'),
TableRow(True, TTF_Offset32(), 'offset'),
TableRow(True, TTF_UInt32(), 'length'),
]
HEAD_TABLE = [
TableRow(False, TTF_UInt16(), 'majorVersion'),
TableRow(False, TTF_UInt16(), 'minorVersion'),
TableRow(False, TTF_Fixed(), 'fontRevision'),
TableRow(False, TTF_UInt32(), 'checkSumAdjustment'),
TableRow(False, TTF_UInt32(), 'magicNumber'),
TableRow(False, TTF_UInt16(), 'flags'),
TableRow(True, TTF_UInt16(), 'unitsPerEm'),
TableRow(False, TTF_LONGDATETIME(), 'created'),
TableRow(False, TTF_LONGDATETIME(), 'modified'),
TableRow(False, TTF_Int16(), 'xMin'),
TableRow(False, TTF_Int16(), 'yMin'),
TableRow(False, TTF_Int16(), 'xMax'),
TableRow(False, TTF_Int16(), 'yMax'),
TableRow(False, TTF_UInt16(), 'macStyle'),
TableRow(False, TTF_UInt16(), 'lowestRecPPEM'),
TableRow(False, TTF_Int16(), 'fontDirectionHint'),
TableRow(True, TTF_Int16(), 'indexToLocFormat'),
TableRow(False, TTF_Int16(), 'glyphDataFormat'),
]
HHEA_TABLE = [
TableRow(False, TTF_UInt16(), 'majorVersion'),
TableRow(False, TTF_UInt16(), 'minorVersion'),
TableRow(True, TTF_FWORD(), 'ascender'),
TableRow(True, TTF_FWORD(), 'descender'),
TableRow(True, TTF_FWORD(), 'lineGap'),
TableRow(False, TTF_UFWORD(), 'advanceWidthMax'),
TableRow(False, TTF_FWORD(), 'minLeftSideBearing'),
TableRow(False, TTF_FWORD(), 'minRightSideBearing'),
TableRow(False, TTF_FWORD(), 'xMaxExtent'),
TableRow(False, TTF_Int16(), 'caretSlopeRise'),
TableRow(False, TTF_Int16(), 'caretSlopeRun'),
TableRow(False, TTF_Int16(), 'caretOffset'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'metricDataFormat'),
TableRow(True, TTF_UInt16(), 'numberOfHMetrics'),
]
HMTX_METRICS = [
TableRow(True, TTF_UInt16(), 'advanceWidth'),
TableRow(True, TTF_Int16(), 'lsb'),
]
VHEA_TABLE = [
TableRow(False, TTF_Fixed(), 'version'),
TableRow(False, TTF_Int16(), 'ascender'),
TableRow(False, TTF_Int16(), 'descender'),
TableRow(False, TTF_Int16(), 'lineGap'),
TableRow(False, TTF_Int16(), 'advanceHeightMax'),
TableRow(False, TTF_Int16(), 'minTopSideBearing'),
TableRow(False, TTF_Int16(), 'minBottomSideBearing'),
TableRow(False, TTF_Int16(), 'yMaxExtent'),
TableRow(False, TTF_Int16(), 'caretSlopeRise'),
TableRow(False, TTF_Int16(), 'caretSlopeRun'),
TableRow(False, TTF_Int16(), 'caretOffset'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'reserved'),
TableRow(False, TTF_Int16(), 'metricDataFormat'),
TableRow(True, TTF_UInt16(), 'numOfLongVerMetrics'),
]
VMTX_METRICS = [
TableRow(True, TTF_UInt16(), 'advanceHeight'),
TableRow(True, TTF_Int16(), 'topSideBearing'),
]
POST_TABLE = [
TableRow(False, TTF_Fixed(), 'version'),
TableRow(False, TTF_Fixed(), 'italicAngle'),
TableRow(True, TTF_FWORD(), 'underlinePosition'),
TableRow(True, TTF_FWORD(), 'underlineThickness'),
TableRow(False, TTF_UInt32(), 'isFixedPitch'),
TableRow(False, TTF_UInt32(), 'minMemType42'),
TableRow(False, TTF_UInt32(), 'maxMemType42'),
TableRow(False, TTF_UInt32(), 'minMemType1'),
TableRow(False, TTF_UInt32(), 'maxMemType1'),
]
NAME_RECORD_TABLE = [
TableRow(True, TTF_UInt16(), 'platformID'),
TableRow(True, TTF_UInt16(), 'encodingID'),
TableRow(True, TTF_UInt16(), 'languageID'),
TableRow(True, TTF_UInt16(), 'nameID'),
TableRow(True, TTF_UInt16(), 'length'),
TableRow(True, TTF_UInt16(), 'offset'),
]
CMAP_ENCODING_RECORD = [
TableRow(True, TTF_UInt16(), 'platformID'),
TableRow(True, TTF_UInt16(), 'encodingID'),
TableRow(True, TTF_Offset32(), 'offset'),
]
CMAP_SUB_HEADER_RECORD = [
TableRow(True, TTF_UInt16(), 'firstCode'),
TableRow(True, TTF_UInt16(), 'entryCount'),
TableRow(True, TTF_Int16(), 'idDelta'),
TableRow(True, TTF_UInt16(), 'idRangeOffset'),
]
CMAP_SEQUENTIAL_MAP_GROUP_RECORD = [
TableRow(True, TTF_UInt32(), 'startCharCode'),
TableRow(True, TTF_UInt32(), 'endCharCode'),
TableRow(True, TTF_UInt32(), 'startGlyphID'),
]
CMAP_UNICODE_RANGE_RECORD = [
TableRow(True, TTF_UInt24(), 'startUnicodeValue'),
TableRow(True, TTF_UInt8(), 'additionalCount'),
]
CMAP_UVS_MAPPING_RECORD = [
TableRow(True, TTF_UInt24(), 'unicodeValue'),
TableRow(True, TTF_GlyphId(), 'glyphID'),
]
CMAP_VARIATION_SELECTOR_RECORD = [
TableRow(True, TTF_UInt24(), 'varSelector'),
TableRow(True, TTF_Optional_Offset32(), 'defaultUVSOffset'),
TableRow(True, TTF_Optional_Offset32(), 'nonDefaultUVSOffset'),
]
MAXP_TABLE = [
TableRow(False, TTF_Fixed(), 'version'),
TableRow(True, TTF_UInt16(), 'numGlyphs'),
]
OS_2_TABLE_V0 = [
TableRow(True, TTF_UInt16(), 'version'),
TableRow(False, TTF_Int16(), 'xAvgCharWidth'),
TableRow(True, TTF_UInt16(), 'usWeightClass'),
TableRow(True, TTF_UInt16(), 'usWidthClass'),
TableRow(False, TTF_UInt16(), 'fsType'),
TableRow(True, TTF_Int16(), 'ySubscriptXSize'),
TableRow(True, TTF_Int16(), 'ySubscriptYSize'),
TableRow(True, TTF_Int16(), 'ySubscriptXOffset'),
TableRow(True, TTF_Int16(), 'ySubscriptYOffset'),
TableRow(True, TTF_Int16(), 'ySuperscriptXSize'),
TableRow(True, TTF_Int16(), 'ySuperscriptYSize'),
TableRow(True, TTF_Int16(), 'ySuperscriptXOffset'),
TableRow(True, TTF_Int16(), 'ySuperscriptYOffset'),
TableRow(True, TTF_Int16(), 'yStrikeoutSize'),
TableRow(True, TTF_Int16(), 'yStrikeoutPosition'),
TableRow(False, TTF_Int16(), 'sFamilyClass'),
TableRow(False, TTF_Panose(), 'panose'),
TableRow(False, TTF_UInt32(), 'ulUnicodeRange1'),
TableRow(False, TTF_UInt32(), 'ulUnicodeRange2'),
TableRow(False, TTF_UInt32(), 'ulUnicodeRange3'),
TableRow(False, TTF_UInt32(), 'ulUnicodeRange4'),
TableRow(False, TTF_Tag(), 'achVendID'),
TableRow(True, TTF_UInt16(), 'fsSelection'),
TableRow(False, TTF_UInt16(), 'usFirstCharIndex'),
TableRow(False, TTF_UInt16(), 'usLastCharIndex'),
TableRow(False, TTF_Int16(), 'sTypoAscender'),
TableRow(False, TTF_Int16(), 'sTypoDescender'),
TableRow(False, TTF_Int16(), 'sTypoLineGap'),
TableRow(False, TTF_UInt16(), 'usWinAscent'),
TableRow(False, TTF_UInt16(), 'usWinDescent'),
]
def print_struct(name: str, size: int, owned: bool) -> None:
print('#[derive(Clone, Copy)]')
if owned:
print(f'pub struct {name} {{ data: [u8; {size}] }}')
else:
print(f'pub struct {name}<\'a> {{ data: &\'a [u8; {size}] }}')
def print_struct_size(size: int) -> None:
print(f'pub const SIZE: usize = {size};')
def print_constructor(name: str, size: int, owned: bool) -> None:
print('#[inline(always)]')
if owned:
print('pub fn new(input: &[u8]) -> Self {')
print(' let mut data = [0u8; Self::SIZE];')
print(' data.clone_from_slice(input);')
print(f' {name} {{ data }}')
print('}')
else:
print('pub fn new(input: &\'a [u8]) -> Self {')
print(f' {name} {{ data: array_ref![input, {size}] }}')
print('}')
def print_method(spec_name: str, ttf_type: TTFType, offset: int) -> None:
fn_name = to_snake_case(spec_name)
rust_type = ttf_type.to_rust()
print(' #[inline(always)]')
print(f' pub fn {fn_name}(&self) -> {rust_type} {{')
ttf_type.print(offset)
print(' }')
def print_impl_from_data(name: str) -> None:
print(f'impl FromData for {name} {{')
print(f' const SIZE: usize = {name}::SIZE;')
print()
print(' #[inline]')
print(' fn parse(data: &[u8]) -> Self {')
print(' Self::new(data)')
print(' }')
print('}')
def generate_table(table: List[TableRow], struct_name: str, owned: bool = False,
impl_from_data: bool = False) -> None:
struct_size = 0
for row in table:
struct_size += row.ttf_type.size()
print_struct(struct_name, struct_size, owned)
print()
if owned:
print(f'impl {struct_name} {{')
else:
print(f'impl<\'a> {struct_name}<\'a> {{')
print_struct_size(struct_size)
print()
print_constructor(struct_name, struct_size, owned)
print()
offset = 0
for row in table:
if not row.enable:
offset += row.ttf_type.size()
continue
print_method(row.name, row.ttf_type, offset)
print()
offset += row.ttf_type.size()
print('}')
if impl_from_data:
print()
print_impl_from_data(struct_name)
print('// This file is autogenerated by scripts/get-tables.py')
print('// Do not edit it!')
print()
print('// By using static arrays we can have compile-time guaranties that')
print('// we are not reading out-ouf-bounds.')
print('// Also, it removes bounds-checking overhead.')
print()
print('// Based on https://github.com/droundy/arrayref')
print('macro_rules! array_ref {')
print(' ($arr:expr, $len:expr) => {{')
print(' // Always check that the slice length is the same as `$len`.')
print(' assert_eq!($arr.len(), $len);')
print(' unsafe { &*($arr.as_ptr() as *const [_; $len]) }')
print(' }}')
print('}')
print()
print('use core::convert::TryInto;')
print('use crate::parser::FromData;')
print()
generate_table(TTC_HEADER, 'TTCHeader')
print()
generate_table(TABLE_RECORD, 'TableRecord', owned=True, impl_from_data=True)
print()
print('pub mod head {')
generate_table(HEAD_TABLE, 'Table')
print('}')
print()
print('pub mod maxp {')
generate_table(MAXP_TABLE, 'Table')
print('}')
print()
print('pub mod hhea {')
generate_table(HHEA_TABLE, 'Table')
print('}')
print()
print('pub mod hmtx {')
print('use crate::parser::FromData;')
print()
generate_table(HMTX_METRICS, 'HorizontalMetrics', owned=True, impl_from_data=True)
print('}')
print()
print('pub mod vhea {')
generate_table(VHEA_TABLE, 'Table')
print('}')
print()
print('pub mod vmtx {')
print('use crate::parser::FromData;')
print()
generate_table(VMTX_METRICS, 'VerticalMetrics', owned=True, impl_from_data=True)
print('}')
print()
print('pub mod post {')
generate_table(POST_TABLE, 'Table')
print('}')
print()
print('pub mod cmap {')
print('use crate::GlyphId;')
print('use crate::parser::FromData;')
print()
generate_table(CMAP_ENCODING_RECORD, 'EncodingRecord', owned=True, impl_from_data=True)
print()
generate_table(CMAP_SUB_HEADER_RECORD, 'SubHeaderRecord', owned=True, impl_from_data=True)
print()
generate_table(CMAP_SEQUENTIAL_MAP_GROUP_RECORD, 'SequentialMapGroup', owned=True, impl_from_data=True)
print()
generate_table(CMAP_UNICODE_RANGE_RECORD, 'UnicodeRangeRecord', owned=True, impl_from_data=True)
print()
generate_table(CMAP_UVS_MAPPING_RECORD, 'UVSMappingRecord', owned=True, impl_from_data=True)
print()
generate_table(CMAP_VARIATION_SELECTOR_RECORD, 'VariationSelectorRecord', owned=True, impl_from_data=True)
print('}')
print()
print('pub mod os_2 {')
generate_table(OS_2_TABLE_V0, 'TableV0')
print('}')
print()
print('pub mod name {')
generate_table(NAME_RECORD_TABLE, 'NameRecord', owned=True)
print('}')
print()