from fontTools.t1Lib import T1Font, T1Error from fontTools.agl import AGL2UV from fontTools.misc.psLib import PSInterpreter from fontTools.misc.transform import Transform from extractor.tools import RelaxedInfo # specification: http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF # ---------------- # Public Functions # ---------------- def isType1(pathOrFile): try: font = T1Font(pathOrFile) del font except T1Error: return False return True def extractFontFromType1(pathOrFile, destination, doGlyphs=True, doInfo=True, doKerning=True, customFunctions=[]): source = T1Font(pathOrFile) destination.lib["public.glyphOrder"] = _extractType1GlyphOrder(source) if doInfo: extractType1Info(source, destination) if doGlyphs: extractType1Glyphs(source, destination) if doKerning: # kerning extraction is not supported yet. # in theory, it could be retried from an AFM. # we need to find the AFM naming rules so that we can sniff for the file. pass for function in customFunctions: function(source, destination) def extractType1Info(source, destination): info = RelaxedInfo(destination.info) _extractType1FontInfo(source, info) _extractType1Private(source, info) _extractType1FontMatrix(source, info) # ---- # Info # ---- def _extractType1FontInfo(source, info): sourceInfo = source["FontInfo"] # FontName info.postscriptFontName = source["FontName"] # version version = sourceInfo.get("version") if version is not None: # the spec says that version will be a string and no formatting info is given. # so, only move forward if the string can actually be parsed. try: # 1. convert to a float version = float(version) # 2. convert it back to a string version = "%.3f" % version # 3. split. versionMajor, versionMinor = version.split(".") # 4. convert. versionMajor = int(versionMajor) versionMinor = int(versionMinor) # 5. set. info.versionMajor = int(versionMajor) info.versionMinor = int(versionMinor) except ValueError: # couldn't parse. leve the object with the default values. pass # Notice notice = sourceInfo.get("Notice") if notice: info.copyright = notice # FullName fullName = sourceInfo.get("FullName") if fullName: info.postscriptFullName = fullName # FamilyName familyName = sourceInfo.get("FamilyName") if familyName: info.familyName = familyName # Weight postscriptWeightName = sourceInfo.get("Weight") if postscriptWeightName: info.postscriptWeightName = postscriptWeightName # ItalicAngle info.italicAngle = sourceInfo.get("ItalicAngle") # IsFixedPitch info.postscriptIsFixedPitch = sourceInfo.get("isFixedPitch") # UnderlinePosition/Thickness info.postscriptUnderlinePosition = sourceInfo.get("UnderlinePosition") info.postscriptUnderlineThickness = sourceInfo.get("UnderlineThickness") def _extractType1FontMatrix(source, info): # units per em matrix = source["FontMatrix"] matrix = Transform(*matrix).inverse() info.unitsPerEm = int(round(matrix[3])) def _extractType1Private(source, info): private = source["Private"] # UniqueID info.openTypeNameUniqueID = private.get("UniqueID", None) # BlueValues and OtherBlues info.postscriptBlueValues = private.get("BlueValues", []) info.postscriptOtherBlues = private.get("OtherBlues", []) # FamilyBlues and FamilyOtherBlues info.postscriptFamilyBlues = private.get("FamilyBlues", []) info.postscriptFamilyOtherBlues = private.get("FamilyOtherBlues", []) # BlueScale/Shift/Fuzz info.postscriptBlueScale = private.get("BlueScale", None) info.postscriptBlueShift = private.get("BlueShift", None) info.postscriptBlueFuzz = private.get("BlueFuzz", None) # StemSnapH/V info.postscriptStemSnapH = private.get("StemSnapH", []) info.postscriptStemSnapV = private.get("StemSnapV", []) # ForceBold info.postscriptForceBold = bool(private.get("ForceBold", None)) # -------- # Outlines # -------- def extractType1Glyphs(source, destination): glyphSet = source.getGlyphSet() for glyphName in sorted(glyphSet.keys()): sourceGlyph = glyphSet[glyphName] # make the new glyph destination.newGlyph(glyphName) destinationGlyph = destination[glyphName] # outlines pen = destinationGlyph.getPen() sourceGlyph.draw(pen) # width destinationGlyph.width = sourceGlyph.width # synthesize the unicode value destinationGlyph.unicode = AGL2UV.get(glyphName) # ----------- # Glyph order # ----------- class GlyphOrderPSInterpreter(PSInterpreter): def __init__(self): PSInterpreter.__init__(self) self.glyphOrder = [] self.collectTokenForGlyphOrder = False def do_literal(self, token): result = PSInterpreter.do_literal(self, token) if token == "/FontName": self.collectTokenForGlyphOrder = False if self.collectTokenForGlyphOrder: self.glyphOrder.append(result.value) if token == "/CharStrings": self.collectTokenForGlyphOrder = True return result def _extractType1GlyphOrder(t1Font): interpreter = GlyphOrderPSInterpreter() interpreter.interpret(t1Font.data) return interpreter.glyphOrder