easyrip 4.3.3__py3-none-any.whl → 4.5.0__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.
@@ -0,0 +1,10 @@
1
+ from .ass import Ass
2
+ from .font import Font
3
+ from .subset import load_fonts, subset
4
+
5
+ __all__ = [
6
+ "Ass",
7
+ "Font",
8
+ "load_fonts",
9
+ "subset",
10
+ ]
@@ -40,7 +40,13 @@ class Font:
40
40
  self.font.close()
41
41
 
42
42
 
43
- def load_fonts(path: str | Path, lazy: bool = True) -> list[Font]:
43
+ def load_fonts(
44
+ path: str | Path,
45
+ *,
46
+ lazy: bool = True,
47
+ strict: bool = False,
48
+ ) -> list[Font]:
49
+ """strict: Skip UnicodeDecodeError font file"""
44
50
  if isinstance(path, str):
45
51
  path = Path(path)
46
52
 
@@ -59,7 +65,8 @@ def load_fonts(path: str | Path, lazy: bool = True) -> list[Font]:
59
65
  if suffix == ".ttc"
60
66
  else [TTFont(file=file, lazy=lazy)]
61
67
  ):
62
- table_name: table__n_a_m_e | None = font.get("name") # pyright: ignore[reportAssignmentType]
68
+ skip_this_font: bool = False
69
+ table_name: table__n_a_m_e | None = font.get("name")
63
70
 
64
71
  if table_name is None:
65
72
  log.warning(f"No 'name' table found in font {file}")
@@ -76,7 +83,16 @@ def load_fonts(path: str | Path, lazy: bool = True) -> list[Font]:
76
83
  if name_id not in {1, 2}:
77
84
  continue
78
85
 
79
- name_str: str = record.toUnicode()
86
+ try:
87
+ name_str: str = record.toUnicode()
88
+ except UnicodeDecodeError as e:
89
+ error_text = f"Unicode decode error in font \"{file}\": {e}: '{record.toUnicode('replace')}'. Skip this {'font' if strict else 'name record'}."
90
+ if strict:
91
+ log.error(error_text, is_format=False)
92
+ skip_this_font = True
93
+ break
94
+ log.warning(error_text, is_format=False)
95
+ continue
80
96
 
81
97
  match name_id:
82
98
  case 1: # Font Family Name
@@ -94,6 +110,9 @@ def load_fonts(path: str | Path, lazy: bool = True) -> list[Font]:
94
110
  case "italic" | "oblique":
95
111
  is_italic = True
96
112
 
113
+ if skip_this_font:
114
+ continue
115
+
97
116
  if not res_font.familys:
98
117
  log.warning(f"Font {file} has no family names. Skip this font")
99
118
  continue
@@ -121,16 +140,18 @@ def load_fonts(path: str | Path, lazy: bool = True) -> list[Font]:
121
140
  res_font_list.append(res_font)
122
141
 
123
142
  except TTLibError as e:
124
- log.warning(f'Error loading font file "{file}": {e}')
125
- except UnicodeDecodeError as e:
126
- log.warning(f"Unicode decode error for font {file}: {e}")
143
+ log.error(f'Error loading font file "{file}": {e}')
127
144
  except Exception as e:
128
145
  log.error(f"Unexpected error for font {file}: {e}")
129
146
 
130
147
  return res_font_list
131
148
 
132
149
 
133
- def load_windows_fonts(lazy: bool = True) -> list[Font]:
150
+ def load_windows_fonts(
151
+ *,
152
+ lazy: bool = True,
153
+ strict: bool = False,
154
+ ) -> list[Font]:
134
155
  paths: tuple[str, ...] = (
135
156
  os.path.join(os.environ["SYSTEMROOT"], "Fonts"),
136
157
  os.path.join(os.environ["LOCALAPPDATA"], "Microsoft/Windows/Fonts"),
@@ -138,7 +159,7 @@ def load_windows_fonts(lazy: bool = True) -> list[Font]:
138
159
 
139
160
  return list(
140
161
  itertools.chain.from_iterable(
141
- load_fonts(path=path, lazy=lazy) for path in paths
162
+ load_fonts(path, lazy=lazy, strict=strict) for path in paths
142
163
  )
143
164
  )
144
165
 
@@ -149,7 +170,9 @@ def subset_font(font: Font, subset_str: str, affix: str) -> tuple[TTFont, bool]:
149
170
  # 检查哪些字符不存在于字体中
150
171
  try:
151
172
  cmap = subset_font.getBestCmap()
152
- available_chars = {chr(key) for key in cmap.keys()} # noqa: SIM118
173
+ if cmap is None:
174
+ raise Exception("cmap is None")
175
+ available_chars = set(map(chr, cmap))
153
176
  except Exception as e:
154
177
  raise Font_error("Can not read best cmap from '{}'", font.pathname) from e
155
178
  input_chars = set(subset_str)
@@ -191,8 +214,13 @@ def subset_font(font: Font, subset_str: str, affix: str) -> tuple[TTFont, bool]:
191
214
  # 修改 Name Record
192
215
  affix_ascii = affix.encode("ascii")
193
216
  affix_utf16be = affix.encode("utf-16-be")
194
- table_name: table__n_a_m_e = font.font.get("name") # pyright: ignore[reportAssignmentType]
195
- subset_table_name: table__n_a_m_e = subset_font.get("name") # pyright: ignore[reportAssignmentType]
217
+ table_name: table__n_a_m_e | None = font.font.get("name")
218
+ subset_table_name: table__n_a_m_e | None = subset_font.get("name")
219
+
220
+ # 这两个都是复制的,所以不可能是 None
221
+ assert table_name is not None
222
+ assert subset_table_name is not None
223
+
196
224
  subset_table_name.names = list[NameRecord]() # 重写 name table
197
225
  for record in table_name.names:
198
226
  name_id = int(record.nameID)
@@ -140,22 +140,32 @@ def subset(
140
140
  for tag, value in re.findall(
141
141
  r"\\\s*(fn|b(?![a-zA-Z])|i(?![a-zA-Z])|r)([^\\}]*)", text
142
142
  ):
143
+ assert isinstance(tag, str) and isinstance(value, str)
144
+
145
+ proc_value = value.strip()
146
+ if proc_value.startswith("("):
147
+ proc_value = proc_value[1:]
148
+ if (_index := proc_value.find(")")) != -1:
149
+ proc_value = proc_value[:_index]
150
+ proc_value = proc_value.strip()
151
+
143
152
  match tag:
144
153
  case "fn":
145
- tag_fn = value
154
+ tag_fn = proc_value
146
155
  case "b":
147
- tag_bold = value
156
+ tag_bold = proc_value
148
157
  case "i":
149
- tag_italic = value
158
+ tag_italic = proc_value
150
159
  case "r":
151
- if value in style__font_sign:
152
- current_font_sign = style__font_sign[value]
160
+ r_value = proc_value if "(" in value else value.rstrip()
161
+ if r_value in style__font_sign:
162
+ current_font_sign = style__font_sign[r_value]
153
163
  else:
154
164
  # 空为还原样式, 非样式表内样式名效果同空, 但发出不规范警告
155
165
  current_font_sign = default_font_sign
156
- if value != "":
166
+ if r_value != "":
157
167
  log.warning(
158
- "The \\r style '{}' not in Styles", value
168
+ "The \\r style '{}' not in Styles", r_value
159
169
  )
160
170
 
161
171
  new_fontname: str = current_font_sign[0]
@@ -164,12 +174,12 @@ def subset(
164
174
  new_bold, new_italic = current_font_sign[1].value
165
175
 
166
176
  if tag_fn is not None:
167
- match _tag_fn := tag_fn.strip():
177
+ match tag_fn:
168
178
  case "":
169
179
  new_fontname = default_font_sign[0]
170
180
  case _:
171
- _is_vertical: bool = _tag_fn[0] == "@"
172
- new_fontname = _tag_fn[1:] if _is_vertical else _tag_fn
181
+ _is_vertical: bool = tag_fn.startswith("@")
182
+ new_fontname = tag_fn[1:] if _is_vertical else tag_fn
173
183
 
174
184
  # 修改
175
185
  text = text.replace(
@@ -178,7 +188,7 @@ def subset(
178
188
  )
179
189
 
180
190
  if tag_bold is not None:
181
- match tag_bold.strip():
191
+ match tag_bold:
182
192
  case "":
183
193
  new_bold = default_font_sign[1].value[0]
184
194
  case "0":
@@ -195,7 +205,7 @@ def subset(
195
205
  return_res = not strict
196
206
 
197
207
  if tag_italic is not None:
198
- match tag_italic.strip():
208
+ match tag_italic:
199
209
  case "":
200
210
  new_italic = default_font_sign[1].value[1]
201
211
  case "0":
@@ -272,9 +282,9 @@ def subset(
272
282
  # 加载 Font
273
283
  fonts: Final[list[Font]] = []
274
284
  for _path in font_path_list:
275
- fonts.extend(load_fonts(_path))
285
+ fonts.extend(load_fonts(_path, strict=strict))
276
286
  if use_win_font:
277
- fonts.extend(load_windows_fonts())
287
+ fonts.extend(load_windows_fonts(strict=strict))
278
288
 
279
289
  font_sign__font: dict[tuple[str, Font_type], Font] = {}
280
290
  for _font in fonts:
@@ -362,6 +372,10 @@ def subset(
362
372
  f"( {key[0]} / {key[1].name} ){mapping_res}",
363
373
  deep=(strict and bool(mapping_res)),
364
374
  )
375
+ log.debug(
376
+ f"{_font.pathname}: {_font.familys} {_font.font_type.name}",
377
+ is_format=False,
378
+ )
365
379
 
366
380
  # 子集化字体
367
381
  for key, val in font__subset_str.items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyrip
3
- Version: 4.3.3
3
+ Version: 4.5.0
4
4
  Author: op200
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Project-URL: Homepage, https://github.com/op200/EasyRip
@@ -21,8 +21,8 @@ Requires-Python: >=3.12
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: prompt-toolkit>=3.0.52
24
- Requires-Dist: fonttools>=4.61.0
25
- Requires-Dist: pycryptodome>=3.21.0
24
+ Requires-Dist: fonttools>=4.61.1
25
+ Requires-Dist: pycryptodome>=3.23.0
26
26
  Dynamic: license-file
27
27
 
28
28
  # Easy Rip
@@ -1,10 +1,10 @@
1
- easyrip/__init__.py,sha256=BlDIN9htmQXETt7M8pjYSOVrKevuuyfRn2ajQI4J7us,648
2
- easyrip/__main__.py,sha256=2i4xz_WNXXEiJqbs_zaVz9hvkY90dAhT_CzB38G68KA,4386
3
- easyrip/easyrip_command.py,sha256=xp2T-J0ahXdxDg1mnIhH7ubVicsTBkSYhhokTBQ6f5k,28659
1
+ easyrip/__init__.py,sha256=PIvSPDgswsIkWL4dsCe87knnxKmtvcrWYzmqAwZix_M,765
2
+ easyrip/__main__.py,sha256=ndKSOQpWOUtpVf009yqX-9vEiEjOgFDye0re45_PfQQ,4454
3
+ easyrip/easyrip_command.py,sha256=YnwtrAh7FtN7Uk8R_Fupn96OTeWtzdCYN0p5rEFe91o,27833
4
4
  easyrip/easyrip_log.py,sha256=0V5wBwbqWt56w_NHoLmQmkTHPnPLkYZCAbhDSYnLnDw,15611
5
- easyrip/easyrip_main.py,sha256=gVRqRJfB8c_nCb9nJqTfbfVkmhQyDLbmywS0DPsrwRg,43460
5
+ easyrip/easyrip_main.py,sha256=39a1cSnQUnNplX5rVTG1ECoo0_RCpQyylPu1uhArnZc,43886
6
6
  easyrip/easyrip_prompt.py,sha256=A5S7ybeJSGFkCmwdJ9TCQ_-lde7NWgkbFytZ2KnvkWI,2145
7
- easyrip/global_val.py,sha256=mgJ85II0MwIede9yRxY10be9kb1p8toA9LHcNzPqpzg,773
7
+ easyrip/global_val.py,sha256=yzNIoY-_F1mkIsk39lsW7iNFbqBAB88HMg-WbE7Jg-E,773
8
8
  easyrip/utils.py,sha256=YM8gaKon-9DLt5llF-7-TE0JqDpKZrKGj7oRScLFatU,6322
9
9
  easyrip/easyrip_config/config.py,sha256=9uskiGIpYLuS-vjzNASZlhdWLWXkEVxtSjODxcZU3pc,7935
10
10
  easyrip/easyrip_config/config_key.py,sha256=wANVusz9kNqsYJUvXM7DUQn6k_G6EO2VWTHWh9dOifw,394
@@ -16,16 +16,16 @@ easyrip/easyrip_mlang/translator.py,sha256=Vg0S0p2rpMNAy3LHTdK7qKFdkPEXlOoCCXcaH
16
16
  easyrip/easyrip_web/__init__.py,sha256=tMyEeaSGeEJjND7MF0MBv9aDiDgaO3MOnppwxA70U2c,177
17
17
  easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr0ephY,8350
18
18
  easyrip/easyrip_web/third_party_api.py,sha256=umj-QsfOa0IM60Ic2pXigVfnGfAiiC95fJSXQ-WFpJA,4419
19
- easyrip/ripper/__init__.py,sha256=o5gj8QRvkuVc0S9jbRjVOWXpLAaQKC0-n-o6Zx9Dowo,171
20
19
  easyrip/ripper/media_info.py,sha256=mQq_vbQ7S9fWpb39HLkoZlAL-pqNfwxewv6X776Nf50,5078
21
- easyrip/ripper/ripper.py,sha256=uyAZFg5PYg77q2WX_EWWDvqgIV2hta8r7UNwoqA-V_Y,62300
22
- easyrip/ripper/font_subset/__init__.py,sha256=a6UoRH1AU1l5FkFgQ-wQxOswLmovgVUUl3rLaJnN8Co,88
23
- easyrip/ripper/font_subset/ass.py,sha256=eGi1eIDWAV1Ti_BYIAcAMwrAlXJ5f_zGYte3nNu4PeE,25532
24
- easyrip/ripper/font_subset/font.py,sha256=-YG6zWfQeOTPJsJfCEiR-W3CR-kAuOoeVQDPVnzmuEw,7334
25
- easyrip/ripper/font_subset/subset.py,sha256=yj5JYZg1XPO1U0zsZUHSsOX105wrmE_g-2jLpcrvqqc,17137
26
- easyrip-4.3.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
27
- easyrip-4.3.3.dist-info/METADATA,sha256=V3W8rWxBtl83cpLAvYhnC3UNKZIp5-O851xWATGMe3U,3506
28
- easyrip-4.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- easyrip-4.3.3.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
30
- easyrip-4.3.3.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
31
- easyrip-4.3.3.dist-info/RECORD,,
20
+ easyrip/ripper/param.py,sha256=x11G7AsU-1Ol37-psKNo8AtjKDdD8YPlXeNwJSbhZxw,11082
21
+ easyrip/ripper/ripper.py,sha256=Ru3FI7c6toMEm1qgt_5XneeIYHTTv269nS6-Dmv1f6Y,50167
22
+ easyrip/ripper/sub_and_font/__init__.py,sha256=cBT7mxL7RRFaJXFPXuZ7RT-YK6FbnanaU5v6U9BOquw,153
23
+ easyrip/ripper/sub_and_font/ass.py,sha256=eGi1eIDWAV1Ti_BYIAcAMwrAlXJ5f_zGYte3nNu4PeE,25532
24
+ easyrip/ripper/sub_and_font/font.py,sha256=pzRtMrcL3ATFOMM5nsJQqSBbZtrnRodIgxLkjHai7kg,8075
25
+ easyrip/ripper/sub_and_font/subset.py,sha256=VsFYTFuVWXW9ltkqWZIiwOIh-nGDKmW1tUv3oi5kpWw,17815
26
+ easyrip-4.5.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
27
+ easyrip-4.5.0.dist-info/METADATA,sha256=10d52t-vOhqDgakGdsn0_0QLCrnMSuoMvQ9rUOwsraY,3506
28
+ easyrip-4.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ easyrip-4.5.0.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
30
+ easyrip-4.5.0.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
31
+ easyrip-4.5.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- from .font_subset import Ass, subset
2
- from .media_info import Media_info
3
- from .ripper import Ripper
4
-
5
- __all__ = [
6
- "Ass",
7
- "Media_info",
8
- "Ripper",
9
- "subset",
10
- ]
@@ -1,7 +0,0 @@
1
- from .ass import Ass
2
- from .subset import subset
3
-
4
- __all__ = [
5
- "Ass",
6
- "subset",
7
- ]
File without changes