123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- from collections import namedtuple
- from fontTools.cffLib import (
- maxStackLimit,
- TopDictIndex,
- buildOrder,
- topDictOperators,
- topDictOperators2,
- privateDictOperators,
- privateDictOperators2,
- FDArrayIndex,
- FontDict,
- VarStoreData,
- )
- from io import BytesIO
- from fontTools.cffLib.specializer import specializeCommands, commandsToProgram
- from fontTools.ttLib import newTable
- from fontTools import varLib
- from fontTools.varLib.models import allEqual
- from fontTools.misc.roundTools import roundFunc
- from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor
- from fontTools.pens.t2CharStringPen import T2CharStringPen
- from functools import partial
- from .errors import (
- VarLibCFFDictMergeError,
- VarLibCFFPointTypeMergeError,
- VarLibCFFHintTypeMergeError,
- VarLibMergeError,
- )
- # Backwards compatibility
- MergeDictError = VarLibCFFDictMergeError
- MergeTypeError = VarLibCFFPointTypeMergeError
- def addCFFVarStore(varFont, varModel, varDataList, masterSupports):
- fvarTable = varFont["fvar"]
- axisKeys = [axis.axisTag for axis in fvarTable.axes]
- varTupleList = varLib.builder.buildVarRegionList(masterSupports, axisKeys)
- varStoreCFFV = varLib.builder.buildVarStore(varTupleList, varDataList)
- topDict = varFont["CFF2"].cff.topDictIndex[0]
- topDict.VarStore = VarStoreData(otVarStore=varStoreCFFV)
- if topDict.FDArray[0].vstore is None:
- fdArray = topDict.FDArray
- for fontDict in fdArray:
- if hasattr(fontDict, "Private"):
- fontDict.Private.vstore = topDict.VarStore
- def lib_convertCFFToCFF2(cff, otFont):
- # This assumes a decompiled CFF table.
- cff2GetGlyphOrder = cff.otFont.getGlyphOrder
- topDictData = TopDictIndex(None, cff2GetGlyphOrder, None)
- topDictData.items = cff.topDictIndex.items
- cff.topDictIndex = topDictData
- topDict = topDictData[0]
- if hasattr(topDict, "Private"):
- privateDict = topDict.Private
- else:
- privateDict = None
- opOrder = buildOrder(topDictOperators2)
- topDict.order = opOrder
- topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
- if not hasattr(topDict, "FDArray"):
- fdArray = topDict.FDArray = FDArrayIndex()
- fdArray.strings = None
- fdArray.GlobalSubrs = topDict.GlobalSubrs
- topDict.GlobalSubrs.fdArray = fdArray
- charStrings = topDict.CharStrings
- if charStrings.charStringsAreIndexed:
- charStrings.charStringsIndex.fdArray = fdArray
- else:
- charStrings.fdArray = fdArray
- fontDict = FontDict()
- fontDict.setCFF2(True)
- fdArray.append(fontDict)
- fontDict.Private = privateDict
- privateOpOrder = buildOrder(privateDictOperators2)
- if privateDict is not None:
- for entry in privateDictOperators:
- key = entry[1]
- if key not in privateOpOrder:
- if key in privateDict.rawDict:
- # print "Removing private dict", key
- del privateDict.rawDict[key]
- if hasattr(privateDict, key):
- delattr(privateDict, key)
- # print "Removing privateDict attr", key
- else:
- # clean up the PrivateDicts in the fdArray
- fdArray = topDict.FDArray
- privateOpOrder = buildOrder(privateDictOperators2)
- for fontDict in fdArray:
- fontDict.setCFF2(True)
- for key in list(fontDict.rawDict.keys()):
- if key not in fontDict.order:
- del fontDict.rawDict[key]
- if hasattr(fontDict, key):
- delattr(fontDict, key)
- privateDict = fontDict.Private
- for entry in privateDictOperators:
- key = entry[1]
- if key not in privateOpOrder:
- if key in privateDict.rawDict:
- # print "Removing private dict", key
- del privateDict.rawDict[key]
- if hasattr(privateDict, key):
- delattr(privateDict, key)
- # print "Removing privateDict attr", key
- # Now delete up the deprecated topDict operators from CFF 1.0
- for entry in topDictOperators:
- key = entry[1]
- if key not in opOrder:
- if key in topDict.rawDict:
- del topDict.rawDict[key]
- if hasattr(topDict, key):
- delattr(topDict, key)
- # At this point, the Subrs and Charstrings are all still T2Charstring class
- # easiest to fix this by compiling, then decompiling again
- cff.major = 2
- file = BytesIO()
- cff.compile(file, otFont, isCFF2=True)
- file.seek(0)
- cff.decompile(file, otFont, isCFF2=True)
- def convertCFFtoCFF2(varFont):
- # Convert base font to a single master CFF2 font.
- cffTable = varFont["CFF "]
- lib_convertCFFToCFF2(cffTable.cff, varFont)
- newCFF2 = newTable("CFF2")
- newCFF2.cff = cffTable.cff
- varFont["CFF2"] = newCFF2
- del varFont["CFF "]
- def conv_to_int(num):
- if isinstance(num, float) and num.is_integer():
- return int(num)
- return num
- pd_blend_fields = (
- "BlueValues",
- "OtherBlues",
- "FamilyBlues",
- "FamilyOtherBlues",
- "BlueScale",
- "BlueShift",
- "BlueFuzz",
- "StdHW",
- "StdVW",
- "StemSnapH",
- "StemSnapV",
- )
- def get_private(regionFDArrays, fd_index, ri, fd_map):
- region_fdArray = regionFDArrays[ri]
- region_fd_map = fd_map[fd_index]
- if ri in region_fd_map:
- region_fdIndex = region_fd_map[ri]
- private = region_fdArray[region_fdIndex].Private
- else:
- private = None
- return private
- def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map):
- """
- I step through the FontDicts in the FDArray of the varfont TopDict.
- For each varfont FontDict:
- * step through each key in FontDict.Private.
- * For each key, step through each relevant source font Private dict, and
- build a list of values to blend.
- The 'relevant' source fonts are selected by first getting the right
- submodel using ``vsindex_dict[vsindex]``. The indices of the
- ``subModel.locations`` are mapped to source font list indices by
- assuming the latter order is the same as the order of the
- ``var_model.locations``. I can then get the index of each subModel
- location in the list of ``var_model.locations``.
- """
- topDict = top_dicts[0]
- region_top_dicts = top_dicts[1:]
- if hasattr(region_top_dicts[0], "FDArray"):
- regionFDArrays = [fdTopDict.FDArray for fdTopDict in region_top_dicts]
- else:
- regionFDArrays = [[fdTopDict] for fdTopDict in region_top_dicts]
- for fd_index, font_dict in enumerate(topDict.FDArray):
- private_dict = font_dict.Private
- vsindex = getattr(private_dict, "vsindex", 0)
- # At the moment, no PrivateDict has a vsindex key, but let's support
- # how it should work. See comment at end of
- # merge_charstrings() - still need to optimize use of vsindex.
- sub_model, _ = vsindex_dict[vsindex]
- master_indices = []
- for loc in sub_model.locations[1:]:
- i = var_model.locations.index(loc) - 1
- master_indices.append(i)
- pds = [private_dict]
- last_pd = private_dict
- for ri in master_indices:
- pd = get_private(regionFDArrays, fd_index, ri, fd_map)
- # If the region font doesn't have this FontDict, just reference
- # the last one used.
- if pd is None:
- pd = last_pd
- else:
- last_pd = pd
- pds.append(pd)
- num_masters = len(pds)
- for key, value in private_dict.rawDict.items():
- dataList = []
- if key not in pd_blend_fields:
- continue
- if isinstance(value, list):
- try:
- values = [pd.rawDict[key] for pd in pds]
- except KeyError:
- print(
- "Warning: {key} in default font Private dict is "
- "missing from another font, and was "
- "discarded.".format(key=key)
- )
- continue
- try:
- values = zip(*values)
- except IndexError:
- raise VarLibCFFDictMergeError(key, value, values)
- """
- Row 0 contains the first value from each master.
- Convert each row from absolute values to relative
- values from the previous row.
- e.g for three masters, a list of values was:
- master 0 OtherBlues = [-217,-205]
- master 1 OtherBlues = [-234,-222]
- master 1 OtherBlues = [-188,-176]
- The call to zip() converts this to:
- [(-217, -234, -188), (-205, -222, -176)]
- and is converted finally to:
- OtherBlues = [[-217, 17.0, 46.0], [-205, 0.0, 0.0]]
- """
- prev_val_list = [0] * num_masters
- any_points_differ = False
- for val_list in values:
- rel_list = [
- (val - prev_val_list[i]) for (i, val) in enumerate(val_list)
- ]
- if (not any_points_differ) and not allEqual(rel_list):
- any_points_differ = True
- prev_val_list = val_list
- deltas = sub_model.getDeltas(rel_list)
- # For PrivateDict BlueValues, the default font
- # values are absolute, not relative to the prior value.
- deltas[0] = val_list[0]
- dataList.append(deltas)
- # If there are no blend values,then
- # we can collapse the blend lists.
- if not any_points_differ:
- dataList = [data[0] for data in dataList]
- else:
- values = [pd.rawDict[key] for pd in pds]
- if not allEqual(values):
- dataList = sub_model.getDeltas(values)
- else:
- dataList = values[0]
- # Convert numbers with no decimal part to an int
- if isinstance(dataList, list):
- for i, item in enumerate(dataList):
- if isinstance(item, list):
- for j, jtem in enumerate(item):
- dataList[i][j] = conv_to_int(jtem)
- else:
- dataList[i] = conv_to_int(item)
- else:
- dataList = conv_to_int(dataList)
- private_dict.rawDict[key] = dataList
- def _cff_or_cff2(font):
- if "CFF " in font:
- return font["CFF "]
- return font["CFF2"]
- def getfd_map(varFont, fonts_list):
- """Since a subset source font may have fewer FontDicts in their
- FDArray than the default font, we have to match up the FontDicts in
- the different fonts . We do this with the FDSelect array, and by
- assuming that the same glyph will reference matching FontDicts in
- each source font. We return a mapping from fdIndex in the default
- font to a dictionary which maps each master list index of each
- region font to the equivalent fdIndex in the region font."""
- fd_map = {}
- default_font = fonts_list[0]
- region_fonts = fonts_list[1:]
- num_regions = len(region_fonts)
- topDict = _cff_or_cff2(default_font).cff.topDictIndex[0]
- if not hasattr(topDict, "FDSelect"):
- # All glyphs reference only one FontDict.
- # Map the FD index for regions to index 0.
- fd_map[0] = {ri: 0 for ri in range(num_regions)}
- return fd_map
- gname_mapping = {}
- default_fdSelect = topDict.FDSelect
- glyphOrder = default_font.getGlyphOrder()
- for gid, fdIndex in enumerate(default_fdSelect):
- gname_mapping[glyphOrder[gid]] = fdIndex
- if fdIndex not in fd_map:
- fd_map[fdIndex] = {}
- for ri, region_font in enumerate(region_fonts):
- region_glyphOrder = region_font.getGlyphOrder()
- region_topDict = _cff_or_cff2(region_font).cff.topDictIndex[0]
- if not hasattr(region_topDict, "FDSelect"):
- # All the glyphs share the same FontDict. Pick any glyph.
- default_fdIndex = gname_mapping[region_glyphOrder[0]]
- fd_map[default_fdIndex][ri] = 0
- else:
- region_fdSelect = region_topDict.FDSelect
- for gid, fdIndex in enumerate(region_fdSelect):
- default_fdIndex = gname_mapping[region_glyphOrder[gid]]
- region_map = fd_map[default_fdIndex]
- if ri not in region_map:
- region_map[ri] = fdIndex
- return fd_map
- CVarData = namedtuple("CVarData", "varDataList masterSupports vsindex_dict")
- def merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder):
- topDict = varFont["CFF2"].cff.topDictIndex[0]
- top_dicts = [topDict] + [
- _cff_or_cff2(ttFont).cff.topDictIndex[0] for ttFont in ordered_fonts_list[1:]
- ]
- num_masters = len(model.mapping)
- cvData = merge_charstrings(glyphOrder, num_masters, top_dicts, model)
- fd_map = getfd_map(varFont, ordered_fonts_list)
- merge_PrivateDicts(top_dicts, cvData.vsindex_dict, model, fd_map)
- addCFFVarStore(varFont, model, cvData.varDataList, cvData.masterSupports)
- def _get_cs(charstrings, glyphName, filterEmpty=False):
- if glyphName not in charstrings:
- return None
- cs = charstrings[glyphName]
- if filterEmpty:
- cs.decompile()
- if cs.program == []: # CFF2 empty charstring
- return None
- elif (
- len(cs.program) <= 2
- and cs.program[-1] == "endchar"
- and (len(cs.program) == 1 or type(cs.program[0]) in (int, float))
- ): # CFF1 empty charstring
- return None
- return cs
- def _add_new_vsindex(
- model, key, masterSupports, vsindex_dict, vsindex_by_key, varDataList
- ):
- varTupleIndexes = []
- for support in model.supports[1:]:
- if support not in masterSupports:
- masterSupports.append(support)
- varTupleIndexes.append(masterSupports.index(support))
- var_data = varLib.builder.buildVarData(varTupleIndexes, None, False)
- vsindex = len(vsindex_dict)
- vsindex_by_key[key] = vsindex
- vsindex_dict[vsindex] = (model, [key])
- varDataList.append(var_data)
- return vsindex
- def merge_charstrings(glyphOrder, num_masters, top_dicts, masterModel):
- vsindex_dict = {}
- vsindex_by_key = {}
- varDataList = []
- masterSupports = []
- default_charstrings = top_dicts[0].CharStrings
- for gid, gname in enumerate(glyphOrder):
- # interpret empty non-default masters as missing glyphs from a sparse master
- all_cs = [
- _get_cs(td.CharStrings, gname, i != 0) for i, td in enumerate(top_dicts)
- ]
- model, model_cs = masterModel.getSubModel(all_cs)
- # create the first pass CFF2 charstring, from
- # the default charstring.
- default_charstring = model_cs[0]
- var_pen = CFF2CharStringMergePen([], gname, num_masters, 0)
- # We need to override outlineExtractor because these
- # charstrings do have widths in the 'program'; we need to drop these
- # values rather than post assertion error for them.
- default_charstring.outlineExtractor = MergeOutlineExtractor
- default_charstring.draw(var_pen)
- # Add the coordinates from all the other regions to the
- # blend lists in the CFF2 charstring.
- region_cs = model_cs[1:]
- for region_idx, region_charstring in enumerate(region_cs, start=1):
- var_pen.restart(region_idx)
- region_charstring.outlineExtractor = MergeOutlineExtractor
- region_charstring.draw(var_pen)
- # Collapse each coordinate list to a blend operator and its args.
- new_cs = var_pen.getCharString(
- private=default_charstring.private,
- globalSubrs=default_charstring.globalSubrs,
- var_model=model,
- optimize=True,
- )
- default_charstrings[gname] = new_cs
- if not region_cs:
- continue
- if (not var_pen.seen_moveto) or ("blend" not in new_cs.program):
- # If this is not a marking glyph, or if there are no blend
- # arguments, then we can use vsindex 0. No need to
- # check if we need a new vsindex.
- continue
- # If the charstring required a new model, create
- # a VarData table to go with, and set vsindex.
- key = tuple(v is not None for v in all_cs)
- try:
- vsindex = vsindex_by_key[key]
- except KeyError:
- vsindex = _add_new_vsindex(
- model, key, masterSupports, vsindex_dict, vsindex_by_key, varDataList
- )
- # We do not need to check for an existing new_cs.private.vsindex,
- # as we know it doesn't exist yet.
- if vsindex != 0:
- new_cs.program[:0] = [vsindex, "vsindex"]
- # If there is no variation in any of the charstrings, then vsindex_dict
- # never gets built. This could still be needed if there is variation
- # in the PrivatDict, so we will build the default data for vsindex = 0.
- if not vsindex_dict:
- key = (True,) * num_masters
- _add_new_vsindex(
- masterModel, key, masterSupports, vsindex_dict, vsindex_by_key, varDataList
- )
- cvData = CVarData(
- varDataList=varDataList,
- masterSupports=masterSupports,
- vsindex_dict=vsindex_dict,
- )
- # XXX To do: optimize use of vsindex between the PrivateDicts and
- # charstrings
- return cvData
- class CFFToCFF2OutlineExtractor(T2OutlineExtractor):
- """This class is used to remove the initial width from the CFF
- charstring without trying to add the width to self.nominalWidthX,
- which is None."""
- def popallWidth(self, evenOdd=0):
- args = self.popall()
- if not self.gotWidth:
- if evenOdd ^ (len(args) % 2):
- args = args[1:]
- self.width = self.defaultWidthX
- self.gotWidth = 1
- return args
- class MergeOutlineExtractor(CFFToCFF2OutlineExtractor):
- """Used to extract the charstring commands - including hints - from a
- CFF charstring in order to merge it as another set of region data
- into a CFF2 variable font charstring."""
- def __init__(
- self,
- pen,
- localSubrs,
- globalSubrs,
- nominalWidthX,
- defaultWidthX,
- private=None,
- blender=None,
- ):
- super().__init__(
- pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private, blender
- )
- def countHints(self):
- args = self.popallWidth()
- self.hintCount = self.hintCount + len(args) // 2
- return args
- def _hint_op(self, type, args):
- self.pen.add_hint(type, args)
- def op_hstem(self, index):
- args = self.countHints()
- self._hint_op("hstem", args)
- def op_vstem(self, index):
- args = self.countHints()
- self._hint_op("vstem", args)
- def op_hstemhm(self, index):
- args = self.countHints()
- self._hint_op("hstemhm", args)
- def op_vstemhm(self, index):
- args = self.countHints()
- self._hint_op("vstemhm", args)
- def _get_hintmask(self, index):
- if not self.hintMaskBytes:
- args = self.countHints()
- if args:
- self._hint_op("vstemhm", args)
- self.hintMaskBytes = (self.hintCount + 7) // 8
- hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
- return index, hintMaskBytes
- def op_hintmask(self, index):
- index, hintMaskBytes = self._get_hintmask(index)
- self.pen.add_hintmask("hintmask", [hintMaskBytes])
- return hintMaskBytes, index
- def op_cntrmask(self, index):
- index, hintMaskBytes = self._get_hintmask(index)
- self.pen.add_hintmask("cntrmask", [hintMaskBytes])
- return hintMaskBytes, index
- class CFF2CharStringMergePen(T2CharStringPen):
- """Pen to merge Type 2 CharStrings."""
- def __init__(
- self, default_commands, glyphName, num_masters, master_idx, roundTolerance=0.01
- ):
- # For roundTolerance see https://github.com/fonttools/fonttools/issues/2838
- super().__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.seen_moveto = False
- self.glyphName = glyphName
- self.round = roundFunc(roundTolerance, round=round)
- 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:
- raise VarLibCFFPointTypeMergeError(
- point_type, self.pt_index, len(cmd[1]), cmd[0], self.glyphName
- )
- cmd[1].append(pt_coords)
- self.pt_index += 1
- def add_hint(self, hint_type, args):
- if self.m_index == 0:
- self._commands.append([hint_type, [args]])
- else:
- cmd = self._commands[self.pt_index]
- if cmd[0] != hint_type:
- raise VarLibCFFHintTypeMergeError(
- hint_type, self.pt_index, len(cmd[1]), cmd[0], self.glyphName
- )
- cmd[1].append(args)
- self.pt_index += 1
- def add_hintmask(self, hint_type, abs_args):
- # For hintmask, fonttools.cffLib.specializer.py expects
- # each of these to be represented by two sequential commands:
- # first holding only the operator name, with an empty arg list,
- # second with an empty string as the op name, and the mask arg list.
- if self.m_index == 0:
- self._commands.append([hint_type, []])
- self._commands.append(["", [abs_args]])
- else:
- cmd = self._commands[self.pt_index]
- if cmd[0] != hint_type:
- raise VarLibCFFHintTypeMergeError(
- hint_type, self.pt_index, len(cmd[1]), cmd[0], self.glyphName
- )
- self.pt_index += 1
- cmd = self._commands[self.pt_index]
- cmd[1].append(abs_args)
- self.pt_index += 1
- def _moveTo(self, pt):
- if not self.seen_moveto:
- self.seen_moveto = True
- 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, get_delta_func):
- """
- 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]
- ]
- If the master values are all the same, we collapse the list to
- as single value instead of a list.
- We then convert this to::
- [ [master_0 x] + [x delta tuple] + [numBlends=1]
- [master_0 y] + [y delta tuple] + [numBlends=1]
- ]
- """
- 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] = list(m_args)
- lastOp = None
- for cmd in commands:
- op = cmd[0]
- # masks are represented by two cmd's: first has only op names,
- # second has only args.
- if lastOp in ["hintmask", "cntrmask"]:
- coord = list(cmd[1])
- if not allEqual(coord):
- raise VarLibMergeError(
- "Hintmask values cannot differ between source fonts."
- )
- cmd[1] = [coord[0][0]]
- else:
- coords = cmd[1]
- new_coords = []
- for coord in coords:
- if allEqual(coord):
- new_coords.append(coord[0])
- else:
- # convert to deltas
- deltas = get_delta_func(coord)[1:]
- coord = [coord[0]] + deltas
- coord.append(1)
- new_coords.append(coord)
- cmd[1] = new_coords
- lastOp = op
- return commands
- def getCharString(
- self, private=None, globalSubrs=None, var_model=None, optimize=True
- ):
- commands = self._commands
- commands = self.reorder_blend_args(
- commands, partial(var_model.getDeltas, round=self.round)
- )
- if optimize:
- commands = specializeCommands(
- commands, generalizeFirst=False, maxstack=maxStackLimit
- )
- program = commandsToProgram(commands)
- charString = T2CharString(
- program=program, private=private, globalSubrs=globalSubrs
- )
- return charString
|