Skip to content

Commit

Permalink
[varLib.avar] Sketch of code to reconstruct mappings from binary
Browse files Browse the repository at this point in the history
  • Loading branch information
behdad committed Aug 30, 2024
1 parent 770917d commit 150d4fc
Showing 1 changed file with 105 additions and 1 deletion.
106 changes: 105 additions & 1 deletion Lib/fontTools/varLib/avar.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,104 @@
from fontTools.varLib import _add_avar, load_designspace
from fontTools.varLib.varStore import VarStoreInstancer
from fontTools.misc.fixedTools import fixedToFloat as fi2fl
from fontTools.misc.cliTools import makeOutputFileName
from itertools import product
import logging

log = logging.getLogger("fontTools.varLib.avar")


def _denormalize(v, axis):
return axis.defaultValue + v * (
(axis.maxValue if v >= 0 else axis.minValue) - axis.defaultValue
)


def mappings_from_avar(font, denormalize=True):
fvarAxes = font["fvar"].axes
axisMap = {a.axisTag: a for a in fvarAxes}
axisTags = [a.axisTag for a in fvarAxes]
axisIndexes = {a.axisTag: i for i, a in enumerate(fvarAxes)}
if "avar" not in font:
return {}, {}
avar = font["avar"]
axisMaps = {
tag: seg
for tag, seg in avar.segments.items()
if seg and seg != {-1: -1, 0: 0, 1: 1}
}
mappings = []

if getattr(avar, "majorVersion", 1) == 2:
varStore = avar.table.VarStore
regions = varStore.VarRegionList.Region
inputLocations = set()
for varData in varStore.VarData:
regionIndices = varData.VarRegionIndex
for regionIndex in regionIndices:
peakLocation = {}
corners = []
region = regions[regionIndex]
for axisIndex, axis in enumerate(region.VarRegionAxis):
if axis.PeakCoord == 0:
continue
axisTag = axisTags[axisIndex]
peakLocation[axisTag] = axis.PeakCoord
corner = []
if axis.StartCoord != 0:
corner.append((axisTag, axis.StartCoord))
if axis.EndCoord != 0:
corner.append((axisTag, axis.EndCoord))
corners.append(corner)
corners = set(product(*corners))
inputLocations.update(corners)

inputLocations = [
dict(t)
for t in sorted(
inputLocations,
key=lambda t: (len(t), tuple(axisIndexes[tag] for tag, _ in t)),
)
]

varIdxMap = avar.table.VarIdxMap
instancer = VarStoreInstancer(varStore, fvarAxes)
for location in inputLocations:
instancer.setLocation(location)
outputLocation = {}
for axisIndex, axisTag in enumerate(axisTags):
varIdx = axisIndex
if varIdxMap is not None:
varIdx = varIdxMap[varIdx]
delta = instancer[varIdx]
if delta != 0:
v = location.get(axisTag, 0)
v = v + fi2fl(delta, 14)
v = max(-1, min(1, v))
outputLocation[axisTag] = v
mappings.append((location, outputLocation))
# Filter out empty mappings
mappings = [io for io in mappings if io[1]]

if denormalize:
for tag, seg in axisMaps.items():
if tag not in axisMap:
raise ValueError(f"Unknown axis tag {tag}")
denorm = lambda v: _denormalize(v, axisMap[tag])
axisMaps[tag] = {denorm(k): denorm(v) for k, v in seg.items()}

for i, (inputLoc, outputLoc) in enumerate(mappings):
inputLoc = {
tag: _denormalize(val, axisMap[tag]) for tag, val in inputLoc.items()
}
outputLoc = {
tag: _denormalize(val, axisMap[tag]) for tag, val in outputLoc.items()
}
mappings[i] = (inputLoc, outputLoc)

return axisMaps, mappings


def main(args=None):
"""Add `avar` table from designspace file to variable font."""

Expand All @@ -24,7 +118,11 @@ def main(args=None):
)
parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
parser.add_argument(
"designspace", metavar="family.designspace", help="Designspace file."
"designspace",
metavar="family.designspace",
help="Designspace file.",
nargs="?",
default=None,
)
parser.add_argument(
"-o",
Expand All @@ -45,6 +143,12 @@ def main(args=None):
log.error("Not a variable font.")
return 1

if options.designspace is None:
from pprint import pprint

pprint(mappings_from_avar(font))
return

axisTags = [a.axisTag for a in font["fvar"].axes]

ds = load_designspace(options.designspace)
Expand Down

0 comments on commit 150d4fc

Please sign in to comment.