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,45 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from PySide6.QtCore import QSize
|
|
7
|
+
from PySide6.QtGui import QFontDatabase, QIcon
|
|
8
|
+
from PySide6.QtWidgets import QApplication
|
|
9
|
+
|
|
10
|
+
from font_converter.gui.window import ConverterWindow
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _app_icon() -> QIcon:
|
|
14
|
+
assets = Path(__file__).parent / "assets"
|
|
15
|
+
ico = assets / "notyafc.ico"
|
|
16
|
+
if not ico.is_file():
|
|
17
|
+
return QIcon(str(assets / "ico.png"))
|
|
18
|
+
icon = QIcon(str(ico))
|
|
19
|
+
icon.addFile(str(assets / "ico.png"), QSize(512, 512))
|
|
20
|
+
return icon
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _set_windows_app_id() -> None:
|
|
24
|
+
if sys.platform != "win32":
|
|
25
|
+
return
|
|
26
|
+
try:
|
|
27
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("NotYAFC.NotYAFC")
|
|
28
|
+
except (AttributeError, OSError) as error:
|
|
29
|
+
logging.getLogger(__name__).warning("Could not set Windows application ID: %s", error)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main() -> int:
|
|
33
|
+
_set_windows_app_id()
|
|
34
|
+
app = QApplication(sys.argv)
|
|
35
|
+
app.setOrganizationName("NotYAFC")
|
|
36
|
+
app.setApplicationName("NotYAFC")
|
|
37
|
+
app.setWindowIcon(_app_icon())
|
|
38
|
+
app.setFont(QFontDatabase.systemFont(QFontDatabase.SystemFont.GeneralFont))
|
|
39
|
+
window = ConverterWindow()
|
|
40
|
+
window.showMaximized()
|
|
41
|
+
return app.exec()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
sys.exit(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpjYzQ4OWVmMS1kOGEzLTRjYTYtYThlOS0xZjE4NGRjNjUyNmUAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaPEONugsDLkGuGU7yvHPVFYAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo5NjUwOWZkYy1iNWUzLTRiNTAtOTlmNC1kZmNlMmRhZmI3NGNscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNoPfljf95yseC9xLTa1+2zSgAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggR6sDXTs+F4U0QOp+lSNik9swNIYuZANnH+1xhYjBErWkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaIuoYu6lm/dgvLRKcK9TyS4AAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCDlyX1bThWxU0YT5LWDQ7LkITlvOlOaGA1YoQktirevqGRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBiaZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmNjNDg5ZWYxLWQ4YTMtNGNhNi1hOGU5LTFmMTg0ZGM2NTI2ZS9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjQ5NGNhYWE3LThjZDUtNGFiOS1hMTkzLWQ4MDc0MzI1NDU3NnJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCBHqwNdOz4XhTRA6n6VI2KT2zA0hi5kA2cf7XGFiMEStaJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggmEWS/YR/gNLZM5qevdDdGtA45EB+5AmTTSyObAC7b/aiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggtkavY5cI9aQanXzT3brr9gVuj4jPPzoPXHgPymQEDa10Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQL7LTTOleUmhYPphPT/eLAhTPeLriEKfSXYoh8WYrm+liAFnzFO912ZTtZFrXyMUXZyYvWwPdmWJuPE7t8mlbg8=</c2pa:manifest></metadata>
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#5875ff"/>
|
|
5
|
+
<stop offset="1" stop-color="#3451d1"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="24" y="24" width="976" height="976" rx="224" fill="url(#bg)"/>
|
|
9
|
+
<path fill="#ffffff" d="M464.9 811.0Q392.4 811.0 345.2 786.9Q298.1 762.7 275.1 723.0Q252.1 683.4 252.1 636.2Q252.1 553.4 316.5 505.1Q380.9 456.8 500.5 456.8H650.0V446.5Q650.0 379.8 613.8 346.4Q577.5 313.1 520.0 313.1Q469.4 313.1 432.1 337.8Q394.7 362.5 386.6 410.8H264.8Q270.5 348.7 306.7 304.4Q343.0 260.2 398.7 236.6Q454.5 213.0 521.2 213.0Q640.8 213.0 706.3 275.7Q771.9 338.4 771.9 446.5V797.2H666.1L655.8 699.5Q631.6 746.6 585.6 778.8Q539.6 811.0 464.9 811.0ZM489.0 712.1Q538.5 712.1 572.4 688.5Q606.3 665.0 625.3 625.9Q644.2 586.8 648.8 539.6H513.1Q440.7 539.6 410.2 564.9Q379.8 590.2 379.8 628.2Q379.8 667.2 409.1 689.7Q438.4 712.1 489.0 712.1Z"/>
|
|
10
|
+
<line x1="371.2" y1="213.0" x2="671.2" y2="213.0" stroke="#ffb703" stroke-width="14"/>
|
|
11
|
+
<circle cx="371.2" cy="213.0" r="24" fill="#ffb703"/>
|
|
12
|
+
<circle cx="671.2" cy="213.0" r="24" fill="#ffb703"/>
|
|
13
|
+
<circle cx="521.2" cy="213.0" r="59" fill="#ffffff"/>
|
|
14
|
+
<circle cx="521.2" cy="213.0" r="46" fill="#ffb703"/>
|
|
15
|
+
</svg>
|
font_converter/cli.py
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
"""Command-line interface for converting fonts."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from font_converter.inspection import inspect_font
|
|
9
|
+
from font_converter.service import (
|
|
10
|
+
ConversionRequest,
|
|
11
|
+
ConversionResult,
|
|
12
|
+
ConversionSettings,
|
|
13
|
+
ConverterService,
|
|
14
|
+
FontOutcome,
|
|
15
|
+
FontOutcomeKind,
|
|
16
|
+
InstanceSpec,
|
|
17
|
+
OtfToTtfOptions,
|
|
18
|
+
SfntToWebOptions,
|
|
19
|
+
TtcToSfntOptions,
|
|
20
|
+
TtfToOtfOptions,
|
|
21
|
+
VarToStaticOptions,
|
|
22
|
+
Woff2ToSfntOptions,
|
|
23
|
+
WoffToSfntOptions,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
app = typer.Typer(
|
|
27
|
+
no_args_is_help=True,
|
|
28
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
29
|
+
help="Convert font files from the command line.",
|
|
30
|
+
rich_markup_mode="markdown",
|
|
31
|
+
pretty_exceptions_enable=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
Inputs = Annotated[
|
|
35
|
+
list[Path],
|
|
36
|
+
typer.Argument(help="Input font files or folders. Folder scans are non-recursive by default."),
|
|
37
|
+
]
|
|
38
|
+
OutputDir = Annotated[
|
|
39
|
+
Path | None,
|
|
40
|
+
typer.Option(
|
|
41
|
+
"--output-dir",
|
|
42
|
+
help="Folder for converted files (defaults to each input folder).",
|
|
43
|
+
rich_help_panel="General options",
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
Recursive = Annotated[
|
|
47
|
+
bool,
|
|
48
|
+
typer.Option(
|
|
49
|
+
"--recursive/--no-recursive",
|
|
50
|
+
help="Scan input folders recursively.",
|
|
51
|
+
rich_help_panel="General options",
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
Overwrite = Annotated[
|
|
55
|
+
bool,
|
|
56
|
+
typer.Option(
|
|
57
|
+
"--overwrite/--no-overwrite",
|
|
58
|
+
help="Replace existing output files (default: keep existing files with numbered copies).",
|
|
59
|
+
rich_help_panel="General options",
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
RecalcBboxes = Annotated[
|
|
63
|
+
bool,
|
|
64
|
+
typer.Option(
|
|
65
|
+
"--recalc-bboxes/--no-recalc-bboxes",
|
|
66
|
+
help="Recalculate glyph bounding boxes.",
|
|
67
|
+
rich_help_panel="General options",
|
|
68
|
+
),
|
|
69
|
+
]
|
|
70
|
+
RecalcTimestamp = Annotated[
|
|
71
|
+
bool,
|
|
72
|
+
typer.Option(
|
|
73
|
+
"--recalc-timestamp/--no-recalc-timestamp",
|
|
74
|
+
help="Set a fresh modified timestamp on output fonts.",
|
|
75
|
+
rich_help_panel="General options",
|
|
76
|
+
),
|
|
77
|
+
]
|
|
78
|
+
ReorderTables = Annotated[
|
|
79
|
+
bool,
|
|
80
|
+
typer.Option(
|
|
81
|
+
"--reorder-tables/--no-reorder-tables",
|
|
82
|
+
help="Reorder tables when saving fonts.",
|
|
83
|
+
rich_help_panel="General options",
|
|
84
|
+
),
|
|
85
|
+
]
|
|
86
|
+
Tolerance = Annotated[
|
|
87
|
+
float,
|
|
88
|
+
typer.Option(
|
|
89
|
+
"--tolerance",
|
|
90
|
+
min=0,
|
|
91
|
+
max=3,
|
|
92
|
+
help="Curve-conversion tolerance.",
|
|
93
|
+
rich_help_panel="OTF to TTF options",
|
|
94
|
+
),
|
|
95
|
+
]
|
|
96
|
+
TtfToOtfTolerance = Annotated[
|
|
97
|
+
float,
|
|
98
|
+
typer.Option(
|
|
99
|
+
"--tolerance",
|
|
100
|
+
min=0.1,
|
|
101
|
+
max=3,
|
|
102
|
+
help="Curve-conversion tolerance (minimum 0.1).",
|
|
103
|
+
rich_help_panel="TTF to OTF options",
|
|
104
|
+
),
|
|
105
|
+
]
|
|
106
|
+
TargetUpmTtfToOtf = Annotated[
|
|
107
|
+
int | None,
|
|
108
|
+
typer.Option(
|
|
109
|
+
"--target-upm",
|
|
110
|
+
min=16,
|
|
111
|
+
max=16384,
|
|
112
|
+
help="Scale output fonts to this units-per-em value.",
|
|
113
|
+
rich_help_panel="TTF to OTF options",
|
|
114
|
+
),
|
|
115
|
+
]
|
|
116
|
+
TargetUpmOtfToTtf = Annotated[
|
|
117
|
+
int | None,
|
|
118
|
+
typer.Option(
|
|
119
|
+
"--target-upm",
|
|
120
|
+
min=16,
|
|
121
|
+
max=16384,
|
|
122
|
+
help="Scale output fonts to this units-per-em value.",
|
|
123
|
+
rich_help_panel="OTF to TTF options",
|
|
124
|
+
),
|
|
125
|
+
]
|
|
126
|
+
TargetUpmVarToStatic = Annotated[
|
|
127
|
+
int | None,
|
|
128
|
+
typer.Option(
|
|
129
|
+
"--target-upm",
|
|
130
|
+
min=16,
|
|
131
|
+
max=16384,
|
|
132
|
+
help="Scale output fonts to this units-per-em value.",
|
|
133
|
+
rich_help_panel="Variable to static options",
|
|
134
|
+
),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _common_options(
|
|
139
|
+
output_dir: Path | None,
|
|
140
|
+
recursive: bool,
|
|
141
|
+
overwrite: bool,
|
|
142
|
+
recalc_bboxes: bool,
|
|
143
|
+
recalc_timestamp: bool,
|
|
144
|
+
reorder_tables: bool,
|
|
145
|
+
) -> dict[str, Any]:
|
|
146
|
+
return {
|
|
147
|
+
"output_dir": output_dir,
|
|
148
|
+
"recursive": recursive,
|
|
149
|
+
"overwrite": overwrite,
|
|
150
|
+
"recalc_bboxes": recalc_bboxes,
|
|
151
|
+
"recalc_timestamp": recalc_timestamp,
|
|
152
|
+
"reorder_tables": reorder_tables,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _input_files(inputs: tuple[Path, ...], recursive: bool) -> list[Path]:
|
|
157
|
+
files: dict[Path, None] = {}
|
|
158
|
+
for source in inputs:
|
|
159
|
+
if source.is_file():
|
|
160
|
+
files[source.resolve()] = None
|
|
161
|
+
elif source.is_dir():
|
|
162
|
+
paths = source.rglob("*") if recursive else source.glob("*")
|
|
163
|
+
files.update((path.resolve(), None) for path in paths if path.is_file())
|
|
164
|
+
return list(files)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _named_instances(
|
|
168
|
+
inputs: tuple[Path, ...], recursive: bool
|
|
169
|
+
) -> tuple[tuple[tuple[Path, tuple[InstanceSpec, ...]], ...], frozenset[Path]]:
|
|
170
|
+
selected: list[tuple[Path, tuple[InstanceSpec, ...]]] = []
|
|
171
|
+
default_paths: set[Path] = set()
|
|
172
|
+
for path in _input_files(inputs, recursive):
|
|
173
|
+
info = inspect_font(path)
|
|
174
|
+
if info is None or not info.variable:
|
|
175
|
+
continue
|
|
176
|
+
specs = tuple(
|
|
177
|
+
InstanceSpec(instance.coordinates, instance.name) for instance in info.instances
|
|
178
|
+
)
|
|
179
|
+
if specs:
|
|
180
|
+
selected.append((path, specs))
|
|
181
|
+
else:
|
|
182
|
+
selected.append((path, (InstanceSpec(()),)))
|
|
183
|
+
default_paths.add(path)
|
|
184
|
+
typer.echo(
|
|
185
|
+
f"Warning: {path.name} has no named instances; exporting its default instance.",
|
|
186
|
+
err=True,
|
|
187
|
+
)
|
|
188
|
+
return tuple(selected), frozenset(default_paths)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _report_outcome(outcome: FontOutcome, default_paths: frozenset[Path]) -> None:
|
|
192
|
+
if outcome.kind is FontOutcomeKind.CONVERTED:
|
|
193
|
+
for output in outcome.output_paths:
|
|
194
|
+
typer.echo(f"Converted: {outcome.input_path} -> {output}", err=True)
|
|
195
|
+
elif outcome.kind is FontOutcomeKind.FAILED:
|
|
196
|
+
typer.echo(f"Failed: {outcome.input_path}: {outcome.error}", err=True)
|
|
197
|
+
if outcome.input_path.resolve() in default_paths:
|
|
198
|
+
typer.echo(
|
|
199
|
+
"The default instance could not be exported; continuing with the next font.",
|
|
200
|
+
err=True,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _run(
|
|
205
|
+
conversion: str,
|
|
206
|
+
inputs: list[Path],
|
|
207
|
+
output_dir: Path | None,
|
|
208
|
+
recursive: bool,
|
|
209
|
+
overwrite: bool,
|
|
210
|
+
recalc_bboxes: bool,
|
|
211
|
+
recalc_timestamp: bool,
|
|
212
|
+
reorder_tables: bool,
|
|
213
|
+
settings_type: type[ConversionSettings],
|
|
214
|
+
**profile_options: Any,
|
|
215
|
+
) -> None:
|
|
216
|
+
input_paths = tuple(path.resolve() for path in inputs)
|
|
217
|
+
common = _common_options(
|
|
218
|
+
output_dir, recursive, overwrite, recalc_bboxes, recalc_timestamp, reorder_tables
|
|
219
|
+
)
|
|
220
|
+
default_paths: frozenset[Path] = frozenset()
|
|
221
|
+
if conversion == "var2static":
|
|
222
|
+
instances, default_paths = _named_instances(input_paths, recursive)
|
|
223
|
+
profile_options["instances"] = instances
|
|
224
|
+
|
|
225
|
+
settings = settings_type(**common, **profile_options)
|
|
226
|
+
request = ConversionRequest(input_paths, settings)
|
|
227
|
+
|
|
228
|
+
def report_progress(progress: Any) -> None:
|
|
229
|
+
typer.echo(
|
|
230
|
+
f"\rProgress: {progress.done}/{progress.total}",
|
|
231
|
+
err=True,
|
|
232
|
+
nl=progress.done == progress.total,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
typer.echo(f"Starting {conversion} conversion for {len(input_paths)} input(s).", err=True)
|
|
236
|
+
result: ConversionResult = ConverterService().convert(
|
|
237
|
+
request,
|
|
238
|
+
on_log=lambda message: typer.echo(message, err=True),
|
|
239
|
+
on_outcome=lambda outcome: _report_outcome(outcome, default_paths),
|
|
240
|
+
on_progress=report_progress,
|
|
241
|
+
)
|
|
242
|
+
if result.error:
|
|
243
|
+
typer.echo(f"Error: {result.error}", err=True)
|
|
244
|
+
typer.echo(
|
|
245
|
+
f"Summary: {result.converted_count} converted, {result.skipped_count} skipped, "
|
|
246
|
+
f"{result.failed_count} failed.",
|
|
247
|
+
)
|
|
248
|
+
if not result.ok:
|
|
249
|
+
raise typer.Exit(code=1)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@app.command("ttf2otf")
|
|
253
|
+
def ttf2otf(
|
|
254
|
+
inputs: Inputs,
|
|
255
|
+
tolerance: TtfToOtfTolerance = 1.0,
|
|
256
|
+
target_upm: TargetUpmTtfToOtf = None,
|
|
257
|
+
subroutinize: Annotated[
|
|
258
|
+
bool,
|
|
259
|
+
typer.Option(
|
|
260
|
+
"--subroutinize/--no-subroutinize",
|
|
261
|
+
help="Subroutinize CFF outlines.",
|
|
262
|
+
rich_help_panel="TTF to OTF options",
|
|
263
|
+
),
|
|
264
|
+
] = True,
|
|
265
|
+
optimize_contours: Annotated[
|
|
266
|
+
int,
|
|
267
|
+
typer.Option(
|
|
268
|
+
"--optimize-contours",
|
|
269
|
+
min=0,
|
|
270
|
+
max=3,
|
|
271
|
+
help="Contour simplification level (0 disables it).",
|
|
272
|
+
rich_help_panel="TTF to OTF options",
|
|
273
|
+
),
|
|
274
|
+
] = 0,
|
|
275
|
+
output_dir: OutputDir = None,
|
|
276
|
+
recursive: Recursive = False,
|
|
277
|
+
overwrite: Overwrite = False,
|
|
278
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
279
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
280
|
+
reorder_tables: ReorderTables = True,
|
|
281
|
+
) -> None:
|
|
282
|
+
"""Convert static TrueType outlines to PostScript outlines (TTF to OTF)."""
|
|
283
|
+
_run(
|
|
284
|
+
"ttf2otf",
|
|
285
|
+
inputs,
|
|
286
|
+
output_dir,
|
|
287
|
+
recursive,
|
|
288
|
+
overwrite,
|
|
289
|
+
recalc_bboxes,
|
|
290
|
+
recalc_timestamp,
|
|
291
|
+
reorder_tables,
|
|
292
|
+
TtfToOtfOptions,
|
|
293
|
+
tolerance=tolerance,
|
|
294
|
+
target_upm=target_upm,
|
|
295
|
+
subroutinize=subroutinize,
|
|
296
|
+
optimize_contours=optimize_contours,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@app.command("otf2ttf")
|
|
301
|
+
def otf2ttf(
|
|
302
|
+
inputs: Inputs,
|
|
303
|
+
tolerance: Tolerance = 1.0,
|
|
304
|
+
target_upm: TargetUpmOtfToTtf = None,
|
|
305
|
+
autohint: Annotated[
|
|
306
|
+
bool,
|
|
307
|
+
typer.Option(
|
|
308
|
+
"--autohint/--no-autohint",
|
|
309
|
+
help="Add TrueType hinting instructions.",
|
|
310
|
+
rich_help_panel="OTF to TTF options",
|
|
311
|
+
),
|
|
312
|
+
] = True,
|
|
313
|
+
output_dir: OutputDir = None,
|
|
314
|
+
recursive: Recursive = False,
|
|
315
|
+
overwrite: Overwrite = False,
|
|
316
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
317
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
318
|
+
reorder_tables: ReorderTables = True,
|
|
319
|
+
) -> None:
|
|
320
|
+
"""Convert static PostScript outlines to TrueType outlines (OTF to TTF)."""
|
|
321
|
+
_run(
|
|
322
|
+
"otf2ttf",
|
|
323
|
+
inputs,
|
|
324
|
+
output_dir,
|
|
325
|
+
recursive,
|
|
326
|
+
overwrite,
|
|
327
|
+
recalc_bboxes,
|
|
328
|
+
recalc_timestamp,
|
|
329
|
+
reorder_tables,
|
|
330
|
+
OtfToTtfOptions,
|
|
331
|
+
tolerance=tolerance,
|
|
332
|
+
target_upm=target_upm,
|
|
333
|
+
autohint=autohint,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@app.command("ft2wf")
|
|
338
|
+
def ft2wf(
|
|
339
|
+
inputs: Inputs,
|
|
340
|
+
woff: Annotated[
|
|
341
|
+
bool,
|
|
342
|
+
typer.Option(
|
|
343
|
+
"--woff/--no-woff",
|
|
344
|
+
help="Create WOFF output.",
|
|
345
|
+
rich_help_panel="SFNT to WOFF options",
|
|
346
|
+
),
|
|
347
|
+
] = True,
|
|
348
|
+
woff2: Annotated[
|
|
349
|
+
bool,
|
|
350
|
+
typer.Option(
|
|
351
|
+
"--woff2/--no-woff2",
|
|
352
|
+
help="Create WOFF2 output.",
|
|
353
|
+
rich_help_panel="SFNT to WOFF options",
|
|
354
|
+
),
|
|
355
|
+
] = True,
|
|
356
|
+
obfuscate_names: Annotated[
|
|
357
|
+
bool,
|
|
358
|
+
typer.Option(
|
|
359
|
+
"--obfuscate-names/--no-obfuscate-names",
|
|
360
|
+
help="Replace identifying font names with hashes.",
|
|
361
|
+
rich_help_panel="SFNT to WOFF options",
|
|
362
|
+
),
|
|
363
|
+
] = False,
|
|
364
|
+
remove_unused_glyphs: Annotated[
|
|
365
|
+
bool,
|
|
366
|
+
typer.Option(
|
|
367
|
+
"--remove-unused-glyphs/--no-remove-unused-glyphs",
|
|
368
|
+
help="Remove glyphs unused by the cmap and OpenType features.",
|
|
369
|
+
rich_help_panel="SFNT to WOFF options",
|
|
370
|
+
),
|
|
371
|
+
] = False,
|
|
372
|
+
remove_non_windows_names: Annotated[
|
|
373
|
+
bool,
|
|
374
|
+
typer.Option(
|
|
375
|
+
"--remove-non-windows-names/--no-remove-non-windows-names",
|
|
376
|
+
help="Remove name records whose platform ID is not Windows.",
|
|
377
|
+
rich_help_panel="SFNT to WOFF options",
|
|
378
|
+
),
|
|
379
|
+
] = False,
|
|
380
|
+
remove_dsig: Annotated[
|
|
381
|
+
bool,
|
|
382
|
+
typer.Option(
|
|
383
|
+
"--remove-dsig/--no-remove-dsig",
|
|
384
|
+
help="Remove the DSIG table from WOFF output.",
|
|
385
|
+
rich_help_panel="SFNT to WOFF options",
|
|
386
|
+
),
|
|
387
|
+
] = False,
|
|
388
|
+
css_kit: Annotated[
|
|
389
|
+
bool,
|
|
390
|
+
typer.Option(
|
|
391
|
+
"--css-kit/--no-css-kit",
|
|
392
|
+
help="Generate fonts.css.",
|
|
393
|
+
rich_help_panel="SFNT to WOFF options",
|
|
394
|
+
),
|
|
395
|
+
] = False,
|
|
396
|
+
output_dir: OutputDir = None,
|
|
397
|
+
recursive: Recursive = False,
|
|
398
|
+
overwrite: Overwrite = False,
|
|
399
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
400
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
401
|
+
reorder_tables: ReorderTables = True,
|
|
402
|
+
) -> None:
|
|
403
|
+
"""Convert SFNT fonts (TTF or OTF) to WOFF and/or WOFF2."""
|
|
404
|
+
if not woff and not woff2:
|
|
405
|
+
raise typer.BadParameter("Select at least one output format with --woff or --woff2.")
|
|
406
|
+
_run(
|
|
407
|
+
"ft2wf",
|
|
408
|
+
inputs,
|
|
409
|
+
output_dir,
|
|
410
|
+
recursive,
|
|
411
|
+
overwrite,
|
|
412
|
+
recalc_bboxes,
|
|
413
|
+
recalc_timestamp,
|
|
414
|
+
reorder_tables,
|
|
415
|
+
SfntToWebOptions,
|
|
416
|
+
format="woff" if woff and not woff2 else "woff2" if woff2 and not woff else None,
|
|
417
|
+
obfuscate_names=obfuscate_names,
|
|
418
|
+
remove_unused_glyphs=remove_unused_glyphs,
|
|
419
|
+
remove_non_windows_names=remove_non_windows_names,
|
|
420
|
+
remove_dsig=remove_dsig and woff,
|
|
421
|
+
css_kit=css_kit,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@app.command("woff2ft")
|
|
426
|
+
def woff2ft(
|
|
427
|
+
inputs: Inputs,
|
|
428
|
+
output_dir: OutputDir = None,
|
|
429
|
+
recursive: Recursive = False,
|
|
430
|
+
overwrite: Overwrite = False,
|
|
431
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
432
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
433
|
+
reorder_tables: ReorderTables = True,
|
|
434
|
+
) -> None:
|
|
435
|
+
"""Convert WOFF fonts back to SFNT (TTF or OTF)."""
|
|
436
|
+
_run(
|
|
437
|
+
"woff2ft",
|
|
438
|
+
inputs,
|
|
439
|
+
output_dir,
|
|
440
|
+
recursive,
|
|
441
|
+
overwrite,
|
|
442
|
+
recalc_bboxes,
|
|
443
|
+
recalc_timestamp,
|
|
444
|
+
reorder_tables,
|
|
445
|
+
WoffToSfntOptions,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@app.command("woff22ft")
|
|
450
|
+
def woff22ft(
|
|
451
|
+
inputs: Inputs,
|
|
452
|
+
output_dir: OutputDir = None,
|
|
453
|
+
recursive: Recursive = False,
|
|
454
|
+
overwrite: Overwrite = False,
|
|
455
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
456
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
457
|
+
reorder_tables: ReorderTables = True,
|
|
458
|
+
) -> None:
|
|
459
|
+
"""Convert WOFF2 fonts back to SFNT (TTF or OTF)."""
|
|
460
|
+
_run(
|
|
461
|
+
"woff22ft",
|
|
462
|
+
inputs,
|
|
463
|
+
output_dir,
|
|
464
|
+
recursive,
|
|
465
|
+
overwrite,
|
|
466
|
+
recalc_bboxes,
|
|
467
|
+
recalc_timestamp,
|
|
468
|
+
reorder_tables,
|
|
469
|
+
Woff2ToSfntOptions,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@app.command("ttc2sfnt")
|
|
474
|
+
def ttc2sfnt(
|
|
475
|
+
inputs: Inputs,
|
|
476
|
+
output_dir: OutputDir = None,
|
|
477
|
+
recursive: Recursive = False,
|
|
478
|
+
overwrite: Overwrite = False,
|
|
479
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
480
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
481
|
+
reorder_tables: ReorderTables = True,
|
|
482
|
+
) -> None:
|
|
483
|
+
"""Extract each font from TrueType Collections (TTC) as a separate SFNT font."""
|
|
484
|
+
_run(
|
|
485
|
+
"ttc2sfnt",
|
|
486
|
+
inputs,
|
|
487
|
+
output_dir,
|
|
488
|
+
recursive,
|
|
489
|
+
overwrite,
|
|
490
|
+
recalc_bboxes,
|
|
491
|
+
recalc_timestamp,
|
|
492
|
+
reorder_tables,
|
|
493
|
+
TtcToSfntOptions,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@app.command("var2static")
|
|
498
|
+
def var2static(
|
|
499
|
+
inputs: Inputs,
|
|
500
|
+
target_upm: TargetUpmVarToStatic = None,
|
|
501
|
+
autohint: Annotated[
|
|
502
|
+
bool,
|
|
503
|
+
typer.Option(
|
|
504
|
+
"--autohint/--no-autohint",
|
|
505
|
+
help="Autohint TrueType instances.",
|
|
506
|
+
rich_help_panel="Variable to static options",
|
|
507
|
+
),
|
|
508
|
+
] = True,
|
|
509
|
+
subroutinize: Annotated[
|
|
510
|
+
bool,
|
|
511
|
+
typer.Option(
|
|
512
|
+
"--subroutinize/--no-subroutinize",
|
|
513
|
+
help="Subroutinize PostScript instances.",
|
|
514
|
+
rich_help_panel="Variable to static options",
|
|
515
|
+
),
|
|
516
|
+
] = True,
|
|
517
|
+
output_dir: OutputDir = None,
|
|
518
|
+
recursive: Recursive = False,
|
|
519
|
+
overwrite: Overwrite = False,
|
|
520
|
+
recalc_bboxes: RecalcBboxes = True,
|
|
521
|
+
recalc_timestamp: RecalcTimestamp = False,
|
|
522
|
+
reorder_tables: ReorderTables = True,
|
|
523
|
+
) -> None:
|
|
524
|
+
"""Export all named instances from variable fonts as static fonts."""
|
|
525
|
+
_run(
|
|
526
|
+
"var2static",
|
|
527
|
+
inputs,
|
|
528
|
+
output_dir,
|
|
529
|
+
recursive,
|
|
530
|
+
overwrite,
|
|
531
|
+
recalc_bboxes,
|
|
532
|
+
recalc_timestamp,
|
|
533
|
+
reorder_tables,
|
|
534
|
+
VarToStaticOptions,
|
|
535
|
+
target_upm=target_upm,
|
|
536
|
+
autohint=autohint,
|
|
537
|
+
subroutinize=subroutinize,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def main() -> None:
|
|
542
|
+
"""Run the font converter command-line application."""
|
|
543
|
+
app()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
PS_SFNT_VERSION = "OTTO"
|
|
2
|
+
TT_SFNT_VERSION = "\0\1\0\0"
|
|
3
|
+
|
|
4
|
+
WOFF_FLAVOR = "woff"
|
|
5
|
+
WOFF2_FLAVOR = "woff2"
|
|
6
|
+
OTF_EXTENSION = ".otf"
|
|
7
|
+
TTF_EXTENSION = ".ttf"
|
|
8
|
+
WOFF_EXTENSION = ".woff"
|
|
9
|
+
WOFF2_EXTENSION = ".woff2"
|
|
10
|
+
|
|
11
|
+
MIN_UPM = 16
|
|
12
|
+
MAX_UPM = 16384
|
|
13
|
+
|
|
14
|
+
# Table tags
|
|
15
|
+
T_CFF = "CFF "
|
|
16
|
+
T_DSIG = "DSIG"
|
|
17
|
+
T_FPGM = "fpgm"
|
|
18
|
+
T_FVAR = "fvar"
|
|
19
|
+
T_GLYF = "glyf"
|
|
20
|
+
T_HEAD = "head"
|
|
21
|
+
T_HMTX = "hmtx"
|
|
22
|
+
T_LOCA = "loca"
|
|
23
|
+
T_MAXP = "maxp"
|
|
24
|
+
T_NAME = "name"
|
|
25
|
+
T_OS_2 = "OS/2"
|
|
26
|
+
T_POST = "post"
|
|
27
|
+
T_VORG = "VORG"
|