Exemple #1
0
 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()
Exemple #2
0
 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()
Exemple #3
0
    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()
Exemple #4
0
    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()
Exemple #5
0
 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')
Exemple #7
0
 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()
Exemple #8
0
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()
Exemple #9
0
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)
Exemple #10
0
    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()
Exemple #11
0
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
Exemple #12
0
    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()