Refactor ttf2otf
Replaced Click with argparse. Updated docstring with usage examples. Modified logging to use warnings for conversion failures.
This commit is contained in:
parent
d4f89c43b2
commit
94cb4f9733
@ -1,9 +1,28 @@
|
|||||||
|
__doc__ = """
|
||||||
|
Convert TrueType flavored fonts to CFF flavored fonts.
|
||||||
|
|
||||||
|
INPUT_PATH argument can be a file or a directory. If it is a directory, all the TrueType
|
||||||
|
flavored fonts found in the directory will be converted.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ ttf2otf font.ttf
|
||||||
|
$ ttf2otf font.ttf -o output_dir
|
||||||
|
$ ttf2otf fonts_dir
|
||||||
|
$ ttf2otf fonts_dir -r
|
||||||
|
$ ttf2otf fonts_dir -o output_dir
|
||||||
|
$ ttf2otf fonts_dir -o output_dir --no-overwrite
|
||||||
|
$ ttf2otf fonts_dir -o output_dir --max-err 0.5
|
||||||
|
$ ttf2otf fonts_dir -o output_dir --new-upem 1000
|
||||||
|
$ ttf2otf fonts_dir -o output_dir --correct-contours
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import typing as t
|
import typing as t
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import click
|
|
||||||
import pathops
|
import pathops
|
||||||
from cffsubr import subroutinize as subr
|
from cffsubr import subroutinize as subr
|
||||||
from fontTools import configLogger
|
from fontTools import configLogger
|
||||||
@ -112,7 +131,7 @@ def simplify_path(path: pathops.Path, glyph_name: str, clockwise: bool) -> patho
|
|||||||
|
|
||||||
|
|
||||||
def quadratics_to_cubics(
|
def quadratics_to_cubics(
|
||||||
font: TTFont, tolerance: float = 1.0, correct_contours: bool = True
|
font: TTFont, tolerance: float = 1.0, correct_contours: bool = False
|
||||||
) -> t.Dict[str, T2CharString]:
|
) -> t.Dict[str, T2CharString]:
|
||||||
"""
|
"""
|
||||||
Get CFF charstrings using Qu2CuPen
|
Get CFF charstrings using Qu2CuPen
|
||||||
@ -140,6 +159,7 @@ def quadratics_to_cubics(
|
|||||||
qu2cu_charstrings[k] = t2_pen.getCharString()
|
qu2cu_charstrings[k] = t2_pen.getCharString()
|
||||||
|
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
|
# Workaround for "oncurve-less contours with all_cubic not implemented"
|
||||||
temp_t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
temp_t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
||||||
glyph_set[k].draw(temp_t2_pen)
|
glyph_set[k].draw(temp_t2_pen)
|
||||||
t2_charstring = temp_t2_pen.getCharString()
|
t2_charstring = temp_t2_pen.getCharString()
|
||||||
@ -153,9 +173,9 @@ def quadratics_to_cubics(
|
|||||||
t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
t2_pen = T2CharStringPen(width=width, glyphSet=None)
|
||||||
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
|
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
|
||||||
tt_glyph.draw(pen=qu2cu_pen, glyfTable=None)
|
tt_glyph.draw(pen=qu2cu_pen, glyfTable=None)
|
||||||
log.info(
|
log.warning(
|
||||||
f"Failed to convert glyph {k} to cubic at first attempt, but succeeded at second "
|
f"Failed to convert glyph '{k}' to cubic at first attempt, but succeeded at second "
|
||||||
f"attempt."
|
f"one."
|
||||||
)
|
)
|
||||||
|
|
||||||
charstring = t2_pen.getCharString()
|
charstring = t2_pen.getCharString()
|
||||||
@ -305,6 +325,7 @@ def find_fonts(
|
|||||||
input_path (Path): The input file or directory.
|
input_path (Path): The input file or directory.
|
||||||
recursive (bool): If input_path is a directory, search for fonts recursively in
|
recursive (bool): If input_path is a directory, search for fonts recursively in
|
||||||
subdirectories.
|
subdirectories.
|
||||||
|
recalc_timestamp (bool): Weather to recalculate the font's timestamp on save.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[TTFont]: A list of TTFont objects.
|
List[TTFont]: A list of TTFont objects.
|
||||||
@ -332,75 +353,7 @@ def find_fonts(
|
|||||||
return fonts
|
return fonts
|
||||||
|
|
||||||
|
|
||||||
@click.command("ttf2otf", no_args_is_help=True)
|
def main(args=None) -> None:
|
||||||
@click.argument("input_path", type=click.Path(exists=True, resolve_path=True, path_type=Path))
|
|
||||||
@click.option(
|
|
||||||
"-r",
|
|
||||||
"--recursive",
|
|
||||||
is_flag=True,
|
|
||||||
default=False,
|
|
||||||
help="If INPUT_PATH is a directory, search for fonts recursively in subdirectories.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"-o",
|
|
||||||
"--output-dir",
|
|
||||||
type=click.Path(file_okay=False, path_type=Path),
|
|
||||||
help="Specify a directory where the output files are to be saved. If the output directory "
|
|
||||||
"doesn't exist, it will be automatically created. If not specified, files will be saved to "
|
|
||||||
"the source directory.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"--no-overwrite",
|
|
||||||
"overwrite",
|
|
||||||
is_flag=True,
|
|
||||||
default=True,
|
|
||||||
help="Do not overwrite the output file if it already exists.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"-rt",
|
|
||||||
"--recalc-timestamp",
|
|
||||||
is_flag=True,
|
|
||||||
default=False,
|
|
||||||
help="Recalculate the font's timestamp.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"--max-err",
|
|
||||||
type=float,
|
|
||||||
default=1.0,
|
|
||||||
help="The maximum error allowed when converting the font to TrueType.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"--new-upem",
|
|
||||||
type=click.IntRange(min=16, max=16384),
|
|
||||||
help="The target UPM to scale the font to.",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"--correct-contours",
|
|
||||||
is_flag=True,
|
|
||||||
default=False,
|
|
||||||
help="""
|
|
||||||
If the TrueType contours fonts have overlaps contours or incorrect directions, set this flag to
|
|
||||||
correct them with pathops.
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"--no-subroutinize",
|
|
||||||
"subroutinize",
|
|
||||||
is_flag=True,
|
|
||||||
default=True,
|
|
||||||
help="Do not subroutinize the converted font.",
|
|
||||||
)
|
|
||||||
def main(
|
|
||||||
input_path: Path,
|
|
||||||
recursive: bool = False,
|
|
||||||
output_dir: t.Optional[Path] = None,
|
|
||||||
overwrite: bool = True,
|
|
||||||
recalc_timestamp: bool = False,
|
|
||||||
max_err: float = 1.0,
|
|
||||||
new_upem: t.Optional[int] = None,
|
|
||||||
correct_contours: bool = True,
|
|
||||||
subroutinize: bool = True,
|
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Convert TrueType flavored fonts to CFF flavored fonts.
|
Convert TrueType flavored fonts to CFF flavored fonts.
|
||||||
|
|
||||||
@ -408,9 +361,77 @@ def main(
|
|||||||
flavored fonts found in the directory will be converted.
|
flavored fonts found in the directory will be converted.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"input_path",
|
||||||
|
type=Path,
|
||||||
|
help="The input file or directory.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--recursive",
|
||||||
|
action="store_true",
|
||||||
|
help="Search for fonts recursively in subdirectories.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output-dir",
|
||||||
|
type=Path,
|
||||||
|
help="Specify a directory where the output files are to be saved.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-overwrite",
|
||||||
|
dest="overwrite",
|
||||||
|
action="store_false",
|
||||||
|
help="Do not overwrite the output file if it already exists.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-rt",
|
||||||
|
"--recalc-timestamp",
|
||||||
|
action="store_true",
|
||||||
|
help="Recalculate the font's modified timestamp on save.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--max-err",
|
||||||
|
type=float,
|
||||||
|
default=1.0,
|
||||||
|
help="The maximum error allowed when converting the font to TrueType."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--new-upem",
|
||||||
|
type=int,
|
||||||
|
help="The target UPM to scale the font to.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--correct-contours",
|
||||||
|
action="store_true",
|
||||||
|
help="Correct contours with pathops.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-subroutinize",
|
||||||
|
dest="subroutinize",
|
||||||
|
action="store_false",
|
||||||
|
help="Do not subroutinize the converted font.",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
|
input_path = args.input_path
|
||||||
|
recursive = args.recursive
|
||||||
|
output_dir = args.output_dir
|
||||||
|
overwrite = args.overwrite
|
||||||
|
recalc_timestamp = args.recalc_timestamp
|
||||||
|
max_err = args.max_err
|
||||||
|
new_upem = args.new_upem
|
||||||
|
correct_contours = args.correct_contours
|
||||||
|
subroutinize = args.subroutinize
|
||||||
|
|
||||||
fonts = find_fonts(input_path, recursive=recursive, recalc_timestamp=recalc_timestamp)
|
fonts = find_fonts(input_path, recursive=recursive, recalc_timestamp=recalc_timestamp)
|
||||||
if not fonts:
|
if not fonts:
|
||||||
log.error("No fonts found.")
|
log.error("No TrueType flavored fonts found.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if output_dir and not output_dir.exists():
|
if output_dir and not output_dir.exists():
|
||||||
@ -418,6 +439,9 @@ def main(
|
|||||||
|
|
||||||
for font in fonts:
|
for font in fonts:
|
||||||
with font:
|
with font:
|
||||||
|
if font.sfntVersion != "\x00\x01\x00\x00":
|
||||||
|
log.error(f"Font {font.reader.file.name} is not a TrueType font.")
|
||||||
|
continue
|
||||||
in_file = font.reader.file.name
|
in_file = font.reader.file.name
|
||||||
log.info(f"Converting {in_file}...")
|
log.info(f"Converting {in_file}...")
|
||||||
|
|
||||||
@ -432,6 +456,7 @@ def main(
|
|||||||
charstrings_dict = quadratics_to_cubics(
|
charstrings_dict = quadratics_to_cubics(
|
||||||
font, tolerance=max_err, correct_contours=correct_contours
|
font, tolerance=max_err, correct_contours=correct_contours
|
||||||
)
|
)
|
||||||
|
|
||||||
ps_name = font["name"].getDebugName(6)
|
ps_name = font["name"].getDebugName(6)
|
||||||
font_info = build_font_info_dict(font=font)
|
font_info = build_font_info_dict(font=font)
|
||||||
private_dict: t.Dict[str, t.Any] = {}
|
private_dict: t.Dict[str, t.Any] = {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user