aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/varLib/instancer/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/varLib/instancer/__init__.py')
-rw-r--r--Lib/fontTools/varLib/instancer/__init__.py154
1 files changed, 98 insertions, 56 deletions
diff --git a/Lib/fontTools/varLib/instancer/__init__.py b/Lib/fontTools/varLib/instancer/__init__.py
index 6dad393e..8f976123 100644
--- a/Lib/fontTools/varLib/instancer/__init__.py
+++ b/Lib/fontTools/varLib/instancer/__init__.py
@@ -90,12 +90,11 @@ from fontTools.varLib import builder
from fontTools.varLib.mvar import MVAR_ENTRIES
from fontTools.varLib.merger import MutatorMerger
from fontTools.varLib.instancer import names
-from contextlib import contextmanager
+from fontTools.misc.cliTools import makeOutputFileName
import collections
from copy import deepcopy
from enum import IntEnum
import logging
-from itertools import islice
import os
import re
@@ -329,7 +328,9 @@ def limitTupleVariationAxisRange(var, axisTag, axisRange):
return [var, newVar]
-def _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True):
+def _instantiateGvarGlyph(
+ glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True
+):
coordinates, ctrl = glyf._getCoordinatesAndControls(glyphname, hMetrics, vMetrics)
endPts = ctrl.endPts
@@ -365,22 +366,26 @@ def _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits,
for var in tupleVarStore:
var.optimize(coordinates, endPts, isComposite)
+
def instantiateGvarGlyph(varfont, glyphname, axisLimits, optimize=True):
"""Remove?
https://github.com/fonttools/fonttools/pull/2266"""
gvar = varfont["gvar"]
glyf = varfont["glyf"]
- hMetrics = varfont['hmtx'].metrics
- vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
- _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+ hMetrics = varfont["hmtx"].metrics
+ vMetrics = getattr(varfont.get("vmtx"), "metrics", None)
+ _instantiateGvarGlyph(
+ glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize
+ )
+
def instantiateGvar(varfont, axisLimits, optimize=True):
log.info("Instantiating glyf/gvar tables")
gvar = varfont["gvar"]
glyf = varfont["glyf"]
- hMetrics = varfont['hmtx'].metrics
- vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
+ hMetrics = varfont["hmtx"].metrics
+ vMetrics = getattr(varfont.get("vmtx"), "metrics", None)
# Get list of glyph names sorted by component depth.
# If a composite glyph is processed before its base glyph, the bounds may
# be calculated incorrectly because deltas haven't been applied to the
@@ -395,7 +400,9 @@ def instantiateGvar(varfont, axisLimits, optimize=True):
),
)
for glyphname in glyphnames:
- _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+ _instantiateGvarGlyph(
+ glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize
+ )
if not gvar.variations:
del varfont["gvar"]
@@ -485,7 +492,7 @@ def _instantiateVHVAR(varfont, axisLimits, tableFields):
# or AdvHeightMap. If a direct, implicit glyphID->VariationIndex mapping is
# used for advances, skip re-optimizing and maintain original VariationIndex.
if getattr(vhvar, tableFields.advMapping):
- varIndexMapping = varStore.optimize()
+ varIndexMapping = varStore.optimize(use_NO_VARIATION_INDEX=False)
glyphOrder = varfont.getGlyphOrder()
_remapVarIdxMap(vhvar, tableFields.advMapping, varIndexMapping, glyphOrder)
if getattr(vhvar, tableFields.sb1): # left or top sidebearings
@@ -633,6 +640,7 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits):
for major, deltas in enumerate(defaultDeltaArray)
for minor, delta in enumerate(deltas)
}
+ defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0
return defaultDeltas
@@ -745,23 +753,7 @@ def _limitFeatureVariationConditionRange(condition, axisRange):
values = [minValue, maxValue]
for i, value in enumerate(values):
- if value < 0:
- if axisRange.minimum == 0:
- newValue = 0
- else:
- newValue = value / abs(axisRange.minimum)
- if newValue <= -1.0:
- newValue = -1.0
- elif value > 0:
- if axisRange.maximum == 0:
- newValue = 0
- else:
- newValue = value / axisRange.maximum
- if newValue >= 1.0:
- newValue = 1.0
- else:
- newValue = 0
- values[i] = newValue
+ values[i] = normalizeValue(value, (axisRange.minimum, 0, axisRange.maximum))
return AxisRange(*values)
@@ -806,12 +798,12 @@ def _instantiateFeatureVariationRecord(
return applies, shouldKeep
-def _limitFeatureVariationRecord(record, axisRanges, fvarAxes):
+def _limitFeatureVariationRecord(record, axisRanges, axisOrder):
newConditions = []
for i, condition in enumerate(record.ConditionSet.ConditionTable):
if condition.Format == 1:
axisIdx = condition.AxisIndex
- axisTag = fvarAxes[axisIdx].axisTag
+ axisTag = axisOrder[axisIdx]
if axisTag in axisRanges:
axisRange = axisRanges[axisTag]
newRange = _limitFeatureVariationConditionRange(condition, axisRange)
@@ -855,7 +847,7 @@ def _instantiateFeatureVariations(table, fvarAxes, axisLimits):
record, i, location, fvarAxes, axisIndexMap
)
if shouldKeep:
- shouldKeep = _limitFeatureVariationRecord(record, axisRanges, fvarAxes)
+ shouldKeep = _limitFeatureVariationRecord(record, axisRanges, axisOrder)
if shouldKeep and _featureVariationRecordIsUnique(record, uniqueRecords):
newRecords.append(record)
@@ -938,24 +930,16 @@ def instantiateAvar(varfont, axisLimits):
)
newMapping = {}
for fromCoord, toCoord in mapping.items():
- if fromCoord < 0:
- if axisRange.minimum == 0 or fromCoord < axisRange.minimum:
- continue
- else:
- fromCoord /= abs(axisRange.minimum)
- elif fromCoord > 0:
- if axisRange.maximum == 0 or fromCoord > axisRange.maximum:
- continue
- else:
- fromCoord /= axisRange.maximum
- if toCoord < 0:
- assert mappedMin != 0
- assert toCoord >= mappedMin
- toCoord /= abs(mappedMin)
- elif toCoord > 0:
- assert mappedMax != 0
- assert toCoord <= mappedMax
- toCoord /= mappedMax
+
+ if fromCoord < axisRange.minimum or fromCoord > axisRange.maximum:
+ continue
+ fromCoord = normalizeValue(
+ fromCoord, (axisRange.minimum, 0, axisRange.maximum)
+ )
+
+ assert mappedMin <= toCoord <= mappedMax
+ toCoord = normalizeValue(toCoord, (mappedMin, 0, mappedMax))
+
fromCoord = floatToFixedToFloat(fromCoord, 14)
toCoord = floatToFixedToFloat(toCoord, 14)
newMapping[fromCoord] = toCoord
@@ -1199,10 +1183,10 @@ def instantiateVariableFont(
requires the skia-pathops package (available to pip install).
The overlap parameter only has effect when generating full static instances.
updateFontNames (bool): if True, update the instantiated font's name table using
- the Axis Value Tables from the STAT table. The name table will be updated so
- it conforms to the R/I/B/BI model. If the STAT table is missing or
- an Axis Value table is missing for a given axis coordinate, a ValueError will
- be raised.
+ the Axis Value Tables from the STAT table. The name table and the style bits
+ in the head and OS/2 table will be updated so they conform to the R/I/B/BI
+ model. If the STAT table is missing or an Axis Value table is missing for
+ a given axis coordinate, a ValueError will be raised.
"""
# 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
overlap = OverlapMode(int(overlap))
@@ -1272,9 +1256,51 @@ def instantiateVariableFont(
},
)
+ if updateFontNames:
+ # Set Regular/Italic/Bold/Bold Italic bits as appropriate, after the
+ # name table has been updated.
+ setRibbiBits(varfont)
+
return varfont
+def setRibbiBits(font):
+ """Set the `head.macStyle` and `OS/2.fsSelection` style bits
+ appropriately."""
+
+ english_ribbi_style = font["name"].getName(names.NameID.SUBFAMILY_NAME, 3, 1, 0x409)
+ if english_ribbi_style is None:
+ return
+
+ styleMapStyleName = english_ribbi_style.toStr().lower()
+ if styleMapStyleName not in {"regular", "bold", "italic", "bold italic"}:
+ return
+
+ if styleMapStyleName == "bold":
+ font["head"].macStyle = 0b01
+ elif styleMapStyleName == "bold italic":
+ font["head"].macStyle = 0b11
+ elif styleMapStyleName == "italic":
+ font["head"].macStyle = 0b10
+
+ selection = font["OS/2"].fsSelection
+ # First clear...
+ selection &= ~(1 << 0)
+ selection &= ~(1 << 5)
+ selection &= ~(1 << 6)
+ # ...then re-set the bits.
+ if styleMapStyleName == "regular":
+ selection |= 1 << 6
+ elif styleMapStyleName == "bold":
+ selection |= 1 << 5
+ elif styleMapStyleName == "italic":
+ selection |= 1 << 0
+ elif styleMapStyleName == "bold italic":
+ selection |= 1 << 0
+ selection |= 1 << 5
+ font["OS/2"].fsSelection = selection
+
+
def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
location, axisRanges = {}, {}
for axisTag, value in axisLimits.items():
@@ -1380,6 +1406,18 @@ def parseArgs(args):
help="Update the instantiated font's `name` table. Input font must have "
"a STAT table with Axis Value Tables",
)
+ parser.add_argument(
+ "--no-recalc-timestamp",
+ dest="recalc_timestamp",
+ action="store_false",
+ help="Don't set the output font's timestamp to the current time.",
+ )
+ parser.add_argument(
+ "--no-recalc-bounds",
+ dest="recalc_bounds",
+ action="store_false",
+ help="Don't recalculate font bounding boxes",
+ )
loggingGroup = parser.add_mutually_exclusive_group(required=False)
loggingGroup.add_argument(
"-v", "--verbose", action="store_true", help="Run more verbosely."
@@ -1417,12 +1455,16 @@ def parseArgs(args):
def main(args=None):
- """Partially instantiate a variable font."""
+ """Partially instantiate a variable font"""
infile, axisLimits, options = parseArgs(args)
log.info("Restricting axes: %s", axisLimits)
log.info("Loading variable font")
- varfont = TTFont(infile)
+ varfont = TTFont(
+ infile,
+ recalcTimestamp=options.recalc_timestamp,
+ recalcBBoxes=options.recalc_bounds,
+ )
isFullInstance = {
axisTag for axisTag, limit in axisLimits.items() if not isinstance(limit, tuple)
@@ -1437,9 +1479,9 @@ def main(args=None):
updateFontNames=options.update_name_table,
)
+ suffix = "-instance" if isFullInstance else "-partial"
outfile = (
- os.path.splitext(infile)[0]
- + "-{}.ttf".format("instance" if isFullInstance else "partial")
+ makeOutputFileName(infile, overWrite=True, suffix=suffix)
if not options.output
else options.output
)