foundrytools-cli 1.1.4__tar.gz → 1.1.6__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.4/foundrytools_cli.egg-info → foundrytools-cli-1.1.6}/PKG-INFO +4 -4
  2. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_cff.py +1 -0
  3. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_otf.py +1 -0
  4. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/Font.py +51 -0
  5. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttf_to_otf.py +7 -0
  6. foundrytools-cli-1.1.6/foundryToolsCLI/Lib/utils/ps_recalc_stems.py +92 -0
  7. foundrytools-cli-1.1.6/foundryToolsCLI/Lib/utils/ps_recalc_zones.py +246 -0
  8. foundrytools-cli-1.1.6/foundryToolsCLI/__init__.py +3 -0
  9. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6/foundrytools_cli.egg-info}/PKG-INFO +4 -4
  10. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/SOURCES.txt +2 -0
  11. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/requires.txt +3 -3
  12. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/requirements.txt +3 -3
  13. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/setup.py +1 -1
  14. foundrytools-cli-1.1.4/foundryToolsCLI/__init__.py +0 -3
  15. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/LICENSE +0 -0
  16. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/MANIFEST.in +0 -0
  17. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/README.md +0 -0
  18. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/__init__.py +0 -0
  19. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_assistant.py +0 -0
  20. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_converter.py +0 -0
  21. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_fix.py +0 -0
  22. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_hhea.py +0 -0
  23. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_metrics.py +0 -0
  24. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_name.py +0 -0
  25. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_os2.py +0 -0
  26. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_post.py +0 -0
  27. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_print.py +0 -0
  28. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_ttf.py +0 -0
  29. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_utils.py +0 -0
  30. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/VFont.py +0 -0
  31. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/__init__.py +0 -0
  32. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/UI.py +0 -0
  33. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/__init__.py +0 -0
  34. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/fonts_data.py +0 -0
  35. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/styles_mapping.py +0 -0
  36. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/constants.py +0 -0
  37. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/__init__.py +0 -0
  38. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/options.py +0 -0
  39. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/otf_to_ttf.py +0 -0
  40. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/sfnt_to_web.py +0 -0
  41. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttc_to_sfnt.py +0 -0
  42. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/variable_to_static.py +0 -0
  43. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/web_to_sfnt.py +0 -0
  44. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/printer/UI.py +0 -0
  45. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/printer/__init__.py +0 -0
  46. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/CFF_.py +0 -0
  47. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/OS_2.py +0 -0
  48. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/__init__.py +0 -0
  49. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/head.py +0 -0
  50. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/hhea.py +0 -0
  51. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/name.py +0 -0
  52. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/post.py +0 -0
  53. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/__init__.py +0 -0
  54. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/bits_tools.py +0 -0
  55. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/cli_tools.py +0 -0
  56. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/click_tools.py +0 -0
  57. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/logger.py +0 -0
  58. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/otf_tools.py +0 -0
  59. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/skia_tools.py +0 -0
  60. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/text_tools.py +0 -0
  61. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/timer.py +0 -0
  62. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/ttf_tools.py +0 -0
  63. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/__main__.py +0 -0
  64. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/dependency_links.txt +0 -0
  65. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/entry_points.txt +0 -0
  66. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/not-zip-safe +0 -0
  67. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/top_level.txt +0 -0
  68. {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foundrytools-cli
3
- Version: 1.1.4
3
+ Version: 1.1.6
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
@@ -10,11 +10,11 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.9
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: afdko>=4.0.0
14
- Requires-Dist: fonttools>=4.44.3
13
+ Requires-Dist: afdko>=4.0.1
14
+ Requires-Dist: fonttools>=4.47.2
15
15
  Requires-Dist: brotli>=1.1.0
16
16
  Requires-Dist: click>=8.1.7
17
- Requires-Dist: cffsubr>=0.2.9.post1
17
+ Requires-Dist: cffsubr>=0.3.0
18
18
  Requires-Dist: dehinter>=4.0.0
19
19
  Requires-Dist: loguru>=0.7.2
20
20
  Requires-Dist: pathvalidate>=3.2.0
@@ -33,6 +33,7 @@ tbl_cff = click.Group("subcommands")
33
33
  "--copyright", "Copyright", is_flag=True, help="Deletes CFF.cff.topDictIndex[0] Copyright"
34
34
  )
35
35
  @click.option("--notice", "Notice", is_flag=True, help="Deletes CFF.cff.topDictIndex[0] Copyright")
36
+ @click.option("--unique-id", "UniqueID", is_flag=True, help="Deletes CFF.cff.topDictIndex[0] UniqueID")
36
37
  @add_recursive_option()
37
38
  @add_common_options()
38
39
  @Timer(logger=logger.info)
@@ -4,6 +4,7 @@ from typing import Optional, List
4
4
 
5
5
  import click
6
6
  from fontTools.misc.cliTools import makeOutputFileName
7
+ from rich.pretty import pprint
7
8
 
8
9
  from foundryToolsCLI.Lib.Font import Font
9
10
  from foundryToolsCLI.Lib.utils.cli_tools import get_fonts_in_path, initial_check_pass
@@ -17,6 +17,8 @@ from foundryToolsCLI.Lib.tables.hhea import TableHhea
17
17
  from foundryToolsCLI.Lib.tables.name import TableName
18
18
  from foundryToolsCLI.Lib.tables.post import TablePost
19
19
  from foundryToolsCLI.Lib.utils.otf_tools import correct_otf_contours
20
+ from foundryToolsCLI.Lib.utils.ps_recalc_zones import recalc_zones
21
+ from foundryToolsCLI.Lib.utils.ps_recalc_stems import recalc_stems
20
22
  from foundryToolsCLI.Lib.utils.ttf_tools import correct_ttf_contours, decomponentize
21
23
 
22
24
 
@@ -505,6 +507,55 @@ class Font(TTFont):
505
507
  def otf_fix_contours(self, min_area: int = 25, verbose: bool = False) -> None:
506
508
  correct_otf_contours(font=self, min_area=min_area, verbose=verbose)
507
509
 
510
+ def otf_recalc_zones(self) -> t.Optional[t.Tuple[t.List[int], t.List[int]]]:
511
+ """
512
+ This function recalculates the zones of an OpenType font.
513
+ """
514
+ if not self.is_otf:
515
+ return
516
+
517
+ zones = recalc_zones(font=self)
518
+ return zones
519
+
520
+ def otf_recalc_stems(self) -> t.Tuple[int, int]:
521
+ """
522
+ This function recalculates the stems of an OpenType font.
523
+ """
524
+ if not self.is_otf:
525
+ raise ValueError("The font is not an OpenType font.")
526
+ if not hasattr(self.reader, "file"):
527
+ raise ValueError("The font is not a file-backed font.")
528
+
529
+ stems = recalc_stems(file_path=Path(self.reader.file.name))
530
+ return stems
531
+
532
+ def set_zones(self, other_blues: t.List[int], blue_values: t.List[int]) -> None:
533
+ """
534
+ Set zones for a font.
535
+
536
+ :param other_blues: Other blues.
537
+ :param blue_values: Blue values.
538
+ """
539
+ if not self.is_otf:
540
+ raise NotImplementedError("Setting zones is only supported for PostScript fonts.")
541
+
542
+ self["CFF "].cff.topDictIndex[0].Private.BlueValues = blue_values
543
+ self["CFF "].cff.topDictIndex[0].Private.OtherBlues = other_blues
544
+
545
+ def set_stems(self, std_h_w: int, std_v_w: int) -> None:
546
+ """
547
+ Set stems for a font.
548
+
549
+ :param std_h_w: StdHW.
550
+ :param std_v_w: StdVW.
551
+ """
552
+ if not self.is_otf:
553
+ raise NotImplementedError("Setting stems is only supported for PostScript fonts.")
554
+
555
+ self["CFF "].cff.topDictIndex[0].Private.StdHW = std_h_w
556
+ self["CFF "].cff.topDictIndex[0].Private.StdVW = std_v_w
557
+
558
+
508
559
  def ttf_fix_contours(
509
560
  self, min_area: int = 25, remove_hinting: bool = True, verbose: bool = False
510
561
  ) -> None:
@@ -86,8 +86,15 @@ 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
+
89
90
  if self.options.subroutinize:
90
91
  cff_font.otf_subroutinize()
92
+
93
+ other_blues, blue_values = cff_font.otf_recalc_zones()
94
+ cff_font.set_zones(other_blues=other_blues, blue_values=blue_values)
95
+ std_h_w, std_v_w = cff_font.otf_recalc_stems()
96
+ cff_font.set_stems(std_h_w=std_h_w, std_v_w=std_v_w)
97
+
91
98
  cff_font.save(output_file)
92
99
  cff_font.close()
93
100
 
@@ -0,0 +1,92 @@
1
+ import typing as t
2
+
3
+ from pathlib import Path
4
+
5
+ from afdko.otfautohint.__main__ import ReportOptions, get_stemhist_options, _validate_path
6
+ from afdko.otfautohint.autohint import fontWrapper, FontInstance, openFont
7
+ from afdko.otfautohint.hinter import glyphHinter
8
+ from afdko.otfautohint.report import Report
9
+
10
+ H_STEM_GLYPHS = ["A", "H", "T", "S", "C", "O"]
11
+ V_STEM_GLYPHS = ["E", "H", "I", "K", "L", "M", "N", "T", "U"]
12
+
13
+ __all__ = ["get_stems", "recalc_stems"]
14
+
15
+
16
+ def get_stems(
17
+ file_path: Path, glyph_list: t.List[str]
18
+ ) -> t.Tuple[t.List[t.Tuple[int, int, t.List[str]]], t.List[t.Tuple[int, int, t.List[str]]]]:
19
+ """
20
+ Retrieves stem data from a font file for a given list of glyphs.
21
+
22
+ Parameters:
23
+ file_path`: The path to the font file.
24
+ glyph_list`: A list of glyph names for which stem data will be
25
+ retrieved.
26
+
27
+ Returns:
28
+ Tuple: a tuple containing two lists: the first list contains the horizontal stem data,
29
+ and the second list contains the vertical stem data. Each list contains tuples of the
30
+ form (stem width, stem count, glyph names).
31
+ """
32
+ file_path = _validate_path(file_path)
33
+ _, parsed_args = get_stemhist_options(args=[file_path])
34
+ options = ReportOptions(parsed_args)
35
+ options.report_all_stems = True
36
+ options.report_zones = False
37
+ options.glyphList = glyph_list
38
+
39
+ font = openFont(file_path, options=options)
40
+ font_instance = FontInstance(font=font, inpath=file_path, outpath=file_path)
41
+
42
+ fw = fontWrapper(options=options, fil=[font_instance])
43
+ dict_record = fw.dictManager.getDictRecord()
44
+
45
+ hinter = glyphHinter(options=options, dictRecord=dict_record)
46
+ hinter.initialize(options=options, dictRecord=dict_record)
47
+ gmap = map(hinter.hint, fw)
48
+
49
+ report = Report()
50
+ for name, r in gmap:
51
+ report.glyphs[name] = r
52
+
53
+ h_stems, v_stems, _, _ = report._get_lists(options)
54
+ h_stems.sort(key=report._sort_count)
55
+ v_stems.sort(key=report._sort_count)
56
+
57
+ return h_stems, v_stems
58
+
59
+
60
+ def recalc_stems(
61
+ file_path: Path,
62
+ h_stems_glyphs: t.Optional[t.List[str]] = None,
63
+ v_stems_glyphs: t.Optional[t.List[str]] = None,
64
+ ) -> t.Tuple[int, int]:
65
+ """
66
+ Recalculates the StdHW and StdVW values for a given font file.
67
+
68
+ Parameters:
69
+ file_path: A `Path` object representing the path to the file.
70
+ h_stems_glyphs: A list of glyph names to use for calculating the horizontal stems.
71
+ v_stems_glyphs: A list of glyph names to use for calculating the vertical stems.
72
+
73
+ Returns:
74
+ A tuple containing the recalculated stem values for horizontal and vertical stems. The first
75
+ value in the tuple represents the horizontal stem value, and the second value represents the
76
+ vertical stem value.
77
+ """
78
+
79
+ if h_stems_glyphs is None:
80
+ h_stems_glyphs = H_STEM_GLYPHS
81
+ if v_stems_glyphs is None:
82
+ v_stems_glyphs = V_STEM_GLYPHS
83
+
84
+ h_stems, _ = get_stems(file_path=file_path, glyph_list=h_stems_glyphs)
85
+ _, v_stems = get_stems(file_path=file_path, glyph_list=v_stems_glyphs)
86
+
87
+ if not h_stems:
88
+ raise ValueError("No horizontal stems found")
89
+ if not v_stems:
90
+ raise ValueError("No vertical stems found")
91
+
92
+ return int(h_stems[0][1]), int(v_stems[0][1])
@@ -0,0 +1,246 @@
1
+ import typing as t
2
+ from collections import Counter
3
+
4
+ from fontTools.pens.boundsPen import BoundsPen
5
+ from fontTools.ttLib.ttFont import TTFont
6
+
7
+ UPPERCASE_LETTERS = [chr(i) for i in range(65, 91)] # A-Z
8
+ UPPERCASE_DESCENDERS = ["J", "Q"]
9
+ LOWERCASE_LETTERS = [chr(i) for i in range(97, 123)] # a-z
10
+ LOWERCASE_DESCENDERS = ["f", "g", "j", "p", "q", "y"]
11
+ LOWERCASE_ASCENDERS = ["b", "d", "f", "h", "k", "l", "t"]
12
+
13
+ DESCENDER_GLYPHS = list(set(LOWERCASE_DESCENDERS) - {"f", "j"})
14
+ BASELINE_GLYPHS = list(
15
+ set(UPPERCASE_LETTERS + LOWERCASE_LETTERS)
16
+ - set(LOWERCASE_DESCENDERS)
17
+ - set(UPPERCASE_DESCENDERS)
18
+ )
19
+ X_HEIGHT_GLYPHS = list(set(LOWERCASE_LETTERS) - set(LOWERCASE_ASCENDERS + ["i", "j"]))
20
+ UPPERCASE_GLYPHS = UPPERCASE_LETTERS
21
+ ASCENDER_GLYPHS = list(set(LOWERCASE_ASCENDERS) - {"t"})
22
+
23
+
24
+ __all__ = ["recalc_zones", "GlyphBounds"]
25
+
26
+
27
+ class GlyphBounds(t.TypedDict):
28
+ """
29
+ A type representing the bounds of a glyph.
30
+ """
31
+
32
+ xMin: float
33
+ yMin: float
34
+ xMax: float
35
+ yMax: float
36
+
37
+
38
+ def get_glyph_bounds(font: TTFont, glyph_name: str) -> GlyphBounds:
39
+ """
40
+ Get the bounds of a glyph.
41
+
42
+ Parameters:
43
+ font (TTFont): The TTFont object.
44
+ glyph_name (str): The name of the glyph.
45
+
46
+ Returns:
47
+ GlyphBounds: The bounds of the glyph.
48
+
49
+ Raises:
50
+ ValueError: If the glyph does not exist in the font.
51
+ """
52
+ glyph_set = font.getGlyphSet()
53
+ if glyph_name not in glyph_set:
54
+ raise ValueError(f"Glyph '{glyph_name}' does not exist in the font.")
55
+
56
+ bounds_pen = BoundsPen(glyphSet=glyph_set)
57
+
58
+ glyph_set[glyph_name].draw(bounds_pen)
59
+ bounds = GlyphBounds(
60
+ xMin=bounds_pen.bounds[0],
61
+ yMin=bounds_pen.bounds[1],
62
+ xMax=bounds_pen.bounds[2],
63
+ yMax=bounds_pen.bounds[3],
64
+ )
65
+
66
+ return bounds
67
+
68
+
69
+ def get_glyph_bounds_many(font: TTFont, glyph_names: t.List[str]) -> t.Dict[str, GlyphBounds]:
70
+ """
71
+ Get the bounds of multiple glyphs in a given font.
72
+
73
+ Parameters:
74
+ font (TTFont): The TTFont object.
75
+ glyph_names (List[str]): A list of glyph names.
76
+
77
+ Returns:
78
+ dict: A dictionary containing glyph names as keys and GlyphBounds objects as values.
79
+ """
80
+ glyphs_bounds = {}
81
+ for glyph_name in glyph_names:
82
+ try:
83
+ bounds = get_glyph_bounds(font=font, glyph_name=glyph_name)
84
+ glyphs_bounds[glyph_name] = bounds
85
+ except ValueError:
86
+ pass
87
+
88
+ return glyphs_bounds
89
+
90
+
91
+ def get_pair(counter: Counter) -> t.List[float]:
92
+ """
93
+ Get the two most common elements from the given counter.
94
+
95
+ Parameters:
96
+ counter (Counter): The counter object containing elements and their counts.
97
+
98
+ Returns:
99
+ List[float]: List containing the pair of most common elements.
100
+ """
101
+ most_common = counter.most_common(2)
102
+ if len(counter) == 1:
103
+ return [most_common[0][0], most_common[0][0]]
104
+ return sorted([most_common[0][0], most_common[1][0]])
105
+
106
+
107
+ def lists_overlaps(lists: t.List[t.List[float]]) -> bool:
108
+ """
109
+ Check if there are overlapping intervals in a list of lists.
110
+
111
+ Args:
112
+ lists (List[List[float]]): A list of lists, where each inner list represents an interval.
113
+
114
+ Returns:
115
+ bool: True if there are overlapping intervals, False otherwise.
116
+ """
117
+ for i in range(len(lists) - 1):
118
+ if lists[i][1] > lists[i + 1][0]:
119
+ return True
120
+ return False
121
+
122
+
123
+ def fix_lists_overlaps(lists: t.List[t.List[float]]) -> t.List[t.List[float]]:
124
+ """
125
+ Fixes overlaps in a list of lists of floats.
126
+
127
+ Args:
128
+ lists (List[List[float]]): A list of lists of floats.
129
+
130
+ Returns:
131
+ List[List[float]]: The input list with any overlaps fixed.
132
+ """
133
+ for i in range(len(lists) - 1):
134
+ if lists[i][1] > lists[i + 1][0]:
135
+ lists[i + 1][0] = lists[i][1]
136
+ lists[i + 1] = sorted(lists[i + 1])
137
+ return lists
138
+
139
+
140
+ def fix_min_separation_limits(lists: t.List[t.List[float]], limit: int) -> t.List[t.List[float]]:
141
+ """
142
+ Fixes the minimum separation between zones.
143
+
144
+ Args:
145
+ lists (List[List[float]]): A list of lists of floats.
146
+ limit (int): The minimum separation between zones.
147
+
148
+ Returns:
149
+ List[List[float]]: The input list with the minimum separation between zones fixed.
150
+ """
151
+ for i in range(len(lists) - 1):
152
+ if lists[i + 1][0] - lists[i][1] < limit:
153
+ # If the difference between the two values is less than 3, then
154
+ # set the second value to the first value
155
+ if lists[i + 1][1] - lists[i][1] > limit:
156
+ lists[i + 1][0] = lists[i + 1][1]
157
+ else:
158
+ # Remove the second list
159
+ lists.pop(i + 1)
160
+ return lists
161
+
162
+
163
+ def calculate_zone(
164
+ font: TTFont, glyph_names: t.List[str], min_or_max: t.Literal["yMin", "yMax"]
165
+ ) -> t.List[float]:
166
+ """
167
+ Calculates the minimum and maximum vertical values for a given zone.
168
+
169
+ Parameters:
170
+ font: TTFont object representing the font.
171
+ glyph_names: List of glyph names to process.
172
+ min_or_max: Literal specifying whether to process the minimum ('yMin') or maximum ('yMax')
173
+ values.
174
+
175
+ Returns:
176
+ List of float values representing the minimum or maximum vertical values for each glyph.
177
+
178
+ """
179
+ data = get_glyph_bounds_many(font=font, glyph_names=glyph_names)
180
+ counter = Counter([v[min_or_max] for v in data.values()])
181
+ return get_pair(counter)
182
+
183
+
184
+ def recalc_zones(
185
+ font: TTFont,
186
+ descender_glyphs: t.Optional[t.List[str]] = None,
187
+ baseline_glyphs: t.Optional[t.List[str]] = None,
188
+ x_height_glyphs: t.Optional[t.List[str]] = None,
189
+ uppercase_glyphs: t.Optional[t.List[str]] = None,
190
+ ascender_glyphs: t.Optional[t.List[str]] = None,
191
+ ) -> t.Tuple[t.List[int], t.List[int]]:
192
+ """
193
+ Recalc Zones
194
+
195
+ Recalculates the zones for a given TTFont object.
196
+
197
+ Parameters:
198
+ font (TTFont): The TTFont object.
199
+ descender_glyphs (List[str]): A list of glyph names to use for calculating the descender
200
+ zone.
201
+ baseline_glyphs (List[str]): A list of glyph names to use for calculating the baseline
202
+ zone.
203
+ x_height_glyphs (List[str]): A list of glyph names to use for calculating the x-height
204
+ zone.
205
+ uppercase_glyphs (List[str]): A list of glyph names to use for calculating the uppercase
206
+ zone.
207
+ ascender_glyphs (List[str]): A list of glyph names to use for calculating the ascender
208
+ zone.
209
+
210
+ Returns:
211
+ Tuple[List[int], List[int]]: A tuple containing two lists. The first list contains the
212
+ values for the OtherBlues zones, and the second list contains the values for the
213
+ BlueValues zones.
214
+ """
215
+
216
+ if descender_glyphs is None:
217
+ descender_glyphs = DESCENDER_GLYPHS
218
+ if baseline_glyphs is None:
219
+ baseline_glyphs = BASELINE_GLYPHS
220
+ if x_height_glyphs is None:
221
+ x_height_glyphs = X_HEIGHT_GLYPHS
222
+ if uppercase_glyphs is None:
223
+ uppercase_glyphs = UPPERCASE_GLYPHS
224
+ if ascender_glyphs is None:
225
+ ascender_glyphs = ASCENDER_GLYPHS
226
+
227
+ descender_zone = calculate_zone(font=font, glyph_names=descender_glyphs, min_or_max="yMin")
228
+ baseline_zone = calculate_zone(font=font, glyph_names=baseline_glyphs, min_or_max="yMin")
229
+ x_height_zone = calculate_zone(font=font, glyph_names=x_height_glyphs, min_or_max="yMax")
230
+ uppercase_zone = calculate_zone(font=font, glyph_names=uppercase_glyphs, min_or_max="yMax")
231
+ ascender_zone = calculate_zone(font=font, glyph_names=ascender_glyphs, min_or_max="yMax")
232
+
233
+ zones = sorted([descender_zone, baseline_zone, x_height_zone, uppercase_zone, ascender_zone])
234
+ if lists_overlaps(zones):
235
+ zones = fix_lists_overlaps(zones)
236
+
237
+ min_separation = font["CFF "].cff.topDictIndex[0].Private.BlueFuzz * 2 + 1
238
+ zones = fix_min_separation_limits(zones, limit=min_separation)
239
+
240
+ other_blues = [int(v) for v in zones[0]]
241
+
242
+ blue_values = []
243
+ for zone in zones[1:]:
244
+ blue_values.extend([int(v) for v in zone])
245
+
246
+ return other_blues, blue_values
@@ -0,0 +1,3 @@
1
+ VERSION = __version__ = "1.1.6"
2
+
3
+ __all__ = ["VERSION"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foundrytools-cli
3
- Version: 1.1.4
3
+ Version: 1.1.6
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
@@ -10,11 +10,11 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.9
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: afdko>=4.0.0
14
- Requires-Dist: fonttools>=4.44.3
13
+ Requires-Dist: afdko>=4.0.1
14
+ Requires-Dist: fonttools>=4.47.2
15
15
  Requires-Dist: brotli>=1.1.0
16
16
  Requires-Dist: click>=8.1.7
17
- Requires-Dist: cffsubr>=0.2.9.post1
17
+ Requires-Dist: cffsubr>=0.3.0
18
18
  Requires-Dist: dehinter>=4.0.0
19
19
  Requires-Dist: loguru>=0.7.2
20
20
  Requires-Dist: pathvalidate>=3.2.0
@@ -50,6 +50,8 @@ foundryToolsCLI/Lib/utils/cli_tools.py
50
50
  foundryToolsCLI/Lib/utils/click_tools.py
51
51
  foundryToolsCLI/Lib/utils/logger.py
52
52
  foundryToolsCLI/Lib/utils/otf_tools.py
53
+ foundryToolsCLI/Lib/utils/ps_recalc_stems.py
54
+ foundryToolsCLI/Lib/utils/ps_recalc_zones.py
53
55
  foundryToolsCLI/Lib/utils/skia_tools.py
54
56
  foundryToolsCLI/Lib/utils/text_tools.py
55
57
  foundryToolsCLI/Lib/utils/timer.py
@@ -1,8 +1,8 @@
1
- afdko>=4.0.0
2
- fonttools>=4.44.3
1
+ afdko>=4.0.1
2
+ fonttools>=4.47.2
3
3
  brotli>=1.1.0
4
4
  click>=8.1.7
5
- cffsubr>=0.2.9.post1
5
+ cffsubr>=0.3.0
6
6
  dehinter>=4.0.0
7
7
  loguru>=0.7.2
8
8
  pathvalidate>=3.2.0
@@ -1,9 +1,9 @@
1
1
  # Hard pinning here gets relaxed to ">=" in setup.py
2
- afdko==4.0.0
3
- fonttools==4.44.3
2
+ afdko==4.0.1
3
+ fonttools==4.47.2
4
4
  brotli==1.1.0
5
5
  click==8.1.7
6
- cffsubr==0.2.9.post1
6
+ cffsubr==0.3.0
7
7
  dehinter==4.0.0
8
8
  loguru==0.7.2
9
9
  pathvalidate==3.2.0
@@ -18,7 +18,7 @@ def _get_requirements():
18
18
 
19
19
  setuptools.setup(
20
20
  name="foundrytools-cli",
21
- version="1.1.4",
21
+ version="1.1.6",
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.4"
2
-
3
- __all__ = ["VERSION"]