def make_ft_program(assembly): program = Program() program.fromAssembly(assembly) # need to compile bytecode for PUSH optimization program._assemble() del program.assembly return program
def rebuildPrep(self): hintProg = Program() hintProg.fromBytecode([184, 1, 255, 133, 184, 0, 4, 141]) prep = newTable("prep") prep.program = hintProg self.font["prep"] = prep return
def test__bool__(self): p = Program() assert not bool(p) bc = array.array("B", [0]) p.fromBytecode(bc) assert bool(p) assert p.bytecode.pop() == 0 assert not bool(p) p = Program() asm = ['SVTCA[0]'] p.fromAssembly(asm) assert bool(p) assert p.assembly.pop() == 'SVTCA[0]' assert not bool(p)
def test_xml_indentation(self): with open(TTPROGRAM_TTX, 'r', encoding='utf-8') as f: ttProgramXML = f.read() p = Program() p.fromBytecode(BYTECODE) ttfont = TestFont() buf = StringIO() writer = XMLWriter(buf) try: p.toXML(writer, ttfont) finally: output_string = buf.getvalue() assert output_string == ttProgramXML
def _merge_TTHinting(font, model, master_ttfs, tolerance=0.5): log.info("Merging TT hinting") assert "cvar" not in font # Check that the existing hinting is compatible # fpgm and prep table for tag in ("fpgm", "prep"): all_pgms = [m[tag].program for m in master_ttfs if tag in m] if len(all_pgms) == 0: continue if tag in font: font_pgm = font[tag].program else: font_pgm = Program() if any(pgm != font_pgm for pgm in all_pgms): log.warning("Masters have incompatible %s tables, hinting is discarded." % tag) _remove_TTHinting(font) return # glyf table for name, glyph in font["glyf"].glyphs.items(): all_pgms = [ m["glyf"][name].program for m in master_ttfs if hasattr(m["glyf"][name], "program") ] if not any(all_pgms): continue glyph.expand(font["glyf"]) if hasattr(glyph, "program"): font_pgm = glyph.program else: font_pgm = Program() if any(pgm != font_pgm for pgm in all_pgms if pgm): log.warning("Masters have incompatible glyph programs in glyph '%s', hinting is discarded." % name) _remove_TTHinting(font) return # cvt table all_cvs = [Vector(m["cvt "].values) for m in master_ttfs if "cvt " in m] if len(all_cvs) == 0: # There is no cvt table to make a cvar table from, we're done here. return if len(all_cvs) != len(master_ttfs): log.warning("Some masters have no cvt table, hinting is discarded.") _remove_TTHinting(font) return num_cvt0 = len(all_cvs[0]) if (any(len(c) != num_cvt0 for c in all_cvs)): log.warning("Masters have incompatible cvt tables, hinting is discarded.") _remove_TTHinting(font) return # We can build the cvar table now. cvar = font["cvar"] = newTable('cvar') cvar.version = 1 cvar.variations = [] deltas = model.getDeltas(all_cvs) supports = model.supports for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])): delta = [int(round(d)) for d in delta] if all(abs(v) <= tolerance for v in delta): continue var = TupleVariation(support, delta) cvar.variations.append(var)
def test_roundtrip(self): p = Program() p.fromBytecode(BYTECODE) asm = p.getAssembly(preserve=True) p.fromAssembly(asm) assert BYTECODE == p.getBytecode()
def _merge_TTHinting(font, masterModel, master_ttfs): log.info("Merging TT hinting") assert "cvar" not in font # Check that the existing hinting is compatible # fpgm and prep table for tag in ("fpgm", "prep"): all_pgms = [m[tag].program for m in master_ttfs if tag in m] if len(all_pgms) == 0: continue if tag in font: font_pgm = font[tag].program else: font_pgm = Program() if any(pgm != font_pgm for pgm in all_pgms): log.warning( "Masters have incompatible %s tables, hinting is discarded." % tag) _remove_TTHinting(font) return # glyf table for name, glyph in font["glyf"].glyphs.items(): all_pgms = [ m["glyf"][name].program for m in master_ttfs if name in m['glyf'] and hasattr(m["glyf"][name], "program") ] if not any(all_pgms): continue glyph.expand(font["glyf"]) if hasattr(glyph, "program"): font_pgm = glyph.program else: font_pgm = Program() if any(pgm != font_pgm for pgm in all_pgms if pgm): log.warning( "Masters have incompatible glyph programs in glyph '%s', hinting is discarded." % name) # TODO Only drop hinting from this glyph. _remove_TTHinting(font) return # cvt table all_cvs = [ Vector(m["cvt "].values) if 'cvt ' in m else None for m in master_ttfs ] nonNone_cvs = models.nonNone(all_cvs) if not nonNone_cvs: # There is no cvt table to make a cvar table from, we're done here. return if not models.allEqual(len(c) for c in nonNone_cvs): log.warning( "Masters have incompatible cvt tables, hinting is discarded.") _remove_TTHinting(font) return variations = [] deltas, supports = masterModel.getDeltasAndSupports( all_cvs, round=round ) # builtin round calls into Vector.__round__, which uses builtin round as we like for i, (delta, support) in enumerate(zip(deltas[1:], supports[1:])): if all(v == 0 for v in delta): continue var = TupleVariation(support, delta) variations.append(var) # We can build the cvar table now. if variations: cvar = font["cvar"] = newTable('cvar') cvar.version = 1 cvar.variations = variations
def otf2ttf(self, maxErr=1.0, postFormat=2.0, reverseDirection=True): # maxErr = 1.0, approximation error, measured in units per em (UPM). # postFormat = 2.0, default `post` table format. # reverseDirection = True, assuming the input contours' direction is correctly set (counter-clockwise), we just flip it to clockwise. if self.font.sfntVersion != "OTTO" or not self.font.has_key( "CFF ") or not self.font.has_key("post"): print("WARNING: Invalid CFF-based font. --otf2ttf is now ignored.", file=sys.stderr) self.jobs.convert_otf2ttf = False return # Convert cubic to quadratic quadGlyphs = {} glyphOrder = self.font.getGlyphOrder() glyphSet = self.font.getGlyphSet() for glyphName in glyphSet.keys(): glyph = glyphSet[glyphName] ttPen = TTGlyphPen(glyphSet) cu2quPen = Cu2QuPen(ttPen, maxErr, reverseDirection) glyph.draw(cu2quPen) quadGlyphs[glyphName] = ttPen.glyph() # Create quadratic `glyf` table glyf = newTable("glyf") glyf.glyphOrder = glyphOrder glyf.glyphs = quadGlyphs self.font["glyf"] = glyf # Create global instruction table `prep` with basic rendering settings hintProg = Program() hintProg.fromBytecode([184, 1, 255, 133, 184, 0, 4, 141]) prep = newTable("prep") prep.program = hintProg self.font["prep"] = prep # Create `gasp` table gasp = newTable("gasp") gasp.version = 1 gasp.gaspRange = {65535: 10} self.font["gasp"] = gasp # Create partial TrueType `maxp` table (v1.0) maxp = newTable("maxp") maxp.tableVersion = 0x00010000 maxp.maxZones = 1 maxp.maxTwilightPoints = 0 maxp.maxStorage = 0 maxp.maxFunctionDefs = 0 maxp.maxInstructionDefs = 0 maxp.maxStackElements = 0 maxp.maxSizeOfInstructions = 0 maxp.maxComponentElements = max( len(g.components if hasattr(g, "components") else []) for g in glyf.glyphs.values()) self.font["maxp"] = maxp # Create an empty `loca` table, which will be automatically generated upon compile self.font["loca"] = newTable("loca") # Modify `post` table post = self.font["post"] post.formatType = postFormat post.extraNames = [] post.mapping = {} post.glyphOrder = glyphOrder # Change sfntVersion from CFF to TrueType self.font.sfntVersion = "\x00\x01\x00\x00" # Recalculate missing properties in `head`, `glyf`, `maxp` upon compile self.font.recalcBBoxes = True # Clean-ups del self.font["CFF "] if self.font.has_key("VORG"): del self.font["VORG"] return