def set_name_record(font, record_id, value, addIfMissing=''): """Sets a record in the 'name' table to a given string. Assumes that the record already exists. If it doesn't, it only adds it if addIfMissing is set. Pass 'win' to add a record in 3/1/0x409 (win UCS2 en-US) and/or 'mac' to add a record in 1/0/0 (mac-roman English), separate by comma if you want both. If 'value' is None, the name record is dropped.""" records_to_drop = set() names = font['name'].names added = [] for record_number, record in enumerate(names): name_ids = (record.platformID, record.platEncID, record.langID) if name_ids not in [(3, 1, 0x409), (1, 0, 0)]: continue if record.nameID == record_id: if value is None: records_to_drop.add(record_number) else: if name_ids == (1, 0, 0): record.string = value.encode('mac-roman') added.append('mac') else: # (3, 1, 0x409) record.string = value.encode('UTF-16BE') added.append('win') if addIfMissing and value: for key in addIfMissing.split(','): if key in added: continue if key == 'win': nr = NameRecord() nr.nameID = record_id nr.platformID = 3 nr.platEncID = 1 nr.langID = 0x409 nr.string = value.encode('UTF-16BE') elif key == 'mac': nr = NameRecord() nr.nameID = record_id nr.platformID = 1 nr.platEncID = 0 nr.langID = 0 nr.string = value.encode('mac-roman') else: nr = None if nr: names.append(nr) if records_to_drop: font['name'].names = [ record for record_number, record in enumerate(names) if record_number not in records_to_drop]
def getName(self, *args, **kwargs): record = NameRecord() record.nameID = 16 record.platformID = 1 record.platEncID = 0 record.string = 'Gaa' return record
def getOrCreateNameRecord(self, nameId, val): logger.error('NAMEID {}: "{}"'.format(nameId, val)) return result_namerec = None for k, p in [[1, 0], [3, 1]]: result_namerec = self.font['name'].getName(nameId, k, p) if result_namerec: if result_namerec.isUnicode(): result_namerec.string = (val or '').encode("utf-16-be") else: result_namerec.string = val or '' if result_namerec: return result_namerec ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = 3 ot_namerecord.langID = 0x409 # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = 1 if ot_namerecord.isUnicode(): ot_namerecord.string = (val or '').encode("utf-16-be") else: ot_namerecord.string = val or '' self.font['name'].names.append(ot_namerecord) return ot_namerecord
def versionClean(font): from fontTools.ttLib.tables._n_a_m_e import NameRecord, table__n_a_m_e nameIDs = [(5, 1, 0, 0), (5, 3, 1, 1033)] nameTable = font["name"] for n in nameIDs: nameRecord = nameTable.getName(n[0], n[1], n[2], n[3]) s = nameRecord.string nameTable.names.remove(nameRecord) parts = s.split(';') s = '' for p in parts: if p != 'PS (version unavailable)': s = s + p + ';' s = s[:-1] record = NameRecord() record.nameID = n[0] record.platformID = n[1] record.platEncID = n[2] record.langID = n[3] record.string = s nameTable.names.append(record)
def setValidNameRecord(font, nameId, val): result_namerec = None for k, p in [[1, 0], [3, 1]]: result_namerec = font['name'].getName(nameId, k, p) if result_namerec: if result_namerec.isUnicode(): result_namerec.string = (val or '').encode("utf-16-be") else: result_namerec.string = val or '' if result_namerec: return ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = 3 ot_namerecord.langID = 0x409 # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = 1 if ot_namerecord.isUnicode(): ot_namerecord.string = (val or '').encode("utf-16-be") else: ot_namerecord.string = val or '' font['name'].names.append(ot_namerecord) return
def makeNameRecord(nameID, string, platformID, platEncID, langID, encoding): rec = NameRecord() rec.nameID = nameID rec.platformID = platformID rec.platEncID = platEncID rec.langID = langID rec.string = unicode(string, "utf8").encode(encoding) return rec
def _add_name_record(self, text, name_id, platform_id, plat_enc_id, lang_id): # TODO: The installed version of fontTools doesn't have # table__n_a_m_e.setName(). record = NameRecord() # PyYAML creates strings, force to Unicode record.string = unicode(text) record.nameID = name_id record.platformID = platform_id record.platEncID = plat_enc_id record.langID = lang_id self.name_table.names.append(record)
def _add_name_record(self, text, name_id, platform_id, plat_enc_id, lang_id): # TODO: The installed version of fontTools doesn't have # table__n_a_m_e.setName(). record = NameRecord() # PyYAML creates strings, which are unicode as of Python3 if sys.version_info.major == 2: text = unicode(text) record.string = text record.nameID = name_id record.platformID = platform_id record.platEncID = plat_enc_id record.langID = lang_id self.name_table.names.append(record)
def findOrCreateNameRecord(nametable, nameId, platformId=3, langId=0x409, platEncId=1): result_namerec = nametable.getName(nameId, platformId, platEncId) if result_namerec: return result_namerec ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = platformId ot_namerecord.langID = langId # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = platEncId nametable.names.append(ot_namerecord) return ot_namerecord
def setValidNameRecord(font, nameId, val): result_namerec = None for k, p in [[1, 0], [3, 1]]: result_namerec = font['name'].getName(nameId, k, p) if result_namerec: result_namerec.string = (val or '').encode(result_namerec.getEncoding()) if result_namerec: return ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = 3 ot_namerecord.langID = 0x409 # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = 1 ot_namerecord.string = (val or '').encode(ot_namerecord.getEncoding()) font['name'].names.append(ot_namerecord) return
def findOrCreateNameRecord(names, nameId, platformId=3, langId=0x409, platEncId=1): result_namerec = None for namerec in names: if (namerec.nameID == nameId and namerec.platformID == platformId and namerec.langID == langId): result_namerec = namerec break if result_namerec: return result_namerec ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = platformId ot_namerecord.langID = langId # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = platEncId names.append(ot_namerecord) return ot_namerecord
def versionClean(font): from fontTools.ttLib.tables._n_a_m_e import NameRecord, table__n_a_m_e nameIDs = [(5, 1, 0, 0), (5, 3, 1, 1033)] nameTable = font["name"] for n in nameIDs: nameRecord = nameTable.getName(n[0], 1, 0, 0) s = nameRecord.string nameTable.names.remove(nameTable.getName(n[0], n[1], n[2], n[3])) parts = s.split(';') if nameTable.getName(4, 1, 0, 0) is not None: f = parts[0] + '; ' + nameTable.getName(4, 1, 0, 0).string if n[1] == 3: f = _convert_line_endings(f, 2) f = f.encode("utf_16_be") record = NameRecord() record.nameID = n[0] record.platformID = n[1] record.platEncID = n[2] record.langID = n[3] record.string = f nameTable.names.append(record)
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { "0": getAttrWithFallback(font.info, "copyright"), "1": familyName, "2": styleName, "3": getAttrWithFallback(font.info, "openTypeNameUniqueID"), "4": fullName, "5": getAttrWithFallback(font.info, "openTypeNameVersion"), "6": getAttrWithFallback(font.info, "postscriptFontName"), "7": getAttrWithFallback(font.info, "trademark"), "8": getAttrWithFallback(font.info, "openTypeNameManufacturer"), "9": getAttrWithFallback(font.info, "openTypeNameDesigner"), "10": getAttrWithFallback(font.info, "openTypeNameDescription"), "11": getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), "12": getAttrWithFallback(font.info, "openTypeNameDesignerURL"), "13": getAttrWithFallback(font.info, "openTypeNameLicense"), "14": getAttrWithFallback(font.info, "openTypeNameLicenseURL") } # don't add typographic names if they are the same as the legacy ones typographicFamilyName = getAttrWithFallback( font.info, "openTypeNamePreferredFamilyName") typographicSubfamilyName = getAttrWithFallback( font.info, "openTypeNamePreferredSubfamilyName") if nameVals["1"] != typographicFamilyName: nameVals["16"] = typographicFamilyName if nameVals["2"] != typographicSubfamilyName: nameVals["17"] = typographicSubfamilyName self.otf["name"] = name = newTable("name") name.names = [] for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameIdVal = int(nameId) if nameIdVal == 6: # postscript font name nameVal = normalizeStringForPostscript(nameVal) rec = NameRecord() rec.platformID = 3 rec.platEncID = 10 if _isNonBMP(nameVal) else 1 rec.langID = 0x409 rec.nameID = nameIdVal rec.string = nameVal.encode(rec.getEncoding()) name.names.append(rec)
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { "0": getAttrWithFallback(font.info, "copyright"), "1": familyName, "2": styleName, "3": getAttrWithFallback(font.info, "openTypeNameUniqueID"), "4": fullName, "5": getAttrWithFallback(font.info, "openTypeNameVersion"), "6": getAttrWithFallback(font.info, "postscriptFontName"), "7": getAttrWithFallback(font.info, "trademark"), "8": getAttrWithFallback(font.info, "openTypeNameManufacturer"), "9": getAttrWithFallback(font.info, "openTypeNameDesigner"), "11": getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), "12": getAttrWithFallback(font.info, "openTypeNameDesignerURL"), "13": getAttrWithFallback(font.info, "openTypeNameLicense"), "14": getAttrWithFallback(font.info, "openTypeNameLicenseURL")} # don't add typographic names if they are the same as the legacy ones typographicFamilyName = getAttrWithFallback(font.info, "openTypeNamePreferredFamilyName") typographicSubfamilyName = getAttrWithFallback(font.info, "openTypeNamePreferredSubfamilyName") if nameVals["1"] != typographicFamilyName: nameVals["16"] = typographicFamilyName if nameVals["2"] != typographicSubfamilyName: nameVals["17"] = typographicSubfamilyName self.otf["name"] = name = newTable("name") name.names = [] for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameIdVal = int(nameId) if nameIdVal == 6: # postscript font name nameVal = normalizeStringForPostscript(nameVal) rec = NameRecord() rec.platformID = 3 rec.platEncID = 10 if _isNonBMP(nameVal) else 1 rec.langID = 0x409 rec.nameID = nameIdVal rec.string = nameVal.encode(rec.getEncoding()) name.names.append(rec)
def main(configfilepath): cfg = Config(configfilepath) f = cfg.toBitmapFont() otf = ttLib.TTFont() otf.importXML(cfg.templateTTXpath, quiet=True) dw, dh = cfg.outlineCfg["dotsize_x"], cfg.outlineCfg["dotsize_y"] glyphOrder = [] otf.setGlyphOrder(glyphOrder) cff = otf["CFF "].cff cffTopDict = cff.topDictIndex[0] cffCharStrings = cffTopDict.CharStrings.charStrings = {} cffSubrs = cffTopDict.Private.Subrs cffSubrs.items = [] counts = Counter(g.bitmap.advanceWidth for g in f.glyphs).most_common(2) defaultWidthX = cffTopDict.Private.defaultWidthX = counts[0][0] * dw nominalWidthX = cffTopDict.Private.nominalWidthX = counts[-1][0] * dw hmtxTable = otf["hmtx"] hmtxTable.metrics = {} if "vmtx" in otf: vmtxTable = otf["vmtx"] vmtxTable.metrics = {} else: vmtxTable = None if "VORG" in otf: vorgTable = otf["VORG"] vorgTable.VOriginRecords = {} counts = Counter(g.bitmap.voriginy for g in f.glyphs).most_common(1) vorgTable.defaultVertOriginY = int(counts[0][0] * dh) else: vorgTable = None vertBearingX = -int(cfg.fontinfo.settings["vertdescent"]) INFINITY = float("inf") bitmap = cfg.generateBitmap if bitmap: bst = otf["EBLC"].strikes[0].bitmapSizeTable eblcIndexSubTables = otf["EBLC"].strikes[0].indexSubTables = [] ebdtGlyphDict = otf["EBDT"].strikeData[0] = {} bst.hori.minOriginSB = +INFINITY bst.hori.minAdvanceSB = +INFINITY bst.hori.maxBeforeBL = -INFINITY bst.hori.minAfterBL = +INFINITY bst.vert.minOriginSB = +INFINITY bst.vert.minAdvanceSB = +INFINITY bst.vert.maxBeforeBL = -INFINITY bst.vert.minAfterBL = +INFINITY else: if "EBLC" in otf: del otf["EBLC"] if "EBDT" in otf: del otf["EBDT"] cmap = otf["cmap"] fontBBX = [+INFINITY, +INFINITY, -INFINITY, -INFINITY] maxAW = maxAH = 0 minRSB = minTSB = minBSB = +INFINITY maxYExtent = -INFINITY shape = cfg.shape() subrs = shape.getSubroutines(dw, dh) subrl = len(subrs) if subrl < 1240: bias = 107 elif subrl < 33900: bias = 1131 else: bias = 32768 subrns = range(-bias, subrl - bias) for subr in subrs: charstring = T2CharString() charstring.fromXML("CharString", {}, subr + " return") cffSubrs.append(charstring) curIndexSubTable = None for i, g in enumerate(f.glyphs): glyphOrder.append(g.name) if g.codepoint != -1: addcmap(cmap, g.codepoint, g.name, i) aw = g.bitmap.advanceWidth * dw ah = g.bitmap.advanceHeight * dh if aw != defaultWidthX: w = "{} ".format(_intorfloat(aw - nominalWidthX)) else: w = "" charstring = T2CharString() charstring.fromXML("CharString", {}, w + shape.bitmap2charstring(g.bitmap, dw, dh, subrns) + " endchar") cffCharStrings[g.name] = charstring bbx = shape.getGlyphBBX(g.bitmap, dw, dh) hmtxTable[g.name] = (int(aw), int(bbx[0])) vorgy = g.bitmap.voriginy * dh if vmtxTable is not None: vmtxTable[g.name] = (int(ah), int(vorgy - bbx[3])) if vorgTable is not None: vorgTable[g.name] = int(vorgy) fontBBX = [ min(fontBBX[0], bbx[0]), min(fontBBX[1], bbx[1]), max(fontBBX[2], bbx[2]), max(fontBBX[3], bbx[3]) ] maxAW = max(maxAW, aw) maxAH = max(maxAH, ah) minRSB = min(minRSB, aw - bbx[2]) minTSB = min(minTSB, vorgy - bbx[3]) minBSB = min(minBSB, bbx[1] - vorgy + ah) maxYExtent = max(maxYExtent, vorgy - bbx[1]) if bitmap: if i + 1 < len(f.glyphs) and g.bitmap.hasSameMetrics(f.glyphs[i + 1].bitmap): if curIndexSubTable is not None and curIndexSubTable.indexFormat == 2: nextIndexSubTable = curIndexSubTable else: curIndexSubTable = eblc_sub_table_classes[2](None, otf) curIndexSubTable.indexFormat = 2 curIndexSubTable.imageFormat = 5 curIndexSubTable.firstGlyphIndex = i curIndexSubTable.names = [] eblcIndexSubTables.append(curIndexSubTable) curIndexSubTable.imageSize = g.bitmap.getImageDataSize() curIndexSubTable.metrics = getBitmapMetrics(g.bitmap, vertBearingX) updatesbitLineMetrics(curIndexSubTable.metrics, bst) nextIndexSubTable = curIndexSubTable else: if curIndexSubTable is None: curIndexSubTable = eblc_sub_table_classes[1](None, otf) curIndexSubTable.indexFormat = 1 curIndexSubTable.imageFormat = 7 curIndexSubTable.firstGlyphIndex = i curIndexSubTable.names = [] eblcIndexSubTables.append(curIndexSubTable) nextIndexSubTable = curIndexSubTable elif curIndexSubTable.indexFormat == 1: nextIndexSubTable = curIndexSubTable else: nextIndexSubTable = None curIndexSubTable.lastGlyphIndex = i if curIndexSubTable.indexFormat == 2: ebdtBitmap = ebdt_bitmap_classes[5](None, otf) else: ebdtBitmap = ebdt_bitmap_classes[7](None, otf) ebdtBitmap.metrics = getBitmapMetrics(g.bitmap, vertBearingX) updatesbitLineMetrics(ebdtBitmap.metrics, bst) ebdtBitmap.imageData = g.bitmap.toImageData() ebdtGlyphDict[g.name] = ebdtBitmap curIndexSubTable.names.append(g.name) curIndexSubTable = nextIndexSubTable if fontBBX[0] == +INFINITY: fontBBX = [0, 0, 0, 0] maxAW = 0 maxAH = 0 minRSB = 0 minTSB = 0 minBSB = 0 maxYExtent = 0 ascent = cfg.fontinfo.settings["ascent"] descent = cfg.fontinfo.settings["descent"] headTable = otf["head"] headTable.unitsPerEm = int((ascent + descent) * dh) headTable.created = headTable.modified = timestampNow() headTable.xMin, headTable.yMin, headTable.xMax, headTable.yMax = [int(v) for v in fontBBX] headTable.lowestRecPPEM = int(ascent + descent) os_2Table = otf["OS/2"] os_2Table.xAvgCharWidth = f.getXAvgCharWidth(dw=dw) os_2Table.ulUnicodeRange1, os_2Table.ulUnicodeRange2, os_2Table.ulUnicodeRange3, os_2Table.ulUnicodeRange4 = f.getOS2ulUnicodeRanges() os_2Table.sTypoAscender = int(ascent * dh) os_2Table.sTypoDescender = -int(descent * dh) os_2Table.usWinAscent = max(os_2Table.usWinAscent, int(fontBBX[3])) os_2Table.usWinDescent = max(os_2Table.usWinDescent, int(-fontBBX[1])) # TODO(kurgm) OS/2.ulCodePageRange1,2 os_2Table.sxHeight = int(cfg.fontinfo.settings["x-height"] * dh) os_2Table.sCapHeight = int(ascent * dh) if cfg.fontinfo.settings["bold"]: headTable.macStyle |= 0b1 os_2Table.usWeightClass = 700 os_2Table.fsSelection |= 0b100000 os_2Table.fsSelection &= ~0b1000000 if cfg.fontinfo.settings["italic"]: headTable.macStyle |= 0b10 os_2Table.fsSelection |= 0b1 os_2Table.fsSelection &= ~0b1000000 hheaTable = otf["hhea"] hheaTable.ascent = int(fontBBX[3]) hheaTable.descent = -int(fontBBX[1]) hheaTable.advanceWidthMax = int(maxAW) hheaTable.minLeftSideBearing = int(fontBBX[0]) hheaTable.minRightSideBearing = int(minRSB) hheaTable.xMaxExtent = int(fontBBX[2]) hheaTable.numberOfHMetrics = len(f.glyphs) nameTable = otf["name"] for namerecords in cfg.fontinfo.names: for platformID, platEncID, langID in zip(namerecords.platformID, namerecords.platEncID, namerecords.langID): for nameID, string in namerecords.records.items(): nameRecord = nameTable.getName(nameID, platformID, platEncID, langID) if nameRecord is None: nameRecord = NameRecord() nameTable.names.append(nameRecord) nameRecord.nameID = nameID nameRecord.platformID = platformID nameRecord.platEncID = platEncID nameRecord.langID = langID nameRecord.string = string.encode(nameRecord.getEncoding()) cffNames = f.fontinfo.getCFFNames() cff.fontNames[0] = cffNames[6] # 6 = PostScript name if 5 in cffNames: # 5 = Version cffTopDict.version = cffNames[5] if 0 in cffNames: # 0 = Copyright cffTopDict.Copyright = cffNames[0] if 4 in cffNames: # 4 = Full name cffTopDict.FullName = cffNames[4] if 1 in cffNames: # 1 = Font Family cffTopDict.FamilyName = cffNames[1] otf["post"].isFixedPitch = cffTopDict.isFixedPitch = f.isFixedPitch() mtxValue = 1.0 / ((ascent + descent) * dh) cffTopDict.FontMatrix = [mtxValue, 0, 0, mtxValue, 0, 0] cffTopDict.FontBBox = fontBBX if "vhea" in otf: vheaTable = otf["vhea"] vheaTable.ascent = int(cfg.fontinfo.settings["vertascent"] * dw) vheaTable.descent = -int(cfg.fontinfo.settings["vertdescent"] * dw) vheaTable.advanceHeightMax = int(maxAH) vheaTable.minTopSideBearing = int(minTSB) vheaTable.minBottomSideBearing = int(minBSB) vheaTable.yMaxExtent = int(maxYExtent) vheaTable.numberOfVMetrics = len(f.glyphs) if bitmap: bst.hori.ascender = int(ascent) bst.hori.descender = -int(descent) bst.hori.widthMax = int(maxAW / dw) if bst.hori.minOriginSB == +INFINITY: bst.hori.minOriginSB = 0 bst.hori.minAdvanceSB = 0 bst.hori.maxBeforeBL = 0 bst.hori.minAfterBL = 0 bst.vert.ascender = int(cfg.fontinfo.settings["vertascent"]) bst.vert.descender = -int(cfg.fontinfo.settings["vertdescent"]) bst.vert.widthMax = int(maxAH / dh) if bst.vert.minOriginSB == +INFINITY: bst.vert.minOriginSB = 0 bst.vert.minAdvanceSB = 0 bst.vert.maxBeforeBL = 0 bst.vert.minAfterBL = 0 bst.startGlyphIndex = 0 bst.endGlyphIndex = len(f.glyphs) - 1 bst.ppemY = int(ascent + descent) bst.ppemX = int((ascent + descent) * dh / dw) for path in cfg.templateTTX2: otf.importXML(path, quiet=True) otf.save(cfg.outputTo)