Source code for foundrytools.app.fix_empty_notdef

import logging

from fontTools.misc.psCharStrings import T2CharString
from fontTools.misc.roundTools import otRound
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.t2CharStringPen import T2CharStringPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.ttLib.tables._g_l_y_f import Glyph

from foundrytools import Font

logger = logging.getLogger(__name__)

NOTDEF = ".notdef"
WIDTH_CONSTANT = 600
HEIGHT_CONSTANT = 1.25
THICKNESS_CONSTANT = 10


[docs] class DrawNotdefError(Exception): """Raised when there is an error drawing the '.notdef' glyph."""
[docs] class DrawNotdefCFFError(DrawNotdefError): """Raised when there is an error drawing the '.notdef' glyph in a CFF font."""
[docs] class DrawNotdefTTError(DrawNotdefError): """Raised when there is an error drawing the '.notdef' glyph in a TTF font."""
[docs] def draw_notdef_cff( font: Font, width: int, height: int, thickness: int, cs_width: int | None = None ) -> T2CharString: """ Draws a '.notdef' glyph in a CFF font. :param font: The ``Font`` object. :type font: Font :param width: The width of the '.notdef' glyph. :type width: int :param height: The height of the '.notdef' glyph. :type height: int :param thickness: The thickness of the '.notdef' glyph. :type thickness: int :param cs_width: The width of the '.notdef' glyph to set in the CharString. :type cs_width: Optional[int] :return: The '.notdef' glyph. :rtype: T2CharString """ try: pen = T2CharStringPen(width=cs_width, glyphSet=font.ttfont.getGlyphSet()) glyph_set = font.ttfont.getGlyphSet() # Draw the outer contour (counterclockwise) pen.moveTo((0, 0)) pen.lineTo((width, 0)) pen.lineTo((width, height)) pen.lineTo((0, height)) pen.closePath() # Draw the inner contour (clockwise) pen.moveTo((thickness, thickness)) pen.lineTo((thickness, height - thickness)) pen.lineTo((width - thickness, height - thickness)) pen.lineTo((width - thickness, thickness)) pen.closePath() glyph_set[NOTDEF].draw(pen) charstring = pen.getCharString() return charstring except Exception as e: raise DrawNotdefCFFError(f"Error drawing '.notdef' glyph in CFF font: {e}") from e
[docs] def draw_notdef_glyf(font: Font, width: int, height: int, thickness: int) -> Glyph: """ Draws a '.notdef' glyph in a TTF font. :param font: The ``Font`` object. :type font: Font :param width: The width of the '.notdef' glyph. :type width: int :param height: The height of the '.notdef' glyph. :type height: int :param thickness: The thickness of the '.notdef' glyph. :type thickness: int :return: The '.notdef' glyph. :rtype: Glyph """ try: # Be aware: TTGlyphPen expects a dict[str, Any] object, not a _TTGlyphSet object pen = TTGlyphPen(glyphSet=font.ttfont.getGlyphSet()) glyph_set = font.ttfont.getGlyphSet() # Draw the outer contour (clockwise) pen.moveTo((0, 0)) pen.lineTo((0, height)) pen.lineTo((width, height)) pen.lineTo((width, 0)) pen.closePath() # Draw the inner contour (counterclockwise) pen.moveTo((thickness, thickness)) pen.lineTo((width - thickness, thickness)) pen.lineTo((width - thickness, height - thickness)) pen.lineTo((thickness, height - thickness)) pen.closePath() glyph_set[NOTDEF].draw(pen) return pen.glyph() except Exception as e: raise DrawNotdefTTError(f"Error drawing '.notdef' glyph in TTF font: {e}") from e
[docs] def run(font: Font) -> bool: """ If the '.notdef' glyph is empty, draws a simple rectangle in it. :param font: The Font object. :type font: Font :return: True if the '.notdef' glyph was fixed, False otherwise. :rtype: bool """ try: glyph_set = font.ttfont.getGlyphSet() if NOTDEF not in glyph_set: logger.warning("Font does not contain a '.notdef' glyph") return False rec_pen = RecordingPen() glyph_set[NOTDEF].draw(rec_pen) if rec_pen.value: logger.warning("The '.notdef' glyph is not empty") return False width = otRound(font.t_head.units_per_em / 1000 * WIDTH_CONSTANT) height = width * HEIGHT_CONSTANT thickness = otRound(width / THICKNESS_CONSTANT) if font.is_ps: cs_width = ( None if width == font.t_cff_.private_dict.defaultWidthX else width - font.t_cff_.private_dict.nominalWidthX ) charstring = draw_notdef_cff( font=font, width=width, height=height, thickness=thickness, cs_width=cs_width ) charstring.compile() font.t_cff_.top_dict.CharStrings[NOTDEF].setBytecode(charstring.bytecode) if font.is_tt: glyph = draw_notdef_glyf(font=font, width=width, height=height, thickness=thickness) font.t_glyf.table[NOTDEF] = glyph font.t_hmtx.table[NOTDEF] = (width, 0) return True except (DrawNotdefCFFError, DrawNotdefTTError) as e: raise DrawNotdefError from e