foundrytools-cli 2.1.1__tar.gz → 2.2.1__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 (40) hide show
  1. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/PKG-INFO +20 -26
  2. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/README.md +8 -16
  3. foundrytools_cli-2.2.1/pyproject.toml +131 -0
  4. foundrytools_cli-2.2.1/pyproject.toml.orig +112 -0
  5. foundrytools_cli-2.2.1/src/foundrytools_cli/__init__.py +3 -0
  6. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/__main__.py +1 -1
  7. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/fix/cli.py +0 -1
  8. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/font.py +40 -0
  9. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/otf.py +71 -16
  10. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/utils/cli.py +0 -1
  11. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/utils/font_renamer.py +1 -1
  12. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/utils/sync_timestamps.py +3 -4
  13. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/utils/task_runner.py +4 -4
  14. foundrytools_cli-2.1.1/pyproject.toml +0 -74
  15. foundrytools_cli-2.1.1/src/foundrytools_cli/__init__.py +0 -3
  16. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/__init__.py +0 -0
  17. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/cff.py +0 -0
  18. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/cmap.py +0 -0
  19. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/converter/__init__.py +0 -0
  20. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/converter/cli.py +0 -0
  21. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/converter/ttf_to_otf.py +0 -0
  22. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/fix/__init__.py +0 -0
  23. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/fix/legacy_accents.py +0 -0
  24. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/fix/vertical_metrics.py +0 -0
  25. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/gsub.py +0 -0
  26. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/hhea.py +0 -0
  27. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/name.py +0 -0
  28. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/os_2.py +0 -0
  29. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/post.py +0 -0
  30. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/print/__init__.py +0 -0
  31. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/print/cli.py +0 -0
  32. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/print/font_info.py +0 -0
  33. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/print/font_names.py +0 -0
  34. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/print/vf_instances.py +0 -0
  35. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/ttf.py +0 -0
  36. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/utils/__init__.py +0 -0
  37. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/commands/utils/font_organizer.py +0 -0
  38. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/utils/__init__.py +0 -0
  39. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/utils/logger.py +0 -0
  40. {foundrytools_cli-2.1.1 → foundrytools_cli-2.2.1}/src/foundrytools_cli/utils/timer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foundrytools-cli
3
- Version: 2.1.1
3
+ Version: 2.2.1
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,23 +11,25 @@ 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.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
17
16
  Classifier: Programming Language :: Python :: 3.13
18
17
  Classifier: Programming Language :: Python :: 3.14
19
- Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3.15
20
19
  Classifier: Topic :: Text Processing :: Fonts
21
- Requires-Dist: afdko>=4.0.3
22
- Requires-Dist: click>=8.3.1
23
- Requires-Dist: foundrytools>=0.1.6
20
+ Requires-Dist: afdko==4.0.3
21
+ Requires-Dist: click>=8.5.0
22
+ Requires-Dist: foundrytools>=0.1.8
24
23
  Requires-Dist: loguru>=0.7.3
25
24
  Requires-Dist: pathvalidate>=3.3.1
26
- Requires-Dist: rich>=14.3.1
27
- Requires-Dist: ufolib2>=0.18.1
28
- Requires-Dist: win32-setctime>=1.2.0 ; sys_platform >= 'win32'
29
- Requires-Python: >=3.10
25
+ Requires-Dist: rich>=15.0.0
26
+ Requires-Dist: ufolib2>=0.19.0
27
+ Requires-Dist: win32-setctime>=1.2.0 ; sys_platform == 'win32'
28
+ Requires-Dist: sphinx-click>=6.2.0 ; extra == 'docs'
29
+ Requires-Dist: sphinx-rtd-theme>=3.0.2 ; extra == 'docs'
30
+ Requires-Python: >=3.11
30
31
  Project-URL: Homepage, https://github.com/ftCLI/FoundryTools-CLI
32
+ Provides-Extra: docs
31
33
  Description-Content-Type: text/markdown
32
34
 
33
35
  # FoundryTools-CLI
@@ -54,10 +56,7 @@ rendered by [rich](https://github.com/Textualize/rich).
54
56
 
55
57
  ## Installation
56
58
 
57
- FoundryTools-CLI requires Python 3.10 or later.
58
-
59
- **IMPORTANT**: If you have Python 2.x installed in your system, you may have to use `python3`
60
- (instead of `python`) in the commands below.
59
+ FoundryTools-CLI requires Python 3.11 or later.
61
60
 
62
61
  ### pip
63
62
 
@@ -71,25 +70,20 @@ python -m pip install foundrytools-cli
71
70
  ### Editable mode
72
71
 
73
72
  If you would like to contribute to the development, you can clone the repository from GitHub,
74
- install the package in 'editable' mode and modify the source code in place. We strongly recommend
75
- using a virtual environment.
73
+ install the package in 'editable' mode and modify the source code in place. Install
74
+ [uv](https://docs.astral.sh/uv/getting-started/installation/) first to create the project
75
+ environment.
76
76
 
77
77
  ```
78
78
  # clone the repository:
79
79
  git clone https://github.com/ftCLI/foundrytools-cli.git
80
80
  cd foundrytools-cli
81
81
 
82
- # create new virtual environment named e.g. ftcli-venv, or whatever you prefer:
83
- python -m venv ftcli-venv
84
-
85
- # to activate the virtual environmtnet in macOS and Linux, do:
86
- . ftcli-venv/bin/activate
87
-
88
- # to activate the virtual environment in Windows, do:
89
- ftcli-venv\Scripts\activate.bat
82
+ # install the project and development tools in a managed environment:
83
+ uv sync --locked --group dev
90
84
 
91
- # install in 'editable' mode
92
- python -m pip install -e .
85
+ # run the CLI:
86
+ uv run ftcli --help
93
87
  ```
94
88
 
95
89
  ## Documentation
@@ -22,10 +22,7 @@ rendered by [rich](https://github.com/Textualize/rich).
22
22
 
23
23
  ## Installation
24
24
 
25
- FoundryTools-CLI requires Python 3.10 or later.
26
-
27
- **IMPORTANT**: If you have Python 2.x installed in your system, you may have to use `python3`
28
- (instead of `python`) in the commands below.
25
+ FoundryTools-CLI requires Python 3.11 or later.
29
26
 
30
27
  ### pip
31
28
 
@@ -39,25 +36,20 @@ python -m pip install foundrytools-cli
39
36
  ### Editable mode
40
37
 
41
38
  If you would like to contribute to the development, you can clone the repository from GitHub,
42
- install the package in 'editable' mode and modify the source code in place. We strongly recommend
43
- using a virtual environment.
39
+ install the package in 'editable' mode and modify the source code in place. Install
40
+ [uv](https://docs.astral.sh/uv/getting-started/installation/) first to create the project
41
+ environment.
44
42
 
45
43
  ```
46
44
  # clone the repository:
47
45
  git clone https://github.com/ftCLI/foundrytools-cli.git
48
46
  cd foundrytools-cli
49
47
 
50
- # create new virtual environment named e.g. ftcli-venv, or whatever you prefer:
51
- python -m venv ftcli-venv
52
-
53
- # to activate the virtual environmtnet in macOS and Linux, do:
54
- . ftcli-venv/bin/activate
55
-
56
- # to activate the virtual environment in Windows, do:
57
- ftcli-venv\Scripts\activate.bat
48
+ # install the project and development tools in a managed environment:
49
+ uv sync --locked --group dev
58
50
 
59
- # install in 'editable' mode
60
- python -m pip install -e .
51
+ # run the CLI:
52
+ uv run ftcli --help
61
53
  ```
62
54
 
63
55
  ## Documentation
@@ -0,0 +1,131 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.13.1,<0.14"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "foundrytools-cli"
7
+ version = "2.2.1"
8
+ description = "A set of command line tools to inspect, manipulate and convert font files"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ dependencies = [
13
+ "afdko==4.0.3",
14
+ "click>=8.5.0",
15
+ "foundrytools>=0.1.8",
16
+ "loguru>=0.7.3",
17
+ "pathvalidate>=3.3.1",
18
+ "rich>=15.0.0",
19
+ "ufolib2>=0.19.0",
20
+ "win32-setctime>=1.2.0; sys_platform == 'win32'",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 5 - Production/Stable",
24
+ "Intended Audience :: Developers",
25
+ "Intended Audience :: End Users/Desktop",
26
+ "Natural Language :: English",
27
+ "Operating System :: OS Independent",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Programming Language :: Python :: 3.14",
33
+ "Programming Language :: Python :: 3.15",
34
+ "Topic :: Text Processing :: Fonts",
35
+ ]
36
+
37
+ [[project.authors]]
38
+ name = "Cesare Gilento"
39
+ email = "ftcli@proton.me"
40
+
41
+ [project.optional-dependencies]
42
+ docs = [
43
+ "sphinx-click>=6.2.0",
44
+ "sphinx-rtd-theme>=3.0.2",
45
+ ]
46
+
47
+ [project.scripts]
48
+ ftcli = "foundrytools_cli.__main__:cli"
49
+
50
+ [project.urls]
51
+ Homepage = "https://github.com/ftCLI/FoundryTools-CLI"
52
+
53
+ [dependency-groups]
54
+ dev = [
55
+ "bump-my-version>=1.6.0",
56
+ "mypy>=2.4.0",
57
+ "pre-commit>=4.6.2",
58
+ "ruff>=0.11.0",
59
+ ]
60
+ security = [
61
+ "bandit>=1.8.6",
62
+ "pip-audit>=2.9.0",
63
+ ]
64
+
65
+ [tool.uv.build-backend]
66
+ module-name = "foundrytools_cli"
67
+ module-root = "src"
68
+
69
+ [tool.mypy]
70
+ warn_unused_configs = true
71
+ disallow_untyped_defs = true
72
+
73
+ [[tool.mypy.overrides]]
74
+ module = [
75
+ "afdko.*",
76
+ "fontTools.*",
77
+ "win32_setctime.*",
78
+ ]
79
+ ignore_missing_imports = true
80
+
81
+ [tool.ruff]
82
+ line-length = 100
83
+ indent-width = 4
84
+ target-version = "py311"
85
+
86
+ [tool.ruff.lint]
87
+ select = [
88
+ "C",
89
+ "E",
90
+ "F",
91
+ "UP",
92
+ "B",
93
+ "SIM",
94
+ "I",
95
+ ]
96
+ ignore = []
97
+ fixable = ["ALL"]
98
+ unfixable = []
99
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
100
+
101
+ [tool.ruff.format]
102
+ quote-style = "double"
103
+ indent-style = "space"
104
+ skip-magic-trailing-comma = false
105
+ line-ending = "auto"
106
+ docstring-code-format = true
107
+ docstring-code-line-length = "dynamic"
108
+
109
+ [tool.bumpversion]
110
+ current_version = "2.2.1"
111
+ commit = true
112
+ tag = true
113
+
114
+ [[tool.bumpversion.files]]
115
+ filename = "src/foundrytools_cli/__init__.py"
116
+ search = '__version__ = "{current_version}"'
117
+ replace = '__version__ = "{new_version}"'
118
+
119
+ [[tool.bumpversion.files]]
120
+ filename = "docs/source/conf.py"
121
+ search = 'release = "{current_version}"'
122
+ replace = 'release = "{new_version}"'
123
+
124
+ [[tool.bumpversion.files]]
125
+ filename = "pyproject.toml"
126
+ search = """
127
+ name = "foundrytools-cli"
128
+ version = "{current_version}""""
129
+ replace = """
130
+ name = "foundrytools-cli"
131
+ version = "{new_version}""""
@@ -0,0 +1,112 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.13.1,<0.14"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "foundrytools-cli"
7
+ version = "2.2.1"
8
+ description = "A set of command line tools to inspect, manipulate and convert font files"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [{ name = "Cesare Gilento", email = "ftcli@proton.me" }]
13
+ dependencies = [
14
+ "afdko==4.0.3",
15
+ "click>=8.5.0",
16
+ "foundrytools>=0.1.8",
17
+ "loguru>=0.7.3",
18
+ "pathvalidate>=3.3.1",
19
+ "rich>=15.0.0",
20
+ "ufolib2>=0.19.0",
21
+ "win32-setctime>=1.2.0; sys_platform == 'win32'",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 5 - Production/Stable",
25
+ "Intended Audience :: Developers",
26
+ "Intended Audience :: End Users/Desktop",
27
+ "Natural Language :: English",
28
+ "Operating System :: OS Independent",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Programming Language :: Python :: 3.14",
34
+ "Programming Language :: Python :: 3.15",
35
+ "Topic :: Text Processing :: Fonts",
36
+ ]
37
+
38
+ [dependency-groups]
39
+ dev = [
40
+ "bump-my-version>=1.6.0",
41
+ "mypy>=2.4.0",
42
+ "pre-commit>=4.6.2",
43
+ "ruff>=0.11.0",
44
+ ]
45
+ security = [
46
+ "bandit>=1.8.6",
47
+ "pip-audit>=2.9.0",
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ docs = [
52
+ "sphinx-click>=6.2.0",
53
+ "sphinx-rtd-theme>=3.0.2",
54
+ ]
55
+
56
+ [project.scripts]
57
+ ftcli = "foundrytools_cli.__main__:cli"
58
+
59
+ [project.urls]
60
+ Homepage = "https://github.com/ftCLI/FoundryTools-CLI"
61
+
62
+ [tool.uv.build-backend]
63
+ module-name = "foundrytools_cli"
64
+ module-root = "src"
65
+
66
+ [tool.mypy]
67
+ warn_unused_configs = true
68
+ disallow_untyped_defs = true
69
+
70
+ [[tool.mypy.overrides]]
71
+ module = ["afdko.*", "fontTools.*", "win32_setctime.*"]
72
+ ignore_missing_imports = true
73
+
74
+ [tool.ruff]
75
+ line-length = 100
76
+ indent-width = 4
77
+ target-version = "py311"
78
+
79
+ [tool.ruff.lint]
80
+ select = ["C", "E", "F", "UP", "B", "SIM", "I"]
81
+ ignore = []
82
+ fixable = ["ALL"]
83
+ unfixable = []
84
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
85
+
86
+ [tool.ruff.format]
87
+ quote-style = "double"
88
+ indent-style = "space"
89
+ skip-magic-trailing-comma = false
90
+ line-ending = "auto"
91
+ docstring-code-format = true
92
+ docstring-code-line-length = "dynamic"
93
+
94
+ [tool.bumpversion]
95
+ current_version = "2.2.1"
96
+ commit = true
97
+ tag = true
98
+
99
+ [[tool.bumpversion.files]]
100
+ filename = "src/foundrytools_cli/__init__.py"
101
+ search = '__version__ = "{current_version}"'
102
+ replace = '__version__ = "{new_version}"'
103
+
104
+ [[tool.bumpversion.files]]
105
+ filename = "docs/source/conf.py"
106
+ search = 'release = "{current_version}"'
107
+ replace = 'release = "{new_version}"'
108
+
109
+ [[tool.bumpversion.files]]
110
+ filename = "pyproject.toml"
111
+ search = "name = \"foundrytools-cli\"\nversion = \"{current_version}\""
112
+ replace = "name = \"foundrytools-cli\"\nversion = \"{new_version}\""
@@ -0,0 +1,3 @@
1
+ VERSION = __version__ = "2.2.1"
2
+
3
+ __all__ = ["VERSION"]
@@ -36,5 +36,5 @@ from foundrytools_cli.commands.utils import cli as utils
36
36
  },
37
37
  )
38
38
  @click.version_option()
39
- def cli() -> None: # pylint: disable=missing-function-docstring
39
+ def cli() -> None:
40
40
  pass
@@ -1,4 +1,3 @@
1
- # pylint: disable=import-outside-toplevel
2
1
  from pathlib import Path
3
2
  from typing import Any, cast
4
3
 
@@ -11,6 +11,46 @@ from foundrytools_cli.utils.task_runner import TaskRunner
11
11
  cli = click.Group(help="Font level utilities.")
12
12
 
13
13
 
14
+ @cli.command("add-extremes", cls=BaseCommand)
15
+ @click.option(
16
+ "--keep-hinting",
17
+ is_flag=True,
18
+ help="Preserve hinting when adding extrema points.",
19
+ )
20
+ @click.option(
21
+ "--tolerance",
22
+ type=click.FloatRange(min=0),
23
+ default=1.0,
24
+ show_default=True,
25
+ help="Do not add extrema points within this distance (font units) of existing points.",
26
+ )
27
+ def add_extremes(input_path: Path, **options: dict[str, Any]) -> None:
28
+ """
29
+ Add points at the horizontal and vertical extrema of glyph curves.
30
+
31
+ Composite glyphs in TrueType fonts and variable fonts are not modified.
32
+ """
33
+
34
+ def task(font: Font, keep_hinting: bool = False, tolerance: float = 1.0) -> bool:
35
+ modified_glyphs = font.add_extremes(
36
+ remove_hinting=not keep_hinting,
37
+ tolerance=tolerance,
38
+ )
39
+ if not modified_glyphs:
40
+ logger.info("No glyphs were modified")
41
+ return False
42
+
43
+ logger.opt(colors=True).info(
44
+ f"{len(modified_glyphs)} glyphs were modified: "
45
+ f"<lc>{', '.join(sorted(modified_glyphs))}</lc>"
46
+ )
47
+ return True
48
+
49
+ runner = TaskRunner(input_path=input_path, task=task, **options)
50
+ runner.filter.filter_out_variable = True
51
+ runner.run()
52
+
53
+
14
54
  @cli.command("correct-contours", cls=BaseCommand)
15
55
  @click.option(
16
56
  "-ma",
@@ -258,12 +258,53 @@ def round_coordinates(input_path: Path, **options: dict[str, Any]) -> None:
258
258
  The number of vertical stem values to extract.
259
259
  """,
260
260
  )
261
+ @click.option(
262
+ "--use-samples/--all-glyphs",
263
+ default=True,
264
+ show_default=True,
265
+ help="Calculate each value from its default or custom glyph sample, or from the whole font.",
266
+ )
267
+ @click.option(
268
+ "--std-h-sample",
269
+ multiple=True,
270
+ help="Characters to sample for StdHW; may be repeated. Defaults to the built-in sample.",
271
+ )
272
+ @click.option(
273
+ "--std-v-sample",
274
+ multiple=True,
275
+ help="Characters to sample for StdVW; may be repeated. Defaults to the built-in sample.",
276
+ )
277
+ @click.option(
278
+ "--snap-h-sample",
279
+ multiple=True,
280
+ help="Characters to sample for StemSnapH; may be repeated. Defaults to the built-in sample.",
281
+ )
282
+ @click.option(
283
+ "--snap-v-sample",
284
+ multiple=True,
285
+ help="Characters to sample for StemSnapV; may be repeated. Defaults to the built-in sample.",
286
+ )
261
287
  def recalc_stems(input_path: Path, **options: dict[str, Any]) -> None:
262
288
  """
263
289
  Recalculate the hinting stems of OpenType-PS fonts.
264
290
  """
265
291
 
266
- def task(font: Font) -> bool:
292
+ sample_options = ("std_h_sample", "std_v_sample", "snap_h_sample", "snap_v_sample")
293
+ if not options["use_samples"] and any(options[name] for name in sample_options):
294
+ raise click.UsageError("Custom glyph samples cannot be used with --all-glyphs.")
295
+
296
+ def task(
297
+ font: Font,
298
+ report_all_stems: bool = False,
299
+ max_distance: int = 1,
300
+ max_h_stems: int = 2,
301
+ max_v_stems: int = 2,
302
+ use_samples: bool = True,
303
+ std_h_sample: tuple[str, ...] = (),
304
+ std_v_sample: tuple[str, ...] = (),
305
+ snap_h_sample: tuple[str, ...] = (),
306
+ snap_v_sample: tuple[str, ...] = (),
307
+ ) -> bool:
267
308
  if not font.is_ps:
268
309
  logger.error("Font is not a PostScript font")
269
310
  return False
@@ -273,11 +314,17 @@ def recalc_stems(input_path: Path, **options: dict[str, Any]) -> None:
273
314
  return False
274
315
 
275
316
  flavor = font.ttfont.flavor
276
- temp_file = get_temp_file_path()
317
+ temp_file: Path | None = None
277
318
  if flavor is not None:
278
319
  font.ttfont.flavor = None
279
320
  temp_file = get_temp_file_path()
280
- font.save(temp_file)
321
+ try:
322
+ font.save(temp_file)
323
+ except Exception:
324
+ temp_file.unlink(missing_ok=True)
325
+ raise
326
+ finally:
327
+ font.ttfont.flavor = flavor
281
328
  input_file = temp_file
282
329
  else:
283
330
  input_file = font.file
@@ -289,22 +336,31 @@ def recalc_stems(input_path: Path, **options: dict[str, Any]) -> None:
289
336
  current_stem_snap_h = font.t_cff_.get_hinting_data().get("StemSnapH", None)
290
337
  current_stem_snap_v = font.t_cff_.get_hinting_data().get("StemSnapV", None)
291
338
 
292
- report_all_stems = cast(bool, options["report_all_stems"])
293
- max_distance = cast(int, options["max_distance"])
294
- max_h_stems = cast(int, options["max_h_stems"])
295
- max_v_stems = cast(int, options["max_v_stems"])
296
- std_h_w, std_v_w, stem_snap_h, stem_snap_v = get_stems(
297
- input_file,
298
- report_all_stems=report_all_stems,
299
- max_distance=max_distance,
300
- max_h_stems=max_h_stems,
301
- max_v_stems=max_v_stems,
302
- )
339
+ def to_codepoints(sample: tuple[str, ...]) -> set[int] | None:
340
+ codepoints = {ord(char) for text in sample for char in text}
341
+ return codepoints or None
342
+
343
+ try:
344
+ std_h_w, std_v_w, stem_snap_h, stem_snap_v = get_stems(
345
+ input_file,
346
+ report_all_stems=report_all_stems,
347
+ max_distance=max_distance,
348
+ max_h_stems=max_h_stems,
349
+ max_v_stems=max_v_stems,
350
+ use_samples=use_samples,
351
+ std_h_codepoints=to_codepoints(std_h_sample),
352
+ std_v_codepoints=to_codepoints(std_v_sample),
353
+ snap_h_codepoints=to_codepoints(snap_h_sample),
354
+ snap_v_codepoints=to_codepoints(snap_v_sample),
355
+ )
356
+ finally:
357
+ if temp_file is not None:
358
+ temp_file.unlink(missing_ok=True)
359
+
303
360
  logger.info(f"StdHW: {current_std_h_w} -> {std_h_w}")
304
361
  logger.info(f"StdVW: {current_std_v_w} -> {std_v_w}")
305
362
  logger.info(f"StemSnapH: {current_stem_snap_h} -> {stem_snap_h}")
306
363
  logger.info(f"StemSnapV: {current_stem_snap_v} -> {stem_snap_v}")
307
- temp_file.unlink(missing_ok=True)
308
364
 
309
365
  if (current_std_h_w, current_std_v_w, current_stem_snap_h, current_stem_snap_v) == (
310
366
  std_h_w,
@@ -321,7 +377,6 @@ def recalc_stems(input_path: Path, **options: dict[str, Any]) -> None:
321
377
  "StemSnapV": stem_snap_v,
322
378
  }
323
379
  font.t_cff_.set_hinting_data(**hinting_data)
324
- font.ttfont.flavor = flavor
325
380
  return True
326
381
 
327
382
  runner = TaskRunner(input_path=input_path, task=task, **options)
@@ -1,4 +1,3 @@
1
- # pylint: disable=import-outside-toplevel
2
1
  from collections.abc import Callable
3
2
  from pathlib import Path
4
3
  from typing import Any, cast
@@ -150,6 +150,6 @@ def main(font: Font, source: int = 1) -> None:
150
150
  logger.opt(colors=True).info(
151
151
  f"<light-black>{old_file.name}</> --> <bold><magenta>{new_file.name}</></>"
152
152
  )
153
- except Exception as e: # pylint: disable=broad-except
153
+ except Exception as e:
154
154
  logger.error(f"Error renaming {old_file.name}: {e}")
155
155
  return
@@ -1,4 +1,3 @@
1
- # pylint: disable=import-outside-toplevel
2
1
  import os
3
2
  import platform
4
3
  from pathlib import Path
@@ -19,7 +18,7 @@ if platform.system() == "Windows":
19
18
  from win32_setctime import setctime
20
19
 
21
20
  SETCTIME = setctime
22
- except (ImportError, Exception) as exc: # pylint: disable=broad-except
21
+ except (ImportError, Exception) as exc:
23
22
  SETCTIME = None
24
23
  IMPORT_ERROR = exc
25
24
 
@@ -98,7 +97,7 @@ def _set_windows_ctime(path: Path, timestamp: int) -> None:
98
97
  try:
99
98
  SETCTIME(path, timestamp + epoch_diff)
100
99
  logger.info(f"created timestamp : {timestampToString(timestamp)}")
101
- except Exception as e: # pylint: disable=broad-except
100
+ except Exception as e:
102
101
  logger.error(f"Error setting file creation time: {e}")
103
102
 
104
103
 
@@ -107,7 +106,7 @@ def _set_mtime_and_atime(path: Path, timestamp: int) -> None:
107
106
  os.utime(path, (timestamp + epoch_diff, timestamp + epoch_diff))
108
107
  logger.info(f"modified timestamp : {timestampToString(timestamp)}")
109
108
  logger.info(f"access timestamp : {timestampToString(timestamp)}")
110
- except Exception as e: # pylint: disable=broad-except
109
+ except Exception as e:
111
110
  logger.error(f"Error setting file timestamps: {e}")
112
111
 
113
112
 
@@ -35,7 +35,7 @@ class SaveOptions:
35
35
  overwrite: bool = False
36
36
 
37
37
 
38
- class TaskRunnerConfig: # pylint: disable=too-few-public-methods
38
+ class TaskRunnerConfig:
39
39
  """
40
40
  Handle options for TaskRunner.
41
41
  """
@@ -85,7 +85,7 @@ class TaskRunnerConfig: # pylint: disable=too-few-public-methods
85
85
  setattr(options_group, key, value)
86
86
 
87
87
 
88
- class TaskRunner: # pylint: disable=too-few-public-methods
88
+ class TaskRunner:
89
89
  """
90
90
  A class for running tasks on multiple fonts.
91
91
 
@@ -162,7 +162,7 @@ class TaskRunner: # pylint: disable=too-few-public-methods
162
162
  def _execute_task(self, font: Font) -> bool:
163
163
  try:
164
164
  return bool(self.task(font, **self.config.task_options))
165
- except Exception as e: # pylint: disable=broad-except
165
+ except Exception as e:
166
166
  self._log_error(e)
167
167
  return False
168
168
 
@@ -181,7 +181,7 @@ class TaskRunner: # pylint: disable=too-few-public-methods
181
181
  out_file = self._get_out_file_name(font)
182
182
  font.save(out_file, reorder_tables=self.config.save_options.reorder_tables)
183
183
  logger.success(f"File saved to {out_file}")
184
- except Exception as e: # pylint: disable=broad-except
184
+ except Exception as e:
185
185
  self._log_error(e)
186
186
 
187
187
  def _get_out_file_name(self, font: Font) -> Path:
@@ -1,74 +0,0 @@
1
- [build-system]
2
- requires = ["uv_build>=0.9.17"]
3
- build-backend = "uv_build"
4
-
5
- [project]
6
- name = "foundrytools-cli"
7
- version = "2.1.1"
8
- description = "A set of command line tools to inspect, manipulate and convert font files"
9
- readme = "README.md"
10
- license = "MIT"
11
- requires-python = ">=3.10"
12
- authors = [
13
- { name = "Cesare Gilento", email = "ftcli@proton.me" },
14
- ]
15
-
16
- classifiers = [
17
- "Development Status :: 5 - Production/Stable",
18
- "Intended Audience :: Developers",
19
- "Intended Audience :: End Users/Desktop",
20
- "Natural Language :: English",
21
- "Operating System :: OS Independent",
22
- "Programming Language :: Python :: 3",
23
- "Programming Language :: Python :: 3.10",
24
- "Programming Language :: Python :: 3.11",
25
- "Programming Language :: Python :: 3.12",
26
- "Programming Language :: Python :: 3.13",
27
- "Programming Language :: Python :: 3.14",
28
- "License :: OSI Approved :: MIT License",
29
- "Topic :: Text Processing :: Fonts",
30
- ]
31
-
32
- dependencies = [
33
- "afdko>=4.0.3",
34
- "click>=8.3.1",
35
- "foundrytools>=0.1.6",
36
- "loguru>=0.7.3",
37
- "pathvalidate>=3.3.1",
38
- "rich>=14.3.1",
39
- "ufolib2>=0.18.1",
40
- "win32-setctime>=1.2.0; sys_platform >= 'win32'",
41
- ]
42
-
43
- [dependency-groups]
44
- dev = [
45
- "bump-my-version>=1.2.6",
46
- "mypy>=1.19.1",
47
- "pre-commit>=4.5.1",
48
- "pylint>=4.0.4",
49
- ]
50
- docs = [
51
- "sphinx-click>=6.2.0",
52
- "sphinx-rtd-theme>=3.1.0",
53
- ]
54
-
55
- [project.scripts]
56
- ftcli = "foundrytools_cli.__main__:cli"
57
-
58
- [project.urls]
59
- Homepage = "https://github.com/ftCLI/FoundryTools-CLI"
60
-
61
- [tool.bumpversion]
62
- current_version = "2.1.1"
63
- commit = false
64
- tag = false
65
-
66
- [[tool.bumpversion.files]]
67
- filename = "src/foundrytools_cli/__init__.py"
68
- search = '__version__ = "{current_version}"'
69
- replace = '__version__ = "{new_version}"'
70
-
71
- [[tool.bumpversion.files]]
72
- filename = "docs/source/conf.py"
73
- search = 'release = "{current_version}"'
74
- replace = 'release = "{new_version}"'
@@ -1,3 +0,0 @@
1
- VERSION = __version__ = "2.1.1"
2
-
3
- __all__ = ["VERSION"]