Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding VF extracting support #40

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions Lib/extractor/formats/opentype.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import time
from fontTools.pens.boundsPen import ControlBoundsPen
from fontTools.pens.hashPointPen import HashPointPen
Expand All @@ -8,6 +9,7 @@
USE_MY_METRICS,
)
from fontTools.ttLib.tables._h_e_a_d import mac_epoch_diff
from fontTools.varLib.mutator import instantiateVariableFont
from extractor.exceptions import ExtractorError
from extractor.stream import InstructionStream
from extractor.tools import RelaxedInfo, copyAttr
Expand All @@ -32,6 +34,23 @@ def isOpenType(pathOrFile):
return False
return True

def isVarFont(pathOrFile):
try:
font = TTFont(pathOrFile)
fvar = font["fvar"]
del font
except TTLibError:
return False
return True

def locationToName(locations):
name = ""
for tag,val in locations.items():
name += f"{tag}_{val};"
return name

def getDefaultLocation(varFont):
return {axis.axisTag:axis.defaultValue for axis in varFont["fvar"].axes}

def extractFontFromOpenType(
pathOrFile,
Expand All @@ -48,6 +67,19 @@ def extractFontFromOpenType(
extractOpenTypeInfo(source, destination)
if doGlyphs:
extractOpenTypeGlyphs(source, destination)
if isVarFont:
'''
Add Support for extracting Variable Instances as RLayer objects
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better separate UFOs or layers? Not sure.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good point, I had imagined a layer-based import just because I thought containing everything in one file was more "UFO-esque" but multiple files could be a better solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be good to have the option to do either

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extractor works by providing a dest font object and a source binary file.

ufo = defcon.Font()
extractor.extractUFO("/path/to/MyFont.ttf", ufo)

Returning a list of ufo's is a big change. Adding arguments is also a big change.

Im thinking that extracting variable fonts should be implicit, and maybe directly into a designspace object: extract axis, ranges, the relationships between sources, store font objects as sources in the designspace document object:

extractVariableFont("path/to/varfont.ttf", designspaceDocumentObject)
# raise error when no fvar table is found

Extractor is not build to round trip from binary to ufo and back, so parts can lost during extracting. But is would be cool to get as much data as possible back into scriptable objects.

Currently this is pulling the VFs instances but I think using the Sources is a better solution?
'''
defLoc = getDefaultLocation(source)
locations = [instance.coordinates for instance in source["fvar"].instances]
for varLoc in locations:
if varLoc != defLoc:
instanceFont = instantiateVariableFont(source,varLoc)
layerName = locationToName(varLoc)
extractOpenTypeGlyphs(instanceFont, destination, layerName)

extractUnicodeVariationSequences(source, destination)
if doGlyphOrder:
extractGlyphOrder(source, destination)
Expand All @@ -59,7 +91,11 @@ def extractFontFromOpenType(
for function in customFunctions:
function(source, destination)
if doInstructions:
extractInstructions(source, destination)
if not isVarFont:
'''
seems to run into issue with extracting VF component indentifier
'''
extractInstructions(source, destination)
source.close()


Expand Down Expand Up @@ -522,19 +558,23 @@ def binaryToIntList(value, start=0):
# --------


def extractOpenTypeGlyphs(source, destination):
def extractOpenTypeGlyphs(source, destination, layerName="public.default"):
# grab the cmap
vmtx = source.get("vmtx")
vorg = source.get("VORG")
is_ttf = "glyf" in source
reversedMapping = source.get("cmap").buildReversed()
# add layers
if layerName != destination.defaultLayerName:
destination.newLayer(layerName)
# grab the glyphs
glyphSet = source.getGlyphSet()
for glyphName in glyphSet.keys():
sourceGlyph = glyphSet[glyphName]
# make the new glyph
destination.newGlyph(glyphName)
destinationGlyph = destination[glyphName]
destinationLayer = destination.getLayer(layerName)
destinationLayer.newGlyph(glyphName)
destinationGlyph = destinationLayer[glyphName]
# outlines
if is_ttf:
pen = destinationGlyph.getPointPen()
Expand Down