def setupFile_featureTables(self): """ Compile and return OpenType feature tables from the source. Raises a FeaLibError if the feature compilation was unsuccessful. **This should not be called externally.** Subclasses may override this method to handle the table compilation in a different way if desired. """ if self.mtiFeaFiles is not None: for tag, feapath in self.mtiFeaFiles.items(): with open(feapath) as feafile: table = mtiLib.build(feafile, self.outline) assert table.tableTag == tag self.outline[tag] = table elif self.features.strip(): if self.font.path is not None: self.features = forceAbsoluteIncludesInFeatures(self.features, self.font.path) fd, fea_path = tempfile.mkstemp() with open(fea_path, "w") as feafile: feafile.write(self.features) addOpenTypeFeatures(fea_path, self.outline) os.close(fd) os.remove(fea_path)
def check_mti_file(self, name, tableTag=None): xml_expected_path = self.getpath("%s.ttx" % name + ('.'+tableTag if tableTag is not None else '')) with open(xml_expected_path, 'rt', encoding="utf-8") as xml_expected_file: xml_expected = xml_expected_file.read() font = self.create_font() with open(self.getpath("%s.txt" % name), 'rt', encoding="utf-8") as f: table = mtiLib.build(f, font, tableTag=tableTag) if tableTag is not None: self.assertEqual(tableTag, table.tableTag) tableTag = table.tableTag # Make sure it compiles. blob = table.compile(font) # Make sure it decompiles. decompiled = table.__class__() decompiled.decompile(blob, font) # XML from built object. writer = XMLWriter(StringIO()) writer.begintag(tableTag); writer.newline() table.toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_built = writer.file.getvalue() # XML from decompiled object. writer = XMLWriter(StringIO()) writer.begintag(tableTag); writer.newline() decompiled.toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_binary = writer.file.getvalue() self.expect_ttx(xml_binary, xml_built, fromfile='decompiled', tofile='built') self.expect_ttx(xml_expected, xml_built, fromfile=xml_expected_path, tofile='built') from fontTools.misc import xmlReader f = StringIO() f.write(xml_expected) f.seek(0) font2 = TTFont() font2.setGlyphOrder(font.getGlyphOrder()) reader = xmlReader.XMLReader(f, font2) reader.read(rootless=True) # XML from object read from XML. writer = XMLWriter(StringIO()) writer.begintag(tableTag); writer.newline() font2[tableTag].toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_fromxml = writer.file.getvalue() self.expect_ttx(xml_expected, xml_fromxml, fromfile=xml_expected_path, tofile='fromxml')
def check_mti_file(self, name, tableTag=None): xml_expected_path = self.getpath("%s.ttx" % name + ('.'+tableTag if tableTag is not None else '')) with open(xml_expected_path, 'rt', encoding="utf-8") as xml_expected_file: xml_expected = xml_expected_file.read() font = self.create_font() with open(self.getpath("%s.txt" % name), 'rt', encoding="utf-8") as f: table = mtiLib.build(f, font, tableTag=tableTag) if tableTag is not None: self.assertEqual(tableTag, table.tableTag) tableTag = table.tableTag # Make sure it compiles. blob = table.compile(font) # Make sure it decompiles. decompiled = table.__class__() decompiled.decompile(blob, font) # XML from built object. writer = XMLWriter(StringIO(), newlinestr='\n') writer.begintag(tableTag); writer.newline() table.toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_built = writer.file.getvalue() # XML from decompiled object. writer = XMLWriter(StringIO(), newlinestr='\n') writer.begintag(tableTag); writer.newline() decompiled.toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_binary = writer.file.getvalue() self.expect_ttx(xml_binary, xml_built, fromfile='decompiled', tofile='built') self.expect_ttx(xml_expected, xml_built, fromfile=xml_expected_path, tofile='built') from fontTools.misc import xmlReader f = StringIO() f.write(xml_expected) f.seek(0) font2 = TTFont() font2.setGlyphOrder(font.getGlyphOrder()) reader = xmlReader.XMLReader(f, font2) reader.read(rootless=True) # XML from object read from XML. writer = XMLWriter(StringIO(), newlinestr='\n') writer.begintag(tableTag); writer.newline() font2[tableTag].toXML(writer, font) writer.endtag(tableTag); writer.newline() xml_fromxml = writer.file.getvalue() self.expect_ttx(xml_expected, xml_fromxml, fromfile=xml_expected_path, tofile='fromxml')
def setupFile_featureTables(self): """ Compile and return OpenType feature tables from the source. Raises a FeaLibError if the feature compilation was unsuccessful. **This should not be called externally.** Subclasses may override this method to handle the table compilation in a different way if desired. """ if self.mtiFeatures is not None: for tag, features in self.mtiFeatures.items(): table = mtiLib.build(features.splitlines(), self.outline) assert table.tableTag == tag self.outline[tag] = table elif self.features.strip(): # the path to features.fea is only used by the lexer to resolve # the relative "include" statements if self.font.path is not None: feapath = os.path.join(self.font.path, "features.fea") else: # in-memory UFO has no path, can't do 'include' either feapath = None # save generated features to a temp file if things go wrong... data = tobytes(self.features, encoding="utf-8") with NamedTemporaryFile(delete=False) as tmp: tmp.write(data) # if compilation succedes or fails for unrelated reasons, clean # up the temporary file try: addOpenTypeFeaturesFromString(self.outline, self.features, filename=feapath) except feaLib.error.FeatureLibError: logger.error("Compilation failed! Inspect temporary file: %r", tmp.name) raise except: os.remove(tmp.name) raise else: os.remove(tmp.name)
def setupFile_featureTables(self): """ Compile and return OpenType feature tables from the source. Raises a FeaLibError if the feature compilation was unsuccessful. **This should not be called externally.** Subclasses may override this method to handle the table compilation in a different way if desired. """ if self.mtiFeaFiles is not None: for tag, feapath in self.mtiFeaFiles.items(): with open(feapath) as feafile: table = mtiLib.build(feafile, self.outline) assert table.tableTag == tag self.outline[tag] = table elif self.features.strip(): feapath = os.path.join(self.font.path, "features.fea") if self.font.path is not None else None addOpenTypeFeaturesFromString(self.outline, self.features, filename=feapath)
def setupFile_featureTables(self): """ Compile and return OpenType feature tables from the source. Raises a FeaLibError if the feature compilation was unsuccessful. **This should not be called externally.** Subclasses may override this method to handle the table compilation in a different way if desired. """ if self.mtiFeaFiles is not None: for tag, feapath in self.mtiFeaFiles.items(): with open(feapath) as feafile: table = mtiLib.build(feafile, self.outline) assert table.tableTag == tag self.outline[tag] = table elif self.features.strip(): feapath = os.path.join(self.font.path, "features.fea") addOpenTypeFeaturesFromString(self.outline, self.features, filename=feapath)
def buildTables(self): for tag, features in self.mtiFeatures.items(): table = mtiLib.build(features.splitlines(), self.ttFont) assert table.tableTag == tag self.ttFont[tag] = table
def make_font(feature_source, fea_type="fea"): """Return font with GSUB compiled from given source. Adds a bunch of filler tables so the font can be saved if needed, for debugging purposes. """ # copied from fontTools' feaLib/builder_test. glyphs = """ .notdef space slash fraction semicolon period comma ampersand quotedblleft quotedblright quoteleft quoteright zero one two three four five six seven eight nine zero.oldstyle one.oldstyle two.oldstyle three.oldstyle four.oldstyle five.oldstyle six.oldstyle seven.oldstyle eight.oldstyle nine.oldstyle onequarter onehalf threequarters onesuperior twosuperior threesuperior ordfeminine ordmasculine A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3 a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid e.begin e.mid e.end m.begin n.end s.end z.end Eng Eng.alt1 Eng.alt2 Eng.alt3 A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash Y.swash Z.swash f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin a_n_d T_h T_h.swash germandbls ydieresis yacute breve grave acute dieresis macron circumflex cedilla umlaut ogonek caron damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial by feature lookup sub table """.split() font = TTFont() font.setGlyphOrder(glyphs) glyph_order = font.getGlyphOrder() font["cmap"] = cmap = newTable("cmap") table = cmap_format_4(4) table.platformID = 3 table.platEncID = 1 table.language = 0 table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV} cmap.tableVersion = 0 cmap.tables = [table] font["glyf"] = glyf = newTable("glyf") glyf.glyphs = {} glyf.glyphOrder = glyph_order for name in glyph_order: pen = TTGlyphPen(None) glyf[name] = pen.glyph() font["head"] = head = newTable("head") head.tableVersion = 1.0 head.fontRevision = 1.0 head.flags = ( head.checkSumAdjustment ) = ( head.magicNumber ) = ( head.created ) = ( head.modified ) = ( head.macStyle ) = ( head.lowestRecPPEM ) = ( head.fontDirectionHint ) = ( head.indexToLocFormat ) = head.glyphDataFormat = head.xMin = head.xMax = head.yMin = head.yMax = 0 head.unitsPerEm = 1000 font["hhea"] = hhea = newTable("hhea") hhea.tableVersion = 0x00010000 hhea.ascent = ( hhea.descent ) = ( hhea.lineGap ) = ( hhea.caretSlopeRise ) = ( hhea.caretSlopeRun ) = ( hhea.caretOffset ) = ( hhea.reserved0 ) = ( hhea.reserved1 ) = ( hhea.reserved2 ) = ( hhea.reserved3 ) = ( hhea.metricDataFormat ) = ( hhea.advanceWidthMax ) = ( hhea.xMaxExtent ) = hhea.minLeftSideBearing = hhea.minRightSideBearing = hhea.numberOfHMetrics = 0 font["hmtx"] = hmtx = newTable("hmtx") hmtx.metrics = {} for name in glyph_order: hmtx[name] = (600, 50) font["loca"] = newTable("loca") font["maxp"] = maxp = newTable("maxp") maxp.tableVersion = 0x00005000 maxp.numGlyphs = 0 font["post"] = post = newTable("post") post.formatType = 2.0 post.extraNames = [] post.mapping = {} post.glyphOrder = glyph_order post.italicAngle = ( post.underlinePosition ) = ( post.underlineThickness ) = ( post.isFixedPitch ) = post.minMemType42 = post.maxMemType42 = post.minMemType1 = post.maxMemType1 = 0 if fea_type == "fea": addOpenTypeFeaturesFromString(font, feature_source) elif fea_type == "mti": font["GSUB"] = mtiLib.build(UnicodeIO(feature_source), font) return font
def make_font(feature_source, fea_type='fea'): """Return font with GSUB compiled from given source. Adds a bunch of filler tables so the font can be saved if needed, for debugging purposes. """ # copied from fontTools' feaLib/builder_test. glyphs = """ .notdef space slash fraction semicolon period comma ampersand quotedblleft quotedblright quoteleft quoteright zero one two three four five six seven eight nine zero.oldstyle one.oldstyle two.oldstyle three.oldstyle four.oldstyle five.oldstyle six.oldstyle seven.oldstyle eight.oldstyle nine.oldstyle onequarter onehalf threequarters onesuperior twosuperior threesuperior ordfeminine ordmasculine A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3 a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid e.begin e.mid e.end m.begin n.end s.end z.end Eng Eng.alt1 Eng.alt2 Eng.alt3 A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash Y.swash Z.swash f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin a_n_d T_h T_h.swash germandbls ydieresis yacute breve grave acute dieresis macron circumflex cedilla umlaut ogonek caron damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial by feature lookup sub table """.split() font = TTFont() font.setGlyphOrder(glyphs) glyph_order = font.getGlyphOrder() font['cmap'] = cmap = newTable('cmap') table = cmap_format_4(4) table.platformID = 3 table.platEncID = 1 table.language = 0 table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV} cmap.tableVersion = 0 cmap.tables = [table] font['glyf'] = glyf = newTable('glyf') glyf.glyphs = {} glyf.glyphOrder = glyph_order for name in glyph_order: pen = TTGlyphPen(None) glyf[name] = pen.glyph() font['head'] = head = newTable('head') head.tableVersion = 1.0 head.fontRevision = 1.0 head.flags = head.checkSumAdjustment = head.magicNumber =\ head.created = head.modified = head.macStyle = head.lowestRecPPEM =\ head.fontDirectionHint = head.indexToLocFormat =\ head.glyphDataFormat =\ head.xMin = head.xMax = head.yMin = head.yMax = 0 head.unitsPerEm = 1000 font['hhea'] = hhea = newTable('hhea') hhea.tableVersion = 0x00010000 hhea.ascent = hhea.descent = hhea.lineGap =\ hhea.caretSlopeRise = hhea.caretSlopeRun = hhea.caretOffset =\ hhea.reserved0 = hhea.reserved1 = hhea.reserved2 = hhea.reserved3 =\ hhea.metricDataFormat = hhea.advanceWidthMax = hhea.xMaxExtent =\ hhea.minLeftSideBearing = hhea.minRightSideBearing =\ hhea.numberOfHMetrics = 0 font['hmtx'] = hmtx = newTable('hmtx') hmtx.metrics = {} for name in glyph_order: hmtx[name] = (600, 50) font['loca'] = newTable('loca') font['maxp'] = maxp = newTable('maxp') maxp.tableVersion = 0x00005000 maxp.numGlyphs = 0 font['post'] = post = newTable('post') post.formatType = 2.0 post.extraNames = [] post.mapping = {} post.glyphOrder = glyph_order post.italicAngle = post.underlinePosition = post.underlineThickness =\ post.isFixedPitch = post.minMemType42 = post.maxMemType42 =\ post.minMemType1 = post.maxMemType1 = 0 if fea_type == 'fea': addOpenTypeFeaturesFromString(font, feature_source) elif fea_type == 'mti': font['GSUB'] = mtiLib.build(UnicodeIO(feature_source), font) return font