pcffont 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcffont/__init__.py +10 -0
- pcffont/error.py +25 -0
- pcffont/font.py +143 -0
- pcffont/header.py +70 -0
- pcffont/internal/__init__.py +0 -0
- pcffont/internal/buffer.py +122 -0
- pcffont/internal/util.py +49 -0
- pcffont/metric.py +59 -0
- pcffont/t_accelerators.py +143 -0
- pcffont/t_bitmaps.py +135 -0
- pcffont/t_encodings.py +104 -0
- pcffont/t_glyph_names.py +58 -0
- pcffont/t_metrics.py +49 -0
- pcffont/t_properties.py +388 -0
- pcffont/t_scalable_widths.py +40 -0
- pcffont/table.py +29 -0
- pcffont/xlfd.py +50 -0
- pcffont-0.0.1.dist-info/METADATA +76 -0
- pcffont-0.0.1.dist-info/RECORD +21 -0
- pcffont-0.0.1.dist-info/WHEEL +4 -0
- pcffont-0.0.1.dist-info/licenses/LICENSE +21 -0
pcffont/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pcffont.font import PcfFont
|
|
2
|
+
from pcffont.header import PcfTableType
|
|
3
|
+
from pcffont.metric import PcfMetric
|
|
4
|
+
from pcffont.t_properties import PcfProperties
|
|
5
|
+
from pcffont.t_accelerators import PcfAccelerators
|
|
6
|
+
from pcffont.t_metrics import PcfMetrics
|
|
7
|
+
from pcffont.t_bitmaps import PcfBitmaps
|
|
8
|
+
from pcffont.t_encodings import PcfBdfEncodings
|
|
9
|
+
from pcffont.t_scalable_widths import PcfScalableWidths
|
|
10
|
+
from pcffont.t_glyph_names import PcfGlyphNames
|
pcffont/error.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
class PcfError(Exception):
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PcfPropKeyError(PcfError):
|
|
7
|
+
def __init__(self, key: str, reason: str):
|
|
8
|
+
self.key = key
|
|
9
|
+
self.reason = reason
|
|
10
|
+
super().__init__(f"'{key}': {reason}")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PcfPropValueError(PcfError):
|
|
14
|
+
def __init__(self, key: str, value: object, reason: str):
|
|
15
|
+
self.key = key
|
|
16
|
+
self.value = value
|
|
17
|
+
self.reason = reason
|
|
18
|
+
super().__init__(f"'{key}': '{value}': {reason}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PcfXlfdError(PcfError):
|
|
22
|
+
def __init__(self, font_name: str, reason: str):
|
|
23
|
+
self.font_name = font_name
|
|
24
|
+
self.reason = reason
|
|
25
|
+
super().__init__(f"'{font_name}': {reason}")
|
pcffont/font.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections import UserDict
|
|
3
|
+
from typing import BinaryIO
|
|
4
|
+
|
|
5
|
+
from pcffont.error import PcfError
|
|
6
|
+
from pcffont.header import PcfTableType, PcfHeader
|
|
7
|
+
from pcffont.internal import util
|
|
8
|
+
from pcffont.internal.buffer import Buffer
|
|
9
|
+
from pcffont.t_accelerators import PcfAccelerators
|
|
10
|
+
from pcffont.t_bitmaps import PcfBitmaps
|
|
11
|
+
from pcffont.t_encodings import PcfBdfEncodings
|
|
12
|
+
from pcffont.t_glyph_names import PcfGlyphNames
|
|
13
|
+
from pcffont.t_metrics import PcfMetrics
|
|
14
|
+
from pcffont.t_properties import PcfProperties
|
|
15
|
+
from pcffont.t_scalable_widths import PcfScalableWidths
|
|
16
|
+
from pcffont.table import PcfTable
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PcfFont(UserDict[PcfTableType, PcfTable]):
|
|
20
|
+
@staticmethod
|
|
21
|
+
def parse(stream: BinaryIO) -> 'PcfFont':
|
|
22
|
+
buffer = Buffer(stream)
|
|
23
|
+
|
|
24
|
+
headers = PcfHeader.parse(buffer)
|
|
25
|
+
|
|
26
|
+
tables = {}
|
|
27
|
+
for header in headers:
|
|
28
|
+
if header.table_type in tables:
|
|
29
|
+
raise PcfError(f"Duplicate table '{header.table_type.name}'")
|
|
30
|
+
tables[header.table_type] = util.parse_table(buffer, header)
|
|
31
|
+
|
|
32
|
+
return PcfFont(tables)
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def load(file_path: str | bytes | os.PathLike[str] | os.PathLike[bytes]) -> 'PcfFont':
|
|
36
|
+
with open(file_path, 'rb') as file:
|
|
37
|
+
return PcfFont.parse(file)
|
|
38
|
+
|
|
39
|
+
def __init__(self, tables: dict[PcfTableType, PcfTable | None] = None):
|
|
40
|
+
super().__init__(tables)
|
|
41
|
+
|
|
42
|
+
def __setitem__(self, table_type: PcfTableType, table: PcfTable | None):
|
|
43
|
+
if table is None:
|
|
44
|
+
self.pop(table_type, None)
|
|
45
|
+
else:
|
|
46
|
+
if not isinstance(table, util.TABLE_TYPE_REGISTRY[table_type]):
|
|
47
|
+
raise PcfError(f"Mismatched table type: '{table_type.name}' -> '{type(table)}'")
|
|
48
|
+
super().__setitem__(table_type, table)
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return object.__repr__(self)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def properties(self) -> PcfProperties | None:
|
|
55
|
+
return self.get(PcfTableType.PROPERTIES, None)
|
|
56
|
+
|
|
57
|
+
@properties.setter
|
|
58
|
+
def properties(self, table: PcfProperties | None):
|
|
59
|
+
self[PcfTableType.PROPERTIES] = table
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def accelerators(self) -> PcfAccelerators | None:
|
|
63
|
+
return self.get(PcfTableType.ACCELERATORS, None)
|
|
64
|
+
|
|
65
|
+
@accelerators.setter
|
|
66
|
+
def accelerators(self, table: PcfAccelerators | None):
|
|
67
|
+
self[PcfTableType.ACCELERATORS] = table
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def metrics(self) -> PcfMetrics | None:
|
|
71
|
+
return self.get(PcfTableType.METRICS, None)
|
|
72
|
+
|
|
73
|
+
@metrics.setter
|
|
74
|
+
def metrics(self, table: PcfMetrics | None):
|
|
75
|
+
self[PcfTableType.METRICS] = table
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def bitmaps(self) -> PcfBitmaps | None:
|
|
79
|
+
return self.get(PcfTableType.BITMAPS, None)
|
|
80
|
+
|
|
81
|
+
@bitmaps.setter
|
|
82
|
+
def bitmaps(self, table: PcfBitmaps | None):
|
|
83
|
+
self[PcfTableType.BITMAPS] = table
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def ink_metrics(self) -> PcfMetrics | None:
|
|
87
|
+
return self.get(PcfTableType.INK_METRICS, None)
|
|
88
|
+
|
|
89
|
+
@ink_metrics.setter
|
|
90
|
+
def ink_metrics(self, table: PcfMetrics | None):
|
|
91
|
+
self[PcfTableType.INK_METRICS] = table
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def bdf_encodings(self) -> PcfBdfEncodings | None:
|
|
95
|
+
return self.get(PcfTableType.BDF_ENCODINGS, None)
|
|
96
|
+
|
|
97
|
+
@bdf_encodings.setter
|
|
98
|
+
def bdf_encodings(self, table: PcfBdfEncodings | None):
|
|
99
|
+
self[PcfTableType.BDF_ENCODINGS] = table
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def scalable_widths(self) -> PcfScalableWidths | None:
|
|
103
|
+
return self.get(PcfTableType.SWIDTHS, None)
|
|
104
|
+
|
|
105
|
+
@scalable_widths.setter
|
|
106
|
+
def scalable_widths(self, table: PcfScalableWidths | None):
|
|
107
|
+
self[PcfTableType.SWIDTHS] = table
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def glyph_names(self) -> PcfGlyphNames | None:
|
|
111
|
+
return self.get(PcfTableType.GLYPH_NAMES, None)
|
|
112
|
+
|
|
113
|
+
@glyph_names.setter
|
|
114
|
+
def glyph_names(self, table: PcfGlyphNames | None):
|
|
115
|
+
self[PcfTableType.GLYPH_NAMES] = table
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def bdf_accelerators(self) -> PcfAccelerators | None:
|
|
119
|
+
return self.get(PcfTableType.BDF_ACCELERATORS, None)
|
|
120
|
+
|
|
121
|
+
@bdf_accelerators.setter
|
|
122
|
+
def bdf_accelerators(self, table: PcfAccelerators | None):
|
|
123
|
+
self[PcfTableType.BDF_ACCELERATORS] = table
|
|
124
|
+
|
|
125
|
+
def dump(self, stream: BinaryIO, compat_mode: bool = False):
|
|
126
|
+
buffer = Buffer(stream)
|
|
127
|
+
|
|
128
|
+
headers = []
|
|
129
|
+
table_offset = 4 + 4 + (4 * 4) * len(self)
|
|
130
|
+
for table_type, table in sorted(self.items()):
|
|
131
|
+
table_size = table.dump(buffer, table_offset, compat_mode)
|
|
132
|
+
headers.append(PcfHeader(table_type, table.table_format, table_size, table_offset))
|
|
133
|
+
table_offset += table_size
|
|
134
|
+
|
|
135
|
+
PcfHeader.dump(buffer, headers)
|
|
136
|
+
|
|
137
|
+
def save(
|
|
138
|
+
self,
|
|
139
|
+
file_path: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
|
140
|
+
compat_mode: bool = False,
|
|
141
|
+
):
|
|
142
|
+
with open(file_path, 'wb') as file:
|
|
143
|
+
self.dump(file, compat_mode)
|
pcffont/header.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from enum import IntEnum, IntFlag
|
|
2
|
+
|
|
3
|
+
from pcffont.error import PcfError
|
|
4
|
+
from pcffont.internal.buffer import Buffer
|
|
5
|
+
|
|
6
|
+
_MAGIC_STRING = b'\x01fcp'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PcfTableType(IntEnum):
|
|
10
|
+
PROPERTIES = 1 << 0
|
|
11
|
+
ACCELERATORS = 1 << 1
|
|
12
|
+
METRICS = 1 << 2
|
|
13
|
+
BITMAPS = 1 << 3
|
|
14
|
+
INK_METRICS = 1 << 4
|
|
15
|
+
BDF_ENCODINGS = 1 << 5
|
|
16
|
+
SWIDTHS = 1 << 6
|
|
17
|
+
GLYPH_NAMES = 1 << 7
|
|
18
|
+
BDF_ACCELERATORS = 1 << 8
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PcfTableFormat(IntFlag):
|
|
22
|
+
DEFAULT_FORMAT = 0b_0000_0000_0000
|
|
23
|
+
INKBOUNDS = 0b_0010_0000_0000
|
|
24
|
+
ACCEL_W_INKBOUNDS = 0b_0001_0000_0000
|
|
25
|
+
COMPRESSED_METRICS = 0b_0001_0000_0000
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PcfTableFormatMask(IntFlag):
|
|
29
|
+
GLYPH_PAD = 0b_0000_0011
|
|
30
|
+
BYTE = 0b_0000_0100
|
|
31
|
+
BIT = 0b_0000_1000
|
|
32
|
+
SCAN_UNIT = 0b_0011_0000
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PcfHeader:
|
|
36
|
+
@staticmethod
|
|
37
|
+
def parse(buffer: Buffer) -> list['PcfHeader']:
|
|
38
|
+
buffer.seek(0)
|
|
39
|
+
if buffer.read(4) != _MAGIC_STRING:
|
|
40
|
+
raise PcfError('Not PCF format')
|
|
41
|
+
|
|
42
|
+
tables_count = buffer.read_int32_le()
|
|
43
|
+
|
|
44
|
+
headers = []
|
|
45
|
+
for _ in range(tables_count):
|
|
46
|
+
table_type = PcfTableType(buffer.read_int32_le())
|
|
47
|
+
table_format = buffer.read_int32_le()
|
|
48
|
+
table_size = buffer.read_int32_le()
|
|
49
|
+
table_offset = buffer.read_int32_le()
|
|
50
|
+
headers.append(PcfHeader(table_type, table_format, table_size, table_offset))
|
|
51
|
+
|
|
52
|
+
return headers
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def dump(buffer: Buffer, headers: list['PcfHeader']):
|
|
56
|
+
buffer.seek(0)
|
|
57
|
+
buffer.write(_MAGIC_STRING)
|
|
58
|
+
|
|
59
|
+
buffer.write_int32_le(len(headers))
|
|
60
|
+
for header in headers:
|
|
61
|
+
buffer.write_int32_le(header.table_type)
|
|
62
|
+
buffer.write_int32_le(header.table_format)
|
|
63
|
+
buffer.write_int32_le(header.table_size)
|
|
64
|
+
buffer.write_int32_le(header.table_offset)
|
|
65
|
+
|
|
66
|
+
def __init__(self, table_type: PcfTableType, table_format: int, table_size: int, table_offset: int):
|
|
67
|
+
self.table_type = table_type
|
|
68
|
+
self.table_format = table_format
|
|
69
|
+
self.table_size = table_size
|
|
70
|
+
self.table_offset = table_offset
|
|
File without changes
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from typing import BinaryIO, Literal, TypeAlias
|
|
2
|
+
|
|
3
|
+
# TODO # type ByteOrder = Literal['little', 'big']
|
|
4
|
+
ByteOrder: TypeAlias = Literal['little', 'big']
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Buffer:
|
|
8
|
+
def __init__(self, stream: BinaryIO):
|
|
9
|
+
self.stream = stream
|
|
10
|
+
|
|
11
|
+
def read(self, n: int) -> bytes:
|
|
12
|
+
return self.stream.read(n)
|
|
13
|
+
|
|
14
|
+
def read_int(self, n: int, byte_order: ByteOrder) -> int:
|
|
15
|
+
return int.from_bytes(self.read(n), byte_order)
|
|
16
|
+
|
|
17
|
+
def read_int_le(self, n: int) -> int:
|
|
18
|
+
return self.read_int(n, 'little')
|
|
19
|
+
|
|
20
|
+
def read_int_be(self, n: int) -> int:
|
|
21
|
+
return self.read_int(n, 'big')
|
|
22
|
+
|
|
23
|
+
def read_int32(self, byte_order: ByteOrder) -> int:
|
|
24
|
+
return self.read_int(4, byte_order)
|
|
25
|
+
|
|
26
|
+
def read_int32_le(self) -> int:
|
|
27
|
+
return self.read_int32('little')
|
|
28
|
+
|
|
29
|
+
def read_int32_be(self) -> int:
|
|
30
|
+
return self.read_int32('big')
|
|
31
|
+
|
|
32
|
+
def read_int16(self, byte_order: ByteOrder) -> int:
|
|
33
|
+
return self.read_int(2, byte_order)
|
|
34
|
+
|
|
35
|
+
def read_int16_le(self) -> int:
|
|
36
|
+
return self.read_int16('little')
|
|
37
|
+
|
|
38
|
+
def read_int16_be(self) -> int:
|
|
39
|
+
return self.read_int16('big')
|
|
40
|
+
|
|
41
|
+
def read_int8(self, byte_order: ByteOrder) -> int:
|
|
42
|
+
return self.read_int(1, byte_order)
|
|
43
|
+
|
|
44
|
+
def read_int8_le(self) -> int:
|
|
45
|
+
return self.read_int8('little')
|
|
46
|
+
|
|
47
|
+
def read_int8_be(self) -> int:
|
|
48
|
+
return self.read_int8('big')
|
|
49
|
+
|
|
50
|
+
def read_bool(self) -> bool:
|
|
51
|
+
return self.read(1) != b'\x00'
|
|
52
|
+
|
|
53
|
+
def read_string(self) -> str:
|
|
54
|
+
data = bytearray()
|
|
55
|
+
while True:
|
|
56
|
+
b = self.read(1)
|
|
57
|
+
if b == b'\x00' or b == b'':
|
|
58
|
+
break
|
|
59
|
+
data.extend(b)
|
|
60
|
+
return data.decode('utf-8')
|
|
61
|
+
|
|
62
|
+
def write(self, s: bytes) -> int:
|
|
63
|
+
return self.stream.write(s)
|
|
64
|
+
|
|
65
|
+
def write_int(self, i: int, n: int, byte_order: ByteOrder) -> int:
|
|
66
|
+
return self.write(i.to_bytes(n, byte_order))
|
|
67
|
+
|
|
68
|
+
def write_int_le(self, i: int, n: int) -> int:
|
|
69
|
+
return self.write_int(i, n, 'little')
|
|
70
|
+
|
|
71
|
+
def write_int_be(self, i: int, n: int) -> int:
|
|
72
|
+
return self.write_int(i, n, 'big')
|
|
73
|
+
|
|
74
|
+
def write_int32(self, i: int, byte_order: ByteOrder) -> int:
|
|
75
|
+
return self.write_int(i, 4, byte_order)
|
|
76
|
+
|
|
77
|
+
def write_int32_le(self, i: int) -> int:
|
|
78
|
+
return self.write_int32(i, 'little')
|
|
79
|
+
|
|
80
|
+
def write_int32_be(self, i: int) -> int:
|
|
81
|
+
return self.write_int32(i, 'big')
|
|
82
|
+
|
|
83
|
+
def write_int16(self, i: int, byte_order: ByteOrder) -> int:
|
|
84
|
+
return self.write_int(i, 2, byte_order)
|
|
85
|
+
|
|
86
|
+
def write_int16_le(self, i: int) -> int:
|
|
87
|
+
return self.write_int16(i, 'little')
|
|
88
|
+
|
|
89
|
+
def write_int16_be(self, i: int) -> int:
|
|
90
|
+
return self.write_int16(i, 'big')
|
|
91
|
+
|
|
92
|
+
def write_int8(self, i: int, byte_order: ByteOrder) -> int:
|
|
93
|
+
return self.write_int(i, 1, byte_order)
|
|
94
|
+
|
|
95
|
+
def write_int8_le(self, i: int) -> int:
|
|
96
|
+
return self.write_int8(i, 'little')
|
|
97
|
+
|
|
98
|
+
def write_int8_be(self, i: int) -> int:
|
|
99
|
+
return self.write_int8(i, 'big')
|
|
100
|
+
|
|
101
|
+
def write_bool(self, b: bool) -> int:
|
|
102
|
+
return self.write(b'\x01' if b else b'\x00')
|
|
103
|
+
|
|
104
|
+
def write_nulls(self, n: int) -> int:
|
|
105
|
+
for _ in range(n):
|
|
106
|
+
self.write(b'\x00')
|
|
107
|
+
return n
|
|
108
|
+
|
|
109
|
+
def write_string(self, s: str) -> int:
|
|
110
|
+
return self.write(s.encode('utf-8')) + self.write_nulls(1)
|
|
111
|
+
|
|
112
|
+
def skip(self, n: int):
|
|
113
|
+
self.seek(self.tell() + n)
|
|
114
|
+
|
|
115
|
+
def skip_int(self):
|
|
116
|
+
self.skip(4)
|
|
117
|
+
|
|
118
|
+
def seek(self, offset: int):
|
|
119
|
+
self.stream.seek(offset)
|
|
120
|
+
|
|
121
|
+
def tell(self) -> int:
|
|
122
|
+
return self.stream.tell()
|
pcffont/internal/util.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from pcffont.error import PcfError
|
|
2
|
+
from pcffont.header import PcfTableType, PcfTableFormatMask, PcfHeader
|
|
3
|
+
from pcffont.internal.buffer import ByteOrder, Buffer
|
|
4
|
+
from pcffont.t_accelerators import PcfAccelerators
|
|
5
|
+
from pcffont.t_bitmaps import PcfBitmaps
|
|
6
|
+
from pcffont.t_encodings import PcfBdfEncodings
|
|
7
|
+
from pcffont.t_glyph_names import PcfGlyphNames
|
|
8
|
+
from pcffont.t_metrics import PcfMetrics
|
|
9
|
+
from pcffont.t_properties import PcfProperties
|
|
10
|
+
from pcffont.t_scalable_widths import PcfScalableWidths
|
|
11
|
+
from pcffont.table import PcfTable
|
|
12
|
+
|
|
13
|
+
TABLE_TYPE_REGISTRY = {
|
|
14
|
+
PcfTableType.PROPERTIES: PcfProperties,
|
|
15
|
+
PcfTableType.ACCELERATORS: PcfAccelerators,
|
|
16
|
+
PcfTableType.METRICS: PcfMetrics,
|
|
17
|
+
PcfTableType.BITMAPS: PcfBitmaps,
|
|
18
|
+
PcfTableType.INK_METRICS: PcfMetrics,
|
|
19
|
+
PcfTableType.BDF_ENCODINGS: PcfBdfEncodings,
|
|
20
|
+
PcfTableType.SWIDTHS: PcfScalableWidths,
|
|
21
|
+
PcfTableType.GLYPH_NAMES: PcfGlyphNames,
|
|
22
|
+
PcfTableType.BDF_ACCELERATORS: PcfAccelerators,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def parse_table(buffer: Buffer, header: PcfHeader) -> PcfTable | None:
|
|
27
|
+
clz = TABLE_TYPE_REGISTRY.get(header.table_type, None)
|
|
28
|
+
if clz is not None:
|
|
29
|
+
return clz.parse(buffer, header)
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def read_and_check_table_format(buffer: Buffer, header: PcfHeader) -> int:
|
|
34
|
+
buffer.seek(header.table_offset)
|
|
35
|
+
table_format = buffer.read_int32_le()
|
|
36
|
+
if table_format != header.table_format:
|
|
37
|
+
raise PcfError(f"The table format definition is inconsistent with the header: type '{header.table_type.name}', offset {header.table_offset}")
|
|
38
|
+
return table_format
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_table_byte_order(table_format: int) -> ByteOrder:
|
|
42
|
+
byte_mask = table_format & PcfTableFormatMask.BYTE > 0
|
|
43
|
+
bit_mask = table_format & PcfTableFormatMask.BIT > 0
|
|
44
|
+
if byte_mask and bit_mask:
|
|
45
|
+
return 'big'
|
|
46
|
+
elif (not byte_mask) and (not bit_mask):
|
|
47
|
+
return 'little'
|
|
48
|
+
else:
|
|
49
|
+
raise PcfError(f'Table format not supported: {table_format:b}')
|
pcffont/metric.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from pcffont.internal.buffer import ByteOrder, Buffer
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PcfMetric:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def parse(buffer: Buffer, byte_order: ByteOrder, is_compressed: bool) -> 'PcfMetric':
|
|
7
|
+
if is_compressed:
|
|
8
|
+
left_sided_bearing = buffer.read_int8(byte_order)
|
|
9
|
+
right_side_bearing = buffer.read_int8(byte_order)
|
|
10
|
+
character_width = buffer.read_int8(byte_order)
|
|
11
|
+
character_ascent = buffer.read_int8(byte_order)
|
|
12
|
+
character_descent = buffer.read_int8(byte_order)
|
|
13
|
+
character_attributes = 0
|
|
14
|
+
else:
|
|
15
|
+
left_sided_bearing = buffer.read_int16(byte_order)
|
|
16
|
+
right_side_bearing = buffer.read_int16(byte_order)
|
|
17
|
+
character_width = buffer.read_int16(byte_order)
|
|
18
|
+
character_ascent = buffer.read_int16(byte_order)
|
|
19
|
+
character_descent = buffer.read_int16(byte_order)
|
|
20
|
+
character_attributes = buffer.read_int16(byte_order)
|
|
21
|
+
return PcfMetric(
|
|
22
|
+
left_sided_bearing,
|
|
23
|
+
right_side_bearing,
|
|
24
|
+
character_width,
|
|
25
|
+
character_ascent,
|
|
26
|
+
character_descent,
|
|
27
|
+
character_attributes,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
left_sided_bearing: int,
|
|
33
|
+
right_side_bearing: int,
|
|
34
|
+
character_width: int,
|
|
35
|
+
character_ascent: int,
|
|
36
|
+
character_descent: int,
|
|
37
|
+
character_attributes: int = 0,
|
|
38
|
+
):
|
|
39
|
+
self.left_sided_bearing = left_sided_bearing
|
|
40
|
+
self.right_side_bearing = right_side_bearing
|
|
41
|
+
self.character_width = character_width
|
|
42
|
+
self.character_ascent = character_ascent
|
|
43
|
+
self.character_descent = character_descent
|
|
44
|
+
self.character_attributes = character_attributes
|
|
45
|
+
|
|
46
|
+
def dump(self, buffer: Buffer, byte_order: ByteOrder, is_compressed: bool):
|
|
47
|
+
if is_compressed:
|
|
48
|
+
buffer.write_int8(self.left_sided_bearing, byte_order)
|
|
49
|
+
buffer.write_int8(self.right_side_bearing, byte_order)
|
|
50
|
+
buffer.write_int8(self.character_width, byte_order)
|
|
51
|
+
buffer.write_int8(self.character_ascent, byte_order)
|
|
52
|
+
buffer.write_int8(self.character_descent, byte_order)
|
|
53
|
+
else:
|
|
54
|
+
buffer.write_int16(self.left_sided_bearing, byte_order)
|
|
55
|
+
buffer.write_int16(self.right_side_bearing, byte_order)
|
|
56
|
+
buffer.write_int16(self.character_width, byte_order)
|
|
57
|
+
buffer.write_int16(self.character_ascent, byte_order)
|
|
58
|
+
buffer.write_int16(self.character_descent, byte_order)
|
|
59
|
+
buffer.write_int16(self.character_attributes, byte_order)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from pcffont.header import PcfTableFormat, PcfHeader
|
|
2
|
+
from pcffont.internal import util
|
|
3
|
+
from pcffont.internal.buffer import Buffer
|
|
4
|
+
from pcffont.metric import PcfMetric
|
|
5
|
+
from pcffont.table import PcfTable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PcfAccelerators(PcfTable):
|
|
9
|
+
@staticmethod
|
|
10
|
+
def parse(buffer: Buffer, header: PcfHeader) -> 'PcfAccelerators':
|
|
11
|
+
table_format = util.read_and_check_table_format(buffer, header)
|
|
12
|
+
byte_order = util.get_table_byte_order(table_format)
|
|
13
|
+
is_accel_w_ink_bounds = table_format & PcfTableFormat.ACCEL_W_INKBOUNDS > 0
|
|
14
|
+
|
|
15
|
+
no_overlap = buffer.read_bool()
|
|
16
|
+
constant_metrics = buffer.read_bool()
|
|
17
|
+
terminal_font = buffer.read_bool()
|
|
18
|
+
constant_width = buffer.read_bool()
|
|
19
|
+
ink_inside = buffer.read_bool()
|
|
20
|
+
ink_metrics = buffer.read_bool()
|
|
21
|
+
draw_direction_right_to_left = buffer.read_bool()
|
|
22
|
+
buffer.skip(1)
|
|
23
|
+
font_ascent = buffer.read_int32(byte_order)
|
|
24
|
+
font_descent = buffer.read_int32(byte_order)
|
|
25
|
+
max_overlap = buffer.read_int32(byte_order)
|
|
26
|
+
|
|
27
|
+
min_bounds = PcfMetric.parse(buffer, byte_order, False)
|
|
28
|
+
max_bounds = PcfMetric.parse(buffer, byte_order, False)
|
|
29
|
+
|
|
30
|
+
_ink_bounds_chunk = None # TODO
|
|
31
|
+
_padding_chunk_info = None # TODO
|
|
32
|
+
|
|
33
|
+
if is_accel_w_ink_bounds:
|
|
34
|
+
ink_min_bounds = PcfMetric.parse(buffer, byte_order, False)
|
|
35
|
+
ink_max_bounds = PcfMetric.parse(buffer, byte_order, False)
|
|
36
|
+
else:
|
|
37
|
+
ink_min_bounds = None
|
|
38
|
+
ink_max_bounds = None
|
|
39
|
+
# TODO
|
|
40
|
+
if header.table_size >= buffer.tell() - header.table_offset + 2 * 6 * 2:
|
|
41
|
+
_ink_bounds_chunk = buffer.read(2 * 6 * 2)
|
|
42
|
+
|
|
43
|
+
# TODO
|
|
44
|
+
_padding = header.table_size - (buffer.tell() - header.table_offset)
|
|
45
|
+
if _padding > 0:
|
|
46
|
+
_padding_chunk_info = buffer.read(_padding), _padding
|
|
47
|
+
|
|
48
|
+
return PcfAccelerators(
|
|
49
|
+
table_format,
|
|
50
|
+
no_overlap,
|
|
51
|
+
constant_metrics,
|
|
52
|
+
terminal_font,
|
|
53
|
+
constant_width,
|
|
54
|
+
ink_inside,
|
|
55
|
+
ink_metrics,
|
|
56
|
+
draw_direction_right_to_left,
|
|
57
|
+
font_ascent,
|
|
58
|
+
font_descent,
|
|
59
|
+
max_overlap,
|
|
60
|
+
min_bounds,
|
|
61
|
+
max_bounds,
|
|
62
|
+
ink_min_bounds,
|
|
63
|
+
ink_max_bounds,
|
|
64
|
+
_ink_bounds_chunk, # TODO
|
|
65
|
+
_padding_chunk_info, # TODO
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
table_format: int = PcfTable.DEFAULT_TABLE_FORMAT,
|
|
71
|
+
no_overlap: bool = False,
|
|
72
|
+
constant_metrics: bool = False,
|
|
73
|
+
terminal_font: bool = False,
|
|
74
|
+
constant_width: bool = False,
|
|
75
|
+
ink_inside: bool = False,
|
|
76
|
+
ink_metrics: bool = False,
|
|
77
|
+
draw_direction_right_to_left: bool = False,
|
|
78
|
+
font_ascent: int = 0,
|
|
79
|
+
font_descent: int = 0,
|
|
80
|
+
max_overlap: int = 0,
|
|
81
|
+
min_bounds: PcfMetric = None,
|
|
82
|
+
max_bounds: PcfMetric = None,
|
|
83
|
+
ink_min_bounds: PcfMetric = None,
|
|
84
|
+
ink_max_bounds: PcfMetric = None,
|
|
85
|
+
_ink_bounds_chunk: bytes = None, # TODO
|
|
86
|
+
_padding_chunk_info: tuple[bytes, int] = None, # TODO
|
|
87
|
+
):
|
|
88
|
+
super().__init__(table_format)
|
|
89
|
+
self.no_overlap = no_overlap
|
|
90
|
+
self.constant_metrics = constant_metrics
|
|
91
|
+
self.terminal_font = terminal_font
|
|
92
|
+
self.constant_width = constant_width
|
|
93
|
+
self.ink_inside = ink_inside
|
|
94
|
+
self.ink_metrics = ink_metrics
|
|
95
|
+
self.draw_direction_right_to_left = draw_direction_right_to_left
|
|
96
|
+
self.font_ascent = font_ascent
|
|
97
|
+
self.font_descent = font_descent
|
|
98
|
+
self.max_overlap = max_overlap
|
|
99
|
+
self.min_bounds = min_bounds
|
|
100
|
+
self.max_bounds = max_bounds
|
|
101
|
+
self.ink_min_bounds = ink_min_bounds
|
|
102
|
+
self.ink_max_bounds = ink_max_bounds
|
|
103
|
+
self._ink_bounds_chunk = _ink_bounds_chunk # TODO
|
|
104
|
+
self._padding_chunk_info = _padding_chunk_info # TODO
|
|
105
|
+
|
|
106
|
+
def _dump(self, buffer: Buffer, table_offset: int, compat_mode: bool = False) -> int:
|
|
107
|
+
byte_order = util.get_table_byte_order(self.table_format)
|
|
108
|
+
is_accel_w_ink_bounds = self.table_format & PcfTableFormat.ACCEL_W_INKBOUNDS > 0
|
|
109
|
+
|
|
110
|
+
buffer.seek(table_offset)
|
|
111
|
+
buffer.write_int32_le(self.table_format)
|
|
112
|
+
buffer.write_bool(self.no_overlap)
|
|
113
|
+
buffer.write_bool(self.constant_metrics)
|
|
114
|
+
buffer.write_bool(self.terminal_font)
|
|
115
|
+
buffer.write_bool(self.constant_width)
|
|
116
|
+
buffer.write_bool(self.ink_inside)
|
|
117
|
+
buffer.write_bool(self.ink_metrics)
|
|
118
|
+
buffer.write_bool(self.draw_direction_right_to_left)
|
|
119
|
+
buffer.write_nulls(1)
|
|
120
|
+
buffer.write_int32(self.font_ascent, byte_order)
|
|
121
|
+
buffer.write_int32(self.font_descent, byte_order)
|
|
122
|
+
buffer.write_int32(self.max_overlap, byte_order)
|
|
123
|
+
|
|
124
|
+
self.min_bounds.dump(buffer, byte_order, False)
|
|
125
|
+
self.max_bounds.dump(buffer, byte_order, False)
|
|
126
|
+
|
|
127
|
+
if is_accel_w_ink_bounds:
|
|
128
|
+
self.ink_min_bounds.dump(buffer, byte_order, False)
|
|
129
|
+
self.ink_max_bounds.dump(buffer, byte_order, False)
|
|
130
|
+
else:
|
|
131
|
+
# TODO
|
|
132
|
+
if compat_mode and self._ink_bounds_chunk is not None:
|
|
133
|
+
buffer.write(self._ink_bounds_chunk)
|
|
134
|
+
|
|
135
|
+
table_size = buffer.tell() - table_offset
|
|
136
|
+
|
|
137
|
+
# TODO
|
|
138
|
+
if compat_mode and self._padding_chunk_info is not None:
|
|
139
|
+
_padding_chunk, _padding = self._padding_chunk_info
|
|
140
|
+
buffer.write(_padding_chunk)
|
|
141
|
+
table_size += _padding
|
|
142
|
+
|
|
143
|
+
return table_size
|