Skip to content

Commit

Permalink
Merge pull request #387 from anthrotype/notdef-outline
Browse files Browse the repository at this point in the history
Take .notdef outline from the default master, add notdefOutline param
  • Loading branch information
anthrotype authored Jun 9, 2020
2 parents 7a33dd1 + deec978 commit e1c08f6
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 42 deletions.
23 changes: 21 additions & 2 deletions Lib/ufo2ft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +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
from ufo2ft.util import getDefaultMasterFont, _getDefaultNotdefGlyph
import logging

try:
Expand Down Expand Up @@ -54,6 +54,7 @@ def compileOTF(
debugFeatureFile=None,
cffVersion=1,
subroutinizer=None,
notdefGlyph=None,
_tables=None,
):
"""Create FontTools CFF font from a UFO.
Expand Down Expand Up @@ -128,6 +129,7 @@ def compileOTF(
ufo,
glyphSet=glyphSet,
glyphOrder=glyphOrder,
notdefGlyph=notdefGlyph,
roundTolerance=roundTolerance,
optimizeCFF=optimizeCFF >= CFFOptimization.SPECIALIZE,
tables=_tables,
Expand Down Expand Up @@ -174,6 +176,7 @@ def compileTTF(
layerName=None,
skipExportGlyphs=None,
debugFeatureFile=None,
notdefGlyph=None,
):
"""Create FontTools TrueType font from a UFO.
Expand Down Expand Up @@ -213,7 +216,7 @@ def compileTTF(

logger.info("Building OpenType tables")
outlineCompiler = outlineCompilerClass(
ufo, glyphSet=glyphSet, glyphOrder=glyphOrder
ufo, glyphSet=glyphSet, glyphOrder=glyphOrder, notdefGlyph=notdefGlyph
)
otf = outlineCompiler.compile()

Expand Down Expand Up @@ -248,6 +251,7 @@ def compileInterpolatableTTFs(
layerNames=None,
skipExportGlyphs=None,
debugFeatureFile=None,
notdefGlyph=None,
):
"""Create FontTools TrueType fonts from a list of UFOs with interpolatable
outlines. Cubic curves are converted compatibly to quadratic curves using
Expand Down Expand Up @@ -303,6 +307,7 @@ def compileInterpolatableTTFs(
ufo,
glyphSet=glyphSet,
glyphOrder=glyphOrder,
notdefGlyph=notdefGlyph,
tables=SPARSE_TTF_MASTER_TABLES if layerName else None,
)
ttf = outlineCompiler.compile()
Expand Down Expand Up @@ -350,6 +355,7 @@ def compileInterpolatableTTFsFromDS(
reverseDirection=True,
inplace=False,
debugFeatureFile=None,
notdefGlyph=None,
):
"""Create FontTools TrueType fonts from the DesignSpaceDocument UFO sources
with interpolatable outlines. Cubic curves are converted compatibly to
Expand Down Expand Up @@ -388,6 +394,9 @@ def compileInterpolatableTTFsFromDS(

skipExportGlyphs = designSpaceDoc.lib.get("public.skipExportGlyphs", [])

if notdefGlyph is None:
notdefGlyph = _getDefaultNotdefGlyph(designSpaceDoc)

ttfs = compileInterpolatableTTFs(
ufos,
preProcessorClass=preProcessorClass,
Expand All @@ -402,6 +411,7 @@ def compileInterpolatableTTFsFromDS(
layerNames=layerNames,
skipExportGlyphs=skipExportGlyphs,
debugFeatureFile=debugFeatureFile,
notdefGlyph=notdefGlyph,
)

if inplace:
Expand All @@ -425,6 +435,7 @@ def compileInterpolatableOTFsFromDS(
roundTolerance=None,
inplace=False,
debugFeatureFile=None,
notdefGlyph=None,
):
"""Create FontTools CFF fonts from the DesignSpaceDocument UFO sources
with interpolatable outlines.
Expand Down Expand Up @@ -461,6 +472,9 @@ def compileInterpolatableOTFsFromDS(

skipExportGlyphs = designSpaceDoc.lib.get("public.skipExportGlyphs", [])

if notdefGlyph is None:
notdefGlyph = _getDefaultNotdefGlyph(designSpaceDoc)

otfs = []
for source in designSpaceDoc.sources:
otfs.append(
Expand All @@ -480,6 +494,7 @@ def compileInterpolatableOTFsFromDS(
inplace=inplace,
skipExportGlyphs=skipExportGlyphs,
debugFeatureFile=debugFeatureFile,
notdefGlyph=notdefGlyph,
_tables=SPARSE_OTF_MASTER_TABLES if source.layerName else None,
)
)
Expand Down Expand Up @@ -555,6 +570,7 @@ def compileVariableTTF(
optimizeGvar=True,
inplace=False,
debugFeatureFile=None,
notdefGlyph=None,
):
"""Create FontTools TrueType variable font from the DesignSpaceDocument UFO sources
with interpolatable outlines, using fontTools.varLib.build.
Expand Down Expand Up @@ -583,6 +599,7 @@ def compileVariableTTF(
reverseDirection=reverseDirection,
inplace=inplace,
debugFeatureFile=debugFeatureFile,
notdefGlyph=notdefGlyph,
)

logger.info("Building variable TTF font")
Expand Down Expand Up @@ -610,6 +627,7 @@ def compileVariableCFF2(
inplace=False,
debugFeatureFile=None,
optimizeCFF=CFFOptimization.SPECIALIZE,
notdefGlyph=None,
):
"""Create FontTools CFF2 variable font from the DesignSpaceDocument UFO sources
with interpolatable outlines, using fontTools.varLib.build.
Expand Down Expand Up @@ -642,6 +660,7 @@ def compileVariableCFF2(
roundTolerance=roundTolerance,
inplace=inplace,
debugFeatureFile=debugFeatureFile,
notdefGlyph=notdefGlyph,
)

logger.info("Building variable CFF2 font")
Expand Down
51 changes: 35 additions & 16 deletions Lib/ufo2ft/outlineCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
makeOfficialGlyphOrder,
makeUnicodeToGlyphNameMapping,
calcCodePageRanges,
_copyGlyph,
)
from ufo2ft.constants import COLOR_LAYERS_KEY, COLOR_PALETTES_KEY

Expand Down Expand Up @@ -76,12 +77,19 @@ class BaseOutlineCompiler(object):
"COLR", "CPAL"]
)

def __init__(self, font, glyphSet=None, glyphOrder=None, tables=None):
def __init__(
self,
font,
glyphSet=None,
glyphOrder=None,
tables=None,
notdefGlyph=None,
):
self.ufo = font
# use the previously filtered glyphSet, if any
if glyphSet is None:
glyphSet = {g.name: g for g in font}
self.makeMissingRequiredGlyphs(font, glyphSet, self.sfntVersion)
self.makeMissingRequiredGlyphs(font, glyphSet, self.sfntVersion, notdefGlyph)
self.allGlyphs = glyphSet
# store the glyph order
if glyphOrder is None:
Expand Down Expand Up @@ -208,7 +216,7 @@ def makeUnicodeToGlyphNameMapping(self):
return makeUnicodeToGlyphNameMapping(self.allGlyphs, self.glyphOrder)

@staticmethod
def makeMissingRequiredGlyphs(font, glyphSet, sfntVersion):
def makeMissingRequiredGlyphs(font, glyphSet, sfntVersion, notdefGlyph=None):
"""
Add .notdef to the glyph set if it is not present.
Expand All @@ -219,18 +227,24 @@ def makeMissingRequiredGlyphs(font, glyphSet, sfntVersion):
if ".notdef" in glyphSet:
return

unitsPerEm = otRound(getAttrWithFallback(font.info, "unitsPerEm"))
ascender = otRound(getAttrWithFallback(font.info, "ascender"))
descender = otRound(getAttrWithFallback(font.info, "descender"))
defaultWidth = otRound(unitsPerEm * 0.5)
glyphSet[".notdef"] = StubGlyph(
name=".notdef",
width=defaultWidth,
unitsPerEm=unitsPerEm,
ascender=ascender,
descender=descender,
reverseContour=(sfntVersion == "\000\001\000\000")
)
reverseContour = sfntVersion == "\000\001\000\000"
if notdefGlyph:
notdefGlyph = _copyGlyph(notdefGlyph, reverseContour=reverseContour)
else:
unitsPerEm = otRound(getAttrWithFallback(font.info, "unitsPerEm"))
ascender = otRound(getAttrWithFallback(font.info, "ascender"))
descender = otRound(getAttrWithFallback(font.info, "descender"))
defaultWidth = otRound(unitsPerEm * 0.5)
notdefGlyph = StubGlyph(
name=".notdef",
width=defaultWidth,
unitsPerEm=unitsPerEm,
ascender=ascender,
descender=descender,
reverseContour=reverseContour,
)

glyphSet[".notdef"] = notdefGlyph

def makeOfficialGlyphOrder(self, glyphOrder):
"""
Expand Down Expand Up @@ -976,6 +990,7 @@ def __init__(
glyphSet=None,
glyphOrder=None,
tables=None,
notdefGlyph=None,
roundTolerance=None,
optimizeCFF=True,
):
Expand All @@ -985,7 +1000,11 @@ def __init__(
# round all coordinates to integers by default
self.roundTolerance = 0.5
super(OutlineOTFCompiler, self).__init__(
font, glyphSet=glyphSet, glyphOrder=glyphOrder, tables=tables
font,
glyphSet=glyphSet,
glyphOrder=glyphOrder,
tables=tables,
notdefGlyph=notdefGlyph,
)
self.optimizeCFF = optimizeCFF
self._defaultAndNominalWidths = None
Expand Down
65 changes: 50 additions & 15 deletions Lib/ufo2ft/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,21 @@ def from_layer(cls, font, layerName=None, copy=False, skipExportGlyphs=None):


def _copyLayer(layer, obj_type=dict):
# defcon.Glyph doesn't take a name argument, ufoLib2 requires one...
try:
g = next(iter(layer))
except StopIteration: # layer is empty
return obj_type()

cls = g.__class__
newGlyph = _getNewGlyphFactory(g)
glyphSet = obj_type()
for glyph in layer:
glyphSet[glyph.name] = _copyGlyph(glyph, glyphFactory=newGlyph)
return glyphSet


def _getNewGlyphFactory(glyph):
# defcon.Glyph doesn't take a name argument, ufoLib2 requires one...
cls = glyph.__class__
if "name" in getargspec(cls.__init__).args:

def newGlyph(name):
Expand All @@ -107,23 +115,34 @@ def newGlyph(name):
def newGlyph(name):
# use instantiateGlyphObject() to keep any custom sub-element classes
# https://github.com/googlefonts/ufo2ft/issues/363
g2 = g.layer.instantiateGlyphObject()
g2 = glyph.layer.instantiateGlyphObject()
g2.name = name
return g2

return newGlyph


def _copyGlyph(glyph, glyphFactory=None, reverseContour=False):
# copy everything except unused attributes: 'guidelines', 'note', 'image'
glyphSet = obj_type()
for glyph in layer:
copy = newGlyph(glyph.name)
copy.width = glyph.width
copy.height = glyph.height
copy.unicodes = list(glyph.unicodes)
copy.anchors = [dict(a) for a in glyph.anchors]
copy.lib = deepcopy(glyph.lib)
pointPen = copy.getPointPen()
glyph.drawPoints(pointPen)
glyphSet[glyph.name] = copy
return glyphSet
if glyphFactory is None:
glyphFactory = _getNewGlyphFactory(glyph)

copy = glyphFactory(glyph.name)
copy.width = glyph.width
copy.height = glyph.height
copy.unicodes = list(glyph.unicodes)
copy.anchors = [dict(a) for a in glyph.anchors]
copy.lib = deepcopy(glyph.lib)

pointPen = copy.getPointPen()
if reverseContour:
from fontTools.pens.pointPen import ReverseContourPointPen

pointPen = ReverseContourPointPen(pointPen)

glyph.drawPoints(pointPen)

return copy


def deepCopyContours(
Expand Down Expand Up @@ -393,3 +412,19 @@ def getDefaultMasterFont(designSpaceDoc):
% getattr(defaultSource, "name", "<Unknown>")
)
return defaultSource.font


def _getDefaultNotdefGlyph(designSpaceDoc):
from ufo2ft.errors import InvalidDesignSpaceData

try:
baseUfo = getDefaultMasterFont(designSpaceDoc)
except InvalidDesignSpaceData:
notdefGlyph = None
else:
# unlike ufoLib2, defcon has no Font.get() method
try:
notdefGlyph = baseUfo[".notdef"]
except KeyError:
notdefGlyph = None
return notdefGlyph
18 changes: 18 additions & 0 deletions tests/data/LayerFont-Bold.ufo/glyphs/_notdef.glif
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<glyph name=".notdef" format="2">
<advance height="1000" width="500"/>
<outline>
<contour>
<point x="50" y="-250" type="line"/>
<point x="450" y="-250" type="line"/>
<point x="450" y="750" type="line"/>
<point x="50" y="750" type="line"/>
</contour>
<contour>
<point x="100" y="-200" type="line"/>
<point x="100" y="700" type="line"/>
<point x="400" y="700" type="line"/>
<point x="400" y="-200" type="line"/>
</contour>
</outline>
</glyph>
2 changes: 2 additions & 0 deletions tests/data/LayerFont-Bold.ufo/glyphs/contents.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>.notdef</key>
<string>_notdef.glif</string>
<key>a</key>
<string>a.glif</string>
<key>dotabovecomb</key>
Expand Down
18 changes: 18 additions & 0 deletions tests/data/LayerFont-Regular.ufo/glyphs/_notdef.glif
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<glyph name=".notdef" format="2">
<advance height="1000" width="500"/>
<outline>
<contour>
<point x="50" y="-250" type="line"/>
<point x="450" y="-250" type="line"/>
<point x="450" y="750" type="line"/>
<point x="50" y="750" type="line"/>
</contour>
<contour>
<point x="100" y="-200" type="line"/>
<point x="100" y="700" type="line"/>
<point x="400" y="700" type="line"/>
<point x="400" y="-200" type="line"/>
</contour>
</outline>
</glyph>
2 changes: 2 additions & 0 deletions tests/data/LayerFont-Regular.ufo/glyphs/contents.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>.notdef</key>
<string>_notdef.glif</string>
<key>a</key>
<string>a.glif</string>
<key>dotabovecomb</key>
Expand Down
6 changes: 3 additions & 3 deletions tests/data/TestVariableFont-CFF2-cffsubr.ttx
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@
<CharStrings>
<CharString name=".notdef">
50 -250 rmoveto
400 1000 -400 -1000 hlineto
50 50 rmoveto
900 300 -900 -300 vlineto
400 1000 -400 hlineto
50 -950 rmoveto
900 300 -900 vlineto
</CharString>
<CharString name="a">
468 -1 rmoveto
Expand Down
Loading

0 comments on commit e1c08f6

Please sign in to comment.