from collections.abc import Mapping
import pathops
from fontTools.cffLib import PrivateDict
from fontTools.misc.psCharStrings import T2CharString
from fontTools.pens.cu2quPen import Cu2QuPen
from fontTools.pens.qu2cuPen import Qu2CuPen
from fontTools.pens.t2CharStringPen import T2CharStringPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.ttLib import TTFont
from fontTools.ttLib.ttGlyphSet import _TTGlyph
from foundrytools.lib.pathops import simplify_path
__all__ = ["quadratics_to_cubics", "quadratics_to_cubics_2"]
_TTGlyphMapping = Mapping[str, _TTGlyph]
def _skia_path_from_charstring(charstring: T2CharString) -> pathops.Path:
path = pathops.Path()
path_pen = path.getPen(glyphSet=None)
charstring.draw(path_pen)
return path
def _charstring_from_skia_path(path: pathops.Path, width: int) -> T2CharString:
t2_pen = T2CharStringPen(width=width, glyphSet=None)
path.draw(t2_pen)
return t2_pen.getCharString()
[docs]
def quadratics_to_cubics(
font: TTFont, tolerance: float = 1.0, correct_contours: bool = True
) -> dict[str, T2CharString]:
"""
Converts quadratic Bézier splines to cubic curves with a specified tolerance using the
``Qu2CuPen``.
Optionally corrects the winding direction of the contours.
:param font: The TTFont object representing the font to process.
:type font: TTFont
:param tolerance: The maximum acceptable deviation in font units when approximating the
quadratic Bézier curves. Default is 1.0.
:type tolerance: float
:param correct_contours: Whether to correct contours for overlapping. Default is True.
:type correct_contours: bool
:return: A dictionary mapping glyph names to their corresponding T2CharString objects after
conversion.
:rtype: dict[str, T2CharString]
"""
qu2cu_charstrings = {}
glyph_set = font.getGlyphSet()
for k, v in glyph_set.items():
width = v.width
try:
t2_pen = T2CharStringPen(width=width, glyphSet={k: v})
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
glyph_set[k].draw(qu2cu_pen)
qu2cu_charstrings[k] = t2_pen.getCharString()
except NotImplementedError:
temp_t2_pen = T2CharStringPen(width=width, glyphSet=None)
glyph_set[k].draw(temp_t2_pen)
t2_charstring = temp_t2_pen.getCharString()
t2_charstring.private = PrivateDict()
tt_pen = TTGlyphPen(glyphSet=None)
cu2qu_pen = Cu2QuPen(other_pen=tt_pen, max_err=tolerance, reverse_direction=False)
t2_charstring.draw(cu2qu_pen)
tt_glyph = tt_pen.glyph()
t2_pen = T2CharStringPen(width=width, glyphSet=None)
qu2cu_pen = Qu2CuPen(t2_pen, max_err=tolerance, all_cubic=True, reverse_direction=True)
tt_glyph.draw(pen=qu2cu_pen, glyfTable=None)
charstring = t2_pen.getCharString()
if correct_contours:
charstring.private = PrivateDict()
path = _skia_path_from_charstring(charstring)
simplified_path = simplify_path(path, glyph_name=k, clockwise=False)
charstring = _charstring_from_skia_path(path=simplified_path, width=width)
qu2cu_charstrings[k] = charstring
return qu2cu_charstrings
[docs]
def quadratics_to_cubics_2(font: TTFont) -> dict[str, T2CharString]:
"""
Converts quadratic Bézier splines to cubic curves using the ``T2CharStringPen``.
:param font: The ``TTFont`` object representing the font to process.
:type font: TTFont
:return: A dictionary mapping glyph names to their corresponding T2CharString objects after
conversion.
:rtype: dict[str, T2CharString]
"""
t2_charstrings = {}
glyph_set = font.getGlyphSet()
for k, v in glyph_set.items():
t2_pen = T2CharStringPen(v.width, glyphSet=glyph_set)
glyph_set[k].draw(t2_pen)
charstring = t2_pen.getCharString()
t2_charstrings[k] = charstring
return t2_charstrings