def pformat_tti(program, preserve=True): from fontTools.ttLib.tables.ttProgram import _pushCountPat assembly = program.getAssembly(preserve=preserve) stream = StringIO() i = 0 indent = 0 nInstr = len(assembly) while i < nInstr: instr = assembly[i] if _unindentRE.match(instr): indent -= 1 stream.write(' ' * indent) stream.write(instr) stream.write("\n") m = _pushCountPat.match(instr) i = i + 1 if m: nValues = int(m.group(1)) line = [] j = 0 for j in range(nValues): if j and not (j % 25): stream.write(' '.join(line)) stream.write("\n") line = [] line.append(assembly[i + j]) stream.write(' ' * indent) stream.write(' '.join(line)) stream.write("\n") i = i + j + 1 if _indentRE.match(instr): indent += 1 return stream.getvalue()
class Font: def __init__(self, filename, features, version): self._font = fontforge.open(filename) self._version = version self._features = StringIO() if features: preprocessor = Preprocessor() for d in ("italic", "sans", "display", "math"): if d in filename.lower(): preprocessor.define(d.upper()) with open(features) as f: preprocessor.parse(f) preprocessor.write(self._features) def _save_features(self, filename): font = self._font features = self._features with NamedTemporaryFile(suffix=".fea", mode="rt") as temp: font.generateFeatureFile(temp.name) lines = temp.readlines() for line in lines: if not line.startswith("languagesystem"): features.write(line) for lookup in font.gpos_lookups + font.gsub_lookups: font.removeLookup(lookup) with open(filename, "wt") as f: f.write(features.getvalue()) def _cleanup_glyphs(self): font = self._font for glyph in font.glyphs(): glyph.unlinkRef() if glyph.unlinkRmOvrlpSave: glyph.removeOverlap() glyph.correctDirection() def _update_metadata(self): version = self._version font = self._font year = datetime.date.today().year font.copyright = (u"Copyright © 2012-%s " % year + u"The Libertinus Project Authors.") font.version = version # Override the default which includes the build date font.appendSFNTName( "English (US)", "UniqueID", "%s;%s;%s" % (version, font.os2_vendor, font.fontname)) font.appendSFNTName("English (US)", "Vendor URL", "https://github.com/alif-type/libertinus") def _draw_over_under_line(self, name, widths): font = self._font bbox = font[name].boundingBox() pos = bbox[1] height = bbox[-1] - bbox[1] for width in sorted(widths): glyph = font.createChar(-1, "%s.%d" % (name, width)) glyph.width = 0 glyph.glyphclass = "mark" pen = glyph.glyphPen() pen.moveTo((-25 - width, pos)) pen.lineTo((-25 - width, pos + height)) pen.lineTo((25, pos + height)) pen.lineTo((25, pos)) pen.closePath() def _make_over_under_line(self): font = self._font minwidth = 50 bases = [n for n in ("uni0305", "uni0332") if n in font] if not bases: return # Collect glyphs grouped by their widths rounded by minwidth, we will # use them to decide the widths of over/underline glyphs we will draw widths = {} for glyph in font.glyphs(): if glyph.glyphclass != 'mark' and glyph.width > 0: width = round(glyph.width / minwidth) * minwidth width = max(width, minwidth) if width not in widths: widths[width] = [] widths[width].append(glyph.glyphname) for name in bases: self._draw_over_under_line(name, widths) dirname = os.path.dirname(font.path) fea = [] fea.append("feature mark {") fea.append(" @OverSet = [%s];" % " ".join(bases)) fea.append(" lookupflag UseMarkFilteringSet @OverSet;") for width in sorted(widths): # For each width group we create an over/underline glyph with the # same width, and add a contextual substitution lookup to use it # when an over/underline follows any glyph in this group replacements = ['%s.%d' % (name, width) for name in bases] fea.append(" sub [%s] [%s]' by [%s];" % (" ".join( widths[width]), " ".join(bases), " ".join(replacements))) fea.append("} mark;") self._features.write("\n".join(fea)) def generate(self, output, output_features): self._update_metadata() self._cleanup_glyphs() self._make_over_under_line() self._save_features(output_features) self._font.generate(output, flags=("opentype"))
def pformat_tti(program, preserve=True): from fontTools.ttLib.tables.ttProgram import _pushCountPat assembly = program.getAssembly(preserve=preserve) stream = StringIO() i = 0 indent = 0 nInstr = len(assembly) while i < nInstr: instr = assembly[i] if _unindentRE.match(instr): indent -= 1 stream.write(" " * indent) stream.write(instr) stream.write("\n") m = _pushCountPat.match(instr) i = i + 1 if m: nValues = int(m.group(1)) line = [] j = 0 for j in range(nValues): if j and not (j % 25): stream.write(" ".join(line)) stream.write("\n") line = [] line.append(assembly[i + j]) stream.write(" " * indent) stream.write(" ".join(line)) stream.write("\n") i = i + j + 1 if _indentRE.match(instr): indent += 1 return stream.getvalue()
class Font: def __init__(self, filename, features, version): self._font = fontforge.open(filename) self._version = version self._features = StringIO() if features: preprocessor = Preprocessor() for d in ("italic", "sans", "display", "math"): if d in filename.lower(): preprocessor.define(d.upper()) with open(features) as f: preprocessor.parse(f) preprocessor.write(self._features) def _merge_features(self): with NamedTemporaryFile(suffix=".fea", mode="w") as temp: temp.write(self._features.getvalue()) temp.flush() self._font.mergeFeature(temp.name) def _cleanup_glyphs(self): font = self._font for glyph in font.glyphs(): glyph.unlinkRef() if glyph.unlinkRmOvrlpSave: glyph.removeOverlap() glyph.correctDirection() glyph.autoHint() def _update_metadata(self): version = self._version font = self._font year = datetime.date.today().year font.copyright = (u"Copyright © 2012-%s " % year + u"The Libertinus Project Authors.") font.version = version # Override the default which includes the build date font.appendSFNTName( "English (US)", "UniqueID", "%s;%s;%s" % (version, font.os2_vendor, font.fontname)) font.appendSFNTName("English (US)", "Vendor URL", "https://github.com/alif-type/libertinus") def _draw_over_under_line(self, name, widths): font = self._font bbox = font[name].boundingBox() pos = bbox[1] height = bbox[-1] - bbox[1] for width in sorted(widths): glyph = font.createChar(-1, "%s.%d" % (name, width)) glyph.width = 0 glyph.glyphclass = "mark" pen = glyph.glyphPen() pen.moveTo((-25 - width, pos)) pen.lineTo((-25 - width, pos + height)) pen.lineTo((25, pos + height)) pen.lineTo((25, pos)) pen.closePath() def _make_over_under_line(self): font = self._font minwidth = 50 bases = [n for n in ("uni0305", "uni0332") if n in font] if not bases: return # Collect glyphs grouped by their widths rounded by minwidth, we will # use them to decide the widths of over/underline glyphs we will draw widths = {} for glyph in font.glyphs(): if glyph.glyphclass != 'mark' and glyph.width > 0: width = round(glyph.width / minwidth) * minwidth width = max(width, minwidth) if width not in widths: widths[width] = [] widths[width].append(glyph.glyphname) for name in bases: self._draw_over_under_line(name, widths) dirname = os.path.dirname(font.path) fea = [] fea.append("feature mark {") fea.append(" @OverSet = [%s];" % " ".join(bases)) fea.append(" lookupflag UseMarkFilteringSet @OverSet;") for width in sorted(widths): # For each width group we create an over/underline glyph with the # same width, and add a contextual substitution lookup to use it # when an over/underline follows any glyph in this group replacements = ['%s.%d' % (name, width) for name in bases] fea.append(" sub [%s] [%s]' by [%s];" % (" ".join( widths[width]), " ".join(bases), " ".join(replacements))) fea.append("} mark;") self._features.write("\n".join(fea)) def generate(self, output): self._update_metadata() self._cleanup_glyphs() self._make_over_under_line() self._merge_features() self._font.generate(output, flags=("opentype")) font = TTFont(output) # https://github.com/fontforge/fontforge/pull/3235 # fontDirectionHint is deprecated and must be set to 2 font["head"].fontDirectionHint = 2 # unset bits 6..10 font["head"].flags &= ~0x7e0 options = subset.Options() options.set(layout_features='*', name_IDs='*', notdef_outline=True, glyph_names=True, recalc_average_width=True, drop_tables=["FFTM"]) unicodes = font["cmap"].getBestCmap().keys() subsetter = subset.Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(font) font.save(output)