Skip to content

Commit

Permalink
Merge pull request #332 from anthrotype/compile-variable
Browse files Browse the repository at this point in the history
add compileVariableTTF and compileVariableCFF2 public functions
  • Loading branch information
anthrotype authored May 13, 2019
2 parents f3ba608 + 5f77e04 commit d2a9f90
Show file tree
Hide file tree
Showing 13 changed files with 2,716 additions and 59 deletions.
103 changes: 102 additions & 1 deletion Lib/ufo2ft/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from __future__ import print_function, division, absolute_import

import os
from enum import IntEnum

from fontTools.misc.py23 import *
from fontTools import varLib

from ufo2ft.preProcessor import (
OTFPreProcessor,
Expand All @@ -18,6 +18,7 @@
from ufo2ft.outlineCompiler import OutlineOTFCompiler, OutlineTTFCompiler
from ufo2ft.postProcessor import PostProcessor
from ufo2ft.constants import SPARSE_TTF_MASTER_TABLES, SPARSE_OTF_MASTER_TABLES
from ufo2ft.util import getDefaultMasterFont
import logging

try:
Expand Down Expand Up @@ -497,3 +498,103 @@ def compileFeatures(
ufo, ttFont, glyphSet=glyphSet, featureWriters=featureWriters
)
return featureCompiler.compile()


def compileVariableTTF(
designSpaceDoc,
preProcessorClass=TTFInterpolatablePreProcessor,
outlineCompilerClass=OutlineTTFCompiler,
featureCompilerClass=None,
featureWriters=None,
glyphOrder=None,
useProductionNames=None,
cubicConversionError=None,
reverseDirection=True,
excludeVariationTables=(),
optimizeGvar=True,
inplace=False,
):
"""Create FontTools TrueType variable font from the DesignSpaceDocument UFO sources
with interpolatable outlines, using fontTools.varLib.build.
*optimizeGvar*, if set to False, will not perform IUP optimization on the
generated 'gvar' table.
*excludeVariationTables* is a list of sfnt table tags (str) that is passed on
to fontTools.varLib.build, to skip building some variation tables.
The rest of the arguments works the same as in the other compile functions.
Returns a new variable TTFont object.
"""
baseUfo = getDefaultMasterFont(designSpaceDoc)

ttfDesignSpace = compileInterpolatableTTFsFromDS(
designSpaceDoc,
preProcessorClass=preProcessorClass,
outlineCompilerClass=outlineCompilerClass,
featureCompilerClass=featureCompilerClass,
featureWriters=featureWriters,
glyphOrder=glyphOrder,
useProductionNames=False, # will rename glyphs after varfont is built
cubicConversionError=cubicConversionError,
reverseDirection=reverseDirection,
inplace=inplace,
)

logger.info("Building variable TTF font")

varfont = varLib.build(
ttfDesignSpace, exclude=excludeVariationTables, optimize=optimizeGvar
)[0]

postProcessor = PostProcessor(varfont, baseUfo)
varfont = postProcessor.process(useProductionNames)

return varfont


def compileVariableCFF2(
designSpaceDoc,
preProcessorClass=OTFPreProcessor,
outlineCompilerClass=OutlineOTFCompiler,
featureCompilerClass=None,
featureWriters=None,
glyphOrder=None,
useProductionNames=None,
roundTolerance=None,
excludeVariationTables=(),
inplace=False,
):
"""Create FontTools CFF2 variable font from the DesignSpaceDocument UFO sources
with interpolatable outlines, using fontTools.varLib.build.
*excludeVariationTables* is a list of sfnt table tags (str) that is passed on
to fontTools.varLib.build, to skip building some variation tables.
The rest of the arguments works the same as in the other compile functions.
Returns a new variable TTFont object.
"""
baseUfo = getDefaultMasterFont(designSpaceDoc)

otfDesignSpace = compileInterpolatableOTFsFromDS(
designSpaceDoc,
preProcessorClass=preProcessorClass,
outlineCompilerClass=outlineCompilerClass,
featureCompilerClass=featureCompilerClass,
featureWriters=featureWriters,
glyphOrder=glyphOrder,
useProductionNames=False, # will rename glyphs after varfont is built
roundTolerance=roundTolerance,
inplace=inplace,
)

logger.info("Building variable CFF2 font")

varfont = varLib.build(otfDesignSpace, exclude=excludeVariationTables)[0]

postProcessor = PostProcessor(varfont, baseUfo)
varfont = postProcessor.process(useProductionNames)

return varfont
6 changes: 6 additions & 0 deletions Lib/ufo2ft/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ class InvalidFontData(Error):
"""Raised when input font contains invalid data."""

pass


class InvalidDesignSpaceData(Error):
"""Raised when input DesignSpace document contains invalid data."""

pass
1 change: 1 addition & 0 deletions Lib/ufo2ft/postProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def process(self, useProductionNames=None, optimizeCFF=True):
and self._postscriptNames is not None,
)
if useProductionNames:
logger.info("Renaming glyphs to final production names")
self._rename_glyphs_from_ufo()
if optimizeCFF and "CFF " in self.otf:
from compreffor import compress
Expand Down
18 changes: 18 additions & 0 deletions Lib/ufo2ft/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,21 @@ def __str__(self):
from ufo2ft.fontInfoData import getAttrWithFallback

return getAttrWithFallback(self.font.info, "postscriptFontName")


def getDefaultMasterFont(designSpaceDoc):
defaultSource = designSpaceDoc.findDefault()
if not defaultSource:
from ufo2ft.errors import InvalidDesignSpaceData

raise InvalidDesignSpaceData(
"Can't find base (neutral) master in DesignSpace document"
)
if not defaultSource.font:
from ufo2ft.errors import InvalidDesignSpaceData

raise InvalidDesignSpaceData(
"DesignSpace source '%s' is missing required 'font' attribute"
% getattr(defaultSource, "name", "<Unknown>")
)
return defaultSource.font
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fonttools[lxml,ufo]==3.41.0
fonttools[lxml,ufo]==3.41.2
defcon==0.6.0
cu2qu==1.6.5
compreffor==0.4.6.post1
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
setup_requires=pytest_runner + wheel + ["setuptools_scm"],
tests_require=["pytest>=2.8"],
install_requires=[
"fonttools[ufo]>=3.41.0",
"fonttools[ufo]>=3.41.2",
"defcon>=0.6.0",
"cu2qu>=1.6.5",
"compreffor>=0.4.6",
Expand Down
63 changes: 62 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import importlib
import os
import py
import pytest

from fontTools import designspaceLib


@pytest.fixture(scope="session", params=["defcon", "ufoLib2"])
def ufo_module(request):
Expand Down Expand Up @@ -30,3 +32,62 @@ def InfoClass(ufo_module):
@pytest.fixture
def datadir():
return py.path.local(py.path.local(__file__).dirname).join("data")


def getpath(filename):
dirname = os.path.dirname(__file__)
return os.path.join(dirname, "data", filename)


@pytest.fixture
def layertestrgufo(FontClass):
font = FontClass(getpath("LayerFont-Regular.ufo"))
return font


@pytest.fixture
def layertestbdufo(FontClass):
font = FontClass(getpath("LayerFont-Bold.ufo"))
return font


@pytest.fixture
def designspace(layertestrgufo, layertestbdufo):
ds = designspaceLib.DesignSpaceDocument()

a1 = designspaceLib.AxisDescriptor()
a1.tag = "wght"
a1.name = "Weight"
a1.default = a1.minimum = 350
a1.maximum = 625
ds.addAxis(a1)

s1 = designspaceLib.SourceDescriptor()
s1.name = "Layer Font Regular"
s1.familyName = "Layer Font"
s1.styleName = "Regular"
s1.filename = "LayerFont-Regular.ufo"
s1.location = {"Weight": 350}
s1.font = layertestrgufo
ds.addSource(s1)

s2 = designspaceLib.SourceDescriptor()
s2.name = "Layer Font Medium"
s2.familyName = "Layer Font"
s2.styleName = "Medium"
s2.filename = "LayerFont-Regular.ufo"
s2.layerName = "Medium"
s2.location = {"Weight": 450}
s2.font = layertestrgufo
ds.addSource(s2)

s3 = designspaceLib.SourceDescriptor()
s3.name = "Layer Font Bold"
s3.familyName = "Layer Font"
s3.styleName = "Bold"
s3.filename = "LayerFont-Bold.ufo"
s3.location = {"Weight": 625}
s3.font = layertestbdufo
ds.addSource(s3)

return ds
Loading

0 comments on commit d2a9f90

Please sign in to comment.