Source code for foundrytools.lib.otf_builder

from typing import Any

from fontTools.fontBuilder import FontBuilder
from fontTools.misc.psCharStrings import T2CharString
from fontTools.ttLib import TTFont

from foundrytools.constants import T_CFF, T_HEAD, T_NAME, T_POST


[docs] def build_otf( font: TTFont, charstrings_dict: dict[str, T2CharString], ps_name: str | None = None, font_info: dict[str, Any] | None = None, private_dict: dict[str, Any] | None = None, ) -> None: """ Builds an OpenType-PS font with ``fontTools.fontBuilder.FontBuilder``. :param font: The ``TTFont`` object. :type font: TTFont :param charstrings_dict: The charstrings dictionary. :type charstrings_dict: dict :param ps_name: The PostScript name of the font. If None, it will be extracted from the font metadata. :type ps_name: str, optional :param font_info: The font info dictionary. If None, it will be extracted from the font metadata. :param private_dict: The private dict dictionary. If None, it will be extracted from the font metadata. :type private_dict: dict, optional """ if not ps_name: ps_name = _get_ps_name(font=font) if not font_info: font_info = _get_font_info_dict(font=font) if not private_dict: private_dict = _get_private_dict(font=font) fb = FontBuilder(font=font) fb.isTTF = False _delete_ttf_tables(font=fb.font) fb.setupGlyphOrder(font.getGlyphOrder()) fb.setupCFF( psName=ps_name, charStringsDict=charstrings_dict, fontInfo=font_info, privateDict=private_dict, ) metrics = _get_hmtx_values(font=fb.font, charstrings=charstrings_dict) fb.setupHorizontalMetrics(metrics) fb.setupDummyDSIG() fb.setupMaxp() post_values = _get_post_values(font=fb.font) fb.setupPost(**post_values)
[docs] def _delete_ttf_tables(font: TTFont) -> None: ttf_tables = ["glyf", "cvt ", "loca", "fpgm", "prep", "gasp", "LTSH", "hdmx"] for table in ttf_tables: if table in font: del font[table]
[docs] def _get_ps_name(font: TTFont) -> str: if T_CFF not in font: return font[T_NAME].getDebugName(6) cff_table = font[T_CFF] return cff_table.cff.fontNames[0]
[docs] def _get_font_info_dict(font: TTFont) -> dict[str, Any]: if T_CFF not in font: return _build_font_info_dict(font) cff_table = font[T_CFF] return { key: value for key, value in cff_table.cff.topDictIndex[0].rawDict.items() if key not in ("FontBBox", "charset", "Encoding", "Private", "CharStrings") }
[docs] def _build_font_info_dict(font: TTFont) -> dict[str, Any]: font_revision = str(round(font[T_HEAD].fontRevision, 3)).split(".") major_version = str(font_revision[0]) minor_version = str(font_revision[1]).ljust(3, "0") name_table = font[T_NAME] post_table = font[T_POST] cff_font_info = { "version": ".".join([major_version, str(int(minor_version))]), "FullName": name_table.getBestFullName(), "FamilyName": name_table.getBestFamilyName(), "ItalicAngle": post_table.italicAngle, "UnderlinePosition": post_table.underlinePosition, "UnderlineThickness": post_table.underlineThickness, "isFixedPitch": bool(post_table.isFixedPitch), } return cff_font_info
[docs] def _get_private_dict(font: TTFont) -> dict[str, Any]: if T_CFF not in font: return {} cff_table = font[T_CFF] return { key: value for key, value in cff_table.cff.topDictIndex[0].Private.rawDict.items() if key not in ("Subrs", "defaultWidthX", "nominalWidthX") }
[docs] def _get_hmtx_values( font: TTFont, charstrings: dict[str, T2CharString] ) -> dict[str, tuple[int, int]]: glyph_set = font.getGlyphSet() advance_widths = {k: v.width for k, v in glyph_set.items()} lsb = {} for gn, cs in charstrings.items(): lsb[gn] = cs.calcBounds(None)[0] if cs.calcBounds(None) is not None else 0 metrics = {} for gn, advance_width in advance_widths.items(): metrics[gn] = (advance_width, lsb[gn]) return metrics
[docs] def _get_post_values(font: TTFont) -> dict[str, Any]: post_table = font[T_POST] post_info = { "italicAngle": round(post_table.italicAngle), "underlinePosition": post_table.underlinePosition, "underlineThickness": post_table.underlineThickness, "isFixedPitch": post_table.isFixedPitch, "minMemType42": post_table.minMemType42, "maxMemType42": post_table.maxMemType42, "minMemType1": post_table.minMemType1, "maxMemType1": post_table.maxMemType1, } return post_info