NotYAFC 1.0.0__py3-none-any.whl
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.
- font_converter/__init__.py +4 -0
- font_converter/__main__.py +45 -0
- font_converter/assets/ico.png +0 -0
- font_converter/assets/notyafc-1024.png +0 -0
- font_converter/assets/notyafc.ico +0 -0
- font_converter/assets/notyafc.svg +15 -0
- font_converter/cli.py +543 -0
- font_converter/constants.py +27 -0
- font_converter/contours.py +336 -0
- font_converter/css_kit.py +77 -0
- font_converter/finder.py +213 -0
- font_converter/font.py +530 -0
- font_converter/gui/__init__.py +1 -0
- font_converter/gui/conversion_dialog.py +110 -0
- font_converter/gui/file_list.py +411 -0
- font_converter/gui/instance_tree.py +583 -0
- font_converter/gui/logs.py +51 -0
- font_converter/gui/organization_dialog.py +417 -0
- font_converter/gui/profile_options.py +316 -0
- font_converter/gui/profiles.py +17 -0
- font_converter/gui/scanner.py +42 -0
- font_converter/gui/styles.py +658 -0
- font_converter/gui/widgets.py +495 -0
- font_converter/gui/window.py +1768 -0
- font_converter/inspection.py +169 -0
- font_converter/merge_curves.py +499 -0
- font_converter/otf_builder.py +144 -0
- font_converter/qu2cu.py +117 -0
- font_converter/service.py +729 -0
- font_converter/ttf_builder.py +107 -0
- notyafc-1.0.0.dist-info/METADATA +256 -0
- notyafc-1.0.0.dist-info/RECORD +34 -0
- notyafc-1.0.0.dist-info/WHEEL +4 -0
- notyafc-1.0.0.dist-info/entry_points.txt +6 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from fontTools.fontBuilder import FontBuilder
|
|
4
|
+
from fontTools.misc.psCharStrings import T2CharString
|
|
5
|
+
from fontTools.ttLib import TTFont
|
|
6
|
+
|
|
7
|
+
from font_converter.constants import T_CFF, T_HEAD, T_NAME, T_POST
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_otf(
|
|
11
|
+
font: TTFont,
|
|
12
|
+
charstrings_dict: dict[str, T2CharString],
|
|
13
|
+
ps_name: str | None = None,
|
|
14
|
+
font_info: dict[str, Any] | None = None,
|
|
15
|
+
private_dict: dict[str, Any] | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Builds an OpenType-PS font with ``fontTools.fontBuilder.FontBuilder``.
|
|
19
|
+
|
|
20
|
+
:param font: The ``TTFont`` object.
|
|
21
|
+
:type font: TTFont
|
|
22
|
+
:param charstrings_dict: The charstrings dictionary.
|
|
23
|
+
:type charstrings_dict: dict
|
|
24
|
+
:param ps_name: The PostScript name of the font. If None, it will be extracted from the font
|
|
25
|
+
metadata.
|
|
26
|
+
:type ps_name: str, optional
|
|
27
|
+
:param font_info: The font info dictionary. If None, it will be extracted from the font
|
|
28
|
+
metadata.
|
|
29
|
+
:param private_dict: The private dict dictionary. If None, it will be extracted from the font
|
|
30
|
+
metadata.
|
|
31
|
+
:type private_dict: dict, optional
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
if not ps_name:
|
|
35
|
+
ps_name = _get_ps_name(font=font)
|
|
36
|
+
if not font_info:
|
|
37
|
+
font_info = _get_font_info_dict(font=font)
|
|
38
|
+
if not private_dict:
|
|
39
|
+
private_dict = _get_private_dict(font=font)
|
|
40
|
+
|
|
41
|
+
fb = FontBuilder(font=font)
|
|
42
|
+
fb.isTTF = False
|
|
43
|
+
_delete_ttf_tables(font=fb.font)
|
|
44
|
+
fb.setupGlyphOrder(font.getGlyphOrder())
|
|
45
|
+
fb.setupCFF(
|
|
46
|
+
psName=ps_name,
|
|
47
|
+
charStringsDict=charstrings_dict,
|
|
48
|
+
fontInfo=font_info,
|
|
49
|
+
privateDict=private_dict,
|
|
50
|
+
)
|
|
51
|
+
metrics = _get_hmtx_values(font=fb.font, charstrings=charstrings_dict)
|
|
52
|
+
fb.setupHorizontalMetrics(metrics)
|
|
53
|
+
fb.setupDummyDSIG()
|
|
54
|
+
fb.setupMaxp()
|
|
55
|
+
post_values = _get_post_values(font=fb.font)
|
|
56
|
+
fb.setupPost(**post_values)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _delete_ttf_tables(font: TTFont) -> None:
|
|
60
|
+
ttf_tables = ["glyf", "cvt ", "loca", "fpgm", "prep", "gasp", "LTSH", "hdmx"]
|
|
61
|
+
for table in ttf_tables:
|
|
62
|
+
if table in font:
|
|
63
|
+
del font[table]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _get_ps_name(font: TTFont) -> str:
|
|
67
|
+
if T_CFF not in font:
|
|
68
|
+
return font[T_NAME].getDebugName(6)
|
|
69
|
+
|
|
70
|
+
cff_table = font[T_CFF]
|
|
71
|
+
return cff_table.cff.fontNames[0]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_font_info_dict(font: TTFont) -> dict[str, Any]:
|
|
75
|
+
if T_CFF not in font:
|
|
76
|
+
return _build_font_info_dict(font)
|
|
77
|
+
|
|
78
|
+
cff_table = font[T_CFF]
|
|
79
|
+
return {
|
|
80
|
+
key: value
|
|
81
|
+
for key, value in cff_table.cff.topDictIndex[0].rawDict.items()
|
|
82
|
+
if key not in ("FontBBox", "charset", "Encoding", "Private", "CharStrings")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _build_font_info_dict(font: TTFont) -> dict[str, Any]:
|
|
87
|
+
font_revision = str(round(font[T_HEAD].fontRevision, 3)).split(".")
|
|
88
|
+
major_version = str(font_revision[0])
|
|
89
|
+
minor_version = str(font_revision[1]).ljust(3, "0")
|
|
90
|
+
|
|
91
|
+
name_table = font[T_NAME]
|
|
92
|
+
post_table = font[T_POST]
|
|
93
|
+
cff_font_info = {
|
|
94
|
+
"version": ".".join([major_version, str(int(minor_version))]),
|
|
95
|
+
"FullName": name_table.getBestFullName(),
|
|
96
|
+
"FamilyName": name_table.getBestFamilyName(),
|
|
97
|
+
"ItalicAngle": post_table.italicAngle,
|
|
98
|
+
"UnderlinePosition": post_table.underlinePosition,
|
|
99
|
+
"UnderlineThickness": post_table.underlineThickness,
|
|
100
|
+
"isFixedPitch": bool(post_table.isFixedPitch),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return cff_font_info
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _get_private_dict(font: TTFont) -> dict[str, Any]:
|
|
107
|
+
if T_CFF not in font:
|
|
108
|
+
return {}
|
|
109
|
+
|
|
110
|
+
cff_table = font[T_CFF]
|
|
111
|
+
return {
|
|
112
|
+
key: value
|
|
113
|
+
for key, value in cff_table.cff.topDictIndex[0].Private.rawDict.items()
|
|
114
|
+
if key not in ("Subrs", "defaultWidthX", "nominalWidthX")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _get_hmtx_values(
|
|
119
|
+
font: TTFont, charstrings: dict[str, T2CharString]
|
|
120
|
+
) -> dict[str, tuple[int, int]]:
|
|
121
|
+
glyph_set = font.getGlyphSet()
|
|
122
|
+
advance_widths = {k: v.width for k, v in glyph_set.items()}
|
|
123
|
+
lsb = {}
|
|
124
|
+
for gn, cs in charstrings.items():
|
|
125
|
+
lsb[gn] = cs.calcBounds(None)[0] if cs.calcBounds(None) is not None else 0
|
|
126
|
+
metrics = {}
|
|
127
|
+
for gn, advance_width in advance_widths.items():
|
|
128
|
+
metrics[gn] = (advance_width, lsb[gn])
|
|
129
|
+
return metrics
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _get_post_values(font: TTFont) -> dict[str, Any]:
|
|
133
|
+
post_table = font[T_POST]
|
|
134
|
+
post_info = {
|
|
135
|
+
"italicAngle": round(post_table.italicAngle),
|
|
136
|
+
"underlinePosition": post_table.underlinePosition,
|
|
137
|
+
"underlineThickness": post_table.underlineThickness,
|
|
138
|
+
"isFixedPitch": post_table.isFixedPitch,
|
|
139
|
+
"minMemType42": post_table.minMemType42,
|
|
140
|
+
"maxMemType42": post_table.maxMemType42,
|
|
141
|
+
"minMemType1": post_table.minMemType1,
|
|
142
|
+
"maxMemType1": post_table.maxMemType1,
|
|
143
|
+
}
|
|
144
|
+
return post_info
|
font_converter/qu2cu.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
|
|
3
|
+
import pathops
|
|
4
|
+
from fontTools.cffLib import PrivateDict
|
|
5
|
+
from fontTools.misc.psCharStrings import T2CharString
|
|
6
|
+
from fontTools.pens.cu2quPen import Cu2QuPen
|
|
7
|
+
from fontTools.pens.qu2cuPen import Qu2CuPen
|
|
8
|
+
from fontTools.pens.t2CharStringPen import T2CharStringPen
|
|
9
|
+
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
|
10
|
+
from fontTools.ttLib import TTFont
|
|
11
|
+
from fontTools.ttLib.ttGlyphSet import _TTGlyph
|
|
12
|
+
|
|
13
|
+
from font_converter.contours import simplify_path
|
|
14
|
+
from font_converter.merge_curves import merge_cubic_segments
|
|
15
|
+
|
|
16
|
+
__all__ = ["quadratics_to_cubics", "quadratics_to_cubics_2"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_TTGlyphMapping = Mapping[str, _TTGlyph]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _skia_path_from_charstring(charstring: T2CharString) -> pathops.Path:
|
|
23
|
+
path = pathops.Path()
|
|
24
|
+
path_pen = path.getPen(glyphSet=None)
|
|
25
|
+
charstring.draw(path_pen)
|
|
26
|
+
return path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _charstring_from_skia_path(path: pathops.Path, width: int) -> T2CharString:
|
|
30
|
+
t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
|
31
|
+
path.draw(t2_pen)
|
|
32
|
+
return t2_pen.getCharString()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def quadratics_to_cubics(
|
|
36
|
+
font: TTFont, tolerance: float = 1.0, optimize_contours: int = 0
|
|
37
|
+
) -> dict[str, T2CharString]:
|
|
38
|
+
"""
|
|
39
|
+
Converts quadratic Bézier splines to cubic curves with a specified tolerance using the
|
|
40
|
+
``Qu2CuPen``, then removes overlaps and corrects the winding direction of the contours.
|
|
41
|
+
|
|
42
|
+
:param font: The TTFont object representing the font to process.
|
|
43
|
+
:type font: TTFont
|
|
44
|
+
:param tolerance: The maximum acceptable deviation in font units when approximating the
|
|
45
|
+
quadratic Bézier curves. Default is 1.0.
|
|
46
|
+
:type tolerance: float
|
|
47
|
+
:param optimize_contours: Merges adjacent smooth cubic segments to reduce the number of
|
|
48
|
+
nodes. The value is the maximum deviation of a merge, as a multiple of ``tolerance``;
|
|
49
|
+
0 (default) disables the optimization.
|
|
50
|
+
:type optimize_contours: int
|
|
51
|
+
:return: A dictionary mapping glyph names to their corresponding T2CharString objects after
|
|
52
|
+
conversion.
|
|
53
|
+
:rtype: dict[str, T2CharString]
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
qu2cu_charstrings = {}
|
|
57
|
+
glyph_set = font.getGlyphSet()
|
|
58
|
+
|
|
59
|
+
for k, v in glyph_set.items():
|
|
60
|
+
width = v.width
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
t2_pen = T2CharStringPen(width=width, glyphSet={k: v})
|
|
64
|
+
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
|
|
65
|
+
glyph_set[k].draw(qu2cu_pen)
|
|
66
|
+
qu2cu_charstrings[k] = t2_pen.getCharString()
|
|
67
|
+
|
|
68
|
+
except NotImplementedError:
|
|
69
|
+
temp_t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
|
70
|
+
glyph_set[k].draw(temp_t2_pen)
|
|
71
|
+
t2_charstring = temp_t2_pen.getCharString()
|
|
72
|
+
t2_charstring.private = PrivateDict()
|
|
73
|
+
|
|
74
|
+
tt_pen = TTGlyphPen(glyphSet=None)
|
|
75
|
+
cu2qu_pen = Cu2QuPen(other_pen=tt_pen, max_err=tolerance, reverse_direction=False)
|
|
76
|
+
t2_charstring.draw(cu2qu_pen)
|
|
77
|
+
tt_glyph = tt_pen.glyph()
|
|
78
|
+
|
|
79
|
+
t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
|
80
|
+
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
|
|
81
|
+
tt_glyph.draw(pen=qu2cu_pen, glyfTable=None)
|
|
82
|
+
|
|
83
|
+
charstring = t2_pen.getCharString()
|
|
84
|
+
charstring.private = PrivateDict()
|
|
85
|
+
path = _skia_path_from_charstring(charstring)
|
|
86
|
+
simplified_path = simplify_path(path, glyph_name=k, clockwise=False)
|
|
87
|
+
if optimize_contours > 0:
|
|
88
|
+
simplified_path = merge_cubic_segments(
|
|
89
|
+
simplified_path,
|
|
90
|
+
tolerance=tolerance * optimize_contours,
|
|
91
|
+
min_extremum_distance=tolerance,
|
|
92
|
+
)
|
|
93
|
+
qu2cu_charstrings[k] = _charstring_from_skia_path(path=simplified_path, width=width)
|
|
94
|
+
|
|
95
|
+
return qu2cu_charstrings
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def quadratics_to_cubics_2(font: TTFont) -> dict[str, T2CharString]:
|
|
99
|
+
"""
|
|
100
|
+
Converts quadratic Bézier splines to cubic curves using the ``T2CharStringPen``.
|
|
101
|
+
|
|
102
|
+
:param font: The ``TTFont`` object representing the font to process.
|
|
103
|
+
:type font: TTFont
|
|
104
|
+
:return: A dictionary mapping glyph names to their corresponding T2CharString objects after
|
|
105
|
+
conversion.
|
|
106
|
+
:rtype: dict[str, T2CharString]
|
|
107
|
+
"""
|
|
108
|
+
t2_charstrings = {}
|
|
109
|
+
glyph_set = font.getGlyphSet()
|
|
110
|
+
|
|
111
|
+
for k, v in glyph_set.items():
|
|
112
|
+
t2_pen = T2CharStringPen(v.width, glyphSet=glyph_set)
|
|
113
|
+
glyph_set[k].draw(t2_pen)
|
|
114
|
+
charstring = t2_pen.getCharString()
|
|
115
|
+
t2_charstrings[k] = charstring
|
|
116
|
+
|
|
117
|
+
return t2_charstrings
|