foundrytools-cli 2.0.4__tar.gz → 2.0.5__tar.gz

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.
Files changed (38) hide show
  1. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/PKG-INFO +8 -9
  2. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/README.md +2 -2
  3. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/pyproject.toml +15 -28
  4. foundrytools_cli-2.0.5/src/foundrytools_cli/__init__.py +3 -0
  5. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/cff.py +2 -1
  6. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/converter/cli.py +7 -7
  7. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/converter/ttf_to_otf.py +5 -6
  8. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/fix/cli.py +33 -0
  9. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/font.py +3 -3
  10. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/name.py +10 -10
  11. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/os_2.py +4 -4
  12. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/otf.py +2 -1
  13. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/post.py +5 -5
  14. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/print/cli.py +1 -2
  15. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/print/font_names.py +4 -5
  16. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/utils/cli.py +2 -1
  17. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/utils/__init__.py +7 -8
  18. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/utils/task_runner.py +5 -4
  19. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/utils/timer.py +5 -4
  20. foundrytools_cli-2.0.4/src/foundrytools_cli/__init__.py +0 -3
  21. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/__main__.py +0 -0
  22. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/__init__.py +0 -0
  23. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/cmap.py +0 -0
  24. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/converter/__init__.py +0 -0
  25. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/fix/__init__.py +0 -0
  26. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/fix/legacy_accents.py +0 -0
  27. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/fix/vertical_metrics.py +0 -0
  28. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/gsub.py +0 -0
  29. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/hhea.py +0 -0
  30. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/print/__init__.py +0 -0
  31. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/print/font_info.py +0 -0
  32. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/print/vf_instances.py +0 -0
  33. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/ttf.py +0 -0
  34. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/utils/__init__.py +0 -0
  35. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/utils/font_organizer.py +0 -0
  36. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/utils/font_renamer.py +0 -0
  37. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/commands/utils/sync_timestamps.py +0 -0
  38. {foundrytools_cli-2.0.4 → foundrytools_cli-2.0.5}/src/foundrytools_cli/utils/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foundrytools-cli
3
- Version: 2.0.4
3
+ Version: 2.0.5
4
4
  Summary: A set of command line tools to inspect, manipulate and convert font files
5
5
  Author: Cesare Gilento
6
6
  Author-email: Cesare Gilento <ftcli@proton.me>
@@ -11,22 +11,21 @@ Classifier: Intended Audience :: End Users/Desktop
11
11
  Classifier: Natural Language :: English
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
19
18
  Classifier: License :: OSI Approved :: MIT License
20
19
  Classifier: Topic :: Text Processing :: Fonts
21
- Requires-Dist: afdko>=4.0.2
22
- Requires-Dist: click>=8.1.8
23
- Requires-Dist: foundrytools>=0.1.4
20
+ Requires-Dist: afdko>=4.0.3
21
+ Requires-Dist: click>=8.3.1
22
+ Requires-Dist: foundrytools>=0.1.5
24
23
  Requires-Dist: loguru>=0.7.3
25
24
  Requires-Dist: pathvalidate>=3.3.1
26
- Requires-Dist: rich>=14.1.0
25
+ Requires-Dist: rich>=14.2.0
27
26
  Requires-Dist: ufolib2>=0.18.1
28
27
  Requires-Dist: win32-setctime>=1.2.0 ; sys_platform >= 'win32'
29
- Requires-Python: >=3.9
28
+ Requires-Python: >=3.10
30
29
  Project-URL: Homepage, https://github.com/ftCLI/FoundryTools-CLI
31
30
  Description-Content-Type: text/markdown
32
31
 
@@ -54,7 +53,7 @@ rendered by [rich](https://github.com/Textualize/rich).
54
53
 
55
54
  ## Installation
56
55
 
57
- FoundryTools-CLI requires Python 3.9 or later.
56
+ FoundryTools-CLI requires Python 3.10 or later.
58
57
 
59
58
  **IMPORTANT**: If you have Python 2.x installed in your system, you may have to use `python3`
60
59
  (instead of `python`) in the commands below.
@@ -96,7 +95,7 @@ python -m pip install -e .
96
95
 
97
96
  FoundryTools-CLI is a Terminal app where commands are logically organized into subcommands.
98
97
 
99
- Please refer to the [user documentation](https://foundrytools-cli.readthedocs.io/).
98
+ Please refer to the [user documentation](https://foundrytools-cli.readthedocs.io/en/stable/).
100
99
 
101
100
  ## License
102
101
 
@@ -22,7 +22,7 @@ rendered by [rich](https://github.com/Textualize/rich).
22
22
 
23
23
  ## Installation
24
24
 
25
- FoundryTools-CLI requires Python 3.9 or later.
25
+ FoundryTools-CLI requires Python 3.10 or later.
26
26
 
27
27
  **IMPORTANT**: If you have Python 2.x installed in your system, you may have to use `python3`
28
28
  (instead of `python`) in the commands below.
@@ -64,7 +64,7 @@ python -m pip install -e .
64
64
 
65
65
  FoundryTools-CLI is a Terminal app where commands are logically organized into subcommands.
66
66
 
67
- Please refer to the [user documentation](https://foundrytools-cli.readthedocs.io/).
67
+ Please refer to the [user documentation](https://foundrytools-cli.readthedocs.io/en/stable/).
68
68
 
69
69
  ## License
70
70
 
@@ -1,14 +1,14 @@
1
1
  [build-system]
2
- requires = ["uv_build>=0.8.13"]
2
+ requires = ["uv_build>=0.9.17"]
3
3
  build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "foundrytools-cli"
7
- version = "2.0.4"
7
+ version = "2.0.5"
8
8
  description = "A set of command line tools to inspect, manipulate and convert font files"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
- requires-python = ">=3.9"
11
+ requires-python = ">=3.10"
12
12
  authors = [
13
13
  { name = "Cesare Gilento", email = "ftcli@proton.me" },
14
14
  ]
@@ -20,7 +20,6 @@ classifiers = [
20
20
  "Natural Language :: English",
21
21
  "Operating System :: OS Independent",
22
22
  "Programming Language :: Python :: 3",
23
- "Programming Language :: Python :: 3.9",
24
23
  "Programming Language :: Python :: 3.10",
25
24
  "Programming Language :: Python :: 3.11",
26
25
  "Programming Language :: Python :: 3.12",
@@ -30,25 +29,25 @@ classifiers = [
30
29
  ]
31
30
 
32
31
  dependencies = [
33
- "afdko>=4.0.2",
34
- "click>=8.1.8",
35
- "foundrytools>=0.1.4",
32
+ "afdko>=4.0.3",
33
+ "click>=8.3.1",
34
+ "foundrytools>=0.1.5",
36
35
  "loguru>=0.7.3",
37
36
  "pathvalidate>=3.3.1",
38
- "rich>=14.1.0",
37
+ "rich>=14.2.0",
39
38
  "ufolib2>=0.18.1",
40
39
  "win32-setctime>=1.2.0; sys_platform >= 'win32'",
41
40
  ]
42
41
 
43
- [dependencies-groups]
42
+ [dependency-groups]
44
43
  dev = [
45
- "bump-my-version>=1.2.1",
46
- "mypy>=1.17.1",
47
- "pre-commit>=4.2.0",
48
- "pylint>=3.3.8",
44
+ "bump-my-version>=1.2.6",
45
+ "mypy>=1.19.1",
46
+ "pre-commit>=4.5.1",
47
+ "pylint>=4.0.4",
49
48
  ]
50
49
  docs = [
51
- "sphinx-click>=6.0.0",
50
+ "sphinx-click>=6.2.0",
52
51
  "sphinx-rtd-theme>=3.0.2",
53
52
  ]
54
53
 
@@ -58,21 +57,9 @@ ftcli = "foundrytools_cli.__main__:cli"
58
57
  [project.urls]
59
58
  Homepage = "https://github.com/ftCLI/FoundryTools-CLI"
60
59
 
61
- [tool.hatch.version]
62
- path = "src/foundrytools_cli/__init__.py"
63
- pattern = 'VERSION = __version__ = "(?P<version>[^"]+)"'
64
-
65
- [tool.hatch.build.targets.sdist]
66
- include = [
67
- "/src",
68
- ]
69
- exclude = [
70
- "/.gitignore",
71
- ]
72
-
73
60
  [tool.bumpversion]
74
- current_version = "2.0.4"
75
- commit = true
61
+ current_version = "2.0.5"
62
+ commit = false
76
63
  tag = true
77
64
 
78
65
  [[tool.bumpversion.files]]
@@ -0,0 +1,3 @@
1
+ VERSION = __version__ = "2.0.5"
2
+
3
+ __all__ = ["VERSION"]
@@ -1,5 +1,6 @@
1
+ from collections.abc import Callable
1
2
  from pathlib import Path
2
- from typing import Any, Callable
3
+ from typing import Any
3
4
 
4
5
  import click
5
6
  from foundrytools import Font
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Any, Literal, Optional
2
+ from typing import Any, Literal
3
3
 
4
4
  import click
5
5
  from fontTools.misc.cliTools import makeOutputFileName
@@ -74,8 +74,8 @@ def otf_to_ttf(input_path: Path, **options: dict[str, Any]) -> None:
74
74
  def task(
75
75
  font: Font,
76
76
  tolerance: float = 1.0,
77
- target_upm: Optional[int] = None,
78
- output_dir: Optional[Path] = None,
77
+ target_upm: int | None = None,
78
+ output_dir: Path | None = None,
79
79
  overwrite: bool = True,
80
80
  ) -> None:
81
81
  flavor = font.ttfont.flavor
@@ -201,7 +201,7 @@ def ttf_to_otf(input_path: Path, **options: dict[str, Any]) -> None:
201
201
  )
202
202
  def web_to_sfnt(
203
203
  input_path: Path,
204
- in_format: Optional[Literal["woff", "woff2"]],
204
+ in_format: Literal["woff", "woff2"] | None,
205
205
  **options: dict[str, Any],
206
206
  ) -> None:
207
207
  """
@@ -236,8 +236,8 @@ def sfnt_to_web(input_path: Path, **options: dict[str, Any]) -> None:
236
236
 
237
237
  def task(
238
238
  font: Font,
239
- output_dir: Optional[Path] = None,
240
- out_format: Optional[Literal["woff", "woff2"]] = None,
239
+ output_dir: Path | None = None,
240
+ out_format: Literal["woff", "woff2"] | None = None,
241
241
  overwrite: bool = True,
242
242
  reorder_tables: bool = False,
243
243
  ) -> None:
@@ -302,7 +302,7 @@ def variable_to_static(input_path: Path, **options: dict[str, Any]) -> None:
302
302
  var_font: Font,
303
303
  select_instance: bool = False,
304
304
  overlap: int = 1,
305
- output_dir: Optional[Path] = None,
305
+ output_dir: Path | None = None,
306
306
  overwrite: bool = True,
307
307
  ) -> None:
308
308
  if select_instance:
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  from afdko.fdkutils import run_shell_command
5
4
  from foundrytools import Font
@@ -10,7 +9,7 @@ from foundrytools.lib.qu2cu import quadratics_to_cubics_2
10
9
  from foundrytools_cli.utils.logger import logger
11
10
 
12
11
 
13
- def _build_out_file_name(font: Font, output_dir: Optional[Path], overwrite: bool = True) -> Path:
12
+ def _build_out_file_name(font: Font, output_dir: Path | None, overwrite: bool = True) -> Path:
14
13
  """
15
14
  When converting a TrueType flavored web font to PS flavored web font, we need to add a suffix to
16
15
  the output file name to avoid overwriting the input file. This function builds the output file
@@ -39,11 +38,11 @@ def _build_out_file_name(font: Font, output_dir: Optional[Path], overwrite: bool
39
38
  def ttf2otf(
40
39
  font: Font,
41
40
  tolerance: float = 1.0,
42
- target_upm: Optional[int] = None,
41
+ target_upm: int | None = None,
43
42
  correct_contours: bool = True,
44
43
  check_outlines: bool = False,
45
44
  subroutinize: bool = True,
46
- output_dir: Optional[Path] = None,
45
+ output_dir: Path | None = None,
47
46
  overwrite: bool = True,
48
47
  ) -> None:
49
48
  """
@@ -104,11 +103,11 @@ def ttf2otf(
104
103
 
105
104
  def ttf2otf_with_tx(
106
105
  font: Font,
107
- target_upm: Optional[int] = None,
106
+ target_upm: int | None = None,
108
107
  correct_contours: bool = True,
109
108
  check_outlines: bool = False,
110
109
  subroutinize: bool = True,
111
- output_dir: Optional[Path] = None,
110
+ output_dir: Path | None = None,
112
111
  recalc_timestamp: bool = False,
113
112
  overwrite: bool = True,
114
113
  ) -> None:
@@ -344,6 +344,39 @@ def fix_monospace(input_path: Path, **options: dict[str, Any]) -> None:
344
344
  runner.run()
345
345
 
346
346
 
347
+ @cli.command("integer-ppem-if-hinted", cls=BaseCommand)
348
+ def fix_integer_ppm_if_hinted(input_path: Path, **options: dict[str, Any]) -> None:
349
+ """
350
+ Sets the ``head.flags`` bit 3 to force integer ppem for hinted fonts.
351
+
352
+ fontbakery check id: com.google.fonts/check/integer_ppem_if_hinted
353
+
354
+ Rationale:
355
+
356
+ Hinted fonts must have head table flag bit 3 set.
357
+
358
+ Per https://docs.microsoft.com/en-us/typography/opentype/spec/head, bit 3 of Head::flags decides
359
+ whether PPEM should be rounded. This bit should always be set for hinted fonts.
360
+
361
+ Note:
362
+ Bit 3 = Force ppem to integer values for all internal scaler math;
363
+
364
+ Fixing procedure:
365
+
366
+ * Check if the font is hinted by looking for the presence of the ``fpgm`` table.
367
+ * Set the ``head.flags`` bit 3 to force integer ppem for hinted fonts.
368
+ """
369
+
370
+ def task(font: Font) -> bool:
371
+ if font.ttfont.get("fpgm"):
372
+ font.t_head.set_bit("flags", 3, True)
373
+ return font.t_head.is_modified
374
+
375
+ runner = TaskRunner(input_path=input_path, task=task, **options)
376
+ runner.filter.filter_out_ps = True
377
+ runner.run()
378
+
379
+
347
380
  @cli.command("transformed-components", cls=BaseCommand)
348
381
  def fix_transformed_components(input_path: Path, **options: dict[str, Any]) -> None:
349
382
  """
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Any, Literal, Optional
2
+ from typing import Any, Literal
3
3
 
4
4
  import click
5
5
  from foundrytools.core.font import Font
@@ -150,8 +150,8 @@ def remove_glyphs(input_path: Path, **options: dict[str, Any]) -> None:
150
150
 
151
151
  def task(
152
152
  font: Font,
153
- glyph_names_to_remove: Optional[set[str]],
154
- glyph_ids_to_remove: Optional[set[int]],
153
+ glyph_names_to_remove: set[str] | None,
154
+ glyph_ids_to_remove: set[int] | None,
155
155
  ) -> bool:
156
156
  removed_glyphs = font.remove_glyphs(
157
157
  glyph_names_to_remove=glyph_names_to_remove,
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
 
4
4
  import click
5
5
  from foundrytools import Font
@@ -55,8 +55,8 @@ def del_names(input_path: Path, **options: dict[str, Any]) -> None:
55
55
  def task(
56
56
  font: Font,
57
57
  name_ids_to_process: tuple[int],
58
- platform_id: Optional[int] = None,
59
- language_string: Optional[str] = None,
58
+ platform_id: int | None = None,
59
+ language_string: str | None = None,
60
60
  ) -> bool:
61
61
  font.t_name.remove_names(
62
62
  name_ids=name_ids_to_process, platform_id=platform_id, language_string=language_string
@@ -171,8 +171,8 @@ def find_replace(input_path: Path, **options: dict[str, Any]) -> None:
171
171
  font: Font,
172
172
  old_string: str,
173
173
  new_string: str,
174
- name_ids_to_process: Optional[tuple[int]] = None,
175
- name_ids_to_skip: Optional[tuple[int]] = None,
174
+ name_ids_to_process: tuple[int] | None = None,
175
+ name_ids_to_skip: tuple[int] | None = None,
176
176
  ) -> bool:
177
177
  font.t_name.find_replace(
178
178
  old_string=old_string,
@@ -227,7 +227,7 @@ def set_name(input_path: Path, **options: dict[str, Any]) -> None:
227
227
  font: Font,
228
228
  name_id: int,
229
229
  name_string: str,
230
- platform_id: Optional[int] = None,
230
+ platform_id: int | None = None,
231
231
  language_string: str = "en",
232
232
  ) -> bool:
233
233
  font.t_name.set_name(
@@ -289,7 +289,7 @@ def build_unique_id(input_path: Path, **options: dict[str, Any]) -> None:
289
289
  ``Font Revision;Vendor ID;PostScript Name``
290
290
  """
291
291
 
292
- def task(font: Font, platform_id: Optional[int] = None, alternate: bool = False) -> bool:
292
+ def task(font: Font, platform_id: int | None = None, alternate: bool = False) -> bool:
293
293
  font.t_name.build_unique_identifier(platform_id=platform_id, alternate=alternate)
294
294
  return font.t_name.is_modified
295
295
 
@@ -313,7 +313,7 @@ def build_full_name(input_path: Path, **options: dict[str, Any]) -> None:
313
313
  Builds the NameID 4 (Full Font Name).
314
314
  """
315
315
 
316
- def task(font: Font, platform_id: Optional[int] = None) -> bool:
316
+ def task(font: Font, platform_id: int | None = None) -> bool:
317
317
  font.t_name.build_full_font_name(platform_id=platform_id)
318
318
  return font.t_name.is_modified
319
319
 
@@ -337,7 +337,7 @@ def build_version_string(input_path: Path, **options: dict[str, Any]) -> None:
337
337
  Builds the NameID 5 (Version String).
338
338
  """
339
339
 
340
- def task(font: Font, platform_id: Optional[int] = None) -> bool:
340
+ def task(font: Font, platform_id: int | None = None) -> bool:
341
341
  font.t_name.build_version_string(platform_id=platform_id)
342
342
  return font.t_name.is_modified
343
343
 
@@ -361,7 +361,7 @@ def build_postscript_name(input_path: Path, **options: dict[str, Any]) -> None:
361
361
  Builds the NameID 6 (PostScript Name).
362
362
  """
363
363
 
364
- def task(font: Font, platform_id: Optional[int] = None) -> bool:
364
+ def task(font: Font, platform_id: int | None = None) -> bool:
365
365
  font.t_name.build_postscript_name(platform_id=platform_id)
366
366
  return font.t_name.is_modified
367
367
 
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Any, Optional, Union
2
+ from typing import Any
3
3
 
4
4
  import click
5
5
  from fontTools.misc.roundTools import otRound
@@ -257,7 +257,7 @@ def set_attrs(input_path: Path, **options: dict[str, Any]) -> None:
257
257
  """
258
258
  ensure_at_least_one_param(click.get_current_context())
259
259
 
260
- def task(font: Font, **kwargs: dict[str, Optional[Union[int, float, str, bool]]]) -> bool:
260
+ def task(font: Font, **kwargs: dict[str, int | float | str | bool | None]) -> bool:
261
261
  for attr, value in kwargs.items():
262
262
  if value is not None:
263
263
  try:
@@ -417,7 +417,7 @@ def set_fs_selection(input_path: Path, **options: dict[str, Any]) -> None:
417
417
  """
418
418
  ensure_at_least_one_param(click.get_current_context())
419
419
 
420
- def task(font: Font, **kwargs: dict[str, Optional[bool]]) -> bool:
420
+ def task(font: Font, **kwargs: dict[str, bool | None]) -> bool:
421
421
  for attr, value in kwargs.items():
422
422
  if value is not None:
423
423
  if hasattr(font.flags, attr):
@@ -497,7 +497,7 @@ def set_fs_type(input_path: Path, **options: dict[str, Any]) -> None:
497
497
  """
498
498
  ensure_at_least_one_param(click.get_current_context())
499
499
 
500
- def task(font: Font, **kwargs: dict[str, Optional[bool]]) -> bool:
500
+ def task(font: Font, **kwargs: dict[str, bool | None]) -> bool:
501
501
  for attr, value in kwargs.items():
502
502
  if hasattr(font.t_os_2, attr) and value is not None:
503
503
  setattr(font.t_os_2, attr, value)
@@ -1,5 +1,6 @@
1
+ from collections.abc import Callable
1
2
  from pathlib import Path
2
- from typing import Any, Callable, cast
3
+ from typing import Any, cast
3
4
 
4
5
  import click
5
6
  from foundrytools import Font
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
 
4
4
  import click
5
5
  from foundrytools import Font
@@ -43,10 +43,10 @@ def cli(input_path: Path, **options: dict[str, Any]) -> None:
43
43
 
44
44
  def task(
45
45
  font: Font,
46
- italic_angle: Optional[float] = None,
47
- underline_position: Optional[int] = None,
48
- underline_thickness: Optional[int] = None,
49
- fixed_pitch: Optional[bool] = None,
46
+ italic_angle: float | None = None,
47
+ underline_position: int | None = None,
48
+ underline_thickness: int | None = None,
49
+ fixed_pitch: bool | None = None,
50
50
  ) -> bool:
51
51
  attrs = {
52
52
  "italic_angle": italic_angle,
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import click
5
4
  from foundrytools import FontFinder
@@ -53,7 +52,7 @@ def font_info(input_path: Path) -> None:
53
52
  Prints a minimal set of NameRecords, omitting the ones with nameID not in 1, 2, 3, 4, 5, 6, 16,
54
53
  17, 18, 21, 22, 25""",
55
54
  )
56
- def font_names(input_path: Path, max_lines: Optional[int] = None, minimal: bool = False) -> None:
55
+ def font_names(input_path: Path, max_lines: int | None = None, minimal: bool = False) -> None:
57
56
  """
58
57
  Prints the name table.
59
58
  """
@@ -1,5 +1,4 @@
1
1
  from shutil import get_terminal_size
2
- from typing import Optional
3
2
 
4
3
  from fontTools.ttLib.tables._n_a_m_e import _MAC_LANGUAGES, _WINDOWS_LANGUAGES, NameRecord
5
4
  from foundrytools import Font
@@ -42,7 +41,7 @@ JUSTIFY = 22
42
41
 
43
42
 
44
43
  def _process_name_table(
45
- font: Font, table: Table, terminal_width: int, max_lines: Optional[int], minimal: bool
44
+ font: Font, table: Table, terminal_width: int, max_lines: int | None, minimal: bool
46
45
  ) -> None:
47
46
  name_table = NameTable(font.ttfont)
48
47
  names = name_table.table.names
@@ -63,7 +62,7 @@ def _process_name_table(
63
62
 
64
63
 
65
64
  def _add_name_row_to_table(
66
- table: Table, name: NameRecord, terminal_width: int, max_lines: Optional[int]
65
+ table: Table, name: NameRecord, terminal_width: int, max_lines: int | None
67
66
  ) -> None:
68
67
  row_string = _get_name_row(name)
69
68
  row_string = wrap_string(
@@ -77,7 +76,7 @@ def _add_name_row_to_table(
77
76
 
78
77
 
79
78
  def _process_cff_table(
80
- font: Font, table: Table, terminal_width: int, max_lines: Optional[int], minimal: bool
79
+ font: Font, table: Table, terminal_width: int, max_lines: int | None, minimal: bool
81
80
  ) -> None:
82
81
  if not font.is_ps:
83
82
  return
@@ -133,7 +132,7 @@ def _get_name_row(name: NameRecord) -> str:
133
132
  )
134
133
 
135
134
 
136
- def main(font: Font, max_lines: Optional[int] = None, minimal: bool = False) -> None:
135
+ def main(font: Font, max_lines: int | None = None, minimal: bool = False) -> None:
137
136
  """
138
137
  Prints the names of the font.
139
138
  """
@@ -1,6 +1,7 @@
1
1
  # pylint: disable=import-outside-toplevel
2
+ from collections.abc import Callable
2
3
  from pathlib import Path
3
- from typing import Any, Callable, cast
4
+ from typing import Any, cast
4
5
 
5
6
  import click
6
7
 
@@ -1,7 +1,8 @@
1
1
  import os
2
+ from collections.abc import Callable
2
3
  from pathlib import Path
3
4
  from textwrap import TextWrapper
4
- from typing import Any, Callable, Optional, Union
5
+ from typing import Any
5
6
 
6
7
  import click
7
8
 
@@ -112,8 +113,8 @@ def make_options(options: list[Callable]) -> Callable:
112
113
 
113
114
 
114
115
  def choice_to_int_callback(
115
- ctx: click.Context, _: click.Parameter, value: Union[str, tuple[str, ...]]
116
- ) -> Optional[Union[int, tuple[int, ...]]]:
116
+ ctx: click.Context, _: click.Parameter, value: str | tuple[str, ...]
117
+ ) -> int | tuple[int, ...] | None:
117
118
  """
118
119
  Callback for click options that accept a choice. Converts the choice to an integer or a tuple
119
120
  of integers.
@@ -129,7 +130,7 @@ def choice_to_int_callback(
129
130
  :param value: The value to convert
130
131
  :type value: Union[str, tuple[str, ...]]
131
132
  :return: The converted value
132
- :rtype: Optional[Union[int, tuple[int, ...]]]
133
+ :rtype: int | tuple[int, ...] | None
133
134
  """
134
135
 
135
136
  # we do not check if the values can be converted to integers here because the click.Choice
@@ -169,9 +170,7 @@ def tuple_to_set_callback(
169
170
  return set(value)
170
171
 
171
172
 
172
- def output_dir_callback(
173
- ctx: click.Context, _: click.Parameter, value: Optional[Path]
174
- ) -> Optional[Path]:
173
+ def output_dir_callback(ctx: click.Context, _: click.Parameter, value: Path | None) -> Path | None:
175
174
  """
176
175
  Callback for ``--output-dir option``.
177
176
 
@@ -227,7 +226,7 @@ def ensure_at_least_one_param(ctx: click.Context) -> None:
227
226
 
228
227
 
229
228
  def wrap_string(
230
- string: str, width: int, initial_indent: int, indent: int, max_lines: Optional[int] = None
229
+ string: str, width: int, initial_indent: int, indent: int, max_lines: int | None = None
231
230
  ) -> str:
232
231
  """
233
232
  Wraps a string to a specified width.
@@ -1,6 +1,7 @@
1
+ from collections.abc import Callable
1
2
  from dataclasses import dataclass
2
3
  from pathlib import Path
3
- from typing import Any, Callable, Optional, Union, get_type_hints
4
+ from typing import Any, get_type_hints
4
5
 
5
6
  from foundrytools import Font
6
7
  from foundrytools.lib.font_finder import (
@@ -28,9 +29,9 @@ class SaveOptions:
28
29
  A class that specifies how to save the font.
29
30
  """
30
31
 
31
- reorder_tables: Optional[bool] = True
32
+ reorder_tables: bool | None = True
32
33
  suffix: str = ""
33
- output_dir: Optional[Path] = None
34
+ output_dir: Path | None = None
34
35
  overwrite: bool = False
35
36
 
36
37
 
@@ -74,7 +75,7 @@ class TaskRunnerConfig: # pylint: disable=too-few-public-methods
74
75
 
75
76
  @staticmethod
76
77
  def _set_options(
77
- options_group: Union[dict, FinderOptions, SaveOptions], options: dict[str, Any]
78
+ options_group: dict | FinderOptions | SaveOptions, options: dict[str, Any]
78
79
  ) -> None:
79
80
  """
80
81
  Update attributes of an options_group with provided options if the attribute exists.
@@ -1,9 +1,10 @@
1
1
  # https://realpython.com/python-timer/#the-python-timer-code
2
2
 
3
3
  import time
4
+ from collections.abc import Callable
4
5
  from contextlib import ContextDecorator
5
6
  from dataclasses import dataclass, field
6
- from typing import Any, Callable, ClassVar, Optional
7
+ from typing import Any, ClassVar
7
8
 
8
9
 
9
10
  class TimerError(Exception):
@@ -15,10 +16,10 @@ class Timer(ContextDecorator):
15
16
  """Time your code using a class, context manager, or decorator"""
16
17
 
17
18
  timers: ClassVar[dict[str, float]] = {}
18
- name: Optional[str] = None
19
+ name: str | None = None
19
20
  text: str = "Elapsed time: {:0.4f} seconds"
20
- logger: Optional[Callable[[str], None]] = print
21
- _start_time: Optional[float] = field(default=None, init=False, repr=False)
21
+ logger: Callable[[str], None] | None = print
22
+ _start_time: float | None = field(default=None, init=False, repr=False)
22
23
 
23
24
  def __post_init__(self) -> None:
24
25
  """Initialization: add timer to dict of timers"""
@@ -1,3 +0,0 @@
1
- VERSION = __version__ = "2.0.4"
2
-
3
- __all__ = ["VERSION"]