def buildStatic(
            self,
            ufo,  # input UFO as filename string or defcon.Font object
            outputFilename,  # output filename string
            cff=True,  # true = makes CFF outlines. false = makes TTF outlines.
            **kwargs,  # passed along to ufo2ft.compile*()
    ):
        if isinstance(ufo, str):
            ufo = Font(ufo)

        # update version to actual, real version. Must come after any call to setFontInfo.
        updateFontVersion(ufo, dummy=False, isVF=False)

        compilerOptions = dict(
            useProductionNames=True,
            inplace=True,  # avoid extra copy
            removeOverlaps=True,
            overlapsBackend='pathops',  # use Skia's pathops
        )

        log.info("compiling %s -> %s (%s)", _LazyFontName(ufo), outputFilename,
                 "OTF/CFF-2" if cff else "TTF")

        if cff:
            font = ufo2ft.compileOTF(ufo, **compilerOptions)
        else:  # ttf
            font = ufo2ft.compileTTF(ufo, **compilerOptions)

        log.debug("writing %s", outputFilename)
        font.save(outputFilename)
Пример #2
0
    def __call__(self, font, glyphSet=None):
        """Run this filter on all the included glyphs.
        Return the set of glyph names that were modified, if any.

        If `glyphSet` (dict) argument is provided, run the filter on
        the glyphs contained therein (which may be copies).
        Otherwise, run the filter in-place on the font's default
        glyph set.
        """
        fontName = _LazyFontName(font)
        if glyphSet is not None and getattr(glyphSet, "name", None):
            logger.info("Running %s on %s-%s", self.name, fontName,
                        glyphSet.name)
        else:
            logger.info("Running %s on %s", self.name, fontName)

        if glyphSet is None:
            glyphSet = _GlyphSet.from_layer(font)

        context = self.set_context(font, glyphSet)

        filter_ = self.filter
        include = self.include
        modified = context.modified

        with Timer() as t:
            # we sort the glyph names to make loop deterministic
            for glyphName in sorted(glyphSet.keys()):
                if glyphName in modified:
                    continue
                glyph = glyphSet[glyphName]
                if include(glyph) and filter_(glyph):
                    modified.add(glyphName)

        num = len(modified)
        if num > 0:
            logger.debug(
                "Took %.3fs to run %s on %d glyph%s",
                t,
                self.name,
                len(modified),
                "" if num == 1 else "s",
            )
        return modified
Пример #3
0
    def buildStatic(
            self,
            ufo,  # input UFO as filename string or defcon.Font object
            outputFilename,  # output filename string
            cff=True,  # true = makes CFF outlines. false = makes TTF outlines.
            **kwargs,  # passed along to ufo2ft.compile*()
    ):
        if isinstance(ufo, str):
            ufo = Font(ufo)

        # update version to actual, real version. Must come after any call to setFontInfo.
        updateFontVersion(ufo, dummy=False, isVF=False)

        # decompose some glyphs
        glyphNamesToDecompose = set()
        for g in ufo:
            directives = findGlyphDirectives(g.note)
            if 'decompose' in directives or (g.components and
                                             not composedGlyphIsTrivial(g)):
                glyphNamesToDecompose.add(g.name)
        self._decompose([ufo], glyphNamesToDecompose)

        compilerOptions = dict(
            useProductionNames=True,
            inplace=True,  # avoid extra copy
            removeOverlaps=True,
            overlapsBackend='pathops',  # use Skia's pathops
        )

        log.info("compiling %s -> %s (%s)", _LazyFontName(ufo), outputFilename,
                 "OTF/CFF-2" if cff else "TTF")

        if cff:
            font = ufo2ft.compileOTF(ufo, **compilerOptions)
        else:  # ttf
            font = ufo2ft.compileTTF(ufo, **compilerOptions)

        log.debug("writing %s", outputFilename)
        font.save(outputFilename)
Пример #4
0
def compileInterpolatableTTFs(
    ufos,
    preProcessorClass=TTFInterpolatablePreProcessor,
    outlineCompilerClass=OutlineTTFCompiler,
    featureCompilerClass=None,
    featureWriters=None,
    glyphOrder=None,
    useProductionNames=None,
    cubicConversionError=None,
    reverseDirection=True,
    inplace=False,
    layerNames=None,
    skipExportGlyphs=None,
):
    """Create FontTools TrueType fonts from a list of UFOs with interpolatable
    outlines. Cubic curves are converted compatibly to quadratic curves using
    the Cu2Qu conversion algorithm.

    Return an iterator object that yields a TTFont instance for each UFO.

    *layerNames* refers to the layer names to use glyphs from in the order of
    the UFOs in *ufos*. By default, this is a list of `[None]` times the number
    of UFOs, i.e. using the default layer from all the UFOs.

    When the layerName is not None for a given UFO, the corresponding TTFont object
    will contain only a minimum set of tables ("head", "hmtx", "glyf", "loca", "maxp",
    "post" and "vmtx"), and no OpenType layout tables.

    *skipExportGlyphs* is a list or set of glyph names to not be exported to the
    final font. If these glyphs are used as components in any other glyph, those
    components get decomposed. If the parameter is not passed in, the union of
    all UFO's "public.skipExportGlyphs" lib keys will be used. If they don't
    exist, all glyphs are exported. UFO groups and kerning will be pruned of
    skipped glyphs.
    """
    from ufo2ft.util import _LazyFontName

    if layerNames is None:
        layerNames = [None] * len(ufos)
    assert len(ufos) == len(layerNames)

    if skipExportGlyphs is None:
        skipExportGlyphs = set()
        for ufo in ufos:
            skipExportGlyphs.update(ufo.lib.get("public.skipExportGlyphs", []))

    logger.info("Pre-processing glyphs")
    preProcessor = preProcessorClass(
        ufos,
        inplace=inplace,
        conversionError=cubicConversionError,
        reverseDirection=reverseDirection,
        layerNames=layerNames,
        skipExportGlyphs=skipExportGlyphs,
    )
    glyphSets = preProcessor.process()

    for ufo, glyphSet, layerName in zip(ufos, glyphSets, layerNames):
        fontName = _LazyFontName(ufo)
        if layerName is not None:
            logger.info("Building OpenType tables for %s-%s", fontName,
                        layerName)
        else:
            logger.info("Building OpenType tables for %s", fontName)

        outlineCompiler = outlineCompilerClass(
            ufo,
            glyphSet=glyphSet,
            glyphOrder=glyphOrder,
            tables=SPARSE_TTF_MASTER_TABLES if layerName else None,
        )
        ttf = outlineCompiler.compile()

        # Only the default layer is likely to have all glyphs used in feature
        # code.
        if layerName is None:
            compileFeatures(
                ufo,
                ttf,
                glyphSet=glyphSet,
                featureWriters=featureWriters,
                featureCompilerClass=featureCompilerClass,
            )

        postProcessor = PostProcessor(ttf, ufo, glyphSet=glyphSet)
        ttf = postProcessor.process(useProductionNames)

        if layerName is not None:
            # for sparse masters (i.e. containing only a subset of the glyphs), we
            # need to include the post table in order to store glyph names, so that
            # fontTools.varLib can interpolate glyphs with same name across masters.
            # However we want to prevent the underlinePosition/underlineThickness
            # fields in such sparse masters to be included when computing the deltas
            # for the MVAR table. Thus, we set them to this unlikely, limit value
            # (-36768) which is a signal varLib should ignore them when building MVAR.
            ttf["post"].underlinePosition = -0x8000
            ttf["post"].underlineThickness = -0x8000

        yield ttf