[CFF] Move variations-specific CFF code to varLib.cff module
This commit is contained in:
parent
aedcc33bbf
commit
93633a85ef
@ -1,247 +0,0 @@
|
|||||||
from __future__ import print_function, division, absolute_import
|
|
||||||
from fontTools.misc.psCharStrings import T2CharString
|
|
||||||
from fontTools.pens.t2CharStringPen import T2CharStringPen, t2c_round
|
|
||||||
from fontTools.cffLib.specializer import (commandsToProgram,
|
|
||||||
specializeCommands)
|
|
||||||
from fontTools.cffLib import maxStackLimit
|
|
||||||
from fontTools.varLib.models import allEqual
|
|
||||||
|
|
||||||
|
|
||||||
class MergeTypeError(TypeError):
|
|
||||||
def __init__(self, point_type, pt_index, m_index, default_type, glyphName):
|
|
||||||
self.error_msg = [
|
|
||||||
"In glyph '{gname}' "
|
|
||||||
"'{point_type}' at point index {pt_index} in master "
|
|
||||||
"index {m_index} differs from the default font point "
|
|
||||||
"type '{default_type}'"
|
|
||||||
"".format(gname=glyphName,
|
|
||||||
point_type=point_type, pt_index=pt_index,
|
|
||||||
m_index=m_index, default_type=default_type)
|
|
||||||
][0]
|
|
||||||
super(MergeTypeError, self).__init__(self.error_msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def makeRoundNumberFunc(tolerance):
|
|
||||||
if tolerance < 0:
|
|
||||||
raise ValueError("Rounding tolerance must be positive")
|
|
||||||
|
|
||||||
def roundNumber(val):
|
|
||||||
return t2c_round(val, tolerance)
|
|
||||||
|
|
||||||
return roundNumber
|
|
||||||
|
|
||||||
|
|
||||||
class CFF2CharStringMergePen(T2CharStringPen):
|
|
||||||
"""Pen to merge Type 2 CharStrings.
|
|
||||||
"""
|
|
||||||
def __init__(self, default_commands,
|
|
||||||
glyphName, num_masters, master_idx, roundTolerance=0.5):
|
|
||||||
super(
|
|
||||||
CFF2CharStringMergePen,
|
|
||||||
self).__init__(width=None,
|
|
||||||
glyphSet=None, CFF2=True,
|
|
||||||
roundTolerance=roundTolerance)
|
|
||||||
self.pt_index = 0
|
|
||||||
self._commands = default_commands
|
|
||||||
self.m_index = master_idx
|
|
||||||
self.num_masters = num_masters
|
|
||||||
self.prev_move_idx = 0
|
|
||||||
self.glyphName = glyphName
|
|
||||||
self.roundNumber = makeRoundNumberFunc(roundTolerance)
|
|
||||||
|
|
||||||
def _p(self, pt):
|
|
||||||
""" Unlike T2CharstringPen, this class stores absolute values.
|
|
||||||
This is to allow the logic in check_and_fix_closepath() to work,
|
|
||||||
where the current or previous absolute point has to be compared to
|
|
||||||
the path start-point.
|
|
||||||
"""
|
|
||||||
self._p0 = pt
|
|
||||||
return list(self._p0)
|
|
||||||
|
|
||||||
def add_point(self, point_type, pt_coords):
|
|
||||||
if self.m_index == 0:
|
|
||||||
self._commands.append([point_type, [pt_coords]])
|
|
||||||
else:
|
|
||||||
cmd = self._commands[self.pt_index]
|
|
||||||
if cmd[0] != point_type:
|
|
||||||
# Fix some issues that show up in some
|
|
||||||
# CFF workflows, even when fonts are
|
|
||||||
# topologically merge compatible.
|
|
||||||
success, pt_coords = self.check_and_fix_flat_curve(
|
|
||||||
cmd, point_type, pt_coords)
|
|
||||||
if not success:
|
|
||||||
success = self.check_and_fix_closepath(
|
|
||||||
cmd, point_type, pt_coords)
|
|
||||||
if success:
|
|
||||||
# We may have incremented self.pt_index
|
|
||||||
cmd = self._commands[self.pt_index]
|
|
||||||
if cmd[0] != point_type:
|
|
||||||
success = False
|
|
||||||
if not success:
|
|
||||||
raise MergeTypeError(point_type,
|
|
||||||
self.pt_index, len(cmd[1]),
|
|
||||||
cmd[0], self.glyphName)
|
|
||||||
cmd[1].append(pt_coords)
|
|
||||||
self.pt_index += 1
|
|
||||||
|
|
||||||
def _moveTo(self, pt):
|
|
||||||
pt_coords = self._p(pt)
|
|
||||||
self.add_point('rmoveto', pt_coords)
|
|
||||||
# I set prev_move_idx here because add_point()
|
|
||||||
# can change self.pt_index.
|
|
||||||
self.prev_move_idx = self.pt_index - 1
|
|
||||||
|
|
||||||
def _lineTo(self, pt):
|
|
||||||
pt_coords = self._p(pt)
|
|
||||||
self.add_point('rlineto', pt_coords)
|
|
||||||
|
|
||||||
def _curveToOne(self, pt1, pt2, pt3):
|
|
||||||
_p = self._p
|
|
||||||
pt_coords = _p(pt1)+_p(pt2)+_p(pt3)
|
|
||||||
self.add_point('rrcurveto', pt_coords)
|
|
||||||
|
|
||||||
def _closePath(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _endPath(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def restart(self, region_idx):
|
|
||||||
self.pt_index = 0
|
|
||||||
self.m_index = region_idx
|
|
||||||
self._p0 = (0, 0)
|
|
||||||
|
|
||||||
def getCommands(self):
|
|
||||||
return self._commands
|
|
||||||
|
|
||||||
def reorder_blend_args(self, commands):
|
|
||||||
"""
|
|
||||||
We first re-order the master coordinate values.
|
|
||||||
For a moveto to lineto, the args are now arranged as:
|
|
||||||
[ [master_0 x,y], [master_1 x,y], [master_2 x,y] ]
|
|
||||||
We re-arrange this to
|
|
||||||
[ [master_0 x, master_1 x, master_2 x],
|
|
||||||
[master_0 y, master_1 y, master_2 y]
|
|
||||||
]
|
|
||||||
We also make the value relative.
|
|
||||||
If the master values are all the same, we collapse the list to
|
|
||||||
as single value instead of a list.
|
|
||||||
"""
|
|
||||||
for cmd in commands:
|
|
||||||
# arg[i] is the set of arguments for this operator from master i.
|
|
||||||
args = cmd[1]
|
|
||||||
m_args = zip(*args)
|
|
||||||
# m_args[n] is now all num_master args for the i'th argument
|
|
||||||
# for this operation.
|
|
||||||
cmd[1] = m_args
|
|
||||||
|
|
||||||
# Now convert from absolute to relative
|
|
||||||
x0 = [0]*self.num_masters
|
|
||||||
y0 = [0]*self.num_masters
|
|
||||||
for cmd in self._commands:
|
|
||||||
is_x = True
|
|
||||||
coords = cmd[1]
|
|
||||||
rel_coords = []
|
|
||||||
for coord in coords:
|
|
||||||
prev_coord = x0 if is_x else y0
|
|
||||||
rel_coord = [pt[0] - pt[1] for pt in zip(coord, prev_coord)]
|
|
||||||
|
|
||||||
if allEqual(rel_coord):
|
|
||||||
rel_coord = rel_coord[0]
|
|
||||||
rel_coords.append(rel_coord)
|
|
||||||
if is_x:
|
|
||||||
x0 = coord
|
|
||||||
else:
|
|
||||||
y0 = coord
|
|
||||||
is_x = not is_x
|
|
||||||
cmd[1] = rel_coords
|
|
||||||
return commands
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mergeCommandsToProgram(commands, var_model, round_func):
|
|
||||||
"""
|
|
||||||
Takes a commands list as returned by programToCommands() and
|
|
||||||
converts it back to a T2CharString or CFF2Charstring program list. I
|
|
||||||
need to use this rather than specialize.commandsToProgram, as the
|
|
||||||
commands produced by CFF2CharStringMergePen initially contains a
|
|
||||||
list of coordinate values, one for each master, wherever a single
|
|
||||||
coordinate value is expected by the regular logic. The problem with
|
|
||||||
doing using the specialize.py functions is that a commands list is
|
|
||||||
expected to be a op name with its associated argument list. For the
|
|
||||||
commands list here, some of the arguments may need to be converted
|
|
||||||
to a new argument list and opcode.
|
|
||||||
This version will convert each list of master arguments to a blend
|
|
||||||
op and its arguments, and will also combine successive blend ops up
|
|
||||||
to the stack limit.
|
|
||||||
"""
|
|
||||||
program = []
|
|
||||||
for op, args in commands:
|
|
||||||
num_args = len(args)
|
|
||||||
# some of the args may be blend lists, and some may be
|
|
||||||
# single coordinate values.
|
|
||||||
i = 0
|
|
||||||
stack_use = 0
|
|
||||||
while i < num_args:
|
|
||||||
arg = args[i]
|
|
||||||
if not isinstance(arg, list):
|
|
||||||
program.append(arg)
|
|
||||||
i += 1
|
|
||||||
stack_use += 1
|
|
||||||
else:
|
|
||||||
prev_stack_use = stack_use
|
|
||||||
""" The arg is a tuple of blend values.
|
|
||||||
These are each (master 0,master 1..master n)
|
|
||||||
Combine as many successive tuples as we can,
|
|
||||||
up to the max stack limit.
|
|
||||||
"""
|
|
||||||
num_masters = len(arg)
|
|
||||||
blendlist = [arg]
|
|
||||||
i += 1
|
|
||||||
stack_use += 1 + num_masters # 1 for the num_blends arg
|
|
||||||
while (i < num_args) and isinstance(args[i], list):
|
|
||||||
blendlist.append(args[i])
|
|
||||||
i += 1
|
|
||||||
stack_use += num_masters
|
|
||||||
if stack_use + num_masters > maxStackLimit:
|
|
||||||
# if we are here, max stack is is the CFF2 max stack.
|
|
||||||
break
|
|
||||||
num_blends = len(blendlist)
|
|
||||||
# append the 'num_blends' default font values
|
|
||||||
for arg in blendlist:
|
|
||||||
if round_func:
|
|
||||||
arg[0] = round_func(arg[0])
|
|
||||||
program.append(arg[0])
|
|
||||||
for arg in blendlist:
|
|
||||||
# for each coordinate tuple, append the region deltas
|
|
||||||
if len(arg) != 3:
|
|
||||||
print(arg)
|
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
|
||||||
deltas = var_model.getDeltas(arg)
|
|
||||||
if round_func:
|
|
||||||
deltas = [round_func(delta) for delta in deltas]
|
|
||||||
# First item in 'deltas' is the default master value;
|
|
||||||
# for CFF2 data, that has already been written.
|
|
||||||
program.extend(deltas[1:])
|
|
||||||
program.append(num_blends)
|
|
||||||
program.append('blend')
|
|
||||||
stack_use = prev_stack_use + num_blends
|
|
||||||
if op:
|
|
||||||
program.append(op)
|
|
||||||
return program
|
|
||||||
|
|
||||||
|
|
||||||
def getCharString(self, private=None, globalSubrs=None,
|
|
||||||
var_model=None, optimize=True):
|
|
||||||
commands = self._commands
|
|
||||||
commands = self.reorder_blend_args(commands)
|
|
||||||
if optimize:
|
|
||||||
commands = specializeCommands(commands, generalizeFirst=False,
|
|
||||||
maxstack=maxStackLimit)
|
|
||||||
program = self.mergeCommandsToProgram(commands, var_model=var_model,
|
|
||||||
round_func=self.roundNumber)
|
|
||||||
charString = T2CharString(program=program, private=private,
|
|
||||||
globalSubrs=globalSubrs)
|
|
||||||
return charString
|
|
@ -624,9 +624,7 @@ _DesignSpaceData = namedtuple(
|
|||||||
|
|
||||||
|
|
||||||
def _add_CFF2(varFont, model, master_fonts):
|
def _add_CFF2(varFont, model, master_fonts):
|
||||||
from fontTools.cffLib.cff2_merge_funcs import (convertCFFtoCFF2,
|
from .cff import (convertCFFtoCFF2, addCFFVarStore, merge_region_fonts)
|
||||||
addCFFVarStore,
|
|
||||||
merge_region_fonts)
|
|
||||||
glyphOrder = varFont.getGlyphOrder()
|
glyphOrder = varFont.getGlyphOrder()
|
||||||
convertCFFtoCFF2(varFont)
|
convertCFFtoCFF2(varFont)
|
||||||
ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping)
|
ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
from fontTools.misc.py23 import BytesIO
|
from fontTools.misc.py23 import BytesIO
|
||||||
|
from fontTools.misc.psCharStrings import T2CharString
|
||||||
|
from fontTools.pens.t2CharStringPen import T2CharStringPen, t2c_round
|
||||||
from fontTools.cffLib import (
|
from fontTools.cffLib import (
|
||||||
|
maxStackLimit,
|
||||||
TopDictIndex,
|
TopDictIndex,
|
||||||
buildOrder,
|
buildOrder,
|
||||||
topDictOperators,
|
topDictOperators,
|
||||||
@ -11,7 +14,7 @@ from fontTools.cffLib import (
|
|||||||
FontDict,
|
FontDict,
|
||||||
VarStoreData
|
VarStoreData
|
||||||
)
|
)
|
||||||
from fontTools.cffLib.cff2mergePen import CFF2CharStringMergePen
|
from fontTools.cffLib.specializer import (commandsToProgram, specializeCommands)
|
||||||
from fontTools.ttLib import newTable
|
from fontTools.ttLib import newTable
|
||||||
from fontTools import varLib
|
from fontTools import varLib
|
||||||
from fontTools.varLib.models import allEqual
|
from fontTools.varLib.models import allEqual
|
||||||
@ -243,3 +246,241 @@ def merge_charstrings(default_charstrings,
|
|||||||
globalSubrs=default_charstring.globalSubrs,
|
globalSubrs=default_charstring.globalSubrs,
|
||||||
var_model=var_model, optimize=True)
|
var_model=var_model, optimize=True)
|
||||||
default_charstrings[gname] = new_charstring
|
default_charstrings[gname] = new_charstring
|
||||||
|
|
||||||
|
|
||||||
|
class MergeTypeError(TypeError):
|
||||||
|
def __init__(self, point_type, pt_index, m_index, default_type, glyphName):
|
||||||
|
self.error_msg = [
|
||||||
|
"In glyph '{gname}' "
|
||||||
|
"'{point_type}' at point index {pt_index} in master "
|
||||||
|
"index {m_index} differs from the default font point "
|
||||||
|
"type '{default_type}'"
|
||||||
|
"".format(gname=glyphName,
|
||||||
|
point_type=point_type, pt_index=pt_index,
|
||||||
|
m_index=m_index, default_type=default_type)
|
||||||
|
][0]
|
||||||
|
super(MergeTypeError, self).__init__(self.error_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def makeRoundNumberFunc(tolerance):
|
||||||
|
if tolerance < 0:
|
||||||
|
raise ValueError("Rounding tolerance must be positive")
|
||||||
|
|
||||||
|
def roundNumber(val):
|
||||||
|
return t2c_round(val, tolerance)
|
||||||
|
|
||||||
|
return roundNumber
|
||||||
|
|
||||||
|
|
||||||
|
class CFF2CharStringMergePen(T2CharStringPen):
|
||||||
|
"""Pen to merge Type 2 CharStrings.
|
||||||
|
"""
|
||||||
|
def __init__(self, default_commands,
|
||||||
|
glyphName, num_masters, master_idx, roundTolerance=0.5):
|
||||||
|
super(
|
||||||
|
CFF2CharStringMergePen,
|
||||||
|
self).__init__(width=None,
|
||||||
|
glyphSet=None, CFF2=True,
|
||||||
|
roundTolerance=roundTolerance)
|
||||||
|
self.pt_index = 0
|
||||||
|
self._commands = default_commands
|
||||||
|
self.m_index = master_idx
|
||||||
|
self.num_masters = num_masters
|
||||||
|
self.prev_move_idx = 0
|
||||||
|
self.glyphName = glyphName
|
||||||
|
self.roundNumber = makeRoundNumberFunc(roundTolerance)
|
||||||
|
|
||||||
|
def _p(self, pt):
|
||||||
|
""" Unlike T2CharstringPen, this class stores absolute values.
|
||||||
|
This is to allow the logic in check_and_fix_closepath() to work,
|
||||||
|
where the current or previous absolute point has to be compared to
|
||||||
|
the path start-point.
|
||||||
|
"""
|
||||||
|
self._p0 = pt
|
||||||
|
return list(self._p0)
|
||||||
|
|
||||||
|
def add_point(self, point_type, pt_coords):
|
||||||
|
if self.m_index == 0:
|
||||||
|
self._commands.append([point_type, [pt_coords]])
|
||||||
|
else:
|
||||||
|
cmd = self._commands[self.pt_index]
|
||||||
|
if cmd[0] != point_type:
|
||||||
|
# Fix some issues that show up in some
|
||||||
|
# CFF workflows, even when fonts are
|
||||||
|
# topologically merge compatible.
|
||||||
|
success, pt_coords = self.check_and_fix_flat_curve(
|
||||||
|
cmd, point_type, pt_coords)
|
||||||
|
if not success:
|
||||||
|
success = self.check_and_fix_closepath(
|
||||||
|
cmd, point_type, pt_coords)
|
||||||
|
if success:
|
||||||
|
# We may have incremented self.pt_index
|
||||||
|
cmd = self._commands[self.pt_index]
|
||||||
|
if cmd[0] != point_type:
|
||||||
|
success = False
|
||||||
|
if not success:
|
||||||
|
raise MergeTypeError(point_type,
|
||||||
|
self.pt_index, len(cmd[1]),
|
||||||
|
cmd[0], self.glyphName)
|
||||||
|
cmd[1].append(pt_coords)
|
||||||
|
self.pt_index += 1
|
||||||
|
|
||||||
|
def _moveTo(self, pt):
|
||||||
|
pt_coords = self._p(pt)
|
||||||
|
self.add_point('rmoveto', pt_coords)
|
||||||
|
# I set prev_move_idx here because add_point()
|
||||||
|
# can change self.pt_index.
|
||||||
|
self.prev_move_idx = self.pt_index - 1
|
||||||
|
|
||||||
|
def _lineTo(self, pt):
|
||||||
|
pt_coords = self._p(pt)
|
||||||
|
self.add_point('rlineto', pt_coords)
|
||||||
|
|
||||||
|
def _curveToOne(self, pt1, pt2, pt3):
|
||||||
|
_p = self._p
|
||||||
|
pt_coords = _p(pt1)+_p(pt2)+_p(pt3)
|
||||||
|
self.add_point('rrcurveto', pt_coords)
|
||||||
|
|
||||||
|
def _closePath(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _endPath(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def restart(self, region_idx):
|
||||||
|
self.pt_index = 0
|
||||||
|
self.m_index = region_idx
|
||||||
|
self._p0 = (0, 0)
|
||||||
|
|
||||||
|
def getCommands(self):
|
||||||
|
return self._commands
|
||||||
|
|
||||||
|
def reorder_blend_args(self, commands):
|
||||||
|
"""
|
||||||
|
We first re-order the master coordinate values.
|
||||||
|
For a moveto to lineto, the args are now arranged as:
|
||||||
|
[ [master_0 x,y], [master_1 x,y], [master_2 x,y] ]
|
||||||
|
We re-arrange this to
|
||||||
|
[ [master_0 x, master_1 x, master_2 x],
|
||||||
|
[master_0 y, master_1 y, master_2 y]
|
||||||
|
]
|
||||||
|
We also make the value relative.
|
||||||
|
If the master values are all the same, we collapse the list to
|
||||||
|
as single value instead of a list.
|
||||||
|
"""
|
||||||
|
for cmd in commands:
|
||||||
|
# arg[i] is the set of arguments for this operator from master i.
|
||||||
|
args = cmd[1]
|
||||||
|
m_args = zip(*args)
|
||||||
|
# m_args[n] is now all num_master args for the i'th argument
|
||||||
|
# for this operation.
|
||||||
|
cmd[1] = m_args
|
||||||
|
|
||||||
|
# Now convert from absolute to relative
|
||||||
|
x0 = [0]*self.num_masters
|
||||||
|
y0 = [0]*self.num_masters
|
||||||
|
for cmd in self._commands:
|
||||||
|
is_x = True
|
||||||
|
coords = cmd[1]
|
||||||
|
rel_coords = []
|
||||||
|
for coord in coords:
|
||||||
|
prev_coord = x0 if is_x else y0
|
||||||
|
rel_coord = [pt[0] - pt[1] for pt in zip(coord, prev_coord)]
|
||||||
|
|
||||||
|
if allEqual(rel_coord):
|
||||||
|
rel_coord = rel_coord[0]
|
||||||
|
rel_coords.append(rel_coord)
|
||||||
|
if is_x:
|
||||||
|
x0 = coord
|
||||||
|
else:
|
||||||
|
y0 = coord
|
||||||
|
is_x = not is_x
|
||||||
|
cmd[1] = rel_coords
|
||||||
|
return commands
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mergeCommandsToProgram(commands, var_model, round_func):
|
||||||
|
"""
|
||||||
|
Takes a commands list as returned by programToCommands() and
|
||||||
|
converts it back to a T2CharString or CFF2Charstring program list. I
|
||||||
|
need to use this rather than specialize.commandsToProgram, as the
|
||||||
|
commands produced by CFF2CharStringMergePen initially contains a
|
||||||
|
list of coordinate values, one for each master, wherever a single
|
||||||
|
coordinate value is expected by the regular logic. The problem with
|
||||||
|
doing using the specialize.py functions is that a commands list is
|
||||||
|
expected to be a op name with its associated argument list. For the
|
||||||
|
commands list here, some of the arguments may need to be converted
|
||||||
|
to a new argument list and opcode.
|
||||||
|
This version will convert each list of master arguments to a blend
|
||||||
|
op and its arguments, and will also combine successive blend ops up
|
||||||
|
to the stack limit.
|
||||||
|
"""
|
||||||
|
program = []
|
||||||
|
for op, args in commands:
|
||||||
|
num_args = len(args)
|
||||||
|
# some of the args may be blend lists, and some may be
|
||||||
|
# single coordinate values.
|
||||||
|
i = 0
|
||||||
|
stack_use = 0
|
||||||
|
while i < num_args:
|
||||||
|
arg = args[i]
|
||||||
|
if not isinstance(arg, list):
|
||||||
|
program.append(arg)
|
||||||
|
i += 1
|
||||||
|
stack_use += 1
|
||||||
|
else:
|
||||||
|
prev_stack_use = stack_use
|
||||||
|
""" The arg is a tuple of blend values.
|
||||||
|
These are each (master 0,master 1..master n)
|
||||||
|
Combine as many successive tuples as we can,
|
||||||
|
up to the max stack limit.
|
||||||
|
"""
|
||||||
|
num_masters = len(arg)
|
||||||
|
blendlist = [arg]
|
||||||
|
i += 1
|
||||||
|
stack_use += 1 + num_masters # 1 for the num_blends arg
|
||||||
|
while (i < num_args) and isinstance(args[i], list):
|
||||||
|
blendlist.append(args[i])
|
||||||
|
i += 1
|
||||||
|
stack_use += num_masters
|
||||||
|
if stack_use + num_masters > maxStackLimit:
|
||||||
|
# if we are here, max stack is is the CFF2 max stack.
|
||||||
|
break
|
||||||
|
num_blends = len(blendlist)
|
||||||
|
# append the 'num_blends' default font values
|
||||||
|
for arg in blendlist:
|
||||||
|
if round_func:
|
||||||
|
arg[0] = round_func(arg[0])
|
||||||
|
program.append(arg[0])
|
||||||
|
for arg in blendlist:
|
||||||
|
# for each coordinate tuple, append the region deltas
|
||||||
|
if len(arg) != 3:
|
||||||
|
print(arg)
|
||||||
|
import pdb
|
||||||
|
pdb.set_trace()
|
||||||
|
deltas = var_model.getDeltas(arg)
|
||||||
|
if round_func:
|
||||||
|
deltas = [round_func(delta) for delta in deltas]
|
||||||
|
# First item in 'deltas' is the default master value;
|
||||||
|
# for CFF2 data, that has already been written.
|
||||||
|
program.extend(deltas[1:])
|
||||||
|
program.append(num_blends)
|
||||||
|
program.append('blend')
|
||||||
|
stack_use = prev_stack_use + num_blends
|
||||||
|
if op:
|
||||||
|
program.append(op)
|
||||||
|
return program
|
||||||
|
|
||||||
|
|
||||||
|
def getCharString(self, private=None, globalSubrs=None,
|
||||||
|
var_model=None, optimize=True):
|
||||||
|
commands = self._commands
|
||||||
|
commands = self.reorder_blend_args(commands)
|
||||||
|
if optimize:
|
||||||
|
commands = specializeCommands(commands, generalizeFirst=False,
|
||||||
|
maxstack=maxStackLimit)
|
||||||
|
program = self.mergeCommandsToProgram(commands, var_model=var_model,
|
||||||
|
round_func=self.roundNumber)
|
||||||
|
charString = T2CharString(program=program, private=private,
|
||||||
|
globalSubrs=globalSubrs)
|
||||||
|
return charString
|
Loading…
x
Reference in New Issue
Block a user