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.
- {foundrytools-cli-1.1.4/foundrytools_cli.egg-info → foundrytools-cli-1.1.6}/PKG-INFO +4 -4
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_cff.py +1 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_otf.py +1 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/Font.py +51 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttf_to_otf.py +7 -0
- foundrytools-cli-1.1.6/foundryToolsCLI/Lib/utils/ps_recalc_stems.py +92 -0
- foundrytools-cli-1.1.6/foundryToolsCLI/Lib/utils/ps_recalc_zones.py +246 -0
- foundrytools-cli-1.1.6/foundryToolsCLI/__init__.py +3 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6/foundrytools_cli.egg-info}/PKG-INFO +4 -4
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/SOURCES.txt +2 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/requires.txt +3 -3
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/requirements.txt +3 -3
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/setup.py +1 -1
- foundrytools-cli-1.1.4/foundryToolsCLI/__init__.py +0 -3
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/LICENSE +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/MANIFEST.in +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/README.md +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_assistant.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_converter.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_fix.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_hhea.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_metrics.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_name.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_os2.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_post.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_print.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_ttf.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/CLI/ftcli_utils.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/VFont.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/UI.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/fonts_data.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/styles_mapping.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/constants.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/options.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/otf_to_ttf.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/sfnt_to_web.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttc_to_sfnt.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/variable_to_static.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/web_to_sfnt.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/printer/UI.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/printer/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/CFF_.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/OS_2.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/head.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/hhea.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/name.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/tables/post.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/__init__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/bits_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/cli_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/click_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/logger.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/otf_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/skia_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/text_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/timer.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/utils/ttf_tools.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/__main__.py +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/dependency_links.txt +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/entry_points.txt +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/not-zip-safe +0 -0
- {foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/top_level.txt +0 -0
- {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.
|
|
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.
|
|
14
|
-
Requires-Dist: fonttools>=4.
|
|
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.
|
|
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:
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttf_to_otf.py
RENAMED
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: foundrytools-cli
|
|
3
|
-
Version: 1.1.
|
|
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.
|
|
14
|
-
Requires-Dist: fonttools>=4.
|
|
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.
|
|
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
|
|
@@ -18,7 +18,7 @@ def _get_requirements():
|
|
|
18
18
|
|
|
19
19
|
setuptools.setup(
|
|
20
20
|
name="foundrytools-cli",
|
|
21
|
-
version="1.1.
|
|
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",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/fonts_data.py
RENAMED
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/assistant/styles_mapping.py
RENAMED
|
File without changes
|
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/otf_to_ttf.py
RENAMED
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/sfnt_to_web.py
RENAMED
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/ttc_to_sfnt.py
RENAMED
|
File without changes
|
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundryToolsCLI/Lib/converters/web_to_sfnt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{foundrytools-cli-1.1.4 → foundrytools-cli-1.1.6}/foundrytools_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|