from fontTools.pens.recordingPen import DecomposingRecordingPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables._g_l_y_f import table__g_l_y_f
from foundrytools.constants import T_FPGM, T_GLYF
from foundrytools.core.tables.default import DefaultTbl
from foundrytools.lib.pathops import correct_glyf_contours
[docs]
class GlyfTable(DefaultTbl): # pylint: disable=too-few-public-methods
"""This class extends the fontTools ``glyf`` table."""
def __init__(self, ttfont: TTFont) -> None:
"""
Initializes the ``glyf`` table handler.
:param ttfont: The ``TTFont`` object.
:type ttfont: TTFont
"""
super().__init__(ttfont=ttfont, table_tag=T_GLYF)
@property
def table(self) -> table__g_l_y_f:
"""
Thw wrapped ``table__g_l_y_f`` table object.
"""
return self._table
@table.setter
def table(self, value: table__g_l_y_f) -> None:
"""
Wraps a new ``table__g_l_y_f`` object.
"""
self._table = value
[docs]
def correct_contours(
self, remove_hinting: bool = True, ignore_errors: bool = True, min_area: int = 25
) -> set[str]:
"""
Corrects contours of the glyf table by removing overlaps, correcting the direction of the
contours, and removing tiny paths.
:param remove_hinting: Whether to remove hinting from the font if one or more glyphs are
modified.
:type remove_hinting: bool
:param ignore_errors: Whether to ignore skia pathops errors while correcting contours.
:type ignore_errors: bool
:param min_area: The minimum area of a contour to be considered. Default is 25.
:type min_area: int
:return: A set of glyph names that were modified.
:rtype: set[str]
"""
return correct_glyf_contours(
font=self.ttfont,
remove_hinting=remove_hinting,
ignore_errors=ignore_errors,
min_area=min_area,
)
[docs]
def decompose_glyph(self, glyph_name: str) -> None:
"""
Decompose the components of a given glyph name.
:param glyph_name: The name of the glyph to decompose.
:type glyph_name: str
"""
glyph_set = self.ttfont.getGlyphSet()
dc_pen = DecomposingRecordingPen(glyph_set)
glyph_set[glyph_name].draw(dc_pen)
tt_pen = TTGlyphPen(None)
dc_pen.replay(tt_pen)
self.table[glyph_name] = tt_pen.glyph()
[docs]
def decompose_all(self) -> set[str]:
"""
Decompose all composite glyphs.
:return: A set of glyph names that were decomposed
:rtype: set[str]
"""
decomposed_glyphs = set()
for glyph_name in self.ttfont.getGlyphOrder():
glyph = self.table[glyph_name]
if not glyph.isComposite():
continue
self.decompose_glyph(glyph_name)
decomposed_glyphs.add(glyph_name)
return decomposed_glyphs
[docs]
def remove_duplicate_components(self) -> set[str]:
"""
Remove duplicate components from composite glyphs.
:return: A set of glyph names that were fixed.
:rtype: set[str]
"""
fixed_glyphs = set()
for glyph_name in self.ttfont.getGlyphOrder():
glyph = self.table[glyph_name]
if not glyph.isComposite():
continue
components = []
for component in glyph.components:
if component not in components:
components.append(component)
else:
fixed_glyphs.add(glyph_name)
glyph.components = components
self.table[glyph_name] = glyph
return fixed_glyphs