foundrytools-cli 1.1.8__tar.gz → 1.1.10__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 (68) hide show
  1. {foundrytools-cli-1.1.8/foundrytools_cli.egg-info → foundrytools-cli-1.1.10}/PKG-INFO +1 -1
  2. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_fix.py +167 -1
  3. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/Font.py +2 -3
  4. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/ttf_to_otf.py +1 -0
  5. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/skia_tools.py +17 -0
  6. foundrytools-cli-1.1.10/foundryToolsCLI/__init__.py +3 -0
  7. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10/foundrytools_cli.egg-info}/PKG-INFO +1 -1
  8. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/setup.py +1 -1
  9. foundrytools-cli-1.1.8/foundryToolsCLI/__init__.py +0 -3
  10. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/LICENSE +0 -0
  11. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/MANIFEST.in +0 -0
  12. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/README.md +0 -0
  13. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/__init__.py +0 -0
  14. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_assistant.py +0 -0
  15. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_cff.py +0 -0
  16. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_converter.py +0 -0
  17. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_hhea.py +0 -0
  18. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_metrics.py +0 -0
  19. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_name.py +0 -0
  20. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_os2.py +0 -0
  21. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_otf.py +0 -0
  22. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_post.py +0 -0
  23. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_print.py +0 -0
  24. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_ttf.py +0 -0
  25. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/CLI/ftcli_utils.py +0 -0
  26. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/VFont.py +0 -0
  27. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/__init__.py +0 -0
  28. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/assistant/UI.py +0 -0
  29. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/assistant/__init__.py +0 -0
  30. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/assistant/fonts_data.py +0 -0
  31. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/assistant/styles_mapping.py +0 -0
  32. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/constants.py +0 -0
  33. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/__init__.py +0 -0
  34. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/options.py +0 -0
  35. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/otf_to_ttf.py +0 -0
  36. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/sfnt_to_web.py +0 -0
  37. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/ttc_to_sfnt.py +0 -0
  38. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/variable_to_static.py +0 -0
  39. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/converters/web_to_sfnt.py +0 -0
  40. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/printer/UI.py +0 -0
  41. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/printer/__init__.py +0 -0
  42. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/CFF_.py +0 -0
  43. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/OS_2.py +0 -0
  44. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/__init__.py +0 -0
  45. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/head.py +0 -0
  46. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/hhea.py +0 -0
  47. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/name.py +0 -0
  48. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/tables/post.py +0 -0
  49. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/__init__.py +0 -0
  50. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/bits_tools.py +0 -0
  51. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/cli_tools.py +0 -0
  52. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/click_tools.py +0 -0
  53. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/logger.py +0 -0
  54. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/otf_tools.py +0 -0
  55. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/ps_recalc_stems.py +0 -0
  56. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/ps_recalc_zones.py +0 -0
  57. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/text_tools.py +0 -0
  58. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/timer.py +0 -0
  59. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/Lib/utils/ttf_tools.py +0 -0
  60. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundryToolsCLI/__main__.py +0 -0
  61. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/SOURCES.txt +0 -0
  62. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/dependency_links.txt +0 -0
  63. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/entry_points.txt +0 -0
  64. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/not-zip-safe +0 -0
  65. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/requires.txt +0 -0
  66. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/foundrytools_cli.egg-info/top_level.txt +0 -0
  67. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/requirements.txt +0 -0
  68. {foundrytools-cli-1.1.8 → foundrytools-cli-1.1.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foundrytools-cli
3
- Version: 1.1.8
3
+ Version: 1.1.10
4
4
  Summary: A set of command line tools to inspect, manipulate and convert font files
5
5
  Home-page: https://github.com/ftCLI/FoundryTools-CLI
6
6
  Author: ftCLI
@@ -1,10 +1,15 @@
1
- from typing import Optional
1
+ from typing import Any, Dict, Optional, Union
2
2
 
3
3
  from copy import deepcopy
4
4
  from pathlib import Path
5
5
 
6
6
  import click
7
7
  from fontTools.misc.cliTools import makeOutputFileName
8
+ from fontTools.misc.psCharStrings import T2CharString
9
+ from fontTools.pens.t2CharStringPen import T2CharStringPen
10
+ from fontTools.pens.ttGlyphPen import TTGlyphPen
11
+ from fontTools.ttLib.tables._g_l_y_f import Glyph
12
+ from fontTools.ttLib.ttGlyphSet import _TTGlyphSetCFF, _TTGlyphSetGlyf
8
13
 
9
14
  from foundryToolsCLI.Lib.tables.OS_2 import TableOS2
10
15
  from foundryToolsCLI.Lib.tables.hhea import TableHhea
@@ -17,11 +22,172 @@ from foundryToolsCLI.Lib.utils.click_tools import (
17
22
  add_common_options,
18
23
  )
19
24
  from foundryToolsCLI.Lib.utils.logger import logger, Logs
25
+ from foundryToolsCLI.Lib.utils.skia_tools import is_empty_glyph
20
26
  from foundryToolsCLI.Lib.utils.timer import Timer
21
27
 
22
28
  fix_fonts = click.Group("subcommands")
23
29
 
24
30
 
31
+ @fix_fonts.command()
32
+ @add_file_or_path_argument()
33
+ @add_recursive_option()
34
+ @add_common_options()
35
+ @Timer(logger=logger.info)
36
+ def empty_notdef(
37
+ input_path: Path,
38
+ recursive: bool = False,
39
+ output_dir: Optional[Path] = None,
40
+ recalc_timestamp: bool = False,
41
+ overwrite: bool = True,
42
+ ):
43
+ """
44
+ Fix empty .notdef glyph.
45
+
46
+ Draw a simple rectangle to fill the empty .notdef glyph.
47
+
48
+ Rationale:
49
+
50
+ Glyph 0 must be assigned to a .notdef glyph. The .notdef glyph is very important for providing
51
+ the user feedback that a glyph is not found in the font. This glyph should not be left without
52
+ an outline as the user will only see what looks like a space if a glyph is missing and not be
53
+ aware of the active font’s limitation.
54
+
55
+ Fixing procedure:
56
+
57
+ * Draw a simple rectangle to fill the empty .notdef glyph
58
+ """
59
+
60
+ def _draw_empty_notdef_cff(
61
+ glyph_set: _TTGlyphSetCFF, width: int, height: int, thickness: int
62
+ ) -> T2CharString:
63
+ """
64
+ Draws an empty .notdef glyph in a CFF font.
65
+
66
+ Parameters:
67
+ glyph_set (_TTGlyphSetCFF): The glyph set to which the .notdef glyph belongs.
68
+ width (int): The width of the .notdef glyph.
69
+ height (int): The height of the .notdef glyph.
70
+ thickness (int): The thickness of the .notdef glyph.
71
+
72
+ Returns:
73
+ None
74
+ """
75
+
76
+ pen = T2CharStringPen(width=width, glyphSet=glyph_set)
77
+ notdef_glyph = glyph_set[".notdef"]
78
+
79
+ # Draw the outer contour (clockwise)
80
+ pen.moveTo((0, 0))
81
+ pen.lineTo((width, 0))
82
+ pen.lineTo((width, height))
83
+ pen.lineTo((0, height))
84
+ pen.closePath()
85
+
86
+ # Draw the inner contour (counterclockwise)
87
+ pen.moveTo((thickness, thickness))
88
+ pen.lineTo((thickness, height - thickness))
89
+ pen.lineTo((width - thickness, height - thickness))
90
+ pen.lineTo((width - thickness, thickness))
91
+ pen.closePath()
92
+
93
+ notdef_glyph.draw(pen)
94
+ charstring = pen.getCharString()
95
+ charstring.compile()
96
+ return charstring
97
+
98
+ def _draw_empty_notdef_glyf(
99
+ glyph_set: Union[Dict[str, Any], _TTGlyphSetGlyf], width: int, height: int,
100
+ thickness: int
101
+ ) -> Glyph:
102
+ """
103
+ Draws an empty .notdef glyph in a glyf font.
104
+
105
+ Parameters:
106
+ glyph_set (_TTGlyphSetGlyf): The glyph set to which the .notdef glyph belongs.
107
+ width (int): The width of the .notdef glyph.
108
+ height (int): The height of the .notdef glyph.
109
+ thickness (int): The thickness of the .notdef glyph.
110
+
111
+ Returns:
112
+ None
113
+ """
114
+ pen = TTGlyphPen(glyphSet=glyph_set)
115
+ notdef_glyph = glyph_set['.notdef']
116
+
117
+ # Draw the outer contour (clockwise)
118
+ pen.moveTo((0, 0))
119
+ pen.lineTo((0, height))
120
+ pen.lineTo((width, height))
121
+ pen.lineTo((width, 0))
122
+ pen.closePath()
123
+
124
+ # Draw the inner contour (clockwise)
125
+ pen.moveTo((thickness, thickness))
126
+ pen.lineTo((width - thickness, thickness))
127
+ pen.lineTo((width - thickness, height - thickness))
128
+ pen.lineTo((thickness, height - thickness))
129
+ pen.closePath()
130
+
131
+ notdef_glyph.draw(pen)
132
+ return pen.glyph()
133
+
134
+ fonts = get_fonts_in_path(
135
+ input_path=input_path, recursive=recursive, recalc_timestamp=recalc_timestamp
136
+ )
137
+ if not initial_check_pass(fonts=fonts, output_dir=output_dir):
138
+ return
139
+
140
+ for font in fonts:
141
+ try:
142
+ file = Path(font.reader.file.name)
143
+ output_file = Path(makeOutputFileName(file, outputDir=output_dir, overWrite=overwrite))
144
+
145
+ logger.opt(colors=True).info(Logs.checking_file, file=file)
146
+
147
+ glyph_set = font.getGlyphSet()
148
+
149
+ if ".notdef" not in glyph_set:
150
+ logger.warning("Font does not contain a .notdef glyph")
151
+ logger.skip(Logs.file_not_changed, file=file)
152
+ continue
153
+
154
+ if not is_empty_glyph(glyph_set=glyph_set, glyph_name='.notdef'):
155
+ logger.warning("The .notdef glyph is not empty")
156
+ logger.skip(Logs.file_not_changed, file=file)
157
+ continue
158
+
159
+ width = round(font['head'].unitsPerEm / 1000 * 600)
160
+ # The sCapHeight attribute is defined in the OS/2 version 2 and later. If the attribute
161
+ # is not present, the height is calculated as a percentage of the width.
162
+ try:
163
+ height = font["OS/2"].sCapHeight
164
+ except AttributeError:
165
+ height = round(width * 1.25)
166
+ thickness = round(width / 10)
167
+
168
+ if font.is_otf:
169
+ cff_table = font["CFF "]
170
+ charstring = _draw_empty_notdef_cff(
171
+ glyph_set=glyph_set, width=width, height=height, thickness=thickness
172
+ )
173
+ cff_table.cff.topDictIndex[0].CharStrings[".notdef"].bytecode = charstring.bytecode
174
+
175
+ else:
176
+ glyph = _draw_empty_notdef_glyf(
177
+ glyph_set=glyph_set, width=width, height=height, thickness=thickness
178
+ )
179
+ font["glyf"][".notdef"] = glyph
180
+
181
+ font["hmtx"][".notdef"] = (width, 0)
182
+ font.save(output_file)
183
+ logger.success(Logs.file_saved, file=output_file)
184
+
185
+ except Exception as e:
186
+ logger.exception(e)
187
+ finally:
188
+ font.close()
189
+
190
+
25
191
  @fix_fonts.command()
26
192
  @add_file_or_path_argument()
27
193
  @add_recursive_option()
@@ -359,7 +359,6 @@ class Font(TTFont):
359
359
  return self.get_glyph_bounds("H")["yMax"]
360
360
 
361
361
  def recalc_avg_char_width(self) -> int:
362
- avg_char_width = 0
363
362
  avg_char_width = self["OS/2"].recalcAvgCharWidth(self)
364
363
  return avg_char_width
365
364
 
@@ -1017,8 +1016,8 @@ class Font(TTFont):
1017
1016
  else:
1018
1017
  return 0
1019
1018
  except KeyError:
1020
- pass
1021
- return 0
1019
+ continue
1020
+ raise ValueError("The font does not contain the glyph 'H' or 'uni0048'.")
1022
1021
 
1023
1022
  def check_italic_angle(self, min_slant: float = 2.0) -> bool:
1024
1023
  # Allow .1 degrees tolerance
@@ -86,6 +86,7 @@ class TTF2OTFRunner(object):
86
86
  cff_font.save(output_file)
87
87
  cff_font = Font(output_file, recalcTimestamp=False)
88
88
  cff_font.otf_fix_contours(min_area=25, verbose=False)
89
+ cff_font["OS/2"].xAvgCharWidth = cff_font.recalc_avg_char_width()
89
90
 
90
91
  if self.options.subroutinize:
91
92
  cff_font.otf_subroutinize()
@@ -7,6 +7,7 @@ from fontTools.pens.t2CharStringPen import T2CharStringPen, T2CharString
7
7
  from fontTools.pens.ttGlyphPen import TTGlyphPen
8
8
  from fontTools.ttLib import ttFont
9
9
  from fontTools.ttLib.tables import _g_l_y_f
10
+ from fontTools.ttLib.ttGlyphSet import _TTGlyphSet
10
11
 
11
12
  from foundryToolsCLI.Lib.utils.logger import logger, Logs
12
13
 
@@ -166,3 +167,19 @@ def skia_path_from_glyph_component(component: _g_l_y_f.GlyphComponent, glyph_set
166
167
  base_glyph_name, transformation = component.getComponentInfo()
167
168
  path = skia_path_from_glyph(base_glyph_name, glyph_set)
168
169
  return path.transform(*transformation)
170
+
171
+
172
+ def is_empty_glyph(glyph_set: _TTGlyphSet, glyph_name: str) -> bool:
173
+ """
174
+ Returns True if the glyph is empty.
175
+
176
+ Parameters:
177
+ glyph_set (_TTGlyphSet): The glyph set.
178
+ glyph_name (str): The name of the glyph.
179
+
180
+ Returns:
181
+ bool: True if the glyph is empty.
182
+ """
183
+
184
+ path = skia_path_from_glyph(glyph_name=glyph_name, glyph_set=glyph_set)
185
+ return path.area == 0
@@ -0,0 +1,3 @@
1
+ VERSION = __version__ = "1.1.10"
2
+
3
+ __all__ = ["VERSION"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foundrytools-cli
3
- Version: 1.1.8
3
+ Version: 1.1.10
4
4
  Summary: A set of command line tools to inspect, manipulate and convert font files
5
5
  Home-page: https://github.com/ftCLI/FoundryTools-CLI
6
6
  Author: ftCLI
@@ -18,7 +18,7 @@ def _get_requirements():
18
18
 
19
19
  setuptools.setup(
20
20
  name="foundrytools-cli",
21
- version="1.1.8",
21
+ version="1.1.10",
22
22
  description="A set of command line tools to inspect, manipulate and convert font files",
23
23
  long_description=long_description,
24
24
  long_description_content_type="text/markdown",
@@ -1,3 +0,0 @@
1
- VERSION = __version__ = "1.1.8"
2
-
3
- __all__ = ["VERSION"]