Coverage for /usr/lib/python3/dist-packages/fontTools/encodings/codecs.py: 19%
62 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""Extend the Python codecs module with a few encodings that are used in OpenType (name table)
2but missing from Python. See https://github.com/fonttools/fonttools/issues/236 for details."""
4import codecs
5import encodings
8class ExtendCodec(codecs.Codec):
9 def __init__(self, name, base_encoding, mapping):
10 self.name = name
11 self.base_encoding = base_encoding
12 self.mapping = mapping
13 self.reverse = {v: k for k, v in mapping.items()}
14 self.max_len = max(len(v) for v in mapping.values())
15 self.info = codecs.CodecInfo(
16 name=self.name, encode=self.encode, decode=self.decode
17 )
18 codecs.register_error(name, self.error)
20 def _map(self, mapper, output_type, exc_type, input, errors):
21 base_error_handler = codecs.lookup_error(errors)
22 length = len(input)
23 out = output_type()
24 while input:
25 # first try to use self.error as the error handler
26 try:
27 part = mapper(input, self.base_encoding, errors=self.name)
28 out += part
29 break # All converted
30 except exc_type as e:
31 # else convert the correct part, handle error as requested and continue
32 out += mapper(input[: e.start], self.base_encoding, self.name)
33 replacement, pos = base_error_handler(e)
34 out += replacement
35 input = input[pos:]
36 return out, length
38 def encode(self, input, errors="strict"):
39 return self._map(codecs.encode, bytes, UnicodeEncodeError, input, errors)
41 def decode(self, input, errors="strict"):
42 return self._map(codecs.decode, str, UnicodeDecodeError, input, errors)
44 def error(self, e):
45 if isinstance(e, UnicodeDecodeError):
46 for end in range(e.start + 1, e.end + 1):
47 s = e.object[e.start : end]
48 if s in self.mapping:
49 return self.mapping[s], end
50 elif isinstance(e, UnicodeEncodeError):
51 for end in range(e.start + 1, e.start + self.max_len + 1):
52 s = e.object[e.start : end]
53 if s in self.reverse:
54 return self.reverse[s], end
55 e.encoding = self.name
56 raise e
59_extended_encodings = {
60 "x_mac_japanese_ttx": (
61 "shift_jis",
62 {
63 b"\xFC": chr(0x007C),
64 b"\x7E": chr(0x007E),
65 b"\x80": chr(0x005C),
66 b"\xA0": chr(0x00A0),
67 b"\xFD": chr(0x00A9),
68 b"\xFE": chr(0x2122),
69 b"\xFF": chr(0x2026),
70 },
71 ),
72 "x_mac_trad_chinese_ttx": (
73 "big5",
74 {
75 b"\x80": chr(0x005C),
76 b"\xA0": chr(0x00A0),
77 b"\xFD": chr(0x00A9),
78 b"\xFE": chr(0x2122),
79 b"\xFF": chr(0x2026),
80 },
81 ),
82 "x_mac_korean_ttx": (
83 "euc_kr",
84 {
85 b"\x80": chr(0x00A0),
86 b"\x81": chr(0x20A9),
87 b"\x82": chr(0x2014),
88 b"\x83": chr(0x00A9),
89 b"\xFE": chr(0x2122),
90 b"\xFF": chr(0x2026),
91 },
92 ),
93 "x_mac_simp_chinese_ttx": (
94 "gb2312",
95 {
96 b"\x80": chr(0x00FC),
97 b"\xA0": chr(0x00A0),
98 b"\xFD": chr(0x00A9),
99 b"\xFE": chr(0x2122),
100 b"\xFF": chr(0x2026),
101 },
102 ),
103}
105_cache = {}
108def search_function(name):
109 name = encodings.normalize_encoding(name) # Rather undocumented...
110 if name in _extended_encodings:
111 if name not in _cache:
112 base_encoding, mapping = _extended_encodings[name]
113 assert name[-4:] == "_ttx"
114 # Python 2 didn't have any of the encodings that we are implementing
115 # in this file. Python 3 added aliases for the East Asian ones, mapping
116 # them "temporarily" to the same base encoding as us, with a comment
117 # suggesting that full implementation will appear some time later.
118 # As such, try the Python version of the x_mac_... first, if that is found,
119 # use *that* as our base encoding. This would make our encoding upgrade
120 # to the full encoding when and if Python finally implements that.
121 # http://bugs.python.org/issue24041
122 base_encodings = [name[:-4], base_encoding]
123 for base_encoding in base_encodings:
124 try:
125 codecs.lookup(base_encoding)
126 except LookupError:
127 continue
128 _cache[name] = ExtendCodec(name, base_encoding, mapping)
129 break
130 return _cache[name].info
132 return None
135codecs.register(search_function)