def import_from_otf(self, otfpath): """Import color data (CPAL/COLR) from a font file. otfpath: Path of the font file""" font = TTFont(otfpath) if not (font.has_key("COLR") and font.has_key("CPAL")): print("ERROR: No COLR and CPAL table present in %s" % otfpath) else: print("Reading palette data ...") cpal = table_C_P_A_L_("CPAL") cpal.decompile(font["CPAL"].data, font) for j in range(len(cpal.palettes)): palette = cpal.palettes[j] _palette = {} for i in range(len(palette)): color = palette[i] _palette[str(i)] = "#%02x%02x%02x%02x" % ( color.red, color.green, color.blue, color.alpha) self.palettes.append(_palette) colr = table_C_O_L_R_("COLR") colr.decompile(font["COLR"].data, font) print("Reading layer data ...") for glyphname in colr.ColorLayers: layers = colr[glyphname] _glyph = ColorGlyph(self) _glyph.basename = glyphname for layer in layers: _glyph.layers.append(layer.name) _glyph.colors.append(layer.colorID) self[glyphname] = _glyph print("Done.") font.close()
def import_from_otf(self, otfpath): """Import color data (CPAL/COLR) from a font file. otfpath: Path of the font file""" font = TTFont(otfpath) if not (font.has_key("COLR") and font.has_key("CPAL")): print("ERROR: No COLR and CPAL table present in %s" % otfpath) else: print("Reading palette data ...") cpal = table_C_P_A_L_("CPAL") cpal.decompile(font["CPAL"].data, font) for j in range(len(cpal.palettes)): palette = cpal.palettes[j] _palette = {} for i in range(len(palette)): color = palette[i] _palette[str(i)] = "#%02x%02x%02x%02x" % ( color.red, color.green, color.blue, color.alpha ) self.palettes.append(_palette) colr = table_C_O_L_R_("COLR") colr.decompile(font["COLR"].data, font) print("Reading layer data ...") for glyphname in colr.ColorLayers: layers = colr[glyphname] _glyph = ColorGlyph(self) _glyph.basename = glyphname for layer in layers: _glyph.layers.append(layer.name) _glyph.colors.append(layer.colorID) self[glyphname] = _glyph print("Done.") font.close()
def _export_colr(self, otfpath): font = TTFont(otfpath) if (font.has_key("COLR") and font.has_key("CPAL")): print( " WARNING: Replacing existing COLR and CPAL tables in %s" % otfpath) print(" Writing palette data ...") cpal = table_C_P_A_L_("CPAL") cpal.version = 0 cpal.numPaletteEntries = len(self.palettes[0]) cpal.palettes = [] for j in range(len(self.palettes)): palette = self.palettes[j] _palette = [] # keep a map of old to new indices (palette indices are # not saved in font) if j == 0: reindex = {0xffff: 0xffff} count = 0 for i in sorted(palette.keys(), key=lambda k: int(k)): _blue, _green, _red, _alpha = 0, 0, 0, 0 _red = int(palette[i][1:3], 16) _green = int(palette[i][3:5], 16) _blue = int(palette[i][5:7], 16) if len(palette[i]) >= 9: _alpha = int(palette[i][7:9], 16) else: _alpha = 0xff _color = Color(_blue, _green, _red, _alpha) if j == 0: reindex[int(i)] = count count += 1 _palette.append(_color) print(" Appending palette", _palette) #print("ReIndex:", reindex) cpal.palettes.append(_palette) print(" Writing layer data ...") colr = table_C_O_L_R_("COLR") colr.version = 0 colr.ColorLayers = {} for glyphname in self.keys(): _layer_records = [] for i in range(len(self[glyphname].layers)): glyph = self[glyphname] _layer_records.append( LayerRecord(glyph.layers[i], reindex[glyph.colors[i]])) colr[glyphname] = _layer_records # save font["CPAL"] = cpal font["COLR"] = colr font.save(otfpath[:-4] + "_colr" + otfpath[-4:]) font.close()
def _export_colr(self, otfpath): font = TTFont(otfpath) if (font.has_key("COLR") and font.has_key("CPAL")): print(" WARNING: Replacing existing COLR and CPAL tables in %s" % otfpath) print(" Writing palette data ...") cpal = table_C_P_A_L_("CPAL") cpal.version = 0 cpal.numPaletteEntries = len(self.palettes[0]) cpal.palettes = [] for j in range(len(self.palettes)): palette = self.palettes[j] _palette = [] # keep a map of old to new indices (palette indices are # not saved in font) if j == 0: reindex = {0xffff: 0xffff} count = 0 for i in sorted(palette.keys(), key=lambda k: int(k)): _color = Color() _color.red = int(palette[i][1:3], 16) _color.green = int(palette[i][3:5], 16) _color.blue = int(palette[i][5:7], 16) if len(palette[i]) >= 9: _color.alpha = int(palette[i][7:9], 16) else: _color.alpha = 0xff if j == 0: reindex[int(i)] = count count += 1 _palette.append(_color) print(" Appending palette", _palette) #print("ReIndex:", reindex) cpal.palettes.append(_palette) print(" Writing layer data ...") colr = table_C_O_L_R_("COLR") colr.version = 0 colr.ColorLayers = {} for glyphname in self.keys(): _layer_records = [] for i in range(len(self[glyphname].layers)): glyph = self[glyphname] _layer_records.append( LayerRecord(glyph.layers[i], reindex[glyph.colors[i]]) ) colr[glyphname] = _layer_records # save font["CPAL"] = cpal font["COLR"] = colr font.save(otfpath[:-4] + "_colr" + otfpath[-4:]) font.close()
def _export_sbix(self, otfpath, palette=0, image_format="png", replace_outlines=False, parent_window=None): if not have_flat: print( "ColorFont._export_sbix: The 'flat' Python module is missing.") print( "Please see <https://github.com/jenskutilek/RoboChrome/blob/master/README.md>" ) return if replace_outlines: alt_glyphname_string = "%s" else: alt_glyphname_string = "%s.mac" font = TTFont(otfpath) if (font.has_key("sbix")): print(" WARNING: Replacing existing sbix table in %s" % otfpath) replace_outlines = True # insert special nodes into glyphs self._format_outlines_special(font, replace_outlines) # build sbix table sbix = table__s_b_i_x("sbix") if parent_window is not None: progress = ProgressWindow("Rendering bitmaps ...", tickCount=len(self.bitmap_sizes) * len(self.keys()), parentWindow=parent_window) for current_ppem in sorted(self.bitmap_sizes): current_set = Strike(ppem=current_ppem) for glyphname in self.keys(): if parent_window is not None: progress.update("Rendering /%s @ %i px ..." % (glyphname, current_ppem)) alt_glyphname = alt_glyphname_string % glyphname if image_format == "png": image_data = self[glyphname].get_png(palette, current_ppem) elif image_format == "pdf": image_data = self[glyphname].get_pdf(palette, current_ppem) else: # TODO: handle tiff, jpg, (dupe, mask) # fallback image_data = self[glyphname].get_png(palette, current_ppem) if image_data is not None: current_set.glyphs[alt_glyphname] = sbixGlyph( glyphName=glyphname, graphicType=image_format, imageData=image_data, ) sbix.strikes[current_ppem] = current_set if parent_window is not None: progress.close() font["sbix"] = sbix font.save(otfpath[:-4] + "_sbix" + otfpath[-4:]) font.close()
def __init__(self, path_list): # Get config data self.c = ConfigData() self.open = self.c.openFile # Save speciment paths for further use at the end self.paths = [] # Receive one or more fonts. self.fontpath_list = self.acceptOnlyAllowedFiletypesInList(path_list) # Copy every font into the document-fonts folder of a predefined directory self.copyFonts() # Read font data for font_path in self.fontpath_list: font = TTFont(font_path) this_fontdict = { 'fontpath': font_path, 'familyname': font['name'].getName(1,1,0).string, 'style': font['name'].getName(2,1,0).string, 'fullname': font['name'].getName(4,1,0).string, 'postscriptname': font['name'].getName(6,1,0).string, 'version': font['name'].getName(5,1,0).string, 'filename': os.path.basename(font_path), 'fonttype': '' } # Get specific placeholders this_placeholders = self.addPlaceholderData(this_fontdict) if font.has_key('CFF '): # There is a space after CFF because table tags have 4 letters this_fontdict['fonttype'] = 'OpenTypeCFF' else: this_fontdict['fonttype'] = 'TrueType' # Create a working directory from idml file self.createTmpIDMLDir() # Add all data to template InDesign file. (Create a file for every font.) self.replaceIDContent(this_fontdict, this_placeholders) # Save IDML file (which is virtually a zip file) shutil.make_archive(this_fontdict['filename'], 'zip', 'temp') temp_zip = this_fontdict['filename'] + '.zip' temp_idml = temp_zip.replace('zip', 'idml') specimen_path = os.path.join(self.c.specimen_folder, temp_idml) os.rename(temp_zip, specimen_path) # Add IDML file to specimen paths self.paths.append(specimen_path) # Remove temporary dir removeDir('temp')
def _export_sbix(self, otfpath, palette=0, image_format="png", replace_outlines=False, parent_window=None): if not have_flat: print("ColorFont._export_sbix: The 'flat' Python module is missing.") print("Please see <https://github.com/jenskutilek/RoboChrome/blob/master/README.md>") return if replace_outlines: alt_glyphname_string = "%s" else: alt_glyphname_string = "%s.mac" font = TTFont(otfpath) if (font.has_key("sbix")): print(" WARNING: Replacing existing sbix table in %s" % otfpath) replace_outlines = True # insert special nodes into glyphs self._format_outlines_special(font, replace_outlines) # build sbix table sbix = table__s_b_i_x("sbix") if parent_window is not None: progress = ProgressWindow("Rendering bitmaps ...", tickCount=len(self.bitmap_sizes)*len(self.keys()), parentWindow=parent_window) for current_ppem in sorted(self.bitmap_sizes): current_set = Strike(ppem=current_ppem) for glyphname in self.keys(): if parent_window is not None: progress.update("Rendering /%s @ %i px ..." % (glyphname, current_ppem)) alt_glyphname = alt_glyphname_string % glyphname if image_format == "png": image_data = self[glyphname].get_png(palette, current_ppem) elif image_format == "pdf": image_data = self[glyphname].get_pdf(palette, current_ppem) else: # TODO: handle tiff, jpg, (dupe, mask) # fallback image_data = self[glyphname].get_png(palette, current_ppem) if image_data is not None: current_set.glyphs[alt_glyphname] = sbixGlyph( glyphName=glyphname, graphicType=image_format, imageData=image_data, ) sbix.strikes[current_ppem] = current_set if parent_window is not None: progress.close() font["sbix"] = sbix font.save(otfpath[:-4] + "_sbix" + otfpath[-4:]) font.close()
class Font(object): def __init__(self, path, glyphClass=None): self.path = path self.fallbackGlyph = ".notdef" self._glyphs = {} if isinstance(path, TTFont): self.source = path else: self.source = TTFont(path) self.loadGlyphSet() self.loadInfo() self.loadCMAP() self.loadFeatures() if glyphClass is None: glyphClass = Glyph self.glyphClass = glyphClass def __del__(self): del self._glyphs self.source.close() del self.source # -------------- # initialization # -------------- def loadCMAP(self): self.cmap = extractCMAP(self.source) self.reversedCMAP = reverseCMAP(self.cmap) def loadGlyphSet(self): self.glyphSet = self.source.getGlyphSet() # the glyph order will be needed later # to assign the proper glyph index to # glyph objects. order = self.source.getGlyphOrder() self._glyphOrder = {} for index, glyphName in enumerate(order): self._glyphOrder[glyphName] = index def loadInfo(self): self.info = info = Info() head = self.source["head"] hhea = self.source["hhea"] os2 = self.source["OS/2"] info.unitsPerEm = head.unitsPerEm info.ascender = hhea.ascent info.descender = hhea.descent info.xHeight = os2.sxHeight info.capHeight = os2.sCapHeight # names nameIDs = {} for nameRecord in self.source["name"].names: nameID = nameRecord.nameID platformID = nameRecord.platformID platEncID = nameRecord.platEncID langID = nameRecord.langID text = nameRecord.string nameIDs[nameID, platformID, platEncID, langID] = text # to retrive the family and style names, first start # with the preferred name entries and progress to less # specific entries until something is found. familyPriority = [(16, 1, 0, 0), (16, 1, None, None), (16, None, None, None), (1, 1, 0, 0), (1, 1, None, None), (1, None, None, None)] familyName = self._skimNameIDs(nameIDs, familyPriority) stylePriority = [(17, 1, 0, 0), (17, 1, None, None), (17, None, None, None), (2, 1, 0, 0), (2, 1, None, None), (2, None, None, None)] styleName = self._skimNameIDs(nameIDs, stylePriority) if familyName is None or styleName is None: raise CompositorError("Could not extract name data from name table.") self.info.familyName = familyName self.info.styleName = styleName def _skimNameIDs(self, nameIDs, priority): for (nameID, platformID, platEncID, langID) in priority: for (nID, pID, pEID, lID), text in nameIDs.items(): if nID != nameID: continue if pID != platformID and platformID is not None: continue if pEID != platEncID and platEncID is not None: continue if lID != langID and langID is not None: continue # make sure there are no endian issues # XXX right way to do this? text = "".join([i for i in text if i != "\x00"]) return text def loadFeatures(self): self.gsub = None self.gpos = None self.gdef = None if self.source.has_key("GDEF"): self.gdef = GDEF().loadFromFontTools(self.source["GDEF"]) if self.source.has_key("GSUB"): self.gsub = GSUB().loadFromFontTools(self.source["GSUB"], self.reversedCMAP, self.gdef) if self.source.has_key("GPOS"): self.gpos = GPOS().loadFromFontTools(self.source["GPOS"], self.reversedCMAP, self.gdef) # ------------- # dict behavior # ------------- def keys(self): return self.glyphSet.keys() def __contains__(self, name): return self.glyphSet.has_key(name) def __getitem__(self, name): if name not in self._glyphs: glyph = self.glyphSet[name] index = self._glyphOrder[name] glyph = self.glyphClass(name, index, glyph, self) self._glyphs[name] = glyph return self._glyphs[name] # ----------------- # string processing # ----------------- def stringToGlyphNames(self, string): glyphNames = [] for c in string: c = unicode(c) v = ord(c) if v in self.cmap: glyphNames.append(self.cmap[v]) elif self.fallbackGlyph is not None: glyphNames.append(self.fallbackGlyph) return glyphNames def stringToGlyphRecords(self, string): return [GlyphRecord(glyphName) for glyphName in self.stringToGlyphNames(string)] def glyphListToGlyphRecords(self, glyphList): glyphRecords = [] for glyphName in glyphList: if glyphName not in self: if self.fallbackGlyph is None: continue glyphName = self.fallbackGlyph record = GlyphRecord(glyphName) glyphRecords.append(record) return glyphRecords def process(self, stringOrGlyphList, script="latn", langSys=None, rightToLeft=False, case="unchanged", logger=None): if isinstance(stringOrGlyphList, basestring): stringOrGlyphList = self.stringToGlyphNames(stringOrGlyphList) if case != "unchanged": l = langSys if l is not None: l = l.strip() stringOrGlyphList = convertCase(case, stringOrGlyphList, self.cmap, self.reversedCMAP, l, self.fallbackGlyph) glyphRecords = self.glyphListToGlyphRecords(stringOrGlyphList) if rightToLeft: glyphRecords.reverse() if logger: logger.logStart() glyphNames = [r.glyphName for r in glyphRecords] logger.logMainSettings(glyphNames, script, langSys) if self.gsub is not None: if logger: logger.logTableStart(self.gsub) glyphRecords = self.gsub.process(glyphRecords, script=script, langSys=langSys, logger=logger) if logger: logger.logResults(glyphRecords) logger.logTableEnd() advancedRecords = [] for glyphRecord in glyphRecords: glyphRecord.advanceWidth = self[glyphRecord.glyphName].width advancedRecords.append(glyphRecord) glyphRecords = advancedRecords if self.gpos is not None: if logger: logger.logTableStart(self.gpos) glyphRecords = self.gpos.process(glyphRecords, script=script, langSys=langSys, logger=logger) if logger: logger.logResults(glyphRecords) logger.logTableEnd() if logger: logger.logEnd() return glyphRecords # ------------------ # feature management # ------------------ def getScriptList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getScriptList() if self.gpos is not None: gpos = self.gpos.getScriptList() return sorted(set(gsub + gpos)) def getLanguageList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getLanguageList() if self.gpos is not None: gpos = self.gpos.getLanguageList() return sorted(set(gsub + gpos)) def getFeatureList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getFeatureList() if self.gpos is not None: gpos = self.gpos.getFeatureList() return sorted(set(gsub + gpos)) def getFeatureState(self, featureTag): gsubState = None gposState = None if self.gsub is not None: if featureTag in self.gsub: gsubState = self.gsub.getFeatureState(featureTag) if self.gpos is not None: if featureTag in self.gpos: gposState = self.gpos.getFeatureState(featureTag) if gsubState is not None and gposState is not None: if gsubState != gposState: raise CompositorError("Inconsistently applied feature: %s" % featureTag) if gsubState is not None: return gsubState if gposState is not None: return gposState raise CompositorError("Feature %s is is not contained in GSUB or GPOS" % featureTag) def setFeatureState(self, featureTag, state): if self.gsub is not None: if featureTag in self.gsub: self.gsub.setFeatureState(featureTag, state) if self.gpos is not None: if featureTag in self.gpos: self.gpos.setFeatureState(featureTag, state) # ------------- # Miscellaneous # ------------- def getGlyphOrder(self): return self.source.getGlyphOrder()
class DFont(TTFont): """Container font for ttfont, freetype and hb fonts""" def __init__(self, path=None, lazy=False, size=1500, ft_load_glyph_flags=FTHintMode.UNHINTED): self.path = path self.ttfont = TTFont(self.path) has_outlines = self.ttfont.has_key("glyf") or self.ttfont.has_key( "CFF ") if not has_outlines: # Create faux empty glyf table with empty glyphs to make # it a valid font, e.g. for old-style CBDT/CBLC fonts logger.warning( "No outlines present, treating {} as bitmap font".format( self.path)) self.ttfont["glyf"] = newTable("glyf") self.ttfont["glyf"].glyphs = {} pen = TTGlyphPen({}) for name in self.ttfont.getGlyphOrder(): self.ttfont["glyf"].glyphs[name] = pen.glyph() self._src_ttfont = TTFont(self.path) self.glyphset = None self.recalc_glyphset() self.axis_order = None self.instance_coordinates = self._get_dflt_instance_coordinates() self.instances_coordinates = self._get_instances_coordinates() self.glyphs = self.marks = self.mkmks = self.kerns = \ self.glyph_metrics = self.names = self.attribs = None self.ftfont = freetype.Face(self.path) self.ftslot = self.ftfont.glyph self.ft_load_glyph_flags = ft_load_glyph_flags self.size = size if self.ftfont.is_scalable: self.ftfont.set_char_size(self.size) with open(self.path, 'rb') as fontfile: self._fontdata = fontfile.read() self.hbface = hb.Face.create(self._fontdata) self.hbfont = hb.Font.create(self.hbface) self.hbfont.scale = (self.size, self.size) if not lazy: self.recalc_tables() def _get_instances_coordinates(self): results = {} if self.is_variable: for inst in self._src_ttfont['fvar'].instances: inst_name = self._src_ttfont['name'].getName( inst.subfamilyNameID, 3, 1, 1033).toUnicode() results[inst_name] = inst.coordinates return results def _get_dflt_instance_coordinates(self): if self.is_variable: return { i.axisTag: i.defaultValue for i in self._src_ttfont['fvar'].axes } return None def glyph(self, name): return self.glyphset[name] def recalc_glyphset(self): if not 'cmap' in self.ttfont.keys(): self.glyphset = [] inputs = InputGenerator(self).all_inputs() self.glyphset = {g.name: g for g in inputs} @property def is_variable(self): if 'fvar' in self._src_ttfont: return True return False def set_variations(self, axes): """Instantiate a ttfont VF with axes vals""" logger.debug("Setting variations to {}".format(axes)) if self.is_variable: font = instantiateVariableFont(self._src_ttfont, axes, inplace=False) self.ttfont = copy(font) self.axis_order = [ a.axisTag for a in self._src_ttfont['fvar'].axes ] self.instance_coordinates = { a.axisTag: a.defaultValue for a in self._src_ttfont['fvar'].axes } for axis in axes: if axis in self.instance_coordinates: self.instance_coordinates[axis] = axes[axis] else: logger.info("font has no axis called {}".format(axis)) self.recalc_tables() coords = [] for name in self.axis_order: coord = FT_Fixed(int(self.instance_coordinates[name]) << 16) coords.append(coord) ft_coords = (FT_Fixed * len(coords))(*coords) FT_Set_Var_Design_Coordinates(self.ftfont._FT_Face, len(ft_coords), ft_coords) self.hbface = hb.Face.create(self._fontdata) self.hbfont = hb.Font.create(self.hbface) self.hbfont.set_variations(self.instance_coordinates) self.hbfont.scale = (self.size, self.size) else: logger.info("Not vf") def set_variations_from_static(self, static_font): """Set VF font variations so they match a static font.""" if not self.is_variable: raise Exception("Not a variable font") # Use an fvar instance if its name matches the static font's # typographic subfamily name or subfamily name subfamilyname = static_font.ttfont['name'].getName(2, 3, 1, 1033) typosubfamilyname = static_font.ttfont['name'].getName(17, 3, 1, 1033) anysubfamilyname = typosubfamilyname or subfamilyname subfamilyname = anysubfamilyname.toUnicode( ) if anysubfamilyname else "" # The Google Fonts v1 api can only handle the wght axis. For families # which have widths, we have to release them as a seperate family, # https://fonts.google.com/?query=condensed # To distinguish the width family from the normal family, we append # the width to the family name e.g Roboto Condensed filename = os.path.basename(static_font.path) family_name = filename.split("-")[0] family_name_width = find_token(family_name, list(WIDTH_NAME_TO_FVAR.keys())) if family_name_width: subfamilyname = f"{family_name_width} {subfamilyname}" if subfamilyname in self.instances_coordinates: logger.debug( f"Instance name '{subfamilyname}' matches static font " "subfamily names. Setting variations using this instance.") variations = self.instances_coordinates[subfamilyname] self.set_variations(variations) return # if the font doesn't contain an instance name which matches the # static font, infer the correct values logger.debug( f"Font does not contain an instance name which matches " f"static font subfamily names '{subfamilyname}'. Inferring " "values instead.") variations = {} # wght parsed_weight = find_token(os.path.basename(static_font.path), list(WEIGHT_NAME_TO_FVAR.keys())) if parsed_weight: variations["wght"] = WEIGHT_NAME_TO_FVAR[parsed_weight] else: logger.debug( f"Couldn't parse weight value from {static_font.path}") weight_class = static_font.ttfont["OS/2"].usWeightClass # Google Fonts used to set the usWeightClass of Thin static # fonts to 250 and the ExtraLight to 275. Override these # values with 100 and 200. if weight_class == 250: weight_class = 100 if weight_class == 275: weight_class = 200 variations["wght"] = weight_class # wdth # We cannot simply use OS/2.usWidthClass since Google Fonts # releases Condensed styles as new families. These new families # have a usWidthClass of 5 (Normal). parsed_width = find_token(os.path.basename(static_font.path), list(WIDTH_NAME_TO_FVAR.keys())) if parsed_width: variations["wdth"] = WIDTH_NAME_TO_FVAR[parsed_width] else: logger.debug( f"Couldn't parse weight value from {static_font.path}") width_class = static_font.ttfont["OS/2"].usWidthClass variations["wdth"] = WIDTH_CLASS_TO_FVAR[width_class] # TODO (M Foley) add slnt axes self.set_variations(variations) def recalc_tables(self): """Recalculate DFont tables""" self.recalc_glyphset() anchors = DumpAnchors(self) self.glyphs = dump_glyphs(self) self.marks = anchors.marks_table self.mkmks = anchors.mkmks_table self.attribs = dump_attribs(self) self.names = dump_nametable(self) self.kerns = dump_kerning(self) self.metrics = dump_glyph_metrics(self) self.gdef_base, self.gdef_mark = dump_gdef(self)
def _export_svg(self, otfpath, palette=0, parent_window=None): font = TTFont(otfpath) if font.has_key("SVG "): print(" WARNING: Replacing existing SVG table in %s" % otfpath) # font.getReverseGlyphMap(rebuild=1) svg = table_S_V_G_("SVG ") svg.version = 0 svg.docList = [] svg.colorPalettes = None if parent_window is not None: progress = ProgressWindow("Rendering SVG ...", tickCount=len(self.keys()), parentWindow=parent_window) _palette = self.palettes[palette] _svg_palette = [] _docList = [] reindex = {0xffff: 0xffff} count = 0 for i in sorted(_palette.keys(), key=lambda k: int(k)): red = int(_palette[i][1:3], 16) green = int(_palette[i][3:5], 16) blue = int(_palette[i][5:7], 16) if len(_palette[i]) >= 9: alpha = int(_palette[i][7:9], 16) else: alpha = 0xff reindex[int(i)] = count count += 1 _svg_palette.append((red, green, blue, alpha)) # print("Palette:", len(_svg_palette), _svg_palette) _pen = SVGpen(self.rfont, optimize_output=True) for glyphname in self.keys(): # ["A", "P"]: #self.keys(): # look up glyph id try: gid = font.getGlyphID(glyphname) except: assert 0, "SVG table contains a glyph name not in font.getGlyphNames(): " + str(glyphname) # update progress bar if parent_window is not None: progress.update("Rendering SVG for /%s ..." % glyphname) # build svg glyph _svg_transfrom_group = """<g transform="scale(1 -1)">%s</g>""" contents = u"" for i in range(len(self[glyphname].layers)): _color_index = reindex[self[glyphname].colors[i]] # print(" Layer %i, color %i" % (i, _color_index)) rglyph = self.rfont[self[glyphname].layers[i]] if _color_index == 0xffff: r, g, b, a = (0, 0, 0, 0xff) else: r, g, b, a = _svg_palette[_color_index] _pen.reset() rglyph.draw(_pen) if _pen.d: contents += u'<g fill="#%02x%02x%02x"><path d="%s"/></g>' % (r, g, b, _pen.d) if contents: contents = _svg_transfrom_group % contents _svg_doc = u"""<svg enable-background="new 0 0 64 64" id="glyph%i" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">%s</svg>""" % (gid, contents) _docList.append((_svg_doc, gid, gid)) svg.docList = sorted(_docList, key=itemgetter(1)) if parent_window is not None: progress.close() # save font["SVG "] = svg font.save(otfpath[:-4] + "_svg" + otfpath[-4:]) font.close()
def openFile(path, txPath): # If input font is CFF or PS, build a dummy ttFont. tempPathCFF = None cffPath = None # If it is CID-keyed font, we need to convert it to a name-keyed font. This is a hack, but I really don't want to add CID support to # the very simple-minded PDF library. command = "%s -dump -0 \"%s\" 2>&1" % (txPath, path) report = FDKUtils.runShellCmd(command) if "CIDFontName" in report: tfd, tempPath1 = tempfile.mkstemp() os.close(tfd) command = "%s -t1 -decid -usefd 0 \"%s\" \"%s\" 2>&1" % ( txPath, path, tempPath1) report = FDKUtils.runShellCmd(command) if "fatal" in report: logMsg(report) logMsg( "Failed to convert CID-keyed font %s to a temporary Typ1 data file." % path) tfd, tempPathCFF = tempfile.mkstemp() os.close(tfd) command = "%s -cff +b \"%s\" \"%s\" 2>&1" % (txPath, tempPath1, tempPathCFF) report = FDKUtils.runShellCmd(command) if "fatal" in report: logMsg(report) logMsg( "Failed to convert CID-keyed font %s to a temporary CFF data file." % path) cffPath = tempPathCFF os.remove(tempPath1) elif os.path.isdir(path): # See if it is a UFO font by truing to dump it. command = "%s -dump -0 \"%s\" 2>&1" % (txPath, path) report = FDKUtils.runShellCmd(command) if not "sup.srcFontType" in report: logMsg(report) logMsg("Failed to open directory %s as a UFO font." % path) tfd, tempPathCFF = tempfile.mkstemp() os.close(tfd) command = "%s -cff +b \"%s\" \"%s\" 2>&1" % (txPath, path, tempPathCFF) report = FDKUtils.runShellCmd(command) if "fatal" in report: logMsg(report) logMsg( "Failed to convert ufo font %s to a temporary CFF data file." % path) cffPath = tempPathCFF else: try: ff = file(path, "rb") data = ff.read(10) ff.close() except (IOError, OSError): traceback.print_exc() raise FontError( "Failed to open and read font file %s. Check file/directory permissions." % path) if len(data) < 10: raise FontError( "Error: font file was zero size: may be a resource fork font, which this program does not process. <%s>." % path) if (data[:4] == "OTTO") or (data[:4] == "true") or ( data[:4] == "\0\1\0\0" ): # it is an OTF/TTF font, can process file directly try: ttFont = TTFont(path) except (IOError, OSError): raise FontError( "Error opening or reading from font file <%s>." % path) except TTLibError: raise FontError("Error parsing font file 333 <%s>." % path) if not (ttFont.has_key('CFF ') or ttFont.has_key('glyf')): raise FontError( "Error: font is not a CFF or TrueType font <%s>." % path) return ttFont, tempPathCFF # It is not an OTF file. if (data[0] == '\1') and (data[1] == '\0'): # CFF file cffPath = path elif not "%" in data: #not a PS file either logMsg("Font file must be a PS, CFF or OTF fontfile: %s." % path) raise FontError("Font file must be PS, CFF or OTF file: %s." % path) else: # It is a PS file. Convert to CFF. tfd, tempPathCFF = tempfile.mkstemp() os.close(tfd) cffPath = tempPathCFF command = "%s -cff +b \"%s\" \"%s\" 2>&1" % (txPath, path, tempPathCFF) report = FDKUtils.runShellCmd(command) if "fatal" in report: logMsg( "Attempted to convert font %s from PS to a temporary CFF data file." % path) logMsg(report) raise FontError( "Failed to convert PS font %s to a temp CFF font." % path) # now package the CFF font as an OTF font ff = file(cffPath, "rb") data = ff.read() ff.close() try: ttFont = TTFont() cffModule = getTableModule('CFF ') cffTable = cffModule.table_C_F_F_('CFF ') ttFont['CFF '] = cffTable cffTable.decompile(data, ttFont) except: traceback.print_exc() logMsg("Attempted to read font %s as CFF." % path) raise FontError("Error parsing font file <%s>." % path) return ttFont, tempPathCFF
def _export_svg(self, otfpath, palette=0, parent_window=None): font = TTFont(otfpath) if font.has_key("SVG "): print(" WARNING: Replacing existing SVG table in %s" % otfpath) # font.getReverseGlyphMap(rebuild=1) svg = table_S_V_G_("SVG ") svg.version = 0 svg.docList = [] svg.colorPalettes = None if parent_window is not None: progress = ProgressWindow("Rendering SVG ...", tickCount=len(self.keys()), parentWindow=parent_window) _palette = self.palettes[palette] _svg_palette = [] _docList = [] reindex = {0xffff: 0xffff} count = 0 for i in sorted(_palette.keys(), key=lambda k: int(k)): red = int(_palette[i][1:3], 16) green = int(_palette[i][3:5], 16) blue = int(_palette[i][5:7], 16) if len(_palette[i]) >= 9: alpha = int(_palette[i][7:9], 16) else: alpha = 0xff reindex[int(i)] = count count += 1 _svg_palette.append((red, green, blue, alpha)) # print("Palette:", len(_svg_palette), _svg_palette) _pen = SVGpen(self.rfont, optimize_output=True) for glyphname in self.keys(): # ["A", "P"]: #self.keys(): # look up glyph id try: gid = font.getGlyphID(glyphname) except: assert 0, "SVG table contains a glyph name not in font.getGlyphNames(): " + str( glyphname) # update progress bar if parent_window is not None: progress.update("Rendering SVG for /%s ..." % glyphname) # build svg glyph _svg_transfrom_group = """<g transform="scale(1 -1)">%s</g>""" contents = u"" for i in range(len(self[glyphname].layers)): _color_index = reindex[self[glyphname].colors[i]] # print(" Layer %i, color %i" % (i, _color_index)) rglyph = self.rfont[self[glyphname].layers[i]] if _color_index == 0xffff: r, g, b, a = (0, 0, 0, 0xff) else: r, g, b, a = _svg_palette[_color_index] _pen.reset() rglyph.draw(_pen) if _pen.d: contents += u'<g fill="#%02x%02x%02x"><path d="%s"/></g>' % ( r, g, b, _pen.d) if contents: contents = _svg_transfrom_group % contents _svg_doc = u"""<svg enable-background="new 0 0 64 64" id="glyph%i" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">%s</svg>""" % ( gid, contents) _docList.append((_svg_doc, gid, gid)) svg.docList = sorted(_docList, key=itemgetter(1)) if parent_window is not None: progress.close() # save font["SVG "] = svg font.save(otfpath[:-4] + "_svg" + otfpath[-4:]) font.close()