diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9f8b454bde020720f884bed45e9eac161a5fb281
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7d289c49b2ab3ecff70afba3f2edde3052b3e934
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f8ce9c42f723b2aedb6befb172502ce485d159ba
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..72e83c197165d87234ba6d5547e75b95177fa94e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2edcedc43d24e35ab343dead269eee56904fd28
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ab7e6cf5554e9546d9f6fdd278a1a96dbf0cff48
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8c84ea6dfd6e104758c4b2714b492e2af3b15a40
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py b/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py
new file mode 100644
index 0000000000000000000000000000000000000000..f33e48cc4c0e003ed907498b5d79d3eade23ccf5
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py
@@ -0,0 +1,258 @@
+"""CFF2 to CFF converter."""
+
+from fontTools.ttLib import TTFont, newTable
+from fontTools.misc.cliTools import makeOutputFileName
+from fontTools.misc.psCharStrings import T2StackUseExtractor
+from fontTools.cffLib import (
+ TopDictIndex,
+ buildOrder,
+ buildDefaults,
+ topDictOperators,
+ privateDictOperators,
+ FDSelect,
+)
+from .transforms import desubroutinizeCharString
+from .specializer import specializeProgram
+from .width import optimizeWidths
+from collections import defaultdict
+import logging
+
+
+__all__ = ["convertCFF2ToCFF", "main"]
+
+
+log = logging.getLogger("fontTools.cffLib")
+
+
+def _convertCFF2ToCFF(cff, otFont):
+ """Converts this object from CFF2 format to CFF format. This conversion
+ is done 'in-place'. The conversion cannot be reversed.
+
+ The CFF2 font cannot be variable. (TODO Accept those and convert to the
+ default instance?)
+
+ This assumes a decompiled CFF2 table. (i.e. that the object has been
+ filled via :meth:`decompile` and e.g. not loaded from XML.)"""
+
+ cff.major = 1
+
+ topDictData = TopDictIndex(None)
+ for item in cff.topDictIndex:
+ # Iterate over, such that all are decompiled
+ item.cff2GetGlyphOrder = None
+ topDictData.append(item)
+ cff.topDictIndex = topDictData
+ topDict = topDictData[0]
+
+ if hasattr(topDict, "VarStore"):
+ raise ValueError("Variable CFF2 font cannot be converted to CFF format.")
+
+ opOrder = buildOrder(topDictOperators)
+ topDict.order = opOrder
+ for key in topDict.rawDict.keys():
+ if key not in opOrder:
+ del topDict.rawDict[key]
+ if hasattr(topDict, key):
+ delattr(topDict, key)
+
+ charStrings = topDict.CharStrings
+
+ fdArray = topDict.FDArray
+ if not hasattr(topDict, "FDSelect"):
+ # FDSelect is optional in CFF2, but required in CFF.
+ fdSelect = topDict.FDSelect = FDSelect()
+ fdSelect.gidArray = [0] * len(charStrings.charStrings)
+
+ defaults = buildDefaults(privateDictOperators)
+ order = buildOrder(privateDictOperators)
+ for fd in fdArray:
+ fd.setCFF2(False)
+ privateDict = fd.Private
+ privateDict.order = order
+ for key in order:
+ if key not in privateDict.rawDict and key in defaults:
+ privateDict.rawDict[key] = defaults[key]
+ for key in privateDict.rawDict.keys():
+ if key not in order:
+ del privateDict.rawDict[key]
+ if hasattr(privateDict, key):
+ delattr(privateDict, key)
+
+ # Add ending operators
+ for cs in charStrings.values():
+ cs.decompile()
+ cs.program.append("endchar")
+ for subrSets in [cff.GlobalSubrs] + [
+ getattr(fd.Private, "Subrs", []) for fd in fdArray
+ ]:
+ for cs in subrSets:
+ cs.program.append("return")
+
+ # Add (optimal) width to CharStrings that need it.
+ widths = defaultdict(list)
+ metrics = otFont["hmtx"].metrics
+ for glyphName in charStrings.keys():
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
+ if fdIndex == None:
+ fdIndex = 0
+ widths[fdIndex].append(metrics[glyphName][0])
+ for fdIndex, widthList in widths.items():
+ bestDefault, bestNominal = optimizeWidths(widthList)
+ private = fdArray[fdIndex].Private
+ private.defaultWidthX = bestDefault
+ private.nominalWidthX = bestNominal
+ for glyphName in charStrings.keys():
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
+ if fdIndex == None:
+ fdIndex = 0
+ private = fdArray[fdIndex].Private
+ width = metrics[glyphName][0]
+ if width != private.defaultWidthX:
+ cs.program.insert(0, width - private.nominalWidthX)
+
+ # Handle stack use since stack-depth is lower in CFF than in CFF2.
+ for glyphName in charStrings.keys():
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
+ if fdIndex is None:
+ fdIndex = 0
+ private = fdArray[fdIndex].Private
+ extractor = T2StackUseExtractor(
+ getattr(private, "Subrs", []), cff.GlobalSubrs, private=private
+ )
+ stackUse = extractor.execute(cs)
+ if stackUse > 48: # CFF stack depth is 48
+ desubroutinizeCharString(cs)
+ cs.program = specializeProgram(cs.program)
+
+ # Unused subroutines are still in CFF2 (ie. lacking 'return' operator)
+ # because they were not decompiled when we added the 'return'.
+ # Moreover, some used subroutines may have become unused after the
+ # stack-use fixup. So we remove all unused subroutines now.
+ cff.remove_unused_subroutines()
+
+ mapping = {
+ name: ("cid" + str(n).zfill(5) if n else ".notdef")
+ for n, name in enumerate(topDict.charset)
+ }
+ topDict.charset = [
+ "cid" + str(n).zfill(5) if n else ".notdef" for n in range(len(topDict.charset))
+ ]
+ charStrings.charStrings = {
+ mapping[name]: v for name, v in charStrings.charStrings.items()
+ }
+
+ topDict.ROS = ("Adobe", "Identity", 0)
+
+
+def convertCFF2ToCFF(font, *, updatePostTable=True):
+ if "CFF2" not in font:
+ raise ValueError("Input font does not contain a CFF2 table.")
+ cff = font["CFF2"].cff
+ _convertCFF2ToCFF(cff, font)
+ del font["CFF2"]
+ table = font["CFF "] = newTable("CFF ")
+ table.cff = cff
+
+ if updatePostTable and "post" in font:
+ # Only version supported for fonts with CFF table is 0x00030000 not 0x20000
+ post = font["post"]
+ if post.formatType == 2.0:
+ post.formatType = 3.0
+
+
+def main(args=None):
+ """Convert CFF2 OTF font to CFF OTF font"""
+ if args is None:
+ import sys
+
+ args = sys.argv[1:]
+
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ "fonttools cffLib.CFF2ToCFF",
+ description="Convert a non-variable CFF2 font to CFF.",
+ )
+ parser.add_argument(
+ "input", metavar="INPUT.ttf", help="Input OTF file with CFF table."
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ metavar="OUTPUT.ttf",
+ default=None,
+ help="Output instance OTF file (default: INPUT-CFF2.ttf).",
+ )
+ 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(
+ "--remove-overlaps",
+ action="store_true",
+ help="Merge overlapping contours and components. Requires skia-pathops",
+ )
+ parser.add_argument(
+ "--ignore-overlap-errors",
+ action="store_true",
+ help="Don't crash if the remove-overlaps operation fails for some glyphs.",
+ )
+ loggingGroup = parser.add_mutually_exclusive_group(required=False)
+ loggingGroup.add_argument(
+ "-v", "--verbose", action="store_true", help="Run more verbosely."
+ )
+ loggingGroup.add_argument(
+ "-q", "--quiet", action="store_true", help="Turn verbosity off."
+ )
+ options = parser.parse_args(args)
+
+ from fontTools import configLogger
+
+ configLogger(
+ level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
+ )
+
+ import os
+
+ infile = options.input
+ if not os.path.isfile(infile):
+ parser.error("No such file '{}'".format(infile))
+
+ outfile = (
+ makeOutputFileName(infile, overWrite=True, suffix="-CFF")
+ if not options.output
+ else options.output
+ )
+
+ font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False)
+
+ convertCFF2ToCFF(font)
+
+ if options.remove_overlaps:
+ from fontTools.ttLib.removeOverlaps import removeOverlaps
+ from io import BytesIO
+
+ log.debug("Removing overlaps")
+
+ stream = BytesIO()
+ font.save(stream)
+ stream.seek(0)
+ font = TTFont(stream, recalcTimestamp=False, recalcBBoxes=False)
+ removeOverlaps(
+ font,
+ ignoreErrors=options.ignore_overlap_errors,
+ )
+
+ log.info(
+ "Saving %s",
+ outfile,
+ )
+ font.save(outfile)
+
+
+if __name__ == "__main__":
+ import sys
+
+ sys.exit(main(sys.argv[1:]))
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py b/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py
new file mode 100644
index 0000000000000000000000000000000000000000..2555f0b242591cde7738f46932fd1cbe2d0a6ccf
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py
@@ -0,0 +1,305 @@
+"""CFF to CFF2 converter."""
+
+from fontTools.ttLib import TTFont, newTable
+from fontTools.misc.cliTools import makeOutputFileName
+from fontTools.misc.psCharStrings import T2WidthExtractor
+from fontTools.cffLib import (
+ TopDictIndex,
+ FDArrayIndex,
+ FontDict,
+ buildOrder,
+ topDictOperators,
+ privateDictOperators,
+ topDictOperators2,
+ privateDictOperators2,
+)
+from io import BytesIO
+import logging
+
+__all__ = ["convertCFFToCFF2", "main"]
+
+
+log = logging.getLogger("fontTools.cffLib")
+
+
+class _NominalWidthUsedError(Exception):
+ def __add__(self, other):
+ raise self
+
+ def __radd__(self, other):
+ raise self
+
+
+def _convertCFFToCFF2(cff, otFont):
+ """Converts this object from CFF format to CFF2 format. This conversion
+ is done 'in-place'. The conversion cannot be reversed.
+
+ This assumes a decompiled CFF table. (i.e. that the object has been
+ filled via :meth:`decompile` and e.g. not loaded from XML.)"""
+
+ # Clean up T2CharStrings
+
+ topDict = cff.topDictIndex[0]
+ fdArray = topDict.FDArray if hasattr(topDict, "FDArray") else None
+ charStrings = topDict.CharStrings
+ globalSubrs = cff.GlobalSubrs
+ localSubrs = (
+ [getattr(fd.Private, "Subrs", []) for fd in fdArray]
+ if fdArray
+ else (
+ [topDict.Private.Subrs]
+ if hasattr(topDict, "Private") and hasattr(topDict.Private, "Subrs")
+ else []
+ )
+ )
+
+ for glyphName in charStrings.keys():
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
+ cs.decompile()
+
+ # Clean up subroutines first
+ for subrs in [globalSubrs] + localSubrs:
+ for subr in subrs:
+ program = subr.program
+ i = j = len(program)
+ try:
+ i = program.index("return")
+ except ValueError:
+ pass
+ try:
+ j = program.index("endchar")
+ except ValueError:
+ pass
+ program[min(i, j) :] = []
+
+ # Clean up glyph charstrings
+ removeUnusedSubrs = False
+ nominalWidthXError = _NominalWidthUsedError()
+ for glyphName in charStrings.keys():
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
+ program = cs.program
+
+ thisLocalSubrs = (
+ localSubrs[fdIndex]
+ if fdIndex is not None
+ else (
+ getattr(topDict.Private, "Subrs", [])
+ if hasattr(topDict, "Private")
+ else []
+ )
+ )
+
+ # Intentionally use custom type for nominalWidthX, such that any
+ # CharString that has an explicit width encoded will throw back to us.
+ extractor = T2WidthExtractor(
+ thisLocalSubrs,
+ globalSubrs,
+ nominalWidthXError,
+ 0,
+ )
+ try:
+ extractor.execute(cs)
+ except _NominalWidthUsedError:
+ # Program has explicit width. We want to drop it, but can't
+ # just pop the first number since it may be a subroutine call.
+ # Instead, when seeing that, we embed the subroutine and recurse.
+ # If this ever happened, we later prune unused subroutines.
+ while len(program) >= 2 and program[1] in ["callsubr", "callgsubr"]:
+ removeUnusedSubrs = True
+ subrNumber = program.pop(0)
+ assert isinstance(subrNumber, int), subrNumber
+ op = program.pop(0)
+ bias = extractor.localBias if op == "callsubr" else extractor.globalBias
+ subrNumber += bias
+ subrSet = thisLocalSubrs if op == "callsubr" else globalSubrs
+ subrProgram = subrSet[subrNumber].program
+ program[:0] = subrProgram
+ # Now pop the actual width
+ assert len(program) >= 1, program
+ program.pop(0)
+
+ if program and program[-1] == "endchar":
+ program.pop()
+
+ if removeUnusedSubrs:
+ cff.remove_unused_subroutines()
+
+ # Upconvert TopDict
+
+ cff.major = 2
+ cff2GetGlyphOrder = cff.otFont.getGlyphOrder
+ topDictData = TopDictIndex(None, cff2GetGlyphOrder)
+ for item in cff.topDictIndex:
+ # Iterate over, such that all are decompiled
+ topDictData.append(item)
+ 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 list(privateDict.rawDict.keys()):
+ # 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]
+ # We seem to need to keep the charset operator for now,
+ # or we fail to compile with some fonts, like AdditionFont.otf.
+ # I don't know which kind of CFF font those are. But keeping
+ # charset seems to work. It will be removed when we save and
+ # read the font again.
+ #
+ # AdditionFont.otf has .
+ if key == "charset":
+ continue
+ if key not in opOrder:
+ if key in topDict.rawDict:
+ del topDict.rawDict[key]
+ if hasattr(topDict, key):
+ delattr(topDict, key)
+
+ # TODO(behdad): What does the following comment even mean? Both CFF and CFF2
+ # use the same T2Charstring class. I *think* what it means is that the CharStrings
+ # were loaded for CFF1, and we need to reload them for CFF2 to set varstore, etc
+ # on them. At least that's what I understand. It's probably safe to remove this
+ # and just set vstore where needed.
+ #
+ # See comment above about charset as well.
+
+ # At this point, the Subrs and Charstrings are all still T2Charstring class
+ # easiest to fix this by compiling, then decompiling again
+ file = BytesIO()
+ cff.compile(file, otFont, isCFF2=True)
+ file.seek(0)
+ cff.decompile(file, otFont, isCFF2=True)
+
+
+def convertCFFToCFF2(font):
+ cff = font["CFF "].cff
+ del font["CFF "]
+ _convertCFFToCFF2(cff, font)
+ table = font["CFF2"] = newTable("CFF2")
+ table.cff = cff
+
+
+def main(args=None):
+ """Convert CFF OTF font to CFF2 OTF font"""
+ if args is None:
+ import sys
+
+ args = sys.argv[1:]
+
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ "fonttools cffLib.CFFToCFF2",
+ description="Upgrade a CFF font to CFF2.",
+ )
+ parser.add_argument(
+ "input", metavar="INPUT.ttf", help="Input OTF file with CFF table."
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ metavar="OUTPUT.ttf",
+ default=None,
+ help="Output instance OTF file (default: INPUT-CFF2.ttf).",
+ )
+ 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.",
+ )
+ loggingGroup = parser.add_mutually_exclusive_group(required=False)
+ loggingGroup.add_argument(
+ "-v", "--verbose", action="store_true", help="Run more verbosely."
+ )
+ loggingGroup.add_argument(
+ "-q", "--quiet", action="store_true", help="Turn verbosity off."
+ )
+ options = parser.parse_args(args)
+
+ from fontTools import configLogger
+
+ configLogger(
+ level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
+ )
+
+ import os
+
+ infile = options.input
+ if not os.path.isfile(infile):
+ parser.error("No such file '{}'".format(infile))
+
+ outfile = (
+ makeOutputFileName(infile, overWrite=True, suffix="-CFF2")
+ if not options.output
+ else options.output
+ )
+
+ font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False)
+
+ convertCFFToCFF2(font)
+
+ log.info(
+ "Saving %s",
+ outfile,
+ )
+ font.save(outfile)
+
+
+if __name__ == "__main__":
+ import sys
+
+ sys.exit(main(sys.argv[1:]))
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__init__.py b/lib/python3.12/site-packages/fontTools/cffLib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ad724a27a812839d6c5e58314a5ff2f583d978c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/__init__.py
@@ -0,0 +1,3694 @@
+"""cffLib: read/write Adobe CFF fonts
+
+OpenType fonts with PostScript outlines embed a completely independent
+font file in Adobe's *Compact Font Format*. So dealing with OpenType fonts
+requires also dealing with CFF. This module allows you to read and write
+fonts written in the CFF format.
+
+In 2016, OpenType 1.8 introduced the `CFF2 `_
+format which, along with other changes, extended the CFF format to deal with
+the demands of variable fonts. This module parses both original CFF and CFF2.
+
+"""
+
+from fontTools.misc import sstruct
+from fontTools.misc import psCharStrings
+from fontTools.misc.arrayTools import unionRect, intRect
+from fontTools.misc.textTools import (
+ bytechr,
+ byteord,
+ bytesjoin,
+ tobytes,
+ tostr,
+ safeEval,
+)
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables.otBase import OTTableWriter
+from fontTools.ttLib.tables.otBase import OTTableReader
+from fontTools.ttLib.tables import otTables as ot
+from io import BytesIO
+import struct
+import logging
+import re
+
+# mute cffLib debug messages when running ttx in verbose mode
+DEBUG = logging.DEBUG - 1
+log = logging.getLogger(__name__)
+
+cffHeaderFormat = """
+ major: B
+ minor: B
+ hdrSize: B
+"""
+
+maxStackLimit = 513
+# maxstack operator has been deprecated. max stack is now always 513.
+
+
+class CFFFontSet(object):
+ """A CFF font "file" can contain more than one font, although this is
+ extremely rare (and not allowed within OpenType fonts).
+
+ This class is the entry point for parsing a CFF table. To actually
+ manipulate the data inside the CFF font, you will want to access the
+ ``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet``
+ object can either be treated as a dictionary (with appropriate
+ ``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict`
+ objects, or as a list.
+
+ .. code:: python
+
+ from fontTools import ttLib
+ tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf")
+ tt["CFF "].cff
+ #
+ tt["CFF "].cff[0] # Here's your actual font data
+ #
+
+ """
+
+ def decompile(self, file, otFont, isCFF2=None):
+ """Parse a binary CFF file into an internal representation. ``file``
+ should be a file handle object. ``otFont`` is the top-level
+ :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
+
+ If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
+ library makes an assertion that the CFF header is of the appropriate
+ version.
+ """
+
+ self.otFont = otFont
+ sstruct.unpack(cffHeaderFormat, file.read(3), self)
+ if isCFF2 is not None:
+ # called from ttLib: assert 'major' as read from file matches the
+ # expected version
+ expected_major = 2 if isCFF2 else 1
+ if self.major != expected_major:
+ raise ValueError(
+ "Invalid CFF 'major' version: expected %d, found %d"
+ % (expected_major, self.major)
+ )
+ else:
+ # use 'major' version from file to determine if isCFF2
+ assert self.major in (1, 2), "Unknown CFF format"
+ isCFF2 = self.major == 2
+ if not isCFF2:
+ self.offSize = struct.unpack("B", file.read(1))[0]
+ file.seek(self.hdrSize)
+ self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2))
+ self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2)
+ self.strings = IndexedStrings(file)
+ else: # isCFF2
+ self.topDictSize = struct.unpack(">H", file.read(2))[0]
+ file.seek(self.hdrSize)
+ self.fontNames = ["CFF2Font"]
+ cff2GetGlyphOrder = otFont.getGlyphOrder
+ # in CFF2, offsetSize is the size of the TopDict data.
+ self.topDictIndex = TopDictIndex(
+ file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2
+ )
+ self.strings = None
+ self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2)
+ self.topDictIndex.strings = self.strings
+ self.topDictIndex.GlobalSubrs = self.GlobalSubrs
+
+ def __len__(self):
+ return len(self.fontNames)
+
+ def keys(self):
+ return list(self.fontNames)
+
+ def values(self):
+ return self.topDictIndex
+
+ def __getitem__(self, nameOrIndex):
+ """Return TopDict instance identified by name (str) or index (int
+ or any object that implements `__index__`).
+ """
+ if hasattr(nameOrIndex, "__index__"):
+ index = nameOrIndex.__index__()
+ elif isinstance(nameOrIndex, str):
+ name = nameOrIndex
+ try:
+ index = self.fontNames.index(name)
+ except ValueError:
+ raise KeyError(nameOrIndex)
+ else:
+ raise TypeError(nameOrIndex)
+ return self.topDictIndex[index]
+
+ def compile(self, file, otFont, isCFF2=None):
+ """Write the object back into binary representation onto the given file.
+ ``file`` should be a file handle object. ``otFont`` is the top-level
+ :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
+
+ If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
+ library makes an assertion that the CFF header is of the appropriate
+ version.
+ """
+ self.otFont = otFont
+ if isCFF2 is not None:
+ # called from ttLib: assert 'major' value matches expected version
+ expected_major = 2 if isCFF2 else 1
+ if self.major != expected_major:
+ raise ValueError(
+ "Invalid CFF 'major' version: expected %d, found %d"
+ % (expected_major, self.major)
+ )
+ else:
+ # use current 'major' value to determine output format
+ assert self.major in (1, 2), "Unknown CFF format"
+ isCFF2 = self.major == 2
+
+ if otFont.recalcBBoxes and not isCFF2:
+ for topDict in self.topDictIndex:
+ topDict.recalcFontBBox()
+
+ if not isCFF2:
+ strings = IndexedStrings()
+ else:
+ strings = None
+ writer = CFFWriter(isCFF2)
+ topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2)
+ if isCFF2:
+ self.hdrSize = 5
+ writer.add(sstruct.pack(cffHeaderFormat, self))
+ # Note: topDictSize will most likely change in CFFWriter.toFile().
+ self.topDictSize = topCompiler.getDataLength()
+ writer.add(struct.pack(">H", self.topDictSize))
+ else:
+ self.hdrSize = 4
+ self.offSize = 4 # will most likely change in CFFWriter.toFile().
+ writer.add(sstruct.pack(cffHeaderFormat, self))
+ writer.add(struct.pack("B", self.offSize))
+ if not isCFF2:
+ fontNames = Index()
+ for name in self.fontNames:
+ fontNames.append(name)
+ writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2))
+ writer.add(topCompiler)
+ if not isCFF2:
+ writer.add(strings.getCompiler())
+ writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2))
+
+ for topDict in self.topDictIndex:
+ if not hasattr(topDict, "charset") or topDict.charset is None:
+ charset = otFont.getGlyphOrder()
+ topDict.charset = charset
+ children = topCompiler.getChildren(strings)
+ for child in children:
+ writer.add(child)
+
+ writer.toFile(file)
+
+ def toXML(self, xmlWriter):
+ """Write the object into XML representation onto the given
+ :class:`fontTools.misc.xmlWriter.XMLWriter`.
+
+ .. code:: python
+
+ writer = xmlWriter.XMLWriter(sys.stdout)
+ tt["CFF "].cff.toXML(writer)
+
+ """
+
+ xmlWriter.simpletag("major", value=self.major)
+ xmlWriter.newline()
+ xmlWriter.simpletag("minor", value=self.minor)
+ xmlWriter.newline()
+ for fontName in self.fontNames:
+ xmlWriter.begintag("CFFFont", name=tostr(fontName))
+ xmlWriter.newline()
+ font = self[fontName]
+ font.toXML(xmlWriter)
+ xmlWriter.endtag("CFFFont")
+ xmlWriter.newline()
+ xmlWriter.newline()
+ xmlWriter.begintag("GlobalSubrs")
+ xmlWriter.newline()
+ self.GlobalSubrs.toXML(xmlWriter)
+ xmlWriter.endtag("GlobalSubrs")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, otFont=None):
+ """Reads data from the XML element into the ``CFFFontSet`` object."""
+ self.otFont = otFont
+
+ # set defaults. These will be replaced if there are entries for them
+ # in the XML file.
+ if not hasattr(self, "major"):
+ self.major = 1
+ if not hasattr(self, "minor"):
+ self.minor = 0
+
+ if name == "CFFFont":
+ if self.major == 1:
+ if not hasattr(self, "offSize"):
+ # this will be recalculated when the cff is compiled.
+ self.offSize = 4
+ if not hasattr(self, "hdrSize"):
+ self.hdrSize = 4
+ if not hasattr(self, "GlobalSubrs"):
+ self.GlobalSubrs = GlobalSubrsIndex()
+ if not hasattr(self, "fontNames"):
+ self.fontNames = []
+ self.topDictIndex = TopDictIndex()
+ fontName = attrs["name"]
+ self.fontNames.append(fontName)
+ topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
+ topDict.charset = None # gets filled in later
+ elif self.major == 2:
+ if not hasattr(self, "hdrSize"):
+ self.hdrSize = 5
+ if not hasattr(self, "GlobalSubrs"):
+ self.GlobalSubrs = GlobalSubrsIndex()
+ if not hasattr(self, "fontNames"):
+ self.fontNames = ["CFF2Font"]
+ cff2GetGlyphOrder = self.otFont.getGlyphOrder
+ topDict = TopDict(
+ GlobalSubrs=self.GlobalSubrs, cff2GetGlyphOrder=cff2GetGlyphOrder
+ )
+ self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder)
+ self.topDictIndex.append(topDict)
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ topDict.fromXML(name, attrs, content)
+
+ if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None:
+ fdArray = topDict.FDArray
+ for fontDict in fdArray:
+ if hasattr(fontDict, "Private"):
+ fontDict.Private.vstore = topDict.VarStore
+
+ elif name == "GlobalSubrs":
+ subrCharStringClass = psCharStrings.T2CharString
+ if not hasattr(self, "GlobalSubrs"):
+ self.GlobalSubrs = GlobalSubrsIndex()
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ subr = subrCharStringClass()
+ subr.fromXML(name, attrs, content)
+ self.GlobalSubrs.append(subr)
+ elif name == "major":
+ self.major = int(attrs["value"])
+ elif name == "minor":
+ self.minor = int(attrs["value"])
+
+ def convertCFFToCFF2(self, otFont):
+ from .CFFToCFF2 import _convertCFFToCFF2
+
+ _convertCFFToCFF2(self, otFont)
+
+ def convertCFF2ToCFF(self, otFont):
+ from .CFF2ToCFF import _convertCFF2ToCFF
+
+ _convertCFF2ToCFF(self, otFont)
+
+ def desubroutinize(self):
+ from .transforms import desubroutinize
+
+ desubroutinize(self)
+
+ def remove_hints(self):
+ from .transforms import remove_hints
+
+ remove_hints(self)
+
+ def remove_unused_subroutines(self):
+ from .transforms import remove_unused_subroutines
+
+ remove_unused_subroutines(self)
+
+
+class CFFWriter(object):
+ """Helper class for serializing CFF data to binary. Used by
+ :meth:`CFFFontSet.compile`."""
+
+ def __init__(self, isCFF2):
+ self.data = []
+ self.isCFF2 = isCFF2
+
+ def add(self, table):
+ self.data.append(table)
+
+ def toFile(self, file):
+ lastPosList = None
+ count = 1
+ while True:
+ log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count)
+ count = count + 1
+ pos = 0
+ posList = [pos]
+ for item in self.data:
+ if hasattr(item, "getDataLength"):
+ endPos = pos + item.getDataLength()
+ if isinstance(item, TopDictIndexCompiler) and item.isCFF2:
+ self.topDictSize = item.getDataLength()
+ else:
+ endPos = pos + len(item)
+ if hasattr(item, "setPos"):
+ item.setPos(pos, endPos)
+ pos = endPos
+ posList.append(pos)
+ if posList == lastPosList:
+ break
+ lastPosList = posList
+ log.log(DEBUG, "CFFWriter.toFile() writing to file.")
+ begin = file.tell()
+ if self.isCFF2:
+ self.data[1] = struct.pack(">H", self.topDictSize)
+ else:
+ self.offSize = calcOffSize(lastPosList[-1])
+ self.data[1] = struct.pack("B", self.offSize)
+ posList = [0]
+ for item in self.data:
+ if hasattr(item, "toFile"):
+ item.toFile(file)
+ else:
+ file.write(item)
+ posList.append(file.tell() - begin)
+ assert posList == lastPosList
+
+
+def calcOffSize(largestOffset):
+ if largestOffset < 0x100:
+ offSize = 1
+ elif largestOffset < 0x10000:
+ offSize = 2
+ elif largestOffset < 0x1000000:
+ offSize = 3
+ else:
+ offSize = 4
+ return offSize
+
+
+class IndexCompiler(object):
+ """Base class for writing CFF `INDEX data `_
+ to binary."""
+
+ def __init__(self, items, strings, parent, isCFF2=None):
+ if isCFF2 is None and hasattr(parent, "isCFF2"):
+ isCFF2 = parent.isCFF2
+ assert isCFF2 is not None
+ self.isCFF2 = isCFF2
+ self.items = self.getItems(items, strings)
+ self.parent = parent
+
+ def getItems(self, items, strings):
+ return items
+
+ def getOffsets(self):
+ # An empty INDEX contains only the count field.
+ if self.items:
+ pos = 1
+ offsets = [pos]
+ for item in self.items:
+ if hasattr(item, "getDataLength"):
+ pos = pos + item.getDataLength()
+ else:
+ pos = pos + len(item)
+ offsets.append(pos)
+ else:
+ offsets = []
+ return offsets
+
+ def getDataLength(self):
+ if self.isCFF2:
+ countSize = 4
+ else:
+ countSize = 2
+
+ if self.items:
+ lastOffset = self.getOffsets()[-1]
+ offSize = calcOffSize(lastOffset)
+ dataLength = (
+ countSize
+ + 1 # count
+ + (len(self.items) + 1) * offSize # offSize
+ + lastOffset # the offsets
+ - 1 # size of object data
+ )
+ else:
+ # count. For empty INDEX tables, this is the only entry.
+ dataLength = countSize
+
+ return dataLength
+
+ def toFile(self, file):
+ offsets = self.getOffsets()
+ if self.isCFF2:
+ writeCard32(file, len(self.items))
+ else:
+ writeCard16(file, len(self.items))
+ # An empty INDEX contains only the count field.
+ if self.items:
+ offSize = calcOffSize(offsets[-1])
+ writeCard8(file, offSize)
+ offSize = -offSize
+ pack = struct.pack
+ for offset in offsets:
+ binOffset = pack(">l", offset)[offSize:]
+ assert len(binOffset) == -offSize
+ file.write(binOffset)
+ for item in self.items:
+ if hasattr(item, "toFile"):
+ item.toFile(file)
+ else:
+ data = tobytes(item, encoding="latin1")
+ file.write(data)
+
+
+class IndexedStringsCompiler(IndexCompiler):
+ def getItems(self, items, strings):
+ return items.strings
+
+
+class TopDictIndexCompiler(IndexCompiler):
+ """Helper class for writing the TopDict to binary."""
+
+ def getItems(self, items, strings):
+ out = []
+ for item in items:
+ out.append(item.getCompiler(strings, self))
+ return out
+
+ def getChildren(self, strings):
+ children = []
+ for topDict in self.items:
+ children.extend(topDict.getChildren(strings))
+ return children
+
+ def getOffsets(self):
+ if self.isCFF2:
+ offsets = [0, self.items[0].getDataLength()]
+ return offsets
+ else:
+ return super(TopDictIndexCompiler, self).getOffsets()
+
+ def getDataLength(self):
+ if self.isCFF2:
+ dataLength = self.items[0].getDataLength()
+ return dataLength
+ else:
+ return super(TopDictIndexCompiler, self).getDataLength()
+
+ def toFile(self, file):
+ if self.isCFF2:
+ self.items[0].toFile(file)
+ else:
+ super(TopDictIndexCompiler, self).toFile(file)
+
+
+class FDArrayIndexCompiler(IndexCompiler):
+ """Helper class for writing the
+ `Font DICT INDEX `_
+ to binary."""
+
+ def getItems(self, items, strings):
+ out = []
+ for item in items:
+ out.append(item.getCompiler(strings, self))
+ return out
+
+ def getChildren(self, strings):
+ children = []
+ for fontDict in self.items:
+ children.extend(fontDict.getChildren(strings))
+ return children
+
+ def toFile(self, file):
+ offsets = self.getOffsets()
+ if self.isCFF2:
+ writeCard32(file, len(self.items))
+ else:
+ writeCard16(file, len(self.items))
+ offSize = calcOffSize(offsets[-1])
+ writeCard8(file, offSize)
+ offSize = -offSize
+ pack = struct.pack
+ for offset in offsets:
+ binOffset = pack(">l", offset)[offSize:]
+ assert len(binOffset) == -offSize
+ file.write(binOffset)
+ for item in self.items:
+ if hasattr(item, "toFile"):
+ item.toFile(file)
+ else:
+ file.write(item)
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["FDArray"] = pos
+
+
+class GlobalSubrsCompiler(IndexCompiler):
+ """Helper class for writing the `global subroutine INDEX `_
+ to binary."""
+
+ def getItems(self, items, strings):
+ out = []
+ for cs in items:
+ cs.compile(self.isCFF2)
+ out.append(cs.bytecode)
+ return out
+
+
+class SubrsCompiler(GlobalSubrsCompiler):
+ """Helper class for writing the `local subroutine INDEX `_
+ to binary."""
+
+ def setPos(self, pos, endPos):
+ offset = pos - self.parent.pos
+ self.parent.rawDict["Subrs"] = offset
+
+
+class CharStringsCompiler(GlobalSubrsCompiler):
+ """Helper class for writing the `CharStrings INDEX `_
+ to binary."""
+
+ def getItems(self, items, strings):
+ out = []
+ for cs in items:
+ cs.compile(self.isCFF2)
+ out.append(cs.bytecode)
+ return out
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["CharStrings"] = pos
+
+
+class Index(object):
+ """This class represents what the CFF spec calls an INDEX (an array of
+ variable-sized objects). `Index` items can be addressed and set using
+ Python list indexing."""
+
+ compilerClass = IndexCompiler
+
+ def __init__(self, file=None, isCFF2=None):
+ self.items = []
+ self.offsets = offsets = []
+ name = self.__class__.__name__
+ if file is None:
+ return
+ self._isCFF2 = isCFF2
+ log.log(DEBUG, "loading %s at %s", name, file.tell())
+ self.file = file
+ if isCFF2:
+ count = readCard32(file)
+ else:
+ count = readCard16(file)
+ if count == 0:
+ return
+ self.items = [None] * count
+ offSize = readCard8(file)
+ log.log(DEBUG, " index count: %s offSize: %s", count, offSize)
+ assert offSize <= 4, "offSize too large: %s" % offSize
+ pad = b"\0" * (4 - offSize)
+ for index in range(count + 1):
+ chunk = file.read(offSize)
+ chunk = pad + chunk
+ (offset,) = struct.unpack(">L", chunk)
+ offsets.append(int(offset))
+ self.offsetBase = file.tell() - 1
+ file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
+ log.log(DEBUG, " end of %s at %s", name, file.tell())
+
+ def __len__(self):
+ return len(self.items)
+
+ def __getitem__(self, index):
+ item = self.items[index]
+ if item is not None:
+ return item
+ offset = self.offsets[index] + self.offsetBase
+ size = self.offsets[index + 1] - self.offsets[index]
+ file = self.file
+ file.seek(offset)
+ data = file.read(size)
+ assert len(data) == size
+ item = self.produceItem(index, data, file, offset)
+ self.items[index] = item
+ return item
+
+ def __setitem__(self, index, item):
+ self.items[index] = item
+
+ def produceItem(self, index, data, file, offset):
+ return data
+
+ def append(self, item):
+ """Add an item to an INDEX."""
+ self.items.append(item)
+
+ def getCompiler(self, strings, parent, isCFF2=None):
+ return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
+
+ def clear(self):
+ """Empty the INDEX."""
+ del self.items[:]
+
+
+class GlobalSubrsIndex(Index):
+ """This index contains all the global subroutines in the font. A global
+ subroutine is a set of ``CharString`` data which is accessible to any
+ glyph in the font, and are used to store repeated instructions - for
+ example, components may be encoded as global subroutines, but so could
+ hinting instructions.
+
+ Remember that when interpreting a ``callgsubr`` instruction (or indeed
+ a ``callsubr`` instruction) that you will need to add the "subroutine
+ number bias" to number given:
+
+ .. code:: python
+
+ tt = ttLib.TTFont("Almendra-Bold.otf")
+ u = tt["CFF "].cff[0].CharStrings["udieresis"]
+ u.decompile()
+
+ u.toXML(XMLWriter(sys.stdout))
+ #
+ # -64 callgsubr <-- Subroutine which implements the dieresis mark
+ #
+
+ tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG
+ #
+
+ tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT
+ #
+
+ ("The bias applied depends on the number of subrs (gsubrs). If the number of
+ subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less
+ than 33900, it is 1131; otherwise it is 32768.",
+ `Subroutine Operators `)
+ """
+
+ compilerClass = GlobalSubrsCompiler
+ subrClass = psCharStrings.T2CharString
+ charStringClass = psCharStrings.T2CharString
+
+ def __init__(
+ self,
+ file=None,
+ globalSubrs=None,
+ private=None,
+ fdSelect=None,
+ fdArray=None,
+ isCFF2=None,
+ ):
+ super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2)
+ self.globalSubrs = globalSubrs
+ self.private = private
+ if fdSelect:
+ self.fdSelect = fdSelect
+ if fdArray:
+ self.fdArray = fdArray
+
+ def produceItem(self, index, data, file, offset):
+ if self.private is not None:
+ private = self.private
+ elif hasattr(self, "fdArray") and self.fdArray is not None:
+ if hasattr(self, "fdSelect") and self.fdSelect is not None:
+ fdIndex = self.fdSelect[index]
+ else:
+ fdIndex = 0
+ private = self.fdArray[fdIndex].Private
+ else:
+ private = None
+ return self.subrClass(data, private=private, globalSubrs=self.globalSubrs)
+
+ def toXML(self, xmlWriter):
+ """Write the subroutines index into XML representation onto the given
+ :class:`fontTools.misc.xmlWriter.XMLWriter`.
+
+ .. code:: python
+
+ writer = xmlWriter.XMLWriter(sys.stdout)
+ tt["CFF "].cff[0].GlobalSubrs.toXML(writer)
+
+ """
+ xmlWriter.comment(
+ "The 'index' attribute is only for humans; " "it is ignored when parsed."
+ )
+ xmlWriter.newline()
+ for i in range(len(self)):
+ subr = self[i]
+ if subr.needsDecompilation():
+ xmlWriter.begintag("CharString", index=i, raw=1)
+ else:
+ xmlWriter.begintag("CharString", index=i)
+ xmlWriter.newline()
+ subr.toXML(xmlWriter)
+ xmlWriter.endtag("CharString")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content):
+ if name != "CharString":
+ return
+ subr = self.subrClass()
+ subr.fromXML(name, attrs, content)
+ self.append(subr)
+
+ def getItemAndSelector(self, index):
+ sel = None
+ if hasattr(self, "fdSelect"):
+ sel = self.fdSelect[index]
+ return self[index], sel
+
+
+class SubrsIndex(GlobalSubrsIndex):
+ """This index contains a glyph's local subroutines. A local subroutine is a
+ private set of ``CharString`` data which is accessible only to the glyph to
+ which the index is attached."""
+
+ compilerClass = SubrsCompiler
+
+
+class TopDictIndex(Index):
+ """This index represents the array of ``TopDict`` structures in the font
+ (again, usually only one entry is present). Hence the following calls are
+ equivalent:
+
+ .. code:: python
+
+ tt["CFF "].cff[0]
+ #
+ tt["CFF "].cff.topDictIndex[0]
+ #
+
+ """
+
+ compilerClass = TopDictIndexCompiler
+
+ def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0, isCFF2=None):
+ self.cff2GetGlyphOrder = cff2GetGlyphOrder
+ if file is not None and isCFF2:
+ self._isCFF2 = isCFF2
+ self.items = []
+ name = self.__class__.__name__
+ log.log(DEBUG, "loading %s at %s", name, file.tell())
+ self.file = file
+ count = 1
+ self.items = [None] * count
+ self.offsets = [0, topSize]
+ self.offsetBase = file.tell()
+ # pretend we've read the whole lot
+ file.seek(self.offsetBase + topSize)
+ log.log(DEBUG, " end of %s at %s", name, file.tell())
+ else:
+ super(TopDictIndex, self).__init__(file, isCFF2=isCFF2)
+
+ def produceItem(self, index, data, file, offset):
+ top = TopDict(
+ self.strings,
+ file,
+ offset,
+ self.GlobalSubrs,
+ self.cff2GetGlyphOrder,
+ isCFF2=self._isCFF2,
+ )
+ top.decompile(data)
+ return top
+
+ def toXML(self, xmlWriter):
+ for i in range(len(self)):
+ xmlWriter.begintag("FontDict", index=i)
+ xmlWriter.newline()
+ self[i].toXML(xmlWriter)
+ xmlWriter.endtag("FontDict")
+ xmlWriter.newline()
+
+
+class FDArrayIndex(Index):
+ compilerClass = FDArrayIndexCompiler
+
+ def toXML(self, xmlWriter):
+ for i in range(len(self)):
+ xmlWriter.begintag("FontDict", index=i)
+ xmlWriter.newline()
+ self[i].toXML(xmlWriter)
+ xmlWriter.endtag("FontDict")
+ xmlWriter.newline()
+
+ def produceItem(self, index, data, file, offset):
+ fontDict = FontDict(
+ self.strings,
+ file,
+ offset,
+ self.GlobalSubrs,
+ isCFF2=self._isCFF2,
+ vstore=self.vstore,
+ )
+ fontDict.decompile(data)
+ return fontDict
+
+ def fromXML(self, name, attrs, content):
+ if name != "FontDict":
+ return
+ fontDict = FontDict()
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ fontDict.fromXML(name, attrs, content)
+ self.append(fontDict)
+
+
+class VarStoreData(object):
+ def __init__(self, file=None, otVarStore=None):
+ self.file = file
+ self.data = None
+ self.otVarStore = otVarStore
+ self.font = TTFont() # dummy font for the decompile function.
+
+ def decompile(self):
+ if self.file:
+ # read data in from file. Assume position is correct.
+ length = readCard16(self.file)
+ # https://github.com/fonttools/fonttools/issues/3673
+ if length == 65535:
+ self.data = self.file.read()
+ else:
+ self.data = self.file.read(length)
+ globalState = {}
+ reader = OTTableReader(self.data, globalState)
+ self.otVarStore = ot.VarStore()
+ self.otVarStore.decompile(reader, self.font)
+ self.data = None
+ return self
+
+ def compile(self):
+ writer = OTTableWriter()
+ self.otVarStore.compile(writer, self.font)
+ # Note that this omits the initial Card16 length from the CFF2
+ # VarStore data block
+ self.data = writer.getAllData()
+
+ def writeXML(self, xmlWriter, name):
+ self.otVarStore.toXML(xmlWriter, self.font)
+
+ def xmlRead(self, name, attrs, content, parent):
+ self.otVarStore = ot.VarStore()
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ self.otVarStore.fromXML(name, attrs, content, self.font)
+ else:
+ pass
+ return None
+
+ def __len__(self):
+ return len(self.data)
+
+ def getNumRegions(self, vsIndex):
+ if vsIndex is None:
+ vsIndex = 0
+ varData = self.otVarStore.VarData[vsIndex]
+ numRegions = varData.VarRegionCount
+ return numRegions
+
+
+class FDSelect(object):
+ def __init__(self, file=None, numGlyphs=None, format=None):
+ if file:
+ # read data in from file
+ self.format = readCard8(file)
+ if self.format == 0:
+ from array import array
+
+ self.gidArray = array("B", file.read(numGlyphs)).tolist()
+ elif self.format == 3:
+ gidArray = [None] * numGlyphs
+ nRanges = readCard16(file)
+ fd = None
+ prev = None
+ for i in range(nRanges):
+ first = readCard16(file)
+ if prev is not None:
+ for glyphID in range(prev, first):
+ gidArray[glyphID] = fd
+ prev = first
+ fd = readCard8(file)
+ if prev is not None:
+ first = readCard16(file)
+ for glyphID in range(prev, first):
+ gidArray[glyphID] = fd
+ self.gidArray = gidArray
+ elif self.format == 4:
+ gidArray = [None] * numGlyphs
+ nRanges = readCard32(file)
+ fd = None
+ prev = None
+ for i in range(nRanges):
+ first = readCard32(file)
+ if prev is not None:
+ for glyphID in range(prev, first):
+ gidArray[glyphID] = fd
+ prev = first
+ fd = readCard16(file)
+ if prev is not None:
+ first = readCard32(file)
+ for glyphID in range(prev, first):
+ gidArray[glyphID] = fd
+ self.gidArray = gidArray
+ else:
+ assert False, "unsupported FDSelect format: %s" % format
+ else:
+ # reading from XML. Make empty gidArray, and leave format as passed in.
+ # format is None will result in the smallest representation being used.
+ self.format = format
+ self.gidArray = []
+
+ def __len__(self):
+ return len(self.gidArray)
+
+ def __getitem__(self, index):
+ return self.gidArray[index]
+
+ def __setitem__(self, index, fdSelectValue):
+ self.gidArray[index] = fdSelectValue
+
+ def append(self, fdSelectValue):
+ self.gidArray.append(fdSelectValue)
+
+
+class CharStrings(object):
+ """The ``CharStrings`` in the font represent the instructions for drawing
+ each glyph. This object presents a dictionary interface to the font's
+ CharStrings, indexed by glyph name:
+
+ .. code:: python
+
+ tt["CFF "].cff[0].CharStrings["a"]
+ #
+
+ See :class:`fontTools.misc.psCharStrings.T1CharString` and
+ :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile,
+ compile and interpret the glyph drawing instructions in the returned objects.
+
+ """
+
+ def __init__(
+ self,
+ file,
+ charset,
+ globalSubrs,
+ private,
+ fdSelect,
+ fdArray,
+ isCFF2=None,
+ varStore=None,
+ ):
+ self.globalSubrs = globalSubrs
+ self.varStore = varStore
+ if file is not None:
+ self.charStringsIndex = SubrsIndex(
+ file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2
+ )
+ self.charStrings = charStrings = {}
+ for i in range(len(charset)):
+ charStrings[charset[i]] = i
+ # read from OTF file: charStrings.values() are indices into
+ # charStringsIndex.
+ self.charStringsAreIndexed = 1
+ else:
+ self.charStrings = {}
+ # read from ttx file: charStrings.values() are actual charstrings
+ self.charStringsAreIndexed = 0
+ self.private = private
+ if fdSelect is not None:
+ self.fdSelect = fdSelect
+ if fdArray is not None:
+ self.fdArray = fdArray
+
+ def keys(self):
+ return list(self.charStrings.keys())
+
+ def values(self):
+ if self.charStringsAreIndexed:
+ return self.charStringsIndex
+ else:
+ return list(self.charStrings.values())
+
+ def has_key(self, name):
+ return name in self.charStrings
+
+ __contains__ = has_key
+
+ def __len__(self):
+ return len(self.charStrings)
+
+ def __getitem__(self, name):
+ charString = self.charStrings[name]
+ if self.charStringsAreIndexed:
+ charString = self.charStringsIndex[charString]
+ return charString
+
+ def __setitem__(self, name, charString):
+ if self.charStringsAreIndexed:
+ index = self.charStrings[name]
+ self.charStringsIndex[index] = charString
+ else:
+ self.charStrings[name] = charString
+
+ def getItemAndSelector(self, name):
+ if self.charStringsAreIndexed:
+ index = self.charStrings[name]
+ return self.charStringsIndex.getItemAndSelector(index)
+ else:
+ if hasattr(self, "fdArray"):
+ if hasattr(self, "fdSelect"):
+ sel = self.charStrings[name].fdSelectIndex
+ else:
+ sel = 0
+ else:
+ sel = None
+ return self.charStrings[name], sel
+
+ def toXML(self, xmlWriter):
+ names = sorted(self.keys())
+ for name in names:
+ charStr, fdSelectIndex = self.getItemAndSelector(name)
+ if charStr.needsDecompilation():
+ raw = [("raw", 1)]
+ else:
+ raw = []
+ if fdSelectIndex is None:
+ xmlWriter.begintag("CharString", [("name", name)] + raw)
+ else:
+ xmlWriter.begintag(
+ "CharString",
+ [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw,
+ )
+ xmlWriter.newline()
+ charStr.toXML(xmlWriter)
+ xmlWriter.endtag("CharString")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content):
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ if name != "CharString":
+ continue
+ fdID = -1
+ if hasattr(self, "fdArray"):
+ try:
+ fdID = safeEval(attrs["fdSelectIndex"])
+ except KeyError:
+ fdID = 0
+ private = self.fdArray[fdID].Private
+ else:
+ private = self.private
+
+ glyphName = attrs["name"]
+ charStringClass = psCharStrings.T2CharString
+ charString = charStringClass(private=private, globalSubrs=self.globalSubrs)
+ charString.fromXML(name, attrs, content)
+ if fdID >= 0:
+ charString.fdSelectIndex = fdID
+ self[glyphName] = charString
+
+
+def readCard8(file):
+ return byteord(file.read(1))
+
+
+def readCard16(file):
+ (value,) = struct.unpack(">H", file.read(2))
+ return value
+
+
+def readCard32(file):
+ (value,) = struct.unpack(">L", file.read(4))
+ return value
+
+
+def writeCard8(file, value):
+ file.write(bytechr(value))
+
+
+def writeCard16(file, value):
+ file.write(struct.pack(">H", value))
+
+
+def writeCard32(file, value):
+ file.write(struct.pack(">L", value))
+
+
+def packCard8(value):
+ return bytechr(value)
+
+
+def packCard16(value):
+ return struct.pack(">H", value)
+
+
+def packCard32(value):
+ return struct.pack(">L", value)
+
+
+def buildOperatorDict(table):
+ d = {}
+ for op, name, arg, default, conv in table:
+ d[op] = (name, arg)
+ return d
+
+
+def buildOpcodeDict(table):
+ d = {}
+ for op, name, arg, default, conv in table:
+ if isinstance(op, tuple):
+ op = bytechr(op[0]) + bytechr(op[1])
+ else:
+ op = bytechr(op)
+ d[name] = (op, arg)
+ return d
+
+
+def buildOrder(table):
+ l = []
+ for op, name, arg, default, conv in table:
+ l.append(name)
+ return l
+
+
+def buildDefaults(table):
+ d = {}
+ for op, name, arg, default, conv in table:
+ if default is not None:
+ d[name] = default
+ return d
+
+
+def buildConverters(table):
+ d = {}
+ for op, name, arg, default, conv in table:
+ d[name] = conv
+ return d
+
+
+class SimpleConverter(object):
+ def read(self, parent, value):
+ if not hasattr(parent, "file"):
+ return self._read(parent, value)
+ file = parent.file
+ pos = file.tell()
+ try:
+ return self._read(parent, value)
+ finally:
+ file.seek(pos)
+
+ def _read(self, parent, value):
+ return value
+
+ def write(self, parent, value):
+ return value
+
+ def xmlWrite(self, xmlWriter, name, value):
+ xmlWriter.simpletag(name, value=value)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ return attrs["value"]
+
+
+class ASCIIConverter(SimpleConverter):
+ def _read(self, parent, value):
+ return tostr(value, encoding="ascii")
+
+ def write(self, parent, value):
+ return tobytes(value, encoding="ascii")
+
+ def xmlWrite(self, xmlWriter, name, value):
+ xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ return tobytes(attrs["value"], encoding=("ascii"))
+
+
+class Latin1Converter(SimpleConverter):
+ def _read(self, parent, value):
+ return tostr(value, encoding="latin1")
+
+ def write(self, parent, value):
+ return tobytes(value, encoding="latin1")
+
+ def xmlWrite(self, xmlWriter, name, value):
+ value = tostr(value, encoding="latin1")
+ if name in ["Notice", "Copyright"]:
+ value = re.sub(r"[\r\n]\s+", " ", value)
+ xmlWriter.simpletag(name, value=value)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ return tobytes(attrs["value"], encoding=("latin1"))
+
+
+def parseNum(s):
+ try:
+ value = int(s)
+ except:
+ value = float(s)
+ return value
+
+
+def parseBlendList(s):
+ valueList = []
+ for element in s:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ blendList = attrs["value"].split()
+ blendList = [eval(val) for val in blendList]
+ valueList.append(blendList)
+ if len(valueList) == 1:
+ valueList = valueList[0]
+ return valueList
+
+
+class NumberConverter(SimpleConverter):
+ def xmlWrite(self, xmlWriter, name, value):
+ if isinstance(value, list):
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ xmlWriter.indent()
+ blendValue = " ".join([str(val) for val in value])
+ xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
+ xmlWriter.newline()
+ xmlWriter.dedent()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+ else:
+ xmlWriter.simpletag(name, value=value)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ valueString = attrs.get("value", None)
+ if valueString is None:
+ value = parseBlendList(content)
+ else:
+ value = parseNum(attrs["value"])
+ return value
+
+
+class ArrayConverter(SimpleConverter):
+ def xmlWrite(self, xmlWriter, name, value):
+ if value and isinstance(value[0], list):
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ xmlWriter.indent()
+ for valueList in value:
+ blendValue = " ".join([str(val) for val in valueList])
+ xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
+ xmlWriter.newline()
+ xmlWriter.dedent()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+ else:
+ value = " ".join([str(val) for val in value])
+ xmlWriter.simpletag(name, value=value)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ valueString = attrs.get("value", None)
+ if valueString is None:
+ valueList = parseBlendList(content)
+ else:
+ values = valueString.split()
+ valueList = [parseNum(value) for value in values]
+ return valueList
+
+
+class TableConverter(SimpleConverter):
+ def xmlWrite(self, xmlWriter, name, value):
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ value.toXML(xmlWriter)
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ ob = self.getClass()()
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ ob.fromXML(name, attrs, content)
+ return ob
+
+
+class PrivateDictConverter(TableConverter):
+ def getClass(self):
+ return PrivateDict
+
+ def _read(self, parent, value):
+ size, offset = value
+ file = parent.file
+ isCFF2 = parent._isCFF2
+ try:
+ vstore = parent.vstore
+ except AttributeError:
+ vstore = None
+ priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore)
+ file.seek(offset)
+ data = file.read(size)
+ assert len(data) == size
+ priv.decompile(data)
+ return priv
+
+ def write(self, parent, value):
+ return (0, 0) # dummy value
+
+
+class SubrsConverter(TableConverter):
+ def getClass(self):
+ return SubrsIndex
+
+ def _read(self, parent, value):
+ file = parent.file
+ isCFF2 = parent._isCFF2
+ file.seek(parent.offset + value) # Offset(self)
+ return SubrsIndex(file, isCFF2=isCFF2)
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+
+class CharStringsConverter(TableConverter):
+ def _read(self, parent, value):
+ file = parent.file
+ isCFF2 = parent._isCFF2
+ charset = parent.charset
+ varStore = getattr(parent, "VarStore", None)
+ globalSubrs = parent.GlobalSubrs
+ if hasattr(parent, "FDArray"):
+ fdArray = parent.FDArray
+ if hasattr(parent, "FDSelect"):
+ fdSelect = parent.FDSelect
+ else:
+ fdSelect = None
+ private = None
+ else:
+ fdSelect, fdArray = None, None
+ private = parent.Private
+ file.seek(value) # Offset(0)
+ charStrings = CharStrings(
+ file,
+ charset,
+ globalSubrs,
+ private,
+ fdSelect,
+ fdArray,
+ isCFF2=isCFF2,
+ varStore=varStore,
+ )
+ return charStrings
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+ def xmlRead(self, name, attrs, content, parent):
+ if hasattr(parent, "FDArray"):
+ # if it is a CID-keyed font, then the private Dict is extracted from the
+ # parent.FDArray
+ fdArray = parent.FDArray
+ if hasattr(parent, "FDSelect"):
+ fdSelect = parent.FDSelect
+ else:
+ fdSelect = None
+ private = None
+ else:
+ # if it is a name-keyed font, then the private dict is in the top dict,
+ # and
+ # there is no fdArray.
+ private, fdSelect, fdArray = parent.Private, None, None
+ charStrings = CharStrings(
+ None,
+ None,
+ parent.GlobalSubrs,
+ private,
+ fdSelect,
+ fdArray,
+ varStore=getattr(parent, "VarStore", None),
+ )
+ charStrings.fromXML(name, attrs, content)
+ return charStrings
+
+
+class CharsetConverter(SimpleConverter):
+ def _read(self, parent, value):
+ isCID = hasattr(parent, "ROS")
+ if value > 2:
+ numGlyphs = parent.numGlyphs
+ file = parent.file
+ file.seek(value)
+ log.log(DEBUG, "loading charset at %s", value)
+ format = readCard8(file)
+ if format == 0:
+ charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
+ elif format == 1 or format == 2:
+ charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
+ else:
+ raise NotImplementedError
+ assert len(charset) == numGlyphs
+ log.log(DEBUG, " charset end at %s", file.tell())
+ # make sure glyph names are unique
+ allNames = {}
+ newCharset = []
+ for glyphName in charset:
+ if glyphName in allNames:
+ # make up a new glyphName that's unique
+ n = allNames[glyphName]
+ names = set(allNames) | set(charset)
+ while (glyphName + "." + str(n)) in names:
+ n += 1
+ allNames[glyphName] = n + 1
+ glyphName = glyphName + "." + str(n)
+ allNames[glyphName] = 1
+ newCharset.append(glyphName)
+ charset = newCharset
+ else: # offset == 0 -> no charset data.
+ if isCID or "CharStrings" not in parent.rawDict:
+ # We get here only when processing fontDicts from the FDArray of
+ # CFF-CID fonts. Only the real topDict references the charset.
+ assert value == 0
+ charset = None
+ elif value == 0:
+ charset = cffISOAdobeStrings
+ elif value == 1:
+ charset = cffIExpertStrings
+ elif value == 2:
+ charset = cffExpertSubsetStrings
+ if charset and (len(charset) != parent.numGlyphs):
+ charset = charset[: parent.numGlyphs]
+ return charset
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+ def xmlWrite(self, xmlWriter, name, value):
+ # XXX only write charset when not in OT/TTX context, where we
+ # dump charset as a separate "GlyphOrder" table.
+ # # xmlWriter.simpletag("charset")
+ xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ pass
+
+
+class CharsetCompiler(object):
+ def __init__(self, strings, charset, parent):
+ assert charset[0] == ".notdef"
+ isCID = hasattr(parent.dictObj, "ROS")
+ data0 = packCharset0(charset, isCID, strings)
+ data = packCharset(charset, isCID, strings)
+ if len(data) < len(data0):
+ self.data = data
+ else:
+ self.data = data0
+ self.parent = parent
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["charset"] = pos
+
+ def getDataLength(self):
+ return len(self.data)
+
+ def toFile(self, file):
+ file.write(self.data)
+
+
+def getStdCharSet(charset):
+ # check to see if we can use a predefined charset value.
+ predefinedCharSetVal = None
+ predefinedCharSets = [
+ (cffISOAdobeStringCount, cffISOAdobeStrings, 0),
+ (cffExpertStringCount, cffIExpertStrings, 1),
+ (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2),
+ ]
+ lcs = len(charset)
+ for cnt, pcs, csv in predefinedCharSets:
+ if predefinedCharSetVal is not None:
+ break
+ if lcs > cnt:
+ continue
+ predefinedCharSetVal = csv
+ for i in range(lcs):
+ if charset[i] != pcs[i]:
+ predefinedCharSetVal = None
+ break
+ return predefinedCharSetVal
+
+
+def getCIDfromName(name, strings):
+ return int(name[3:])
+
+
+def getSIDfromName(name, strings):
+ return strings.getSID(name)
+
+
+def packCharset0(charset, isCID, strings):
+ fmt = 0
+ data = [packCard8(fmt)]
+ if isCID:
+ getNameID = getCIDfromName
+ else:
+ getNameID = getSIDfromName
+
+ for name in charset[1:]:
+ data.append(packCard16(getNameID(name, strings)))
+ return bytesjoin(data)
+
+
+def packCharset(charset, isCID, strings):
+ fmt = 1
+ ranges = []
+ first = None
+ end = 0
+ if isCID:
+ getNameID = getCIDfromName
+ else:
+ getNameID = getSIDfromName
+
+ for name in charset[1:]:
+ SID = getNameID(name, strings)
+ if first is None:
+ first = SID
+ elif end + 1 != SID:
+ nLeft = end - first
+ if nLeft > 255:
+ fmt = 2
+ ranges.append((first, nLeft))
+ first = SID
+ end = SID
+ if end:
+ nLeft = end - first
+ if nLeft > 255:
+ fmt = 2
+ ranges.append((first, nLeft))
+
+ data = [packCard8(fmt)]
+ if fmt == 1:
+ nLeftFunc = packCard8
+ else:
+ nLeftFunc = packCard16
+ for first, nLeft in ranges:
+ data.append(packCard16(first) + nLeftFunc(nLeft))
+ return bytesjoin(data)
+
+
+def parseCharset0(numGlyphs, file, strings, isCID):
+ charset = [".notdef"]
+ if isCID:
+ for i in range(numGlyphs - 1):
+ CID = readCard16(file)
+ charset.append("cid" + str(CID).zfill(5))
+ else:
+ for i in range(numGlyphs - 1):
+ SID = readCard16(file)
+ charset.append(strings[SID])
+ return charset
+
+
+def parseCharset(numGlyphs, file, strings, isCID, fmt):
+ charset = [".notdef"]
+ count = 1
+ if fmt == 1:
+ nLeftFunc = readCard8
+ else:
+ nLeftFunc = readCard16
+ while count < numGlyphs:
+ first = readCard16(file)
+ nLeft = nLeftFunc(file)
+ if isCID:
+ for CID in range(first, first + nLeft + 1):
+ charset.append("cid" + str(CID).zfill(5))
+ else:
+ for SID in range(first, first + nLeft + 1):
+ charset.append(strings[SID])
+ count = count + nLeft + 1
+ return charset
+
+
+class EncodingCompiler(object):
+ def __init__(self, strings, encoding, parent):
+ assert not isinstance(encoding, str)
+ data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
+ data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
+ if len(data0) < len(data1):
+ self.data = data0
+ else:
+ self.data = data1
+ self.parent = parent
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["Encoding"] = pos
+
+ def getDataLength(self):
+ return len(self.data)
+
+ def toFile(self, file):
+ file.write(self.data)
+
+
+class EncodingConverter(SimpleConverter):
+ def _read(self, parent, value):
+ if value == 0:
+ return "StandardEncoding"
+ elif value == 1:
+ return "ExpertEncoding"
+ # custom encoding at offset `value`
+ assert value > 1
+ file = parent.file
+ file.seek(value)
+ log.log(DEBUG, "loading Encoding at %s", value)
+ fmt = readCard8(file)
+ haveSupplement = bool(fmt & 0x80)
+ fmt = fmt & 0x7F
+
+ if fmt == 0:
+ encoding = parseEncoding0(parent.charset, file)
+ elif fmt == 1:
+ encoding = parseEncoding1(parent.charset, file)
+ else:
+ raise ValueError(f"Unknown Encoding format: {fmt}")
+
+ if haveSupplement:
+ parseEncodingSupplement(file, encoding, parent.strings)
+
+ return encoding
+
+ def write(self, parent, value):
+ if value == "StandardEncoding":
+ return 0
+ elif value == "ExpertEncoding":
+ return 1
+ return 0 # dummy value
+
+ def xmlWrite(self, xmlWriter, name, value):
+ if value in ("StandardEncoding", "ExpertEncoding"):
+ xmlWriter.simpletag(name, name=value)
+ xmlWriter.newline()
+ return
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ for code in range(len(value)):
+ glyphName = value[code]
+ if glyphName != ".notdef":
+ xmlWriter.simpletag("map", code=hex(code), name=glyphName)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ if "name" in attrs:
+ return attrs["name"]
+ encoding = [".notdef"] * 256
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ code = safeEval(attrs["code"])
+ glyphName = attrs["name"]
+ encoding[code] = glyphName
+ return encoding
+
+
+def readSID(file):
+ """Read a String ID (SID) — 2-byte unsigned integer."""
+ data = file.read(2)
+ if len(data) != 2:
+ raise EOFError("Unexpected end of file while reading SID")
+ return struct.unpack(">H", data)[0] # big-endian uint16
+
+
+def parseEncodingSupplement(file, encoding, strings):
+ """
+ Parse the CFF Encoding supplement data:
+ - nSups: number of supplementary mappings
+ - each mapping: (code, SID) pair
+ and apply them to the `encoding` list in place.
+ """
+ nSups = readCard8(file)
+ for _ in range(nSups):
+ code = readCard8(file)
+ sid = readSID(file)
+ name = strings[sid]
+ encoding[code] = name
+
+
+def parseEncoding0(charset, file):
+ """
+ Format 0: simple list of codes.
+ After reading the base table, optionally parse the supplement.
+ """
+ nCodes = readCard8(file)
+ encoding = [".notdef"] * 256
+ for glyphID in range(1, nCodes + 1):
+ code = readCard8(file)
+ if code != 0:
+ encoding[code] = charset[glyphID]
+
+ return encoding
+
+
+def parseEncoding1(charset, file):
+ """
+ Format 1: range-based encoding.
+ After reading the base ranges, optionally parse the supplement.
+ """
+ nRanges = readCard8(file)
+ encoding = [".notdef"] * 256
+ glyphID = 1
+ for _ in range(nRanges):
+ code = readCard8(file)
+ nLeft = readCard8(file)
+ for _ in range(nLeft + 1):
+ encoding[code] = charset[glyphID]
+ code += 1
+ glyphID += 1
+
+ return encoding
+
+
+def packEncoding0(charset, encoding, strings):
+ fmt = 0
+ m = {}
+ for code in range(len(encoding)):
+ name = encoding[code]
+ if name != ".notdef":
+ m[name] = code
+ codes = []
+ for name in charset[1:]:
+ code = m.get(name)
+ codes.append(code)
+
+ while codes and codes[-1] is None:
+ codes.pop()
+
+ data = [packCard8(fmt), packCard8(len(codes))]
+ for code in codes:
+ if code is None:
+ code = 0
+ data.append(packCard8(code))
+ return bytesjoin(data)
+
+
+def packEncoding1(charset, encoding, strings):
+ fmt = 1
+ m = {}
+ for code in range(len(encoding)):
+ name = encoding[code]
+ if name != ".notdef":
+ m[name] = code
+ ranges = []
+ first = None
+ end = 0
+ for name in charset[1:]:
+ code = m.get(name, -1)
+ if first is None:
+ first = code
+ elif end + 1 != code:
+ nLeft = end - first
+ ranges.append((first, nLeft))
+ first = code
+ end = code
+ nLeft = end - first
+ ranges.append((first, nLeft))
+
+ # remove unencoded glyphs at the end.
+ while ranges and ranges[-1][0] == -1:
+ ranges.pop()
+
+ data = [packCard8(fmt), packCard8(len(ranges))]
+ for first, nLeft in ranges:
+ if first == -1: # unencoded
+ first = 0
+ data.append(packCard8(first) + packCard8(nLeft))
+ return bytesjoin(data)
+
+
+class FDArrayConverter(TableConverter):
+ def _read(self, parent, value):
+ try:
+ vstore = parent.VarStore
+ except AttributeError:
+ vstore = None
+ file = parent.file
+ isCFF2 = parent._isCFF2
+ file.seek(value)
+ fdArray = FDArrayIndex(file, isCFF2=isCFF2)
+ fdArray.vstore = vstore
+ fdArray.strings = parent.strings
+ fdArray.GlobalSubrs = parent.GlobalSubrs
+ return fdArray
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+ def xmlRead(self, name, attrs, content, parent):
+ fdArray = FDArrayIndex()
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ fdArray.fromXML(name, attrs, content)
+ return fdArray
+
+
+class FDSelectConverter(SimpleConverter):
+ def _read(self, parent, value):
+ file = parent.file
+ file.seek(value)
+ fdSelect = FDSelect(file, parent.numGlyphs)
+ return fdSelect
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+ # The FDSelect glyph data is written out to XML in the charstring keys,
+ # so we write out only the format selector
+ def xmlWrite(self, xmlWriter, name, value):
+ xmlWriter.simpletag(name, [("format", value.format)])
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ fmt = safeEval(attrs["format"])
+ file = None
+ numGlyphs = None
+ fdSelect = FDSelect(file, numGlyphs, fmt)
+ return fdSelect
+
+
+class VarStoreConverter(SimpleConverter):
+ def _read(self, parent, value):
+ file = parent.file
+ file.seek(value)
+ varStore = VarStoreData(file)
+ varStore.decompile()
+ return varStore
+
+ def write(self, parent, value):
+ return 0 # dummy value
+
+ def xmlWrite(self, xmlWriter, name, value):
+ value.writeXML(xmlWriter, name)
+
+ def xmlRead(self, name, attrs, content, parent):
+ varStore = VarStoreData()
+ varStore.xmlRead(name, attrs, content, parent)
+ return varStore
+
+
+def packFDSelect0(fdSelectArray):
+ fmt = 0
+ data = [packCard8(fmt)]
+ for index in fdSelectArray:
+ data.append(packCard8(index))
+ return bytesjoin(data)
+
+
+def packFDSelect3(fdSelectArray):
+ fmt = 3
+ fdRanges = []
+ lenArray = len(fdSelectArray)
+ lastFDIndex = -1
+ for i in range(lenArray):
+ fdIndex = fdSelectArray[i]
+ if lastFDIndex != fdIndex:
+ fdRanges.append([i, fdIndex])
+ lastFDIndex = fdIndex
+ sentinelGID = i + 1
+
+ data = [packCard8(fmt)]
+ data.append(packCard16(len(fdRanges)))
+ for fdRange in fdRanges:
+ data.append(packCard16(fdRange[0]))
+ data.append(packCard8(fdRange[1]))
+ data.append(packCard16(sentinelGID))
+ return bytesjoin(data)
+
+
+def packFDSelect4(fdSelectArray):
+ fmt = 4
+ fdRanges = []
+ lenArray = len(fdSelectArray)
+ lastFDIndex = -1
+ for i in range(lenArray):
+ fdIndex = fdSelectArray[i]
+ if lastFDIndex != fdIndex:
+ fdRanges.append([i, fdIndex])
+ lastFDIndex = fdIndex
+ sentinelGID = i + 1
+
+ data = [packCard8(fmt)]
+ data.append(packCard32(len(fdRanges)))
+ for fdRange in fdRanges:
+ data.append(packCard32(fdRange[0]))
+ data.append(packCard16(fdRange[1]))
+ data.append(packCard32(sentinelGID))
+ return bytesjoin(data)
+
+
+class FDSelectCompiler(object):
+ def __init__(self, fdSelect, parent):
+ fmt = fdSelect.format
+ fdSelectArray = fdSelect.gidArray
+ if fmt == 0:
+ self.data = packFDSelect0(fdSelectArray)
+ elif fmt == 3:
+ self.data = packFDSelect3(fdSelectArray)
+ elif fmt == 4:
+ self.data = packFDSelect4(fdSelectArray)
+ else:
+ # choose smaller of the two formats
+ data0 = packFDSelect0(fdSelectArray)
+ data3 = packFDSelect3(fdSelectArray)
+ if len(data0) < len(data3):
+ self.data = data0
+ fdSelect.format = 0
+ else:
+ self.data = data3
+ fdSelect.format = 3
+
+ self.parent = parent
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["FDSelect"] = pos
+
+ def getDataLength(self):
+ return len(self.data)
+
+ def toFile(self, file):
+ file.write(self.data)
+
+
+class VarStoreCompiler(object):
+ def __init__(self, varStoreData, parent):
+ self.parent = parent
+ if not varStoreData.data:
+ varStoreData.compile()
+ varStoreDataLen = min(0xFFFF, len(varStoreData.data))
+ data = [packCard16(varStoreDataLen), varStoreData.data]
+ self.data = bytesjoin(data)
+
+ def setPos(self, pos, endPos):
+ self.parent.rawDict["VarStore"] = pos
+
+ def getDataLength(self):
+ return len(self.data)
+
+ def toFile(self, file):
+ file.write(self.data)
+
+
+class ROSConverter(SimpleConverter):
+ def xmlWrite(self, xmlWriter, name, value):
+ registry, order, supplement = value
+ xmlWriter.simpletag(
+ name,
+ [
+ ("Registry", tostr(registry)),
+ ("Order", tostr(order)),
+ ("Supplement", supplement),
+ ],
+ )
+ xmlWriter.newline()
+
+ def xmlRead(self, name, attrs, content, parent):
+ return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"]))
+
+
+topDictOperators = [
+ # opcode name argument type default converter
+ (25, "maxstack", "number", None, None),
+ ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()),
+ ((12, 20), "SyntheticBase", "number", None, None),
+ (0, "version", "SID", None, None),
+ (1, "Notice", "SID", None, Latin1Converter()),
+ ((12, 0), "Copyright", "SID", None, Latin1Converter()),
+ (2, "FullName", "SID", None, Latin1Converter()),
+ ((12, 38), "FontName", "SID", None, Latin1Converter()),
+ (3, "FamilyName", "SID", None, Latin1Converter()),
+ (4, "Weight", "SID", None, None),
+ ((12, 1), "isFixedPitch", "number", 0, None),
+ ((12, 2), "ItalicAngle", "number", 0, None),
+ ((12, 3), "UnderlinePosition", "number", -100, None),
+ ((12, 4), "UnderlineThickness", "number", 50, None),
+ ((12, 5), "PaintType", "number", 0, None),
+ ((12, 6), "CharstringType", "number", 2, None),
+ ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
+ (13, "UniqueID", "number", None, None),
+ (5, "FontBBox", "array", [0, 0, 0, 0], None),
+ ((12, 8), "StrokeWidth", "number", 0, None),
+ (14, "XUID", "array", None, None),
+ ((12, 21), "PostScript", "SID", None, None),
+ ((12, 22), "BaseFontName", "SID", None, None),
+ ((12, 23), "BaseFontBlend", "delta", None, None),
+ ((12, 31), "CIDFontVersion", "number", 0, None),
+ ((12, 32), "CIDFontRevision", "number", 0, None),
+ ((12, 33), "CIDFontType", "number", 0, None),
+ ((12, 34), "CIDCount", "number", 8720, None),
+ (15, "charset", "number", None, CharsetConverter()),
+ ((12, 35), "UIDBase", "number", None, None),
+ (16, "Encoding", "number", 0, EncodingConverter()),
+ (18, "Private", ("number", "number"), None, PrivateDictConverter()),
+ ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
+ ((12, 36), "FDArray", "number", None, FDArrayConverter()),
+ (17, "CharStrings", "number", None, CharStringsConverter()),
+ (24, "VarStore", "number", None, VarStoreConverter()),
+]
+
+topDictOperators2 = [
+ # opcode name argument type default converter
+ (25, "maxstack", "number", None, None),
+ ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
+ ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
+ ((12, 36), "FDArray", "number", None, FDArrayConverter()),
+ (17, "CharStrings", "number", None, CharStringsConverter()),
+ (24, "VarStore", "number", None, VarStoreConverter()),
+]
+
+# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
+# in order for the font to compile back from xml.
+
+kBlendDictOpName = "blend"
+blendOp = 23
+
+privateDictOperators = [
+ # opcode name argument type default converter
+ (22, "vsindex", "number", None, None),
+ (
+ blendOp,
+ kBlendDictOpName,
+ "blendList",
+ None,
+ None,
+ ), # This is for reading to/from XML: it not written to CFF.
+ (6, "BlueValues", "delta", None, None),
+ (7, "OtherBlues", "delta", None, None),
+ (8, "FamilyBlues", "delta", None, None),
+ (9, "FamilyOtherBlues", "delta", None, None),
+ ((12, 9), "BlueScale", "number", 0.039625, None),
+ ((12, 10), "BlueShift", "number", 7, None),
+ ((12, 11), "BlueFuzz", "number", 1, None),
+ (10, "StdHW", "number", None, None),
+ (11, "StdVW", "number", None, None),
+ ((12, 12), "StemSnapH", "delta", None, None),
+ ((12, 13), "StemSnapV", "delta", None, None),
+ ((12, 14), "ForceBold", "number", 0, None),
+ ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated
+ ((12, 16), "lenIV", "number", None, None), # deprecated
+ ((12, 17), "LanguageGroup", "number", 0, None),
+ ((12, 18), "ExpansionFactor", "number", 0.06, None),
+ ((12, 19), "initialRandomSeed", "number", 0, None),
+ (20, "defaultWidthX", "number", 0, None),
+ (21, "nominalWidthX", "number", 0, None),
+ (19, "Subrs", "number", None, SubrsConverter()),
+]
+
+privateDictOperators2 = [
+ # opcode name argument type default converter
+ (22, "vsindex", "number", None, None),
+ (
+ blendOp,
+ kBlendDictOpName,
+ "blendList",
+ None,
+ None,
+ ), # This is for reading to/from XML: it not written to CFF.
+ (6, "BlueValues", "delta", None, None),
+ (7, "OtherBlues", "delta", None, None),
+ (8, "FamilyBlues", "delta", None, None),
+ (9, "FamilyOtherBlues", "delta", None, None),
+ ((12, 9), "BlueScale", "number", 0.039625, None),
+ ((12, 10), "BlueShift", "number", 7, None),
+ ((12, 11), "BlueFuzz", "number", 1, None),
+ (10, "StdHW", "number", None, None),
+ (11, "StdVW", "number", None, None),
+ ((12, 12), "StemSnapH", "delta", None, None),
+ ((12, 13), "StemSnapV", "delta", None, None),
+ ((12, 17), "LanguageGroup", "number", 0, None),
+ ((12, 18), "ExpansionFactor", "number", 0.06, None),
+ (19, "Subrs", "number", None, SubrsConverter()),
+]
+
+
+def addConverters(table):
+ for i in range(len(table)):
+ op, name, arg, default, conv = table[i]
+ if conv is not None:
+ continue
+ if arg in ("delta", "array"):
+ conv = ArrayConverter()
+ elif arg == "number":
+ conv = NumberConverter()
+ elif arg == "SID":
+ conv = ASCIIConverter()
+ elif arg == "blendList":
+ conv = None
+ else:
+ assert False
+ table[i] = op, name, arg, default, conv
+
+
+addConverters(privateDictOperators)
+addConverters(topDictOperators)
+
+
+class TopDictDecompiler(psCharStrings.DictDecompiler):
+ operators = buildOperatorDict(topDictOperators)
+
+
+class PrivateDictDecompiler(psCharStrings.DictDecompiler):
+ operators = buildOperatorDict(privateDictOperators)
+
+
+class DictCompiler(object):
+ maxBlendStack = 0
+
+ def __init__(self, dictObj, strings, parent, isCFF2=None):
+ if strings:
+ assert isinstance(strings, IndexedStrings)
+ if isCFF2 is None and hasattr(parent, "isCFF2"):
+ isCFF2 = parent.isCFF2
+ assert isCFF2 is not None
+ self.isCFF2 = isCFF2
+ self.dictObj = dictObj
+ self.strings = strings
+ self.parent = parent
+ rawDict = {}
+ for name in dictObj.order:
+ value = getattr(dictObj, name, None)
+ if value is None:
+ continue
+ conv = dictObj.converters[name]
+ value = conv.write(dictObj, value)
+ if value == dictObj.defaults.get(name):
+ continue
+ rawDict[name] = value
+ self.rawDict = rawDict
+
+ def setPos(self, pos, endPos):
+ pass
+
+ def getDataLength(self):
+ return len(self.compile("getDataLength"))
+
+ def compile(self, reason):
+ log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason)
+ rawDict = self.rawDict
+ data = []
+ for name in self.dictObj.order:
+ value = rawDict.get(name)
+ if value is None:
+ continue
+ op, argType = self.opcodes[name]
+ if isinstance(argType, tuple):
+ l = len(argType)
+ assert len(value) == l, "value doesn't match arg type"
+ for i in range(l):
+ arg = argType[i]
+ v = value[i]
+ arghandler = getattr(self, "arg_" + arg)
+ data.append(arghandler(v))
+ else:
+ arghandler = getattr(self, "arg_" + argType)
+ data.append(arghandler(value))
+ data.append(op)
+ data = bytesjoin(data)
+ return data
+
+ def toFile(self, file):
+ data = self.compile("toFile")
+ file.write(data)
+
+ def arg_number(self, num):
+ if isinstance(num, list):
+ data = [encodeNumber(val) for val in num]
+ data.append(encodeNumber(1))
+ data.append(bytechr(blendOp))
+ datum = bytesjoin(data)
+ else:
+ datum = encodeNumber(num)
+ return datum
+
+ def arg_SID(self, s):
+ return psCharStrings.encodeIntCFF(self.strings.getSID(s))
+
+ def arg_array(self, value):
+ data = []
+ for num in value:
+ data.append(self.arg_number(num))
+ return bytesjoin(data)
+
+ def arg_delta(self, value):
+ if not value:
+ return b""
+ val0 = value[0]
+ if isinstance(val0, list):
+ data = self.arg_delta_blend(value)
+ else:
+ out = []
+ last = 0
+ for v in value:
+ out.append(v - last)
+ last = v
+ data = []
+ for num in out:
+ data.append(encodeNumber(num))
+ return bytesjoin(data)
+
+ def arg_delta_blend(self, value):
+ """A delta list with blend lists has to be *all* blend lists.
+
+ The value is a list is arranged as follows::
+
+ [
+ [V0, d0..dn]
+ [V1, d0..dn]
+ ...
+ [Vm, d0..dn]
+ ]
+
+ ``V`` is the absolute coordinate value from the default font, and ``d0-dn``
+ are the delta values from the *n* regions. Each ``V`` is an absolute
+ coordinate from the default font.
+
+ We want to return a list::
+
+ [
+ [v0, v1..vm]
+ [d0..dn]
+ ...
+ [d0..dn]
+ numBlends
+ blendOp
+ ]
+
+ where each ``v`` is relative to the previous default font value.
+ """
+ numMasters = len(value[0])
+ numBlends = len(value)
+ numStack = (numBlends * numMasters) + 1
+ if numStack > self.maxBlendStack:
+ # Figure out the max number of value we can blend
+ # and divide this list up into chunks of that size.
+
+ numBlendValues = int((self.maxBlendStack - 1) / numMasters)
+ out = []
+ while True:
+ numVal = min(len(value), numBlendValues)
+ if numVal == 0:
+ break
+ valList = value[0:numVal]
+ out1 = self.arg_delta_blend(valList)
+ out.extend(out1)
+ value = value[numVal:]
+ else:
+ firstList = [0] * numBlends
+ deltaList = [None] * numBlends
+ i = 0
+ prevVal = 0
+ while i < numBlends:
+ # For PrivateDict BlueValues, the default font
+ # values are absolute, not relative.
+ # Must convert these back to relative coordinates
+ # before writing to CFF2.
+ defaultValue = value[i][0]
+ firstList[i] = defaultValue - prevVal
+ prevVal = defaultValue
+ deltaList[i] = value[i][1:]
+ i += 1
+
+ relValueList = firstList
+ for blendList in deltaList:
+ relValueList.extend(blendList)
+ out = [encodeNumber(val) for val in relValueList]
+ out.append(encodeNumber(numBlends))
+ out.append(bytechr(blendOp))
+ return out
+
+
+def encodeNumber(num):
+ if isinstance(num, float):
+ return psCharStrings.encodeFloat(num)
+ else:
+ return psCharStrings.encodeIntCFF(num)
+
+
+class TopDictCompiler(DictCompiler):
+ opcodes = buildOpcodeDict(topDictOperators)
+
+ def getChildren(self, strings):
+ isCFF2 = self.isCFF2
+ children = []
+ if self.dictObj.cff2GetGlyphOrder is None:
+ if hasattr(self.dictObj, "charset") and self.dictObj.charset:
+ if hasattr(self.dictObj, "ROS"): # aka isCID
+ charsetCode = None
+ else:
+ charsetCode = getStdCharSet(self.dictObj.charset)
+ if charsetCode is None:
+ children.append(
+ CharsetCompiler(strings, self.dictObj.charset, self)
+ )
+ else:
+ self.rawDict["charset"] = charsetCode
+ if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
+ encoding = self.dictObj.Encoding
+ if not isinstance(encoding, str):
+ children.append(EncodingCompiler(strings, encoding, self))
+ else:
+ if hasattr(self.dictObj, "VarStore"):
+ varStoreData = self.dictObj.VarStore
+ varStoreComp = VarStoreCompiler(varStoreData, self)
+ children.append(varStoreComp)
+ if hasattr(self.dictObj, "FDSelect"):
+ # I have not yet supported merging a ttx CFF-CID font, as there are
+ # interesting issues about merging the FDArrays. Here I assume that
+ # either the font was read from XML, and the FDSelect indices are all
+ # in the charstring data, or the FDSelect array is already fully defined.
+ fdSelect = self.dictObj.FDSelect
+ # probably read in from XML; assume fdIndex in CharString data
+ if len(fdSelect) == 0:
+ charStrings = self.dictObj.CharStrings
+ for name in self.dictObj.charset:
+ fdSelect.append(charStrings[name].fdSelectIndex)
+ fdSelectComp = FDSelectCompiler(fdSelect, self)
+ children.append(fdSelectComp)
+ if hasattr(self.dictObj, "CharStrings"):
+ items = []
+ charStrings = self.dictObj.CharStrings
+ for name in self.dictObj.charset:
+ items.append(charStrings[name])
+ charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2)
+ children.append(charStringsComp)
+ if hasattr(self.dictObj, "FDArray"):
+ # I have not yet supported merging a ttx CFF-CID font, as there are
+ # interesting issues about merging the FDArrays. Here I assume that the
+ # FDArray info is correct and complete.
+ fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
+ children.append(fdArrayIndexComp)
+ children.extend(fdArrayIndexComp.getChildren(strings))
+ if hasattr(self.dictObj, "Private"):
+ privComp = self.dictObj.Private.getCompiler(strings, self)
+ children.append(privComp)
+ children.extend(privComp.getChildren(strings))
+ return children
+
+
+class FontDictCompiler(DictCompiler):
+ opcodes = buildOpcodeDict(topDictOperators)
+
+ def __init__(self, dictObj, strings, parent, isCFF2=None):
+ super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2)
+ #
+ # We now take some effort to detect if there were any key/value pairs
+ # supplied that were ignored in the FontDict context, and issue a warning
+ # for those cases.
+ #
+ ignoredNames = []
+ dictObj = self.dictObj
+ for name in sorted(set(dictObj.converters) - set(dictObj.order)):
+ if name in dictObj.rawDict:
+ # The font was directly read from binary. In this
+ # case, we want to report *all* "useless" key/value
+ # pairs that are in the font, not just the ones that
+ # are different from the default.
+ ignoredNames.append(name)
+ else:
+ # The font was probably read from a TTX file. We only
+ # warn about keys whos value is not the default. The
+ # ones that have the default value will not be written
+ # to binary anyway.
+ default = dictObj.defaults.get(name)
+ if default is not None:
+ conv = dictObj.converters[name]
+ default = conv.read(dictObj, default)
+ if getattr(dictObj, name, None) != default:
+ ignoredNames.append(name)
+ if ignoredNames:
+ log.warning(
+ "Some CFF FDArray/FontDict keys were ignored upon compile: "
+ + " ".join(sorted(ignoredNames))
+ )
+
+ def getChildren(self, strings):
+ children = []
+ if hasattr(self.dictObj, "Private"):
+ privComp = self.dictObj.Private.getCompiler(strings, self)
+ children.append(privComp)
+ children.extend(privComp.getChildren(strings))
+ return children
+
+
+class PrivateDictCompiler(DictCompiler):
+ maxBlendStack = maxStackLimit
+ opcodes = buildOpcodeDict(privateDictOperators)
+
+ def setPos(self, pos, endPos):
+ size = endPos - pos
+ self.parent.rawDict["Private"] = size, pos
+ self.pos = pos
+
+ def getChildren(self, strings):
+ children = []
+ if hasattr(self.dictObj, "Subrs"):
+ children.append(self.dictObj.Subrs.getCompiler(strings, self))
+ return children
+
+
+class BaseDict(object):
+ def __init__(self, strings=None, file=None, offset=None, isCFF2=None):
+ assert (isCFF2 is None) == (file is None)
+ self.rawDict = {}
+ self.skipNames = []
+ self.strings = strings
+ if file is None:
+ return
+ self._isCFF2 = isCFF2
+ self.file = file
+ if offset is not None:
+ log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset)
+ self.offset = offset
+
+ def decompile(self, data):
+ log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data))
+ dec = self.decompilerClass(self.strings, self)
+ dec.decompile(data)
+ self.rawDict = dec.getDict()
+ self.postDecompile()
+
+ def postDecompile(self):
+ pass
+
+ def getCompiler(self, strings, parent, isCFF2=None):
+ return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
+
+ def __getattr__(self, name):
+ if name[:2] == name[-2:] == "__":
+ # to make deepcopy() and pickle.load() work, we need to signal with
+ # AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
+ # aren't implemented. For more details, see:
+ # https://github.com/fonttools/fonttools/pull/1488
+ raise AttributeError(name)
+ value = self.rawDict.get(name, None)
+ if value is None:
+ value = self.defaults.get(name)
+ if value is None:
+ raise AttributeError(name)
+ conv = self.converters[name]
+ value = conv.read(self, value)
+ setattr(self, name, value)
+ return value
+
+ def toXML(self, xmlWriter):
+ for name in self.order:
+ if name in self.skipNames:
+ continue
+ value = getattr(self, name, None)
+ # XXX For "charset" we never skip calling xmlWrite even if the
+ # value is None, so we always write the following XML comment:
+ #
+ #
+ #
+ # Charset is None when 'CFF ' table is imported from XML into an
+ # empty TTFont(). By writing this comment all the time, we obtain
+ # the same XML output whether roundtripping XML-to-XML or
+ # dumping binary-to-XML
+ if value is None and name != "charset":
+ continue
+ conv = self.converters[name]
+ conv.xmlWrite(xmlWriter, name, value)
+ ignoredNames = set(self.rawDict) - set(self.order)
+ if ignoredNames:
+ xmlWriter.comment(
+ "some keys were ignored: %s" % " ".join(sorted(ignoredNames))
+ )
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content):
+ conv = self.converters[name]
+ value = conv.xmlRead(name, attrs, content, self)
+ setattr(self, name, value)
+
+
+class TopDict(BaseDict):
+ """The ``TopDict`` represents the top-level dictionary holding font
+ information. CFF2 tables contain a restricted set of top-level entries
+ as described `here `_,
+ but CFF tables may contain a wider range of information. This information
+ can be accessed through attributes or through the dictionary returned
+ through the ``rawDict`` property:
+
+ .. code:: python
+
+ font = tt["CFF "].cff[0]
+ font.FamilyName
+ # 'Linux Libertine O'
+ font.rawDict["FamilyName"]
+ # 'Linux Libertine O'
+
+ More information is available in the CFF file's private dictionary, accessed
+ via the ``Private`` property:
+
+ .. code:: python
+
+ tt["CFF "].cff[0].Private.BlueValues
+ # [-15, 0, 515, 515, 666, 666]
+
+ """
+
+ defaults = buildDefaults(topDictOperators)
+ converters = buildConverters(topDictOperators)
+ compilerClass = TopDictCompiler
+ order = buildOrder(topDictOperators)
+ decompilerClass = TopDictDecompiler
+
+ def __init__(
+ self,
+ strings=None,
+ file=None,
+ offset=None,
+ GlobalSubrs=None,
+ cff2GetGlyphOrder=None,
+ isCFF2=None,
+ ):
+ super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+ self.cff2GetGlyphOrder = cff2GetGlyphOrder
+ self.GlobalSubrs = GlobalSubrs
+ if isCFF2:
+ self.defaults = buildDefaults(topDictOperators2)
+ self.charset = cff2GetGlyphOrder()
+ self.order = buildOrder(topDictOperators2)
+ else:
+ self.defaults = buildDefaults(topDictOperators)
+ self.order = buildOrder(topDictOperators)
+
+ def getGlyphOrder(self):
+ """Returns a list of glyph names in the CFF font."""
+ return self.charset
+
+ def postDecompile(self):
+ offset = self.rawDict.get("CharStrings")
+ if offset is None:
+ return
+ # get the number of glyphs beforehand.
+ self.file.seek(offset)
+ if self._isCFF2:
+ self.numGlyphs = readCard32(self.file)
+ else:
+ self.numGlyphs = readCard16(self.file)
+
+ def toXML(self, xmlWriter):
+ if hasattr(self, "CharStrings"):
+ self.decompileAllCharStrings()
+ if hasattr(self, "ROS"):
+ self.skipNames = ["Encoding"]
+ if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
+ # these values have default values, but I only want them to show up
+ # in CID fonts.
+ self.skipNames = [
+ "CIDFontVersion",
+ "CIDFontRevision",
+ "CIDFontType",
+ "CIDCount",
+ ]
+ BaseDict.toXML(self, xmlWriter)
+
+ def decompileAllCharStrings(self):
+ # Make sure that all the Private Dicts have been instantiated.
+ for i, charString in enumerate(self.CharStrings.values()):
+ try:
+ charString.decompile()
+ except:
+ log.error("Error in charstring %s", i)
+ raise
+
+ def recalcFontBBox(self):
+ fontBBox = None
+ for charString in self.CharStrings.values():
+ bounds = charString.calcBounds(self.CharStrings)
+ if bounds is not None:
+ if fontBBox is not None:
+ fontBBox = unionRect(fontBBox, bounds)
+ else:
+ fontBBox = bounds
+
+ if fontBBox is None:
+ self.FontBBox = self.defaults["FontBBox"][:]
+ else:
+ self.FontBBox = list(intRect(fontBBox))
+
+
+class FontDict(BaseDict):
+ #
+ # Since fonttools used to pass a lot of fields that are not relevant in the FDArray
+ # FontDict, there are 'ttx' files in the wild that contain all these. These got in
+ # the ttx files because fonttools writes explicit values for all the TopDict default
+ # values. These are not actually illegal in the context of an FDArray FontDict - you
+ # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are
+ # useless since current major company CFF interpreters ignore anything but the set
+ # listed in this file. So, we just silently skip them. An exception is Weight: this
+ # is not used by any interpreter, but some foundries have asked that this be
+ # supported in FDArray FontDicts just to preserve information about the design when
+ # the font is being inspected.
+ #
+ # On top of that, there are fonts out there that contain such useless FontDict values.
+ #
+ # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading
+ # from binary or when reading from XML, but by overriding `order` with a limited
+ # list of names, we ensure that only the useful names ever get exported to XML and
+ # ever get compiled into the binary font.
+ #
+ # We override compilerClass so we can warn about "useless" key/value pairs, either
+ # from the original binary font or from TTX input.
+ #
+ # See:
+ # - https://github.com/fonttools/fonttools/issues/740
+ # - https://github.com/fonttools/fonttools/issues/601
+ # - https://github.com/adobe-type-tools/afdko/issues/137
+ #
+ defaults = {}
+ converters = buildConverters(topDictOperators)
+ compilerClass = FontDictCompiler
+ orderCFF = ["FontName", "FontMatrix", "Weight", "Private"]
+ orderCFF2 = ["Private"]
+ decompilerClass = TopDictDecompiler
+
+ def __init__(
+ self,
+ strings=None,
+ file=None,
+ offset=None,
+ GlobalSubrs=None,
+ isCFF2=None,
+ vstore=None,
+ ):
+ super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+ self.vstore = vstore
+ self.setCFF2(isCFF2)
+
+ def setCFF2(self, isCFF2):
+ # isCFF2 may be None.
+ if isCFF2:
+ self.order = self.orderCFF2
+ self._isCFF2 = True
+ else:
+ self.order = self.orderCFF
+ self._isCFF2 = False
+
+
+class PrivateDict(BaseDict):
+ defaults = buildDefaults(privateDictOperators)
+ converters = buildConverters(privateDictOperators)
+ order = buildOrder(privateDictOperators)
+ decompilerClass = PrivateDictDecompiler
+ compilerClass = PrivateDictCompiler
+
+ def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None):
+ super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+ self.vstore = vstore
+ if isCFF2:
+ self.defaults = buildDefaults(privateDictOperators2)
+ self.order = buildOrder(privateDictOperators2)
+ # Provide dummy values. This avoids needing to provide
+ # an isCFF2 state in a lot of places.
+ self.nominalWidthX = self.defaultWidthX = None
+ self._isCFF2 = True
+ else:
+ self.defaults = buildDefaults(privateDictOperators)
+ self.order = buildOrder(privateDictOperators)
+ self._isCFF2 = False
+
+ @property
+ def in_cff2(self):
+ return self._isCFF2
+
+ def getNumRegions(self, vi=None): # called from misc/psCharStrings.py
+ # if getNumRegions is being called, we can assume that VarStore exists.
+ if vi is None:
+ if hasattr(self, "vsindex"):
+ vi = self.vsindex
+ else:
+ vi = 0
+ numRegions = self.vstore.getNumRegions(vi)
+ return numRegions
+
+
+class IndexedStrings(object):
+ """SID -> string mapping."""
+
+ def __init__(self, file=None):
+ if file is None:
+ strings = []
+ else:
+ strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)]
+ self.strings = strings
+
+ def getCompiler(self):
+ return IndexedStringsCompiler(self, None, self, isCFF2=False)
+
+ def __len__(self):
+ return len(self.strings)
+
+ def __getitem__(self, SID):
+ if SID < cffStandardStringCount:
+ return cffStandardStrings[SID]
+ else:
+ return self.strings[SID - cffStandardStringCount]
+
+ def getSID(self, s):
+ if not hasattr(self, "stringMapping"):
+ self.buildStringMapping()
+ s = tostr(s, encoding="latin1")
+ if s in cffStandardStringMapping:
+ SID = cffStandardStringMapping[s]
+ elif s in self.stringMapping:
+ SID = self.stringMapping[s]
+ else:
+ SID = len(self.strings) + cffStandardStringCount
+ self.strings.append(s)
+ self.stringMapping[s] = SID
+ return SID
+
+ def getStrings(self):
+ return self.strings
+
+ def buildStringMapping(self):
+ self.stringMapping = {}
+ for index in range(len(self.strings)):
+ self.stringMapping[self.strings[index]] = index + cffStandardStringCount
+
+
+# The 391 Standard Strings as used in the CFF format.
+# from Adobe Technical None #5176, version 1.0, 18 March 1998
+
+cffStandardStrings = [
+ ".notdef",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "onesuperior",
+ "logicalnot",
+ "mu",
+ "trademark",
+ "Eth",
+ "onehalf",
+ "plusminus",
+ "Thorn",
+ "onequarter",
+ "divide",
+ "brokenbar",
+ "degree",
+ "thorn",
+ "threequarters",
+ "twosuperior",
+ "registered",
+ "minus",
+ "eth",
+ "multiply",
+ "threesuperior",
+ "copyright",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "Zcaron",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "ccedilla",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "otilde",
+ "scaron",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "zcaron",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ "Dotaccentsmall",
+ "Macronsmall",
+ "figuredash",
+ "hypheninferior",
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall",
+ "001.000",
+ "001.001",
+ "001.002",
+ "001.003",
+ "Black",
+ "Bold",
+ "Book",
+ "Light",
+ "Medium",
+ "Regular",
+ "Roman",
+ "Semibold",
+]
+
+cffStandardStringCount = 391
+assert len(cffStandardStrings) == cffStandardStringCount
+# build reverse mapping
+cffStandardStringMapping = {}
+for _i in range(cffStandardStringCount):
+ cffStandardStringMapping[cffStandardStrings[_i]] = _i
+
+cffISOAdobeStrings = [
+ ".notdef",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "onesuperior",
+ "logicalnot",
+ "mu",
+ "trademark",
+ "Eth",
+ "onehalf",
+ "plusminus",
+ "Thorn",
+ "onequarter",
+ "divide",
+ "brokenbar",
+ "degree",
+ "thorn",
+ "threequarters",
+ "twosuperior",
+ "registered",
+ "minus",
+ "eth",
+ "multiply",
+ "threesuperior",
+ "copyright",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "Zcaron",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "ccedilla",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "otilde",
+ "scaron",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "zcaron",
+]
+
+cffISOAdobeStringCount = 229
+assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
+
+cffIExpertStrings = [
+ ".notdef",
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ "Dotaccentsmall",
+ "Macronsmall",
+ "figuredash",
+ "hypheninferior",
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall",
+]
+
+cffExpertStringCount = 166
+assert len(cffIExpertStrings) == cffExpertStringCount
+
+cffExpertSubsetStrings = [
+ ".notdef",
+ "space",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "hyphensuperior",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "centoldstyle",
+ "figuredash",
+ "hypheninferior",
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+]
+
+cffExpertSubsetStringCount = 87
+assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..786c28f94c0503c70945dc85181f11e88fe4e56f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2b4be6a267075e2110d5dcd2ac4d5b6ac59fc719
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ac134b5004c88a70bab557b80a00ef9557475a03
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f148a649881306e8ad01198eab13e396ef32ceea
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0b650141386d6159f90512f90c6d1f0e2587f812
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/specializer.py b/lib/python3.12/site-packages/fontTools/cffLib/specializer.py
new file mode 100644
index 0000000000000000000000000000000000000000..974060c40ec21a9c9ede2db0a34d0e60e4b42cf9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/specializer.py
@@ -0,0 +1,927 @@
+# -*- coding: utf-8 -*-
+
+"""T2CharString operator specializer and generalizer.
+
+PostScript glyph drawing operations can be expressed in multiple different
+ways. For example, as well as the ``lineto`` operator, there is also a
+``hlineto`` operator which draws a horizontal line, removing the need to
+specify a ``dx`` coordinate, and a ``vlineto`` operator which draws a
+vertical line, removing the need to specify a ``dy`` coordinate. As well
+as decompiling :class:`fontTools.misc.psCharStrings.T2CharString` objects
+into lists of operations, this module allows for conversion between general
+and specific forms of the operation.
+
+"""
+
+from fontTools.cffLib import maxStackLimit
+
+
+def stringToProgram(string):
+ if isinstance(string, str):
+ string = string.split()
+ program = []
+ for token in string:
+ try:
+ token = int(token)
+ except ValueError:
+ try:
+ token = float(token)
+ except ValueError:
+ pass
+ program.append(token)
+ return program
+
+
+def programToString(program):
+ return " ".join(str(x) for x in program)
+
+
+def programToCommands(program, getNumRegions=None):
+ """Takes a T2CharString program list and returns list of commands.
+ Each command is a two-tuple of commandname,arg-list. The commandname might
+ be empty string if no commandname shall be emitted (used for glyph width,
+ hintmask/cntrmask argument, as well as stray arguments at the end of the
+ program (🤷).
+ 'getNumRegions' may be None, or a callable object. It must return the
+ number of regions. 'getNumRegions' takes a single argument, vsindex. It
+ returns the numRegions for the vsindex.
+ The Charstring may or may not start with a width value. If the first
+ non-blend operator has an odd number of arguments, then the first argument is
+ a width, and is popped off. This is complicated with blend operators, as
+ there may be more than one before the first hint or moveto operator, and each
+ one reduces several arguments to just one list argument. We have to sum the
+ number of arguments that are not part of the blend arguments, and all the
+ 'numBlends' values. We could instead have said that by definition, if there
+ is a blend operator, there is no width value, since CFF2 Charstrings don't
+ have width values. I discussed this with Behdad, and we are allowing for an
+ initial width value in this case because developers may assemble a CFF2
+ charstring from CFF Charstrings, which could have width values.
+ """
+
+ seenWidthOp = False
+ vsIndex = 0
+ lenBlendStack = 0
+ lastBlendIndex = 0
+ commands = []
+ stack = []
+ it = iter(program)
+
+ for token in it:
+ if not isinstance(token, str):
+ stack.append(token)
+ continue
+
+ if token == "blend":
+ assert getNumRegions is not None
+ numSourceFonts = 1 + getNumRegions(vsIndex)
+ # replace the blend op args on the stack with a single list
+ # containing all the blend op args.
+ numBlends = stack[-1]
+ numBlendArgs = numBlends * numSourceFonts + 1
+ # replace first blend op by a list of the blend ops.
+ stack[-numBlendArgs:] = [stack[-numBlendArgs:]]
+ lenStack = len(stack)
+ lenBlendStack += numBlends + lenStack - 1
+ lastBlendIndex = lenStack
+ # if a blend op exists, this is or will be a CFF2 charstring.
+ continue
+
+ elif token == "vsindex":
+ vsIndex = stack[-1]
+ assert type(vsIndex) is int
+
+ elif (not seenWidthOp) and token in {
+ "hstem",
+ "hstemhm",
+ "vstem",
+ "vstemhm",
+ "cntrmask",
+ "hintmask",
+ "hmoveto",
+ "vmoveto",
+ "rmoveto",
+ "endchar",
+ }:
+ seenWidthOp = True
+ parity = token in {"hmoveto", "vmoveto"}
+ if lenBlendStack:
+ # lenBlendStack has the number of args represented by the last blend
+ # arg and all the preceding args. We need to now add the number of
+ # args following the last blend arg.
+ numArgs = lenBlendStack + len(stack[lastBlendIndex:])
+ else:
+ numArgs = len(stack)
+ if numArgs and (numArgs % 2) ^ parity:
+ width = stack.pop(0)
+ commands.append(("", [width]))
+
+ if token in {"hintmask", "cntrmask"}:
+ if stack:
+ commands.append(("", stack))
+ commands.append((token, []))
+ commands.append(("", [next(it)]))
+ else:
+ commands.append((token, stack))
+ stack = []
+ if stack:
+ commands.append(("", stack))
+ return commands
+
+
+def _flattenBlendArgs(args):
+ token_list = []
+ for arg in args:
+ if isinstance(arg, list):
+ token_list.extend(arg)
+ token_list.append("blend")
+ else:
+ token_list.append(arg)
+ return token_list
+
+
+def commandsToProgram(commands):
+ """Takes a commands list as returned by programToCommands() and converts
+ it back to a T2CharString program list."""
+ program = []
+ for op, args in commands:
+ if any(isinstance(arg, list) for arg in args):
+ args = _flattenBlendArgs(args)
+ program.extend(args)
+ if op:
+ program.append(op)
+ return program
+
+
+def _everyN(el, n):
+ """Group the list el into groups of size n"""
+ l = len(el)
+ if l % n != 0:
+ raise ValueError(el)
+ for i in range(0, l, n):
+ yield el[i : i + n]
+
+
+class _GeneralizerDecombinerCommandsMap(object):
+ @staticmethod
+ def rmoveto(args):
+ if len(args) != 2:
+ raise ValueError(args)
+ yield ("rmoveto", args)
+
+ @staticmethod
+ def hmoveto(args):
+ if len(args) != 1:
+ raise ValueError(args)
+ yield ("rmoveto", [args[0], 0])
+
+ @staticmethod
+ def vmoveto(args):
+ if len(args) != 1:
+ raise ValueError(args)
+ yield ("rmoveto", [0, args[0]])
+
+ @staticmethod
+ def rlineto(args):
+ if not args:
+ raise ValueError(args)
+ for args in _everyN(args, 2):
+ yield ("rlineto", args)
+
+ @staticmethod
+ def hlineto(args):
+ if not args:
+ raise ValueError(args)
+ it = iter(args)
+ try:
+ while True:
+ yield ("rlineto", [next(it), 0])
+ yield ("rlineto", [0, next(it)])
+ except StopIteration:
+ pass
+
+ @staticmethod
+ def vlineto(args):
+ if not args:
+ raise ValueError(args)
+ it = iter(args)
+ try:
+ while True:
+ yield ("rlineto", [0, next(it)])
+ yield ("rlineto", [next(it), 0])
+ except StopIteration:
+ pass
+
+ @staticmethod
+ def rrcurveto(args):
+ if not args:
+ raise ValueError(args)
+ for args in _everyN(args, 6):
+ yield ("rrcurveto", args)
+
+ @staticmethod
+ def hhcurveto(args):
+ l = len(args)
+ if l < 4 or l % 4 > 1:
+ raise ValueError(args)
+ if l % 2 == 1:
+ yield ("rrcurveto", [args[1], args[0], args[2], args[3], args[4], 0])
+ args = args[5:]
+ for args in _everyN(args, 4):
+ yield ("rrcurveto", [args[0], 0, args[1], args[2], args[3], 0])
+
+ @staticmethod
+ def vvcurveto(args):
+ l = len(args)
+ if l < 4 or l % 4 > 1:
+ raise ValueError(args)
+ if l % 2 == 1:
+ yield ("rrcurveto", [args[0], args[1], args[2], args[3], 0, args[4]])
+ args = args[5:]
+ for args in _everyN(args, 4):
+ yield ("rrcurveto", [0, args[0], args[1], args[2], 0, args[3]])
+
+ @staticmethod
+ def hvcurveto(args):
+ l = len(args)
+ if l < 4 or l % 8 not in {0, 1, 4, 5}:
+ raise ValueError(args)
+ last_args = None
+ if l % 2 == 1:
+ lastStraight = l % 8 == 5
+ args, last_args = args[:-5], args[-5:]
+ it = _everyN(args, 4)
+ try:
+ while True:
+ args = next(it)
+ yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]])
+ args = next(it)
+ yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0])
+ except StopIteration:
+ pass
+ if last_args:
+ args = last_args
+ if lastStraight:
+ yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]])
+ else:
+ yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]])
+
+ @staticmethod
+ def vhcurveto(args):
+ l = len(args)
+ if l < 4 or l % 8 not in {0, 1, 4, 5}:
+ raise ValueError(args)
+ last_args = None
+ if l % 2 == 1:
+ lastStraight = l % 8 == 5
+ args, last_args = args[:-5], args[-5:]
+ it = _everyN(args, 4)
+ try:
+ while True:
+ args = next(it)
+ yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0])
+ args = next(it)
+ yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]])
+ except StopIteration:
+ pass
+ if last_args:
+ args = last_args
+ if lastStraight:
+ yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]])
+ else:
+ yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]])
+
+ @staticmethod
+ def rcurveline(args):
+ l = len(args)
+ if l < 8 or l % 6 != 2:
+ raise ValueError(args)
+ args, last_args = args[:-2], args[-2:]
+ for args in _everyN(args, 6):
+ yield ("rrcurveto", args)
+ yield ("rlineto", last_args)
+
+ @staticmethod
+ def rlinecurve(args):
+ l = len(args)
+ if l < 8 or l % 2 != 0:
+ raise ValueError(args)
+ args, last_args = args[:-6], args[-6:]
+ for args in _everyN(args, 2):
+ yield ("rlineto", args)
+ yield ("rrcurveto", last_args)
+
+
+def _convertBlendOpToArgs(blendList):
+ # args is list of blend op args. Since we are supporting
+ # recursive blend op calls, some of these args may also
+ # be a list of blend op args, and need to be converted before
+ # we convert the current list.
+ if any([isinstance(arg, list) for arg in blendList]):
+ args = [
+ i
+ for e in blendList
+ for i in (_convertBlendOpToArgs(e) if isinstance(e, list) else [e])
+ ]
+ else:
+ args = blendList
+
+ # We now know that blendList contains a blend op argument list, even if
+ # some of the args are lists that each contain a blend op argument list.
+ # Convert from:
+ # [default font arg sequence x0,...,xn] + [delta tuple for x0] + ... + [delta tuple for xn]
+ # to:
+ # [ [x0] + [delta tuple for x0],
+ # ...,
+ # [xn] + [delta tuple for xn] ]
+ numBlends = args[-1]
+ # Can't use args.pop() when the args are being used in a nested list
+ # comprehension. See calling context
+ args = args[:-1]
+
+ l = len(args)
+ numRegions = l // numBlends - 1
+ if not (numBlends * (numRegions + 1) == l):
+ raise ValueError(blendList)
+
+ defaultArgs = [[arg] for arg in args[:numBlends]]
+ deltaArgs = args[numBlends:]
+ numDeltaValues = len(deltaArgs)
+ deltaList = [
+ deltaArgs[i : i + numRegions] for i in range(0, numDeltaValues, numRegions)
+ ]
+ blend_args = [a + b + [1] for a, b in zip(defaultArgs, deltaList)]
+ return blend_args
+
+
+def generalizeCommands(commands, ignoreErrors=False):
+ result = []
+ mapping = _GeneralizerDecombinerCommandsMap
+ for op, args in commands:
+ # First, generalize any blend args in the arg list.
+ if any([isinstance(arg, list) for arg in args]):
+ try:
+ args = [
+ n
+ for arg in args
+ for n in (
+ _convertBlendOpToArgs(arg) if isinstance(arg, list) else [arg]
+ )
+ ]
+ except ValueError:
+ if ignoreErrors:
+ # Store op as data, such that consumers of commands do not have to
+ # deal with incorrect number of arguments.
+ result.append(("", args))
+ result.append(("", [op]))
+ else:
+ raise
+
+ func = getattr(mapping, op, None)
+ if func is None:
+ result.append((op, args))
+ continue
+ try:
+ for command in func(args):
+ result.append(command)
+ except ValueError:
+ if ignoreErrors:
+ # Store op as data, such that consumers of commands do not have to
+ # deal with incorrect number of arguments.
+ result.append(("", args))
+ result.append(("", [op]))
+ else:
+ raise
+ return result
+
+
+def generalizeProgram(program, getNumRegions=None, **kwargs):
+ return commandsToProgram(
+ generalizeCommands(programToCommands(program, getNumRegions), **kwargs)
+ )
+
+
+def _categorizeVector(v):
+ """
+ Takes X,Y vector v and returns one of r, h, v, or 0 depending on which
+ of X and/or Y are zero, plus tuple of nonzero ones. If both are zero,
+ it returns a single zero still.
+
+ >>> _categorizeVector((0,0))
+ ('0', (0,))
+ >>> _categorizeVector((1,0))
+ ('h', (1,))
+ >>> _categorizeVector((0,2))
+ ('v', (2,))
+ >>> _categorizeVector((1,2))
+ ('r', (1, 2))
+ """
+ if not v[0]:
+ if not v[1]:
+ return "0", v[:1]
+ else:
+ return "v", v[1:]
+ else:
+ if not v[1]:
+ return "h", v[:1]
+ else:
+ return "r", v
+
+
+def _mergeCategories(a, b):
+ if a == "0":
+ return b
+ if b == "0":
+ return a
+ if a == b:
+ return a
+ return None
+
+
+def _negateCategory(a):
+ if a == "h":
+ return "v"
+ if a == "v":
+ return "h"
+ assert a in "0r"
+ return a
+
+
+def _convertToBlendCmds(args):
+ # return a list of blend commands, and
+ # the remaining non-blended args, if any.
+ num_args = len(args)
+ stack_use = 0
+ new_args = []
+ i = 0
+ while i < num_args:
+ arg = args[i]
+ i += 1
+ if not isinstance(arg, list):
+ new_args.append(arg)
+ stack_use += 1
+ else:
+ prev_stack_use = stack_use
+ # The arg is a tuple of blend values.
+ # These are each (master 0,delta 1..delta n, 1)
+ # Combine as many successive tuples as we can,
+ # up to the max stack limit.
+ num_sources = len(arg) - 1
+ blendlist = [arg]
+ stack_use += 1 + num_sources # 1 for the num_blends arg
+
+ # if we are here, max stack is the CFF2 max stack.
+ # I use the CFF2 max stack limit here rather than
+ # the 'maxstack' chosen by the client, as the default
+ # maxstack may have been used unintentionally. For all
+ # the other operators, this just produces a little less
+ # optimization, but here it puts a hard (and low) limit
+ # on the number of source fonts that can be used.
+ #
+ # Make sure the stack depth does not exceed (maxstack - 1), so
+ # that subroutinizer can insert subroutine calls at any point.
+ while (
+ (i < num_args)
+ and isinstance(args[i], list)
+ and stack_use + num_sources < maxStackLimit
+ ):
+ blendlist.append(args[i])
+ i += 1
+ stack_use += num_sources
+ # blendList now contains as many single blend tuples as can be
+ # combined without exceeding the CFF2 stack limit.
+ num_blends = len(blendlist)
+ # append the 'num_blends' default font values
+ blend_args = []
+ for arg in blendlist:
+ blend_args.append(arg[0])
+ for arg in blendlist:
+ assert arg[-1] == 1
+ blend_args.extend(arg[1:-1])
+ blend_args.append(num_blends)
+ new_args.append(blend_args)
+ stack_use = prev_stack_use + num_blends
+
+ return new_args
+
+
+def _addArgs(a, b):
+ if isinstance(b, list):
+ if isinstance(a, list):
+ if len(a) != len(b) or a[-1] != b[-1]:
+ raise ValueError()
+ return [_addArgs(va, vb) for va, vb in zip(a[:-1], b[:-1])] + [a[-1]]
+ else:
+ a, b = b, a
+ if isinstance(a, list):
+ assert a[-1] == 1
+ return [_addArgs(a[0], b)] + a[1:]
+ return a + b
+
+
+def _argsStackUse(args):
+ stackLen = 0
+ maxLen = 0
+ for arg in args:
+ if type(arg) is list:
+ # Blended arg
+ maxLen = max(maxLen, stackLen + _argsStackUse(arg))
+ stackLen += arg[-1]
+ else:
+ stackLen += 1
+ return max(stackLen, maxLen)
+
+
+def specializeCommands(
+ commands,
+ ignoreErrors=False,
+ generalizeFirst=True,
+ preserveTopology=False,
+ maxstack=48,
+):
+ # We perform several rounds of optimizations. They are carefully ordered and are:
+ #
+ # 0. Generalize commands.
+ # This ensures that they are in our expected simple form, with each line/curve only
+ # having arguments for one segment, and using the generic form (rlineto/rrcurveto).
+ # If caller is sure the input is in this form, they can turn off generalization to
+ # save time.
+ #
+ # 1. Combine successive rmoveto operations.
+ #
+ # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
+ # We specialize into some, made-up, variants as well, which simplifies following
+ # passes.
+ #
+ # 3. Merge or delete redundant operations, to the extent requested.
+ # OpenType spec declares point numbers in CFF undefined. As such, we happily
+ # change topology. If client relies on point numbers (in GPOS anchors, or for
+ # hinting purposes(what?)) they can turn this off.
+ #
+ # 4. Peephole optimization to revert back some of the h/v variants back into their
+ # original "relative" operator (rline/rrcurveto) if that saves a byte.
+ #
+ # 5. Combine adjacent operators when possible, minding not to go over max stack size.
+ #
+ # 6. Resolve any remaining made-up operators into real operators.
+ #
+ # I have convinced myself that this produces optimal bytecode (except for, possibly
+ # one byte each time maxstack size prohibits combining.) YMMV, but you'd be wrong. :-)
+ # A dynamic-programming approach can do the same but would be significantly slower.
+ #
+ # 7. For any args which are blend lists, convert them to a blend command.
+
+ # 0. Generalize commands.
+ if generalizeFirst:
+ commands = generalizeCommands(commands, ignoreErrors=ignoreErrors)
+ else:
+ commands = list(commands) # Make copy since we modify in-place later.
+
+ # 1. Combine successive rmoveto operations.
+ for i in range(len(commands) - 1, 0, -1):
+ if "rmoveto" == commands[i][0] == commands[i - 1][0]:
+ v1, v2 = commands[i - 1][1], commands[i][1]
+ commands[i - 1] = (
+ "rmoveto",
+ [_addArgs(v1[0], v2[0]), _addArgs(v1[1], v2[1])],
+ )
+ del commands[i]
+
+ # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
+ #
+ # We, in fact, specialize into more, made-up, variants that special-case when both
+ # X and Y components are zero. This simplifies the following optimization passes.
+ # This case is rare, but OCD does not let me skip it.
+ #
+ # After this round, we will have four variants that use the following mnemonics:
+ #
+ # - 'r' for relative, ie. non-zero X and non-zero Y,
+ # - 'h' for horizontal, ie. zero X and non-zero Y,
+ # - 'v' for vertical, ie. non-zero X and zero Y,
+ # - '0' for zeros, ie. zero X and zero Y.
+ #
+ # The '0' pseudo-operators are not part of the spec, but help simplify the following
+ # optimization rounds. We resolve them at the end. So, after this, we will have four
+ # moveto and four lineto variants:
+ #
+ # - 0moveto, 0lineto
+ # - hmoveto, hlineto
+ # - vmoveto, vlineto
+ # - rmoveto, rlineto
+ #
+ # and sixteen curveto variants. For example, a '0hcurveto' operator means a curve
+ # dx0,dy0,dx1,dy1,dx2,dy2,dx3,dy3 where dx0, dx1, and dy3 are zero but not dx3.
+ # An 'rvcurveto' means dx3 is zero but not dx0,dy0,dy3.
+ #
+ # There are nine different variants of curves without the '0'. Those nine map exactly
+ # to the existing curve variants in the spec: rrcurveto, and the four variants hhcurveto,
+ # vvcurveto, hvcurveto, and vhcurveto each cover two cases, one with an odd number of
+ # arguments and one without. Eg. an hhcurveto with an extra argument (odd number of
+ # arguments) is in fact an rhcurveto. The operators in the spec are designed such that
+ # all four of rhcurveto, rvcurveto, hrcurveto, and vrcurveto are encodable for one curve.
+ #
+ # Of the curve types with '0', the 00curveto is equivalent to a lineto variant. The rest
+ # of the curve types with a 0 need to be encoded as a h or v variant. Ie. a '0' can be
+ # thought of a "don't care" and can be used as either an 'h' or a 'v'. As such, we always
+ # encode a number 0 as argument when we use a '0' variant. Later on, we can just substitute
+ # the '0' with either 'h' or 'v' and it works.
+ #
+ # When we get to curve splines however, things become more complicated... XXX finish this.
+ # There's one more complexity with splines. If one side of the spline is not horizontal or
+ # vertical (or zero), ie. if it's 'r', then it limits which spline types we can encode.
+ # Only hhcurveto and vvcurveto operators can encode a spline starting with 'r', and
+ # only hvcurveto and vhcurveto operators can encode a spline ending with 'r'.
+ # This limits our merge opportunities later.
+ #
+ for i in range(len(commands)):
+ op, args = commands[i]
+
+ if op in {"rmoveto", "rlineto"}:
+ c, args = _categorizeVector(args)
+ commands[i] = c + op[1:], args
+ continue
+
+ if op == "rrcurveto":
+ c1, args1 = _categorizeVector(args[:2])
+ c2, args2 = _categorizeVector(args[-2:])
+ commands[i] = c1 + c2 + "curveto", args1 + args[2:4] + args2
+ continue
+
+ # 3. Merge or delete redundant operations, to the extent requested.
+ #
+ # TODO
+ # A 0moveto that comes before all other path operations can be removed.
+ # though I find conflicting evidence for this.
+ #
+ # TODO
+ # "If hstem and vstem hints are both declared at the beginning of a
+ # CharString, and this sequence is followed directly by the hintmask or
+ # cntrmask operators, then the vstem hint operator (or, if applicable,
+ # the vstemhm operator) need not be included."
+ #
+ # "The sequence and form of a CFF2 CharString program may be represented as:
+ # {hs* vs* cm* hm* mt subpath}? {mt subpath}*"
+ #
+ # https://www.microsoft.com/typography/otspec/cff2charstr.htm#section3.1
+ #
+ # For Type2 CharStrings the sequence is:
+ # w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar"
+
+ # Some other redundancies change topology (point numbers).
+ if not preserveTopology:
+ for i in range(len(commands) - 1, -1, -1):
+ op, args = commands[i]
+
+ # A 00curveto is demoted to a (specialized) lineto.
+ if op == "00curveto":
+ assert len(args) == 4
+ c, args = _categorizeVector(args[1:3])
+ op = c + "lineto"
+ commands[i] = op, args
+ # and then...
+
+ # A 0lineto can be deleted.
+ if op == "0lineto":
+ del commands[i]
+ continue
+
+ # Merge adjacent hlineto's and vlineto's.
+ # In CFF2 charstrings from variable fonts, each
+ # arg item may be a list of blendable values, one from
+ # each source font.
+ if i and op in {"hlineto", "vlineto"} and (op == commands[i - 1][0]):
+ _, other_args = commands[i - 1]
+ assert len(args) == 1 and len(other_args) == 1
+ try:
+ new_args = [_addArgs(args[0], other_args[0])]
+ except ValueError:
+ continue
+ commands[i - 1] = (op, new_args)
+ del commands[i]
+ continue
+
+ # 4. Peephole optimization to revert back some of the h/v variants back into their
+ # original "relative" operator (rline/rrcurveto) if that saves a byte.
+ for i in range(1, len(commands) - 1):
+ op, args = commands[i]
+ prv, nxt = commands[i - 1][0], commands[i + 1][0]
+
+ if op in {"0lineto", "hlineto", "vlineto"} and prv == nxt == "rlineto":
+ assert len(args) == 1
+ args = [0, args[0]] if op[0] == "v" else [args[0], 0]
+ commands[i] = ("rlineto", args)
+ continue
+
+ if op[2:] == "curveto" and len(args) == 5 and prv == nxt == "rrcurveto":
+ assert (op[0] == "r") ^ (op[1] == "r")
+ if op[0] == "v":
+ pos = 0
+ elif op[0] != "r":
+ pos = 1
+ elif op[1] == "v":
+ pos = 4
+ else:
+ pos = 5
+ # Insert, while maintaining the type of args (can be tuple or list).
+ args = args[:pos] + type(args)((0,)) + args[pos:]
+ commands[i] = ("rrcurveto", args)
+ continue
+
+ # 5. Combine adjacent operators when possible, minding not to go over max stack size.
+ stackUse = _argsStackUse(commands[-1][1]) if commands else 0
+ for i in range(len(commands) - 1, 0, -1):
+ op1, args1 = commands[i - 1]
+ op2, args2 = commands[i]
+ new_op = None
+
+ # Merge logic...
+ if {op1, op2} <= {"rlineto", "rrcurveto"}:
+ if op1 == op2:
+ new_op = op1
+ else:
+ l = len(args2)
+ if op2 == "rrcurveto" and l == 6:
+ new_op = "rlinecurve"
+ elif l == 2:
+ new_op = "rcurveline"
+
+ elif (op1, op2) in {("rlineto", "rlinecurve"), ("rrcurveto", "rcurveline")}:
+ new_op = op2
+
+ elif {op1, op2} == {"vlineto", "hlineto"}:
+ new_op = op1
+
+ elif "curveto" == op1[2:] == op2[2:]:
+ d0, d1 = op1[:2]
+ d2, d3 = op2[:2]
+
+ if d1 == "r" or d2 == "r" or d0 == d3 == "r":
+ continue
+
+ d = _mergeCategories(d1, d2)
+ if d is None:
+ continue
+ if d0 == "r":
+ d = _mergeCategories(d, d3)
+ if d is None:
+ continue
+ new_op = "r" + d + "curveto"
+ elif d3 == "r":
+ d0 = _mergeCategories(d0, _negateCategory(d))
+ if d0 is None:
+ continue
+ new_op = d0 + "r" + "curveto"
+ else:
+ d0 = _mergeCategories(d0, d3)
+ if d0 is None:
+ continue
+ new_op = d0 + d + "curveto"
+
+ # Make sure the stack depth does not exceed (maxstack - 1), so
+ # that subroutinizer can insert subroutine calls at any point.
+ args1StackUse = _argsStackUse(args1)
+ combinedStackUse = max(args1StackUse, len(args1) + stackUse)
+ if new_op and combinedStackUse < maxstack:
+ commands[i - 1] = (new_op, args1 + args2)
+ del commands[i]
+ stackUse = combinedStackUse
+ else:
+ stackUse = args1StackUse
+
+ # 6. Resolve any remaining made-up operators into real operators.
+ for i in range(len(commands)):
+ op, args = commands[i]
+
+ if op in {"0moveto", "0lineto"}:
+ commands[i] = "h" + op[1:], args
+ continue
+
+ if op[2:] == "curveto" and op[:2] not in {"rr", "hh", "vv", "vh", "hv"}:
+ l = len(args)
+
+ op0, op1 = op[:2]
+ if (op0 == "r") ^ (op1 == "r"):
+ assert l % 2 == 1
+ if op0 == "0":
+ op0 = "h"
+ if op1 == "0":
+ op1 = "h"
+ if op0 == "r":
+ op0 = op1
+ if op1 == "r":
+ op1 = _negateCategory(op0)
+ assert {op0, op1} <= {"h", "v"}, (op0, op1)
+
+ if l % 2:
+ if op0 != op1: # vhcurveto / hvcurveto
+ if (op0 == "h") ^ (l % 8 == 1):
+ # Swap last two args order
+ args = args[:-2] + args[-1:] + args[-2:-1]
+ else: # hhcurveto / vvcurveto
+ if op0 == "h": # hhcurveto
+ # Swap first two args order
+ args = args[1:2] + args[:1] + args[2:]
+
+ commands[i] = op0 + op1 + "curveto", args
+ continue
+
+ # 7. For any series of args which are blend lists, convert the series to a single blend arg.
+ for i in range(len(commands)):
+ op, args = commands[i]
+ if any(isinstance(arg, list) for arg in args):
+ commands[i] = op, _convertToBlendCmds(args)
+
+ return commands
+
+
+def specializeProgram(program, getNumRegions=None, **kwargs):
+ return commandsToProgram(
+ specializeCommands(programToCommands(program, getNumRegions), **kwargs)
+ )
+
+
+if __name__ == "__main__":
+ import sys
+
+ if len(sys.argv) == 1:
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
+
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ "fonttools cffLib.specializer",
+ description="CFF CharString generalizer/specializer",
+ )
+ parser.add_argument("program", metavar="command", nargs="*", help="Commands.")
+ parser.add_argument(
+ "--num-regions",
+ metavar="NumRegions",
+ nargs="*",
+ default=None,
+ help="Number of variable-font regions for blend opertaions.",
+ )
+ parser.add_argument(
+ "--font",
+ metavar="FONTFILE",
+ default=None,
+ help="CFF2 font to specialize.",
+ )
+ parser.add_argument(
+ "-o",
+ "--output-file",
+ type=str,
+ help="Output font file name.",
+ )
+
+ options = parser.parse_args(sys.argv[1:])
+
+ if options.program:
+ getNumRegions = (
+ None
+ if options.num_regions is None
+ else lambda vsIndex: int(
+ options.num_regions[0 if vsIndex is None else vsIndex]
+ )
+ )
+
+ program = stringToProgram(options.program)
+ print("Program:")
+ print(programToString(program))
+ commands = programToCommands(program, getNumRegions)
+ print("Commands:")
+ print(commands)
+ program2 = commandsToProgram(commands)
+ print("Program from commands:")
+ print(programToString(program2))
+ assert program == program2
+ print("Generalized program:")
+ print(programToString(generalizeProgram(program, getNumRegions)))
+ print("Specialized program:")
+ print(programToString(specializeProgram(program, getNumRegions)))
+
+ if options.font:
+ from fontTools.ttLib import TTFont
+
+ font = TTFont(options.font)
+ cff2 = font["CFF2"].cff.topDictIndex[0]
+ charstrings = cff2.CharStrings
+ for glyphName in charstrings.keys():
+ charstring = charstrings[glyphName]
+ charstring.decompile()
+ getNumRegions = charstring.private.getNumRegions
+ charstring.program = specializeProgram(
+ charstring.program, getNumRegions, maxstack=maxStackLimit
+ )
+
+ if options.output_file is None:
+ from fontTools.misc.cliTools import makeOutputFileName
+
+ outfile = makeOutputFileName(
+ options.font, overWrite=True, suffix=".specialized"
+ )
+ else:
+ outfile = options.output_file
+ if outfile:
+ print("Saving", outfile)
+ font.save(outfile)
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/transforms.py b/lib/python3.12/site-packages/fontTools/cffLib/transforms.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9b7c86c8b450d8a599780b1af53004afa9004d7
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/transforms.py
@@ -0,0 +1,495 @@
+from fontTools.misc.psCharStrings import (
+ SimpleT2Decompiler,
+ T2WidthExtractor,
+ calcSubrBias,
+)
+
+
+def _uniq_sort(l):
+ return sorted(set(l))
+
+
+class StopHintCountEvent(Exception):
+ pass
+
+
+class _DesubroutinizingT2Decompiler(SimpleT2Decompiler):
+ stop_hintcount_ops = (
+ "op_hintmask",
+ "op_cntrmask",
+ "op_rmoveto",
+ "op_hmoveto",
+ "op_vmoveto",
+ )
+
+ def __init__(self, localSubrs, globalSubrs, private=None):
+ SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private)
+
+ def execute(self, charString):
+ self.need_hintcount = True # until proven otherwise
+ for op_name in self.stop_hintcount_ops:
+ setattr(self, op_name, self.stop_hint_count)
+
+ if hasattr(charString, "_desubroutinized"):
+ # If a charstring has already been desubroutinized, we will still
+ # need to execute it if we need to count hints in order to
+ # compute the byte length for mask arguments, and haven't finished
+ # counting hints pairs.
+ if self.need_hintcount and self.callingStack:
+ try:
+ SimpleT2Decompiler.execute(self, charString)
+ except StopHintCountEvent:
+ del self.callingStack[-1]
+ return
+
+ charString._patches = []
+ SimpleT2Decompiler.execute(self, charString)
+ desubroutinized = charString.program[:]
+ for idx, expansion in reversed(charString._patches):
+ assert idx >= 2
+ assert desubroutinized[idx - 1] in [
+ "callsubr",
+ "callgsubr",
+ ], desubroutinized[idx - 1]
+ assert type(desubroutinized[idx - 2]) == int
+ if expansion[-1] == "return":
+ expansion = expansion[:-1]
+ desubroutinized[idx - 2 : idx] = expansion
+ if not self.private.in_cff2:
+ if "endchar" in desubroutinized:
+ # Cut off after first endchar
+ desubroutinized = desubroutinized[
+ : desubroutinized.index("endchar") + 1
+ ]
+
+ charString._desubroutinized = desubroutinized
+ del charString._patches
+
+ def op_callsubr(self, index):
+ subr = self.localSubrs[self.operandStack[-1] + self.localBias]
+ SimpleT2Decompiler.op_callsubr(self, index)
+ self.processSubr(index, subr)
+
+ def op_callgsubr(self, index):
+ subr = self.globalSubrs[self.operandStack[-1] + self.globalBias]
+ SimpleT2Decompiler.op_callgsubr(self, index)
+ self.processSubr(index, subr)
+
+ def stop_hint_count(self, *args):
+ self.need_hintcount = False
+ for op_name in self.stop_hintcount_ops:
+ setattr(self, op_name, None)
+ cs = self.callingStack[-1]
+ if hasattr(cs, "_desubroutinized"):
+ raise StopHintCountEvent()
+
+ def op_hintmask(self, index):
+ SimpleT2Decompiler.op_hintmask(self, index)
+ if self.need_hintcount:
+ self.stop_hint_count()
+
+ def processSubr(self, index, subr):
+ cs = self.callingStack[-1]
+ if not hasattr(cs, "_desubroutinized"):
+ cs._patches.append((index, subr._desubroutinized))
+
+
+def desubroutinizeCharString(cs):
+ """Desubroutinize a charstring in-place."""
+ cs.decompile()
+ subrs = getattr(cs.private, "Subrs", [])
+ decompiler = _DesubroutinizingT2Decompiler(subrs, cs.globalSubrs, cs.private)
+ decompiler.execute(cs)
+ cs.program = cs._desubroutinized
+ del cs._desubroutinized
+
+
+def desubroutinize(cff):
+ for fontName in cff.fontNames:
+ font = cff[fontName]
+ cs = font.CharStrings
+ for c in cs.values():
+ desubroutinizeCharString(c)
+ # Delete all the local subrs
+ if hasattr(font, "FDArray"):
+ for fd in font.FDArray:
+ pd = fd.Private
+ if hasattr(pd, "Subrs"):
+ del pd.Subrs
+ if "Subrs" in pd.rawDict:
+ del pd.rawDict["Subrs"]
+ else:
+ pd = font.Private
+ if hasattr(pd, "Subrs"):
+ del pd.Subrs
+ if "Subrs" in pd.rawDict:
+ del pd.rawDict["Subrs"]
+ # as well as the global subrs
+ cff.GlobalSubrs.clear()
+
+
+class _MarkingT2Decompiler(SimpleT2Decompiler):
+ def __init__(self, localSubrs, globalSubrs, private):
+ SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private)
+ for subrs in [localSubrs, globalSubrs]:
+ if subrs and not hasattr(subrs, "_used"):
+ subrs._used = set()
+
+ def op_callsubr(self, index):
+ self.localSubrs._used.add(self.operandStack[-1] + self.localBias)
+ SimpleT2Decompiler.op_callsubr(self, index)
+
+ def op_callgsubr(self, index):
+ self.globalSubrs._used.add(self.operandStack[-1] + self.globalBias)
+ SimpleT2Decompiler.op_callgsubr(self, index)
+
+
+class _DehintingT2Decompiler(T2WidthExtractor):
+ class Hints(object):
+ def __init__(self):
+ # Whether calling this charstring produces any hint stems
+ # Note that if a charstring starts with hintmask, it will
+ # have has_hint set to True, because it *might* produce an
+ # implicit vstem if called under certain conditions.
+ self.has_hint = False
+ # Index to start at to drop all hints
+ self.last_hint = 0
+ # Index up to which we know more hints are possible.
+ # Only relevant if status is 0 or 1.
+ self.last_checked = 0
+ # The status means:
+ # 0: after dropping hints, this charstring is empty
+ # 1: after dropping hints, there may be more hints
+ # continuing after this, or there might be
+ # other things. Not clear yet.
+ # 2: no more hints possible after this charstring
+ self.status = 0
+ # Has hintmask instructions; not recursive
+ self.has_hintmask = False
+ # List of indices of calls to empty subroutines to remove.
+ self.deletions = []
+
+ pass
+
+ def __init__(
+ self, css, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None
+ ):
+ self._css = css
+ T2WidthExtractor.__init__(
+ self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX
+ )
+ self.private = private
+
+ def execute(self, charString):
+ old_hints = charString._hints if hasattr(charString, "_hints") else None
+ charString._hints = self.Hints()
+
+ T2WidthExtractor.execute(self, charString)
+
+ hints = charString._hints
+
+ if hints.has_hint or hints.has_hintmask:
+ self._css.add(charString)
+
+ if hints.status != 2:
+ # Check from last_check, make sure we didn't have any operators.
+ for i in range(hints.last_checked, len(charString.program) - 1):
+ if isinstance(charString.program[i], str):
+ hints.status = 2
+ break
+ else:
+ hints.status = 1 # There's *something* here
+ hints.last_checked = len(charString.program)
+
+ if old_hints:
+ assert hints.__dict__ == old_hints.__dict__
+
+ def op_callsubr(self, index):
+ subr = self.localSubrs[self.operandStack[-1] + self.localBias]
+ T2WidthExtractor.op_callsubr(self, index)
+ self.processSubr(index, subr)
+
+ def op_callgsubr(self, index):
+ subr = self.globalSubrs[self.operandStack[-1] + self.globalBias]
+ T2WidthExtractor.op_callgsubr(self, index)
+ self.processSubr(index, subr)
+
+ def op_hstem(self, index):
+ T2WidthExtractor.op_hstem(self, index)
+ self.processHint(index)
+
+ def op_vstem(self, index):
+ T2WidthExtractor.op_vstem(self, index)
+ self.processHint(index)
+
+ def op_hstemhm(self, index):
+ T2WidthExtractor.op_hstemhm(self, index)
+ self.processHint(index)
+
+ def op_vstemhm(self, index):
+ T2WidthExtractor.op_vstemhm(self, index)
+ self.processHint(index)
+
+ def op_hintmask(self, index):
+ rv = T2WidthExtractor.op_hintmask(self, index)
+ self.processHintmask(index)
+ return rv
+
+ def op_cntrmask(self, index):
+ rv = T2WidthExtractor.op_cntrmask(self, index)
+ self.processHintmask(index)
+ return rv
+
+ def processHintmask(self, index):
+ cs = self.callingStack[-1]
+ hints = cs._hints
+ hints.has_hintmask = True
+ if hints.status != 2:
+ # Check from last_check, see if we may be an implicit vstem
+ for i in range(hints.last_checked, index - 1):
+ if isinstance(cs.program[i], str):
+ hints.status = 2
+ break
+ else:
+ # We are an implicit vstem
+ hints.has_hint = True
+ hints.last_hint = index + 1
+ hints.status = 0
+ hints.last_checked = index + 1
+
+ def processHint(self, index):
+ cs = self.callingStack[-1]
+ hints = cs._hints
+ hints.has_hint = True
+ hints.last_hint = index
+ hints.last_checked = index
+
+ def processSubr(self, index, subr):
+ cs = self.callingStack[-1]
+ hints = cs._hints
+ subr_hints = subr._hints
+
+ # Check from last_check, make sure we didn't have
+ # any operators.
+ if hints.status != 2:
+ for i in range(hints.last_checked, index - 1):
+ if isinstance(cs.program[i], str):
+ hints.status = 2
+ break
+ hints.last_checked = index
+
+ if hints.status != 2:
+ if subr_hints.has_hint:
+ hints.has_hint = True
+
+ # Decide where to chop off from
+ if subr_hints.status == 0:
+ hints.last_hint = index
+ else:
+ hints.last_hint = index - 2 # Leave the subr call in
+
+ elif subr_hints.status == 0:
+ hints.deletions.append(index)
+
+ hints.status = max(hints.status, subr_hints.status)
+
+
+def _cs_subset_subroutines(charstring, subrs, gsubrs):
+ p = charstring.program
+ for i in range(1, len(p)):
+ if p[i] == "callsubr":
+ assert isinstance(p[i - 1], int)
+ p[i - 1] = subrs._used.index(p[i - 1] + subrs._old_bias) - subrs._new_bias
+ elif p[i] == "callgsubr":
+ assert isinstance(p[i - 1], int)
+ p[i - 1] = (
+ gsubrs._used.index(p[i - 1] + gsubrs._old_bias) - gsubrs._new_bias
+ )
+
+
+def _cs_drop_hints(charstring):
+ hints = charstring._hints
+
+ if hints.deletions:
+ p = charstring.program
+ for idx in reversed(hints.deletions):
+ del p[idx - 2 : idx]
+
+ if hints.has_hint:
+ assert not hints.deletions or hints.last_hint <= hints.deletions[0]
+ charstring.program = charstring.program[hints.last_hint :]
+ if not charstring.program:
+ # TODO CFF2 no need for endchar.
+ charstring.program.append("endchar")
+ if hasattr(charstring, "width"):
+ # Insert width back if needed
+ if charstring.width != charstring.private.defaultWidthX:
+ # For CFF2 charstrings, this should never happen
+ assert (
+ charstring.private.defaultWidthX is not None
+ ), "CFF2 CharStrings must not have an initial width value"
+ charstring.program.insert(
+ 0, charstring.width - charstring.private.nominalWidthX
+ )
+
+ if hints.has_hintmask:
+ i = 0
+ p = charstring.program
+ while i < len(p):
+ if p[i] in ["hintmask", "cntrmask"]:
+ assert i + 1 <= len(p)
+ del p[i : i + 2]
+ continue
+ i += 1
+
+ assert len(charstring.program)
+
+ del charstring._hints
+
+
+def remove_hints(cff, *, removeUnusedSubrs: bool = True):
+ for fontname in cff.keys():
+ font = cff[fontname]
+ cs = font.CharStrings
+ # This can be tricky, but doesn't have to. What we do is:
+ #
+ # - Run all used glyph charstrings and recurse into subroutines,
+ # - For each charstring (including subroutines), if it has any
+ # of the hint stem operators, we mark it as such.
+ # Upon returning, for each charstring we note all the
+ # subroutine calls it makes that (recursively) contain a stem,
+ # - Dropping hinting then consists of the following two ops:
+ # * Drop the piece of the program in each charstring before the
+ # last call to a stem op or a stem-calling subroutine,
+ # * Drop all hintmask operations.
+ # - It's trickier... A hintmask right after hints and a few numbers
+ # will act as an implicit vstemhm. As such, we track whether
+ # we have seen any non-hint operators so far and do the right
+ # thing, recursively... Good luck understanding that :(
+ css = set()
+ for c in cs.values():
+ c.decompile()
+ subrs = getattr(c.private, "Subrs", [])
+ decompiler = _DehintingT2Decompiler(
+ css,
+ subrs,
+ c.globalSubrs,
+ c.private.nominalWidthX,
+ c.private.defaultWidthX,
+ c.private,
+ )
+ decompiler.execute(c)
+ c.width = decompiler.width
+ for charstring in css:
+ _cs_drop_hints(charstring)
+ del css
+
+ # Drop font-wide hinting values
+ all_privs = []
+ if hasattr(font, "FDArray"):
+ all_privs.extend(fd.Private for fd in font.FDArray)
+ else:
+ all_privs.append(font.Private)
+ for priv in all_privs:
+ for k in [
+ "BlueValues",
+ "OtherBlues",
+ "FamilyBlues",
+ "FamilyOtherBlues",
+ "BlueScale",
+ "BlueShift",
+ "BlueFuzz",
+ "StemSnapH",
+ "StemSnapV",
+ "StdHW",
+ "StdVW",
+ "ForceBold",
+ "LanguageGroup",
+ "ExpansionFactor",
+ ]:
+ if hasattr(priv, k):
+ setattr(priv, k, None)
+ if removeUnusedSubrs:
+ remove_unused_subroutines(cff)
+
+
+def _pd_delete_empty_subrs(private_dict):
+ if hasattr(private_dict, "Subrs") and not private_dict.Subrs:
+ if "Subrs" in private_dict.rawDict:
+ del private_dict.rawDict["Subrs"]
+ del private_dict.Subrs
+
+
+def remove_unused_subroutines(cff):
+ for fontname in cff.keys():
+ font = cff[fontname]
+ cs = font.CharStrings
+ # Renumber subroutines to remove unused ones
+
+ # Mark all used subroutines
+ for c in cs.values():
+ subrs = getattr(c.private, "Subrs", [])
+ decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs, c.private)
+ decompiler.execute(c)
+
+ all_subrs = [font.GlobalSubrs]
+ if hasattr(font, "FDArray"):
+ all_subrs.extend(
+ fd.Private.Subrs
+ for fd in font.FDArray
+ if hasattr(fd.Private, "Subrs") and fd.Private.Subrs
+ )
+ elif hasattr(font.Private, "Subrs") and font.Private.Subrs:
+ all_subrs.append(font.Private.Subrs)
+
+ subrs = set(subrs) # Remove duplicates
+
+ # Prepare
+ for subrs in all_subrs:
+ if not hasattr(subrs, "_used"):
+ subrs._used = set()
+ subrs._used = _uniq_sort(subrs._used)
+ subrs._old_bias = calcSubrBias(subrs)
+ subrs._new_bias = calcSubrBias(subrs._used)
+
+ # Renumber glyph charstrings
+ for c in cs.values():
+ subrs = getattr(c.private, "Subrs", None)
+ _cs_subset_subroutines(c, subrs, font.GlobalSubrs)
+
+ # Renumber subroutines themselves
+ for subrs in all_subrs:
+ if subrs == font.GlobalSubrs:
+ if not hasattr(font, "FDArray") and hasattr(font.Private, "Subrs"):
+ local_subrs = font.Private.Subrs
+ elif (
+ hasattr(font, "FDArray")
+ and len(font.FDArray) == 1
+ and hasattr(font.FDArray[0].Private, "Subrs")
+ ):
+ # Technically we shouldn't do this. But I've run into fonts that do it.
+ local_subrs = font.FDArray[0].Private.Subrs
+ else:
+ local_subrs = None
+ else:
+ local_subrs = subrs
+
+ subrs.items = [subrs.items[i] for i in subrs._used]
+ if hasattr(subrs, "file"):
+ del subrs.file
+ if hasattr(subrs, "offsets"):
+ del subrs.offsets
+
+ for subr in subrs.items:
+ _cs_subset_subroutines(subr, local_subrs, font.GlobalSubrs)
+
+ # Delete local SubrsIndex if empty
+ if hasattr(font, "FDArray"):
+ for fd in font.FDArray:
+ _pd_delete_empty_subrs(fd.Private)
+ else:
+ _pd_delete_empty_subrs(font.Private)
+
+ # Cleanup
+ for subrs in all_subrs:
+ del subrs._used, subrs._old_bias, subrs._new_bias
diff --git a/lib/python3.12/site-packages/fontTools/cffLib/width.py b/lib/python3.12/site-packages/fontTools/cffLib/width.py
new file mode 100644
index 0000000000000000000000000000000000000000..78ff27e4fd85636141cddac2ee8035c0d7e33e03
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/cffLib/width.py
@@ -0,0 +1,210 @@
+# -*- coding: utf-8 -*-
+
+"""T2CharString glyph width optimizer.
+
+CFF glyphs whose width equals the CFF Private dictionary's ``defaultWidthX``
+value do not need to specify their width in their charstring, saving bytes.
+This module determines the optimum ``defaultWidthX`` and ``nominalWidthX``
+values for a font, when provided with a list of glyph widths."""
+
+from fontTools.ttLib import TTFont
+from collections import defaultdict
+from operator import add
+from functools import reduce
+
+
+__all__ = ["optimizeWidths", "main"]
+
+
+class missingdict(dict):
+ def __init__(self, missing_func):
+ self.missing_func = missing_func
+
+ def __missing__(self, v):
+ return self.missing_func(v)
+
+
+def cumSum(f, op=add, start=0, decreasing=False):
+ keys = sorted(f.keys())
+ minx, maxx = keys[0], keys[-1]
+
+ total = reduce(op, f.values(), start)
+
+ if decreasing:
+ missing = lambda x: start if x > maxx else total
+ domain = range(maxx, minx - 1, -1)
+ else:
+ missing = lambda x: start if x < minx else total
+ domain = range(minx, maxx + 1)
+
+ out = missingdict(missing)
+
+ v = start
+ for x in domain:
+ v = op(v, f[x])
+ out[x] = v
+
+ return out
+
+
+def byteCost(widths, default, nominal):
+ if not hasattr(widths, "items"):
+ d = defaultdict(int)
+ for w in widths:
+ d[w] += 1
+ widths = d
+
+ cost = 0
+ for w, freq in widths.items():
+ if w == default:
+ continue
+ diff = abs(w - nominal)
+ if diff <= 107:
+ cost += freq
+ elif diff <= 1131:
+ cost += freq * 2
+ else:
+ cost += freq * 5
+ return cost
+
+
+def optimizeWidthsBruteforce(widths):
+ """Bruteforce version. Veeeeeeeeeeeeeeeeery slow. Only works for smallests of fonts."""
+
+ d = defaultdict(int)
+ for w in widths:
+ d[w] += 1
+
+ # Maximum number of bytes using default can possibly save
+ maxDefaultAdvantage = 5 * max(d.values())
+
+ minw, maxw = min(widths), max(widths)
+ domain = list(range(minw, maxw + 1))
+
+ bestCostWithoutDefault = min(byteCost(widths, None, nominal) for nominal in domain)
+
+ bestCost = len(widths) * 5 + 1
+ for nominal in domain:
+ if byteCost(widths, None, nominal) > bestCost + maxDefaultAdvantage:
+ continue
+ for default in domain:
+ cost = byteCost(widths, default, nominal)
+ if cost < bestCost:
+ bestCost = cost
+ bestDefault = default
+ bestNominal = nominal
+
+ return bestDefault, bestNominal
+
+
+def optimizeWidths(widths):
+ """Given a list of glyph widths, or dictionary mapping glyph width to number of
+ glyphs having that, returns a tuple of best CFF default and nominal glyph widths.
+
+ This algorithm is linear in UPEM+numGlyphs."""
+
+ if not hasattr(widths, "items"):
+ d = defaultdict(int)
+ for w in widths:
+ d[w] += 1
+ widths = d
+
+ keys = sorted(widths.keys())
+ minw, maxw = keys[0], keys[-1]
+ domain = list(range(minw, maxw + 1))
+
+ # Cumulative sum/max forward/backward.
+ cumFrqU = cumSum(widths, op=add)
+ cumMaxU = cumSum(widths, op=max)
+ cumFrqD = cumSum(widths, op=add, decreasing=True)
+ cumMaxD = cumSum(widths, op=max, decreasing=True)
+
+ # Cost per nominal choice, without default consideration.
+ nomnCostU = missingdict(
+ lambda x: cumFrqU[x] + cumFrqU[x - 108] + cumFrqU[x - 1132] * 3
+ )
+ nomnCostD = missingdict(
+ lambda x: cumFrqD[x] + cumFrqD[x + 108] + cumFrqD[x + 1132] * 3
+ )
+ nomnCost = missingdict(lambda x: nomnCostU[x] + nomnCostD[x] - widths[x])
+
+ # Cost-saving per nominal choice, by best default choice.
+ dfltCostU = missingdict(
+ lambda x: max(cumMaxU[x], cumMaxU[x - 108] * 2, cumMaxU[x - 1132] * 5)
+ )
+ dfltCostD = missingdict(
+ lambda x: max(cumMaxD[x], cumMaxD[x + 108] * 2, cumMaxD[x + 1132] * 5)
+ )
+ dfltCost = missingdict(lambda x: max(dfltCostU[x], dfltCostD[x]))
+
+ # Combined cost per nominal choice.
+ bestCost = missingdict(lambda x: nomnCost[x] - dfltCost[x])
+
+ # Best nominal.
+ nominal = min(domain, key=lambda x: bestCost[x])
+
+ # Work back the best default.
+ bestC = bestCost[nominal]
+ dfltC = nomnCost[nominal] - bestCost[nominal]
+ ends = []
+ if dfltC == dfltCostU[nominal]:
+ starts = [nominal, nominal - 108, nominal - 1132]
+ for start in starts:
+ while cumMaxU[start] and cumMaxU[start] == cumMaxU[start - 1]:
+ start -= 1
+ ends.append(start)
+ else:
+ starts = [nominal, nominal + 108, nominal + 1132]
+ for start in starts:
+ while cumMaxD[start] and cumMaxD[start] == cumMaxD[start + 1]:
+ start += 1
+ ends.append(start)
+ default = min(ends, key=lambda default: byteCost(widths, default, nominal))
+
+ return default, nominal
+
+
+def main(args=None):
+ """Calculate optimum defaultWidthX/nominalWidthX values"""
+
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ "fonttools cffLib.width",
+ description=main.__doc__,
+ )
+ parser.add_argument(
+ "inputs", metavar="FILE", type=str, nargs="+", help="Input TTF files"
+ )
+ parser.add_argument(
+ "-b",
+ "--brute-force",
+ dest="brute",
+ action="store_true",
+ help="Use brute-force approach (VERY slow)",
+ )
+
+ args = parser.parse_args(args)
+
+ for fontfile in args.inputs:
+ font = TTFont(fontfile)
+ hmtx = font["hmtx"]
+ widths = [m[0] for m in hmtx.metrics.values()]
+ if args.brute:
+ default, nominal = optimizeWidthsBruteforce(widths)
+ else:
+ default, nominal = optimizeWidths(widths)
+ print(
+ "glyphs=%d default=%d nominal=%d byteCost=%d"
+ % (len(widths), default, nominal, byteCost(widths, default, nominal))
+ )
+
+
+if __name__ == "__main__":
+ import sys
+
+ if len(sys.argv) == 1:
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
+ main()
diff --git a/lib/python3.12/site-packages/fontTools/config/__init__.py b/lib/python3.12/site-packages/fontTools/config/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff0328a3d4593246336bc2a9dff938040fe62e36
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/config/__init__.py
@@ -0,0 +1,90 @@
+"""
+Define all configuration options that can affect the working of fontTools
+modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level,
+etc. If this file gets too big, split it into smaller files per-module.
+
+An instance of the Config class can be attached to a TTFont object, so that
+the various modules can access their configuration options from it.
+"""
+
+from textwrap import dedent
+
+from fontTools.misc.configTools import *
+
+
+class Config(AbstractConfig):
+ options = Options()
+
+
+OPTIONS = Config.options
+
+
+Config.register_option(
+ name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL",
+ help=dedent(
+ """\
+ GPOS Lookup type 2 (PairPos) compression level:
+ 0 = do not attempt to compact PairPos lookups;
+ 1 to 8 = create at most 1 to 8 new subtables for each existing
+ subtable, provided that it would yield a 50%% file size saving;
+ 9 = create as many new subtables as needed to yield a file size saving.
+ Default: 0.
+
+ This compaction aims to save file size, by splitting large class
+ kerning subtables (Format 2) that contain many zero values into
+ smaller and denser subtables. It's a trade-off between the overhead
+ of several subtables versus the sparseness of one big subtable.
+
+ See the pull request: https://github.com/fonttools/fonttools/pull/2326
+ """
+ ),
+ default=0,
+ parse=int,
+ validate=lambda v: v in range(10),
+)
+
+Config.register_option(
+ name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER",
+ help=dedent(
+ """\
+ FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables
+ if the uharfbuzz python bindings are importable, otherwise falls back to its
+ slower, less efficient serializer. Set to False to always use the latter.
+ Set to True to explicitly request the HarfBuzz Repacker (will raise an
+ error if uharfbuzz cannot be imported).
+ """
+ ),
+ default=None,
+ parse=Option.parse_optional_bool,
+ validate=Option.validate_optional_bool,
+)
+
+Config.register_option(
+ name="fontTools.otlLib.builder:WRITE_GPOS7",
+ help=dedent(
+ """\
+ macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining
+ ContextPos lookups), so FontTools.otlLib.builder disables a file size
+ optimisation that would use LookupType 7 instead of 8 when there is no
+ chaining (no prefix or suffix). Set to True to enable the optimization.
+ """
+ ),
+ default=False,
+ parse=Option.parse_optional_bool,
+ validate=Option.validate_optional_bool,
+)
+
+Config.register_option(
+ name="fontTools.ttLib:OPTIMIZE_FONT_SPEED",
+ help=dedent(
+ """\
+ Enable optimizations that prioritize speed over file size. This
+ mainly affects how glyf table and gvar / VARC tables are compiled.
+ The produced fonts will be larger, but rendering performance will
+ be improved with HarfBuzz and other text layout engines.
+ """
+ ),
+ default=False,
+ parse=Option.parse_optional_bool,
+ validate=Option.validate_optional_bool,
+)
diff --git a/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1007532d03bffc1ec67430ff72d436b9644899dd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__init__.py b/lib/python3.12/site-packages/fontTools/merge/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1f4b65de077517f614b0bb6a31d7bc698ea978c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/__init__.py
@@ -0,0 +1,248 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools import ttLib
+import fontTools.merge.base
+from fontTools.merge.cmap import (
+ computeMegaGlyphOrder,
+ computeMegaCmap,
+ renameCFFCharStrings,
+)
+from fontTools.merge.layout import layoutPreMerge, layoutPostMerge
+from fontTools.merge.options import Options
+import fontTools.merge.tables
+from fontTools.misc.loggingTools import Timer
+from functools import reduce
+import sys
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+timer = Timer(logger=logging.getLogger(__name__ + ".timer"), level=logging.INFO)
+
+
+class Merger(object):
+ """Font merger.
+
+ This class merges multiple files into a single OpenType font, taking into
+ account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
+ cross-font metrics (for example ``hhea.ascent`` is set to the maximum value
+ across all the fonts).
+
+ If multiple glyphs map to the same Unicode value, and the glyphs are considered
+ sufficiently different (that is, they differ in any of paths, widths, or
+ height), then subsequent glyphs are renamed and a lookup in the ``locl``
+ feature will be created to disambiguate them. For example, if the arguments
+ are an Arabic font and a Latin font and both contain a set of parentheses,
+ the Latin glyphs will be renamed to ``parenleft.1`` and ``parenright.1``,
+ and a lookup will be inserted into the to ``locl`` feature (creating it if
+ necessary) under the ``latn`` script to substitute ``parenleft`` with
+ ``parenleft.1`` etc.
+
+ Restrictions:
+
+ - All fonts must have the same units per em.
+ - If duplicate glyph disambiguation takes place as described above then the
+ fonts must have a ``GSUB`` table.
+
+ Attributes:
+ options: Currently unused.
+ """
+
+ def __init__(self, options=None):
+ if not options:
+ options = Options()
+
+ self.options = options
+
+ def _openFonts(self, fontfiles):
+ fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
+ for font, fontfile in zip(fonts, fontfiles):
+ font._merger__fontfile = fontfile
+ font._merger__name = font["name"].getDebugName(4)
+ return fonts
+
+ def merge(self, fontfiles):
+ """Merges fonts together.
+
+ Args:
+ fontfiles: A list of file names to be merged
+
+ Returns:
+ A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
+ this to write it out to an OTF file.
+ """
+ #
+ # Settle on a mega glyph order.
+ #
+ fonts = self._openFonts(fontfiles)
+ glyphOrders = [list(font.getGlyphOrder()) for font in fonts]
+ computeMegaGlyphOrder(self, glyphOrders)
+
+ # Take first input file sfntVersion
+ sfntVersion = fonts[0].sfntVersion
+
+ # Reload fonts and set new glyph names on them.
+ fonts = self._openFonts(fontfiles)
+ for font, glyphOrder in zip(fonts, glyphOrders):
+ font.setGlyphOrder(glyphOrder)
+ if "CFF " in font:
+ renameCFFCharStrings(self, glyphOrder, font["CFF "])
+
+ cmaps = [font["cmap"] for font in fonts]
+ self.duplicateGlyphsPerFont = [{} for _ in fonts]
+ computeMegaCmap(self, cmaps)
+
+ mega = ttLib.TTFont(sfntVersion=sfntVersion)
+ mega.setGlyphOrder(self.glyphOrder)
+
+ for font in fonts:
+ self._preMerge(font)
+
+ self.fonts = fonts
+
+ allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
+ allTags.remove("GlyphOrder")
+
+ for tag in sorted(allTags):
+ if tag in self.options.drop_tables:
+ continue
+
+ with timer("merge '%s'" % tag):
+ tables = [font.get(tag, NotImplemented) for font in fonts]
+
+ log.info("Merging '%s'.", tag)
+ clazz = ttLib.getTableClass(tag)
+ table = clazz(tag).merge(self, tables)
+ # XXX Clean this up and use: table = mergeObjects(tables)
+
+ if table is not NotImplemented and table is not False:
+ mega[tag] = table
+ log.info("Merged '%s'.", tag)
+ else:
+ log.info("Dropped '%s'.", tag)
+
+ del self.duplicateGlyphsPerFont
+ del self.fonts
+
+ self._postMerge(mega)
+
+ return mega
+
+ def mergeObjects(self, returnTable, logic, tables):
+ # Right now we don't use self at all. Will use in the future
+ # for options and logging.
+
+ allKeys = set.union(
+ set(),
+ *(vars(table).keys() for table in tables if table is not NotImplemented),
+ )
+ for key in allKeys:
+ log.info(" %s", key)
+ try:
+ mergeLogic = logic[key]
+ except KeyError:
+ try:
+ mergeLogic = logic["*"]
+ except KeyError:
+ raise Exception(
+ "Don't know how to merge key %s of class %s"
+ % (key, returnTable.__class__.__name__)
+ )
+ if mergeLogic is NotImplemented:
+ continue
+ value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
+ if value is not NotImplemented:
+ setattr(returnTable, key, value)
+
+ return returnTable
+
+ def _preMerge(self, font):
+ layoutPreMerge(font)
+
+ def _postMerge(self, font):
+ layoutPostMerge(font)
+
+ if "OS/2" in font:
+ # https://github.com/fonttools/fonttools/issues/2538
+ # TODO: Add an option to disable this?
+ font["OS/2"].recalcAvgCharWidth(font)
+
+
+__all__ = ["Options", "Merger", "main"]
+
+
+@timer("make one with everything (TOTAL TIME)")
+def main(args=None):
+ """Merge multiple fonts into one"""
+ from fontTools import configLogger
+
+ if args is None:
+ args = sys.argv[1:]
+
+ options = Options()
+ args = options.parse_opts(args)
+ fontfiles = []
+ if options.input_file:
+ with open(options.input_file) as inputfile:
+ fontfiles = [
+ line.strip()
+ for line in inputfile.readlines()
+ if not line.lstrip().startswith("#")
+ ]
+ for g in args:
+ fontfiles.append(g)
+
+ if len(fontfiles) < 1:
+ print(
+ "usage: fonttools merge [font1 ... fontN] [--input-file=filelist.txt] [--output-file=merged.ttf] [--import-file=tables.ttx]",
+ file=sys.stderr,
+ )
+ print(
+ " [--drop-tables=tags] [--verbose] [--timing]",
+ file=sys.stderr,
+ )
+ print("", file=sys.stderr)
+ print(" font1 ... fontN Files to merge.", file=sys.stderr)
+ print(
+ " --input-file= Read files to merge from a text file, each path new line. # Comment lines allowed.",
+ file=sys.stderr,
+ )
+ print(
+ " --output-file= Specify output file name (default: merged.ttf).",
+ file=sys.stderr,
+ )
+ print(
+ " --import-file= TTX file to import after merging. This can be used to set metadata.",
+ file=sys.stderr,
+ )
+ print(
+ " --drop-tables= Comma separated list of table tags to skip, case sensitive.",
+ file=sys.stderr,
+ )
+ print(
+ " --verbose Output progress information.",
+ file=sys.stderr,
+ )
+ print(" --timing Output progress timing.", file=sys.stderr)
+ return 1
+
+ configLogger(level=logging.INFO if options.verbose else logging.WARNING)
+ if options.timing:
+ timer.logger.setLevel(logging.DEBUG)
+ else:
+ timer.logger.disabled = True
+
+ merger = Merger(options=options)
+ font = merger.merge(fontfiles)
+
+ if options.import_file:
+ font.importXML(options.import_file)
+
+ with timer("compile and save font"):
+ font.save(options.output_file)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/lib/python3.12/site-packages/fontTools/merge/__main__.py b/lib/python3.12/site-packages/fontTools/merge/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff632d49c54e678623a27998a9d51b7cf84df81f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/__main__.py
@@ -0,0 +1,6 @@
+import sys
+from fontTools.merge import main
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..265fc0482e541bda4fbb23fb8ae540f38dbfca94
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5177db0075c3716f3cd637e415904998d25e3c71
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/__main__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/base.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..79e4fdd8d5f74370d7be227e9e351e11f1d805bb
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/base.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/cmap.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/cmap.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a51e2fe10bd30b3ec8ffb137dc731240504d66bd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/cmap.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/layout.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/layout.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a07aba82db97ea5d6fb81fe6003f560d6e0b8112
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/layout.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/options.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/options.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8dc6aafb124853b725e5c2b956372a81663193e0
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/options.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/tables.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/tables.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2914b9c6b7a34de68014667dab814ddd6bc6bfbd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/tables.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/unicode.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/unicode.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cc2bccbecc48000e1a0948006b12d437e6397a62
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/unicode.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/__pycache__/util.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/merge/__pycache__/util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dc849bccb5b03b2ea0d405afe69e6ac0fb02230b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/merge/__pycache__/util.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/merge/base.py b/lib/python3.12/site-packages/fontTools/merge/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..37f9097ab2595413066cebd102fdf697280a93bb
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/base.py
@@ -0,0 +1,81 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+def add_method(*clazzes, **kwargs):
+ """Returns a decorator function that adds a new method to one or
+ more classes."""
+ allowDefault = kwargs.get("allowDefaultTable", False)
+
+ def wrapper(method):
+ done = []
+ for clazz in clazzes:
+ if clazz in done:
+ continue # Support multiple names of a clazz
+ done.append(clazz)
+ assert allowDefault or clazz != DefaultTable, "Oops, table class not found."
+ assert (
+ method.__name__ not in clazz.__dict__
+ ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
+ setattr(clazz, method.__name__, method)
+ return None
+
+ return wrapper
+
+
+def mergeObjects(lst):
+ lst = [item for item in lst if item is not NotImplemented]
+ if not lst:
+ return NotImplemented
+ lst = [item for item in lst if item is not None]
+ if not lst:
+ return None
+
+ clazz = lst[0].__class__
+ assert all(type(item) == clazz for item in lst), lst
+
+ logic = clazz.mergeMap
+ returnTable = clazz()
+ returnDict = {}
+
+ allKeys = set.union(set(), *(vars(table).keys() for table in lst))
+ for key in allKeys:
+ try:
+ mergeLogic = logic[key]
+ except KeyError:
+ try:
+ mergeLogic = logic["*"]
+ except KeyError:
+ raise Exception(
+ "Don't know how to merge key %s of class %s" % (key, clazz.__name__)
+ )
+ if mergeLogic is NotImplemented:
+ continue
+ value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
+ if value is not NotImplemented:
+ returnDict[key] = value
+
+ returnTable.__dict__ = returnDict
+
+ return returnTable
+
+
+@add_method(DefaultTable, allowDefaultTable=True)
+def merge(self, m, tables):
+ if not hasattr(self, "mergeMap"):
+ log.info("Don't know how to merge '%s'.", self.tableTag)
+ return NotImplemented
+
+ logic = self.mergeMap
+
+ if isinstance(logic, dict):
+ return m.mergeObjects(self, self.mergeMap, tables)
+ else:
+ return logic(tables)
diff --git a/lib/python3.12/site-packages/fontTools/merge/cmap.py b/lib/python3.12/site-packages/fontTools/merge/cmap.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cc4a4ead158b7f3991a90ef9c6a6d96e5ad80eb
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/cmap.py
@@ -0,0 +1,173 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools.merge.unicode import is_Default_Ignorable
+from fontTools.pens.recordingPen import DecomposingRecordingPen
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+def computeMegaGlyphOrder(merger, glyphOrders):
+ """Modifies passed-in glyphOrders to reflect new glyph names.
+ Stores merger.glyphOrder."""
+ megaOrder = {}
+ for glyphOrder in glyphOrders:
+ for i, glyphName in enumerate(glyphOrder):
+ if glyphName in megaOrder:
+ n = megaOrder[glyphName]
+ while (glyphName + "." + repr(n)) in megaOrder:
+ n += 1
+ megaOrder[glyphName] = n
+ glyphName += "." + repr(n)
+ glyphOrder[i] = glyphName
+ megaOrder[glyphName] = 1
+ merger.glyphOrder = megaOrder = list(megaOrder.keys())
+
+
+def _glyphsAreSame(
+ glyphSet1,
+ glyphSet2,
+ glyph1,
+ glyph2,
+ advanceTolerance=0.05,
+ advanceToleranceEmpty=0.20,
+):
+ pen1 = DecomposingRecordingPen(glyphSet1)
+ pen2 = DecomposingRecordingPen(glyphSet2)
+ g1 = glyphSet1[glyph1]
+ g2 = glyphSet2[glyph2]
+ g1.draw(pen1)
+ g2.draw(pen2)
+ if pen1.value != pen2.value:
+ return False
+ # Allow more width tolerance for glyphs with no ink
+ tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty
+ # TODO Warn if advances not the same but within tolerance.
+ if abs(g1.width - g2.width) > g1.width * tolerance:
+ return False
+ if hasattr(g1, "height") and g1.height is not None:
+ if abs(g1.height - g2.height) > g1.height * tolerance:
+ return False
+ return True
+
+
+def computeMegaUvs(merger, uvsTables):
+ """Returns merged UVS subtable (cmap format=14)."""
+ uvsDict = {}
+ cmap = merger.cmap
+ for table in uvsTables:
+ for variationSelector, uvsMapping in table.uvsDict.items():
+ if variationSelector not in uvsDict:
+ uvsDict[variationSelector] = {}
+ for unicodeValue, glyphName in uvsMapping:
+ if cmap.get(unicodeValue) == glyphName:
+ # this is a default variation
+ glyphName = None
+ # prefer previous glyph id if both fonts defined UVS
+ if unicodeValue not in uvsDict[variationSelector]:
+ uvsDict[variationSelector][unicodeValue] = glyphName
+
+ for variationSelector in uvsDict:
+ uvsDict[variationSelector] = [*uvsDict[variationSelector].items()]
+
+ return uvsDict
+
+
+# Valid (format, platformID, platEncID) triplets for cmap subtables containing
+# Unicode BMP-only and Unicode Full Repertoire semantics.
+# Cf. OpenType spec for "Platform specific encodings":
+# https://docs.microsoft.com/en-us/typography/opentype/spec/name
+class _CmapUnicodePlatEncodings:
+ BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
+ FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
+ UVS = {(14, 0, 5)}
+
+
+def computeMegaCmap(merger, cmapTables):
+ """Sets merger.cmap and merger.uvsDict."""
+
+ # TODO Handle format=14.
+ # Only merge format 4 and 12 Unicode subtables, ignores all other subtables
+ # If there is a format 12 table for a font, ignore the format 4 table of it
+ chosenCmapTables = []
+ chosenUvsTables = []
+ for fontIdx, table in enumerate(cmapTables):
+ format4 = None
+ format12 = None
+ format14 = None
+ for subtable in table.tables:
+ properties = (subtable.format, subtable.platformID, subtable.platEncID)
+ if properties in _CmapUnicodePlatEncodings.BMP:
+ format4 = subtable
+ elif properties in _CmapUnicodePlatEncodings.FullRepertoire:
+ format12 = subtable
+ elif properties in _CmapUnicodePlatEncodings.UVS:
+ format14 = subtable
+ else:
+ log.warning(
+ "Dropped cmap subtable from font '%s':\t"
+ "format %2s, platformID %2s, platEncID %2s",
+ fontIdx,
+ subtable.format,
+ subtable.platformID,
+ subtable.platEncID,
+ )
+ if format12 is not None:
+ chosenCmapTables.append((format12, fontIdx))
+ elif format4 is not None:
+ chosenCmapTables.append((format4, fontIdx))
+
+ if format14 is not None:
+ chosenUvsTables.append(format14)
+
+ # Build the unicode mapping
+ merger.cmap = cmap = {}
+ fontIndexForGlyph = {}
+ glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None
+
+ for table, fontIdx in chosenCmapTables:
+ # handle duplicates
+ for uni, gid in table.cmap.items():
+ oldgid = cmap.get(uni, None)
+ if oldgid is None:
+ cmap[uni] = gid
+ fontIndexForGlyph[gid] = fontIdx
+ elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE
+ continue
+ elif oldgid != gid:
+ # Char previously mapped to oldgid, now to gid.
+ # Record, to fix up in GSUB 'locl' later.
+ if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None:
+ if glyphSets is not None:
+ oldFontIdx = fontIndexForGlyph[oldgid]
+ for idx in (fontIdx, oldFontIdx):
+ if glyphSets[idx] is None:
+ glyphSets[idx] = merger.fonts[idx].getGlyphSet()
+ # if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid):
+ # continue
+ merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
+ elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid:
+ # Char previously mapped to oldgid but oldgid is already remapped to a different
+ # gid, because of another Unicode character.
+ # TODO: Try harder to do something about these.
+ log.warning(
+ "Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid
+ )
+
+ merger.uvsDict = computeMegaUvs(merger, chosenUvsTables)
+
+
+def renameCFFCharStrings(merger, glyphOrder, cffTable):
+ """Rename topDictIndex charStrings based on glyphOrder."""
+ td = cffTable.cff.topDictIndex[0]
+
+ charStrings = {}
+ for i, v in enumerate(td.CharStrings.charStrings.values()):
+ glyphName = glyphOrder[i]
+ charStrings[glyphName] = v
+ td.CharStrings.charStrings = charStrings
+
+ td.charset = list(glyphOrder)
diff --git a/lib/python3.12/site-packages/fontTools/merge/layout.py b/lib/python3.12/site-packages/fontTools/merge/layout.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1b504e6198fbee20070078c8391a4a922255889
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/layout.py
@@ -0,0 +1,526 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools import ttLib
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+from fontTools.ttLib.tables import otTables
+from fontTools.merge.base import add_method, mergeObjects
+from fontTools.merge.util import *
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+def mergeLookupLists(lst):
+ # TODO Do smarter merge.
+ return sumLists(lst)
+
+
+def mergeFeatures(lst):
+ assert lst
+ self = otTables.Feature()
+ self.FeatureParams = None
+ self.LookupListIndex = mergeLookupLists(
+ [l.LookupListIndex for l in lst if l.LookupListIndex]
+ )
+ self.LookupCount = len(self.LookupListIndex)
+ return self
+
+
+def mergeFeatureLists(lst):
+ d = {}
+ for l in lst:
+ for f in l:
+ tag = f.FeatureTag
+ if tag not in d:
+ d[tag] = []
+ d[tag].append(f.Feature)
+ ret = []
+ for tag in sorted(d.keys()):
+ rec = otTables.FeatureRecord()
+ rec.FeatureTag = tag
+ rec.Feature = mergeFeatures(d[tag])
+ ret.append(rec)
+ return ret
+
+
+def mergeLangSyses(lst):
+ assert lst
+
+ # TODO Support merging ReqFeatureIndex
+ assert all(l.ReqFeatureIndex == 0xFFFF for l in lst)
+
+ self = otTables.LangSys()
+ self.LookupOrder = None
+ self.ReqFeatureIndex = 0xFFFF
+ self.FeatureIndex = mergeFeatureLists(
+ [l.FeatureIndex for l in lst if l.FeatureIndex]
+ )
+ self.FeatureCount = len(self.FeatureIndex)
+ return self
+
+
+def mergeScripts(lst):
+ assert lst
+
+ if len(lst) == 1:
+ return lst[0]
+ langSyses = {}
+ for sr in lst:
+ for lsr in sr.LangSysRecord:
+ if lsr.LangSysTag not in langSyses:
+ langSyses[lsr.LangSysTag] = []
+ langSyses[lsr.LangSysTag].append(lsr.LangSys)
+ lsrecords = []
+ for tag, langSys_list in sorted(langSyses.items()):
+ lsr = otTables.LangSysRecord()
+ lsr.LangSys = mergeLangSyses(langSys_list)
+ lsr.LangSysTag = tag
+ lsrecords.append(lsr)
+
+ self = otTables.Script()
+ self.LangSysRecord = lsrecords
+ self.LangSysCount = len(lsrecords)
+ dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys]
+ if dfltLangSyses:
+ self.DefaultLangSys = mergeLangSyses(dfltLangSyses)
+ else:
+ self.DefaultLangSys = None
+ return self
+
+
+def mergeScriptRecords(lst):
+ d = {}
+ for l in lst:
+ for s in l:
+ tag = s.ScriptTag
+ if tag not in d:
+ d[tag] = []
+ d[tag].append(s.Script)
+ ret = []
+ for tag in sorted(d.keys()):
+ rec = otTables.ScriptRecord()
+ rec.ScriptTag = tag
+ rec.Script = mergeScripts(d[tag])
+ ret.append(rec)
+ return ret
+
+
+otTables.ScriptList.mergeMap = {
+ "ScriptCount": lambda lst: None, # TODO
+ "ScriptRecord": mergeScriptRecords,
+}
+otTables.BaseScriptList.mergeMap = {
+ "BaseScriptCount": lambda lst: None, # TODO
+ # TODO: Merge duplicate entries
+ "BaseScriptRecord": lambda lst: sorted(
+ sumLists(lst), key=lambda s: s.BaseScriptTag
+ ),
+}
+
+otTables.FeatureList.mergeMap = {
+ "FeatureCount": sum,
+ "FeatureRecord": lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag),
+}
+
+otTables.LookupList.mergeMap = {
+ "LookupCount": sum,
+ "Lookup": sumLists,
+}
+
+otTables.Coverage.mergeMap = {
+ "Format": min,
+ "glyphs": sumLists,
+}
+
+otTables.ClassDef.mergeMap = {
+ "Format": min,
+ "classDefs": sumDicts,
+}
+
+otTables.LigCaretList.mergeMap = {
+ "Coverage": mergeObjects,
+ "LigGlyphCount": sum,
+ "LigGlyph": sumLists,
+}
+
+otTables.AttachList.mergeMap = {
+ "Coverage": mergeObjects,
+ "GlyphCount": sum,
+ "AttachPoint": sumLists,
+}
+
+# XXX Renumber MarkFilterSets of lookups
+otTables.MarkGlyphSetsDef.mergeMap = {
+ "MarkSetTableFormat": equal,
+ "MarkSetCount": sum,
+ "Coverage": sumLists,
+}
+
+otTables.Axis.mergeMap = {
+ "*": mergeObjects,
+}
+
+# XXX Fix BASE table merging
+otTables.BaseTagList.mergeMap = {
+ "BaseTagCount": sum,
+ "BaselineTag": sumLists,
+}
+
+otTables.GDEF.mergeMap = otTables.GSUB.mergeMap = otTables.GPOS.mergeMap = (
+ otTables.BASE.mergeMap
+) = otTables.JSTF.mergeMap = otTables.MATH.mergeMap = {
+ "*": mergeObjects,
+ "Version": max,
+}
+
+ttLib.getTableClass("GDEF").mergeMap = ttLib.getTableClass("GSUB").mergeMap = (
+ ttLib.getTableClass("GPOS").mergeMap
+) = ttLib.getTableClass("BASE").mergeMap = ttLib.getTableClass(
+ "JSTF"
+).mergeMap = ttLib.getTableClass(
+ "MATH"
+).mergeMap = {
+ "tableTag": onlyExisting(equal), # XXX clean me up
+ "table": mergeObjects,
+}
+
+
+@add_method(ttLib.getTableClass("GSUB"))
+def merge(self, m, tables):
+ assert len(tables) == len(m.duplicateGlyphsPerFont)
+ for i, (table, dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)):
+ if not dups:
+ continue
+ if table is None or table is NotImplemented:
+ log.warning(
+ "Have non-identical duplicates to resolve for '%s' but no GSUB. Are duplicates intended?: %s",
+ m.fonts[i]._merger__name,
+ dups,
+ )
+ continue
+
+ synthFeature = None
+ synthLookup = None
+ for script in table.table.ScriptList.ScriptRecord:
+ if script.ScriptTag == "DFLT":
+ continue # XXX
+ for langsys in [script.Script.DefaultLangSys] + [
+ l.LangSys for l in script.Script.LangSysRecord
+ ]:
+ if langsys is None:
+ continue # XXX Create!
+ feature = [v for v in langsys.FeatureIndex if v.FeatureTag == "locl"]
+ assert len(feature) <= 1
+ if feature:
+ feature = feature[0]
+ else:
+ if not synthFeature:
+ synthFeature = otTables.FeatureRecord()
+ synthFeature.FeatureTag = "locl"
+ f = synthFeature.Feature = otTables.Feature()
+ f.FeatureParams = None
+ f.LookupCount = 0
+ f.LookupListIndex = []
+ table.table.FeatureList.FeatureRecord.append(synthFeature)
+ table.table.FeatureList.FeatureCount += 1
+ feature = synthFeature
+ langsys.FeatureIndex.append(feature)
+ langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag)
+
+ if not synthLookup:
+ subtable = otTables.SingleSubst()
+ subtable.mapping = dups
+ synthLookup = otTables.Lookup()
+ synthLookup.LookupFlag = 0
+ synthLookup.LookupType = 1
+ synthLookup.SubTableCount = 1
+ synthLookup.SubTable = [subtable]
+ if table.table.LookupList is None:
+ # mtiLib uses None as default value for LookupList,
+ # while feaLib points to an empty array with count 0
+ # TODO: make them do the same
+ table.table.LookupList = otTables.LookupList()
+ table.table.LookupList.Lookup = []
+ table.table.LookupList.LookupCount = 0
+ table.table.LookupList.Lookup.append(synthLookup)
+ table.table.LookupList.LookupCount += 1
+
+ if feature.Feature.LookupListIndex[:1] != [synthLookup]:
+ feature.Feature.LookupListIndex[:0] = [synthLookup]
+ feature.Feature.LookupCount += 1
+
+ DefaultTable.merge(self, m, tables)
+ return self
+
+
+@add_method(
+ otTables.SingleSubst,
+ otTables.MultipleSubst,
+ otTables.AlternateSubst,
+ otTables.LigatureSubst,
+ otTables.ReverseChainSingleSubst,
+ otTables.SinglePos,
+ otTables.PairPos,
+ otTables.CursivePos,
+ otTables.MarkBasePos,
+ otTables.MarkLigPos,
+ otTables.MarkMarkPos,
+)
+def mapLookups(self, lookupMap):
+ pass
+
+
+# Copied and trimmed down from subset.py
+@add_method(
+ otTables.ContextSubst,
+ otTables.ChainContextSubst,
+ otTables.ContextPos,
+ otTables.ChainContextPos,
+)
+def __merge_classify_context(self):
+ class ContextHelper(object):
+ def __init__(self, klass, Format):
+ if klass.__name__.endswith("Subst"):
+ Typ = "Sub"
+ Type = "Subst"
+ else:
+ Typ = "Pos"
+ Type = "Pos"
+ if klass.__name__.startswith("Chain"):
+ Chain = "Chain"
+ else:
+ Chain = ""
+ ChainTyp = Chain + Typ
+
+ self.Typ = Typ
+ self.Type = Type
+ self.Chain = Chain
+ self.ChainTyp = ChainTyp
+
+ self.LookupRecord = Type + "LookupRecord"
+
+ if Format == 1:
+ self.Rule = ChainTyp + "Rule"
+ self.RuleSet = ChainTyp + "RuleSet"
+ elif Format == 2:
+ self.Rule = ChainTyp + "ClassRule"
+ self.RuleSet = ChainTyp + "ClassSet"
+
+ if self.Format not in [1, 2, 3]:
+ return None # Don't shoot the messenger; let it go
+ if not hasattr(self.__class__, "_merge__ContextHelpers"):
+ self.__class__._merge__ContextHelpers = {}
+ if self.Format not in self.__class__._merge__ContextHelpers:
+ helper = ContextHelper(self.__class__, self.Format)
+ self.__class__._merge__ContextHelpers[self.Format] = helper
+ return self.__class__._merge__ContextHelpers[self.Format]
+
+
+@add_method(
+ otTables.ContextSubst,
+ otTables.ChainContextSubst,
+ otTables.ContextPos,
+ otTables.ChainContextPos,
+)
+def mapLookups(self, lookupMap):
+ c = self.__merge_classify_context()
+
+ if self.Format in [1, 2]:
+ for rs in getattr(self, c.RuleSet):
+ if not rs:
+ continue
+ for r in getattr(rs, c.Rule):
+ if not r:
+ continue
+ for ll in getattr(r, c.LookupRecord):
+ if not ll:
+ continue
+ ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+ elif self.Format == 3:
+ for ll in getattr(self, c.LookupRecord):
+ if not ll:
+ continue
+ ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+ else:
+ assert 0, "unknown format: %s" % self.Format
+
+
+@add_method(otTables.ExtensionSubst, otTables.ExtensionPos)
+def mapLookups(self, lookupMap):
+ if self.Format == 1:
+ self.ExtSubTable.mapLookups(lookupMap)
+ else:
+ assert 0, "unknown format: %s" % self.Format
+
+
+@add_method(otTables.Lookup)
+def mapLookups(self, lookupMap):
+ for st in self.SubTable:
+ if not st:
+ continue
+ st.mapLookups(lookupMap)
+
+
+@add_method(otTables.LookupList)
+def mapLookups(self, lookupMap):
+ for l in self.Lookup:
+ if not l:
+ continue
+ l.mapLookups(lookupMap)
+
+
+@add_method(otTables.Lookup)
+def mapMarkFilteringSets(self, markFilteringSetMap):
+ if self.LookupFlag & 0x0010:
+ self.MarkFilteringSet = markFilteringSetMap[self.MarkFilteringSet]
+
+
+@add_method(otTables.LookupList)
+def mapMarkFilteringSets(self, markFilteringSetMap):
+ for l in self.Lookup:
+ if not l:
+ continue
+ l.mapMarkFilteringSets(markFilteringSetMap)
+
+
+@add_method(otTables.Feature)
+def mapLookups(self, lookupMap):
+ self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex]
+
+
+@add_method(otTables.FeatureList)
+def mapLookups(self, lookupMap):
+ for f in self.FeatureRecord:
+ if not f or not f.Feature:
+ continue
+ f.Feature.mapLookups(lookupMap)
+
+
+@add_method(otTables.DefaultLangSys, otTables.LangSys)
+def mapFeatures(self, featureMap):
+ self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
+ if self.ReqFeatureIndex != 65535:
+ self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex]
+
+
+@add_method(otTables.Script)
+def mapFeatures(self, featureMap):
+ if self.DefaultLangSys:
+ self.DefaultLangSys.mapFeatures(featureMap)
+ for l in self.LangSysRecord:
+ if not l or not l.LangSys:
+ continue
+ l.LangSys.mapFeatures(featureMap)
+
+
+@add_method(otTables.ScriptList)
+def mapFeatures(self, featureMap):
+ for s in self.ScriptRecord:
+ if not s or not s.Script:
+ continue
+ s.Script.mapFeatures(featureMap)
+
+
+def layoutPreMerge(font):
+ # Map indices to references
+
+ GDEF = font.get("GDEF")
+ GSUB = font.get("GSUB")
+ GPOS = font.get("GPOS")
+
+ for t in [GSUB, GPOS]:
+ if not t:
+ continue
+
+ if t.table.LookupList:
+ lookupMap = {i: v for i, v in enumerate(t.table.LookupList.Lookup)}
+ t.table.LookupList.mapLookups(lookupMap)
+ t.table.FeatureList.mapLookups(lookupMap)
+
+ if (
+ GDEF
+ and GDEF.table.Version >= 0x00010002
+ and GDEF.table.MarkGlyphSetsDef
+ ):
+ markFilteringSetMap = {
+ i: v for i, v in enumerate(GDEF.table.MarkGlyphSetsDef.Coverage)
+ }
+ t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
+
+ if t.table.FeatureList and t.table.ScriptList:
+ featureMap = {i: v for i, v in enumerate(t.table.FeatureList.FeatureRecord)}
+ t.table.ScriptList.mapFeatures(featureMap)
+
+ # TODO FeatureParams nameIDs
+
+
+def layoutPostMerge(font):
+ # Map references back to indices
+
+ GDEF = font.get("GDEF")
+ GSUB = font.get("GSUB")
+ GPOS = font.get("GPOS")
+
+ for t in [GSUB, GPOS]:
+ if not t:
+ continue
+
+ if t.table.FeatureList and t.table.ScriptList:
+ # Collect unregistered (new) features.
+ featureMap = GregariousIdentityDict(t.table.FeatureList.FeatureRecord)
+ t.table.ScriptList.mapFeatures(featureMap)
+
+ # Record used features.
+ featureMap = AttendanceRecordingIdentityDict(
+ t.table.FeatureList.FeatureRecord
+ )
+ t.table.ScriptList.mapFeatures(featureMap)
+ usedIndices = featureMap.s
+
+ # Remove unused features
+ t.table.FeatureList.FeatureRecord = [
+ f
+ for i, f in enumerate(t.table.FeatureList.FeatureRecord)
+ if i in usedIndices
+ ]
+
+ # Map back to indices.
+ featureMap = NonhashableDict(t.table.FeatureList.FeatureRecord)
+ t.table.ScriptList.mapFeatures(featureMap)
+
+ t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord)
+
+ if t.table.LookupList:
+ # Collect unregistered (new) lookups.
+ lookupMap = GregariousIdentityDict(t.table.LookupList.Lookup)
+ t.table.FeatureList.mapLookups(lookupMap)
+ t.table.LookupList.mapLookups(lookupMap)
+
+ # Record used lookups.
+ lookupMap = AttendanceRecordingIdentityDict(t.table.LookupList.Lookup)
+ t.table.FeatureList.mapLookups(lookupMap)
+ t.table.LookupList.mapLookups(lookupMap)
+ usedIndices = lookupMap.s
+
+ # Remove unused lookups
+ t.table.LookupList.Lookup = [
+ l for i, l in enumerate(t.table.LookupList.Lookup) if i in usedIndices
+ ]
+
+ # Map back to indices.
+ lookupMap = NonhashableDict(t.table.LookupList.Lookup)
+ t.table.FeatureList.mapLookups(lookupMap)
+ t.table.LookupList.mapLookups(lookupMap)
+
+ t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup)
+
+ if GDEF and GDEF.table.Version >= 0x00010002:
+ markFilteringSetMap = NonhashableDict(
+ GDEF.table.MarkGlyphSetsDef.Coverage
+ )
+ t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
+
+ # TODO FeatureParams nameIDs
diff --git a/lib/python3.12/site-packages/fontTools/merge/options.py b/lib/python3.12/site-packages/fontTools/merge/options.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bc8947138a80aa51278def2ebaa46523407a4e7
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/options.py
@@ -0,0 +1,85 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+
+class Options(object):
+ class UnknownOptionError(Exception):
+ pass
+
+ def __init__(self, **kwargs):
+ self.verbose = False
+ self.timing = False
+ self.drop_tables = []
+ self.input_file = None
+ self.output_file = "merged.ttf"
+ self.import_file = None
+
+ self.set(**kwargs)
+
+ def set(self, **kwargs):
+ for k, v in kwargs.items():
+ if not hasattr(self, k):
+ raise self.UnknownOptionError("Unknown option '%s'" % k)
+ setattr(self, k, v)
+
+ def parse_opts(self, argv, ignore_unknown=[]):
+ ret = []
+ opts = {}
+ for a in argv:
+ orig_a = a
+ if not a.startswith("--"):
+ ret.append(a)
+ continue
+ a = a[2:]
+ i = a.find("=")
+ op = "="
+ if i == -1:
+ if a.startswith("no-"):
+ k = a[3:]
+ v = False
+ else:
+ k = a
+ v = True
+ else:
+ k = a[:i]
+ if k[-1] in "-+":
+ op = k[-1] + "=" # Ops is '-=' or '+=' now.
+ k = k[:-1]
+ v = a[i + 1 :]
+ ok = k
+ k = k.replace("-", "_")
+ if not hasattr(self, k):
+ if ignore_unknown is True or ok in ignore_unknown:
+ ret.append(orig_a)
+ continue
+ else:
+ raise self.UnknownOptionError("Unknown option '%s'" % a)
+
+ ov = getattr(self, k)
+ if isinstance(ov, bool):
+ v = bool(v)
+ elif isinstance(ov, int):
+ v = int(v)
+ elif isinstance(ov, list):
+ vv = v.split(",")
+ if vv == [""]:
+ vv = []
+ vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
+ if op == "=":
+ v = vv
+ elif op == "+=":
+ v = ov
+ v.extend(vv)
+ elif op == "-=":
+ v = ov
+ for x in vv:
+ if x in v:
+ v.remove(x)
+ else:
+ assert 0
+
+ opts[k] = v
+ self.set(**opts)
+
+ return ret
diff --git a/lib/python3.12/site-packages/fontTools/merge/tables.py b/lib/python3.12/site-packages/fontTools/merge/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..a46977f0b4b962e731f56ce31cc59b018ce88ed4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/tables.py
@@ -0,0 +1,352 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools import ttLib, cffLib
+from fontTools.misc.psCharStrings import T2WidthExtractor
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+from fontTools.merge.base import add_method, mergeObjects
+from fontTools.merge.cmap import computeMegaCmap
+from fontTools.merge.util import *
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+ttLib.getTableClass("maxp").mergeMap = {
+ "*": max,
+ "tableTag": equal,
+ "tableVersion": equal,
+ "numGlyphs": sum,
+ "maxStorage": first,
+ "maxFunctionDefs": first,
+ "maxInstructionDefs": first,
+ # TODO When we correctly merge hinting data, update these values:
+ # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
+}
+
+headFlagsMergeBitMap = {
+ "size": 16,
+ "*": bitwise_or,
+ 1: bitwise_and, # Baseline at y = 0
+ 2: bitwise_and, # lsb at x = 0
+ 3: bitwise_and, # Force ppem to integer values. FIXME?
+ 5: bitwise_and, # Font is vertical
+ 6: lambda bit: 0, # Always set to zero
+ 11: bitwise_and, # Font data is 'lossless'
+ 13: bitwise_and, # Optimized for ClearType
+ 14: bitwise_and, # Last resort font. FIXME? equal or first may be better
+ 15: lambda bit: 0, # Always set to zero
+}
+
+ttLib.getTableClass("head").mergeMap = {
+ "tableTag": equal,
+ "tableVersion": max,
+ "fontRevision": max,
+ "checkSumAdjustment": lambda lst: 0, # We need *something* here
+ "magicNumber": equal,
+ "flags": mergeBits(headFlagsMergeBitMap),
+ "unitsPerEm": equal,
+ "created": current_time,
+ "modified": current_time,
+ "xMin": min,
+ "yMin": min,
+ "xMax": max,
+ "yMax": max,
+ "macStyle": first,
+ "lowestRecPPEM": max,
+ "fontDirectionHint": lambda lst: 2,
+ "indexToLocFormat": first,
+ "glyphDataFormat": equal,
+}
+
+ttLib.getTableClass("hhea").mergeMap = {
+ "*": equal,
+ "tableTag": equal,
+ "tableVersion": max,
+ "ascent": max,
+ "descent": min,
+ "lineGap": max,
+ "advanceWidthMax": max,
+ "minLeftSideBearing": min,
+ "minRightSideBearing": min,
+ "xMaxExtent": max,
+ "caretSlopeRise": first,
+ "caretSlopeRun": first,
+ "caretOffset": first,
+ "numberOfHMetrics": recalculate,
+}
+
+ttLib.getTableClass("vhea").mergeMap = {
+ "*": equal,
+ "tableTag": equal,
+ "tableVersion": max,
+ "ascent": max,
+ "descent": min,
+ "lineGap": max,
+ "advanceHeightMax": max,
+ "minTopSideBearing": min,
+ "minBottomSideBearing": min,
+ "yMaxExtent": max,
+ "caretSlopeRise": first,
+ "caretSlopeRun": first,
+ "caretOffset": first,
+ "numberOfVMetrics": recalculate,
+}
+
+os2FsTypeMergeBitMap = {
+ "size": 16,
+ "*": lambda bit: 0,
+ 1: bitwise_or, # no embedding permitted
+ 2: bitwise_and, # allow previewing and printing documents
+ 3: bitwise_and, # allow editing documents
+ 8: bitwise_or, # no subsetting permitted
+ 9: bitwise_or, # no embedding of outlines permitted
+}
+
+
+def mergeOs2FsType(lst):
+ lst = list(lst)
+ if all(item == 0 for item in lst):
+ return 0
+
+ # Compute least restrictive logic for each fsType value
+ for i in range(len(lst)):
+ # unset bit 1 (no embedding permitted) if either bit 2 or 3 is set
+ if lst[i] & 0x000C:
+ lst[i] &= ~0x0002
+ # set bit 2 (allow previewing) if bit 3 is set (allow editing)
+ elif lst[i] & 0x0008:
+ lst[i] |= 0x0004
+ # set bits 2 and 3 if everything is allowed
+ elif lst[i] == 0:
+ lst[i] = 0x000C
+
+ fsType = mergeBits(os2FsTypeMergeBitMap)(lst)
+ # unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
+ if fsType & 0x0002:
+ fsType &= ~0x000C
+ return fsType
+
+
+ttLib.getTableClass("OS/2").mergeMap = {
+ "*": first,
+ "tableTag": equal,
+ "version": max,
+ "xAvgCharWidth": first, # Will be recalculated at the end on the merged font
+ "fsType": mergeOs2FsType, # Will be overwritten
+ "panose": first, # FIXME: should really be the first Latin font
+ "ulUnicodeRange1": bitwise_or,
+ "ulUnicodeRange2": bitwise_or,
+ "ulUnicodeRange3": bitwise_or,
+ "ulUnicodeRange4": bitwise_or,
+ "fsFirstCharIndex": min,
+ "fsLastCharIndex": max,
+ "sTypoAscender": max,
+ "sTypoDescender": min,
+ "sTypoLineGap": max,
+ "usWinAscent": max,
+ "usWinDescent": max,
+ # Version 1
+ "ulCodePageRange1": onlyExisting(bitwise_or),
+ "ulCodePageRange2": onlyExisting(bitwise_or),
+ # Version 2, 3, 4
+ "sxHeight": onlyExisting(max),
+ "sCapHeight": onlyExisting(max),
+ "usDefaultChar": onlyExisting(first),
+ "usBreakChar": onlyExisting(first),
+ "usMaxContext": onlyExisting(max),
+ # version 5
+ "usLowerOpticalPointSize": onlyExisting(min),
+ "usUpperOpticalPointSize": onlyExisting(max),
+}
+
+
+@add_method(ttLib.getTableClass("OS/2"))
+def merge(self, m, tables):
+ DefaultTable.merge(self, m, tables)
+ if self.version < 2:
+ # bits 8 and 9 are reserved and should be set to zero
+ self.fsType &= ~0x0300
+ if self.version >= 3:
+ # Only one of bits 1, 2, and 3 may be set. We already take
+ # care of bit 1 implications in mergeOs2FsType. So unset
+ # bit 2 if bit 3 is already set.
+ if self.fsType & 0x0008:
+ self.fsType &= ~0x0004
+ return self
+
+
+ttLib.getTableClass("post").mergeMap = {
+ "*": first,
+ "tableTag": equal,
+ "formatType": max,
+ "isFixedPitch": min,
+ "minMemType42": max,
+ "maxMemType42": lambda lst: 0,
+ "minMemType1": max,
+ "maxMemType1": lambda lst: 0,
+ "mapping": onlyExisting(sumDicts),
+ "extraNames": lambda lst: [],
+}
+
+ttLib.getTableClass("vmtx").mergeMap = ttLib.getTableClass("hmtx").mergeMap = {
+ "tableTag": equal,
+ "metrics": sumDicts,
+}
+
+ttLib.getTableClass("name").mergeMap = {
+ "tableTag": equal,
+ "names": first, # FIXME? Does mixing name records make sense?
+}
+
+ttLib.getTableClass("loca").mergeMap = {
+ "*": recalculate,
+ "tableTag": equal,
+}
+
+ttLib.getTableClass("glyf").mergeMap = {
+ "tableTag": equal,
+ "glyphs": sumDicts,
+ "glyphOrder": sumLists,
+ "_reverseGlyphOrder": recalculate,
+ "axisTags": equal,
+}
+
+
+@add_method(ttLib.getTableClass("glyf"))
+def merge(self, m, tables):
+ for i, table in enumerate(tables):
+ for g in table.glyphs.values():
+ if i:
+ # Drop hints for all but first font, since
+ # we don't map functions / CVT values.
+ g.removeHinting()
+ # Expand composite glyphs to load their
+ # composite glyph names.
+ if g.isComposite():
+ g.expand(table)
+ return DefaultTable.merge(self, m, tables)
+
+
+ttLib.getTableClass("prep").mergeMap = lambda self, lst: first(lst)
+ttLib.getTableClass("fpgm").mergeMap = lambda self, lst: first(lst)
+ttLib.getTableClass("cvt ").mergeMap = lambda self, lst: first(lst)
+ttLib.getTableClass("gasp").mergeMap = lambda self, lst: first(
+ lst
+) # FIXME? Appears irreconcilable
+
+
+@add_method(ttLib.getTableClass("CFF "))
+def merge(self, m, tables):
+ if any(hasattr(table.cff[0], "FDSelect") for table in tables):
+ raise NotImplementedError("Merging CID-keyed CFF tables is not supported yet")
+
+ for table in tables:
+ table.cff.desubroutinize()
+
+ newcff = tables[0]
+ newfont = newcff.cff[0]
+ private = newfont.Private
+ newDefaultWidthX, newNominalWidthX = private.defaultWidthX, private.nominalWidthX
+ storedNamesStrings = []
+ glyphOrderStrings = []
+ glyphOrder = set(newfont.getGlyphOrder())
+
+ for name in newfont.strings.strings:
+ if name not in glyphOrder:
+ storedNamesStrings.append(name)
+ else:
+ glyphOrderStrings.append(name)
+
+ chrset = list(newfont.charset)
+ newcs = newfont.CharStrings
+ log.debug("FONT 0 CharStrings: %d.", len(newcs))
+
+ for i, table in enumerate(tables[1:], start=1):
+ font = table.cff[0]
+ defaultWidthX, nominalWidthX = (
+ font.Private.defaultWidthX,
+ font.Private.nominalWidthX,
+ )
+ widthsDiffer = (
+ defaultWidthX != newDefaultWidthX or nominalWidthX != newNominalWidthX
+ )
+ font.Private = private
+ fontGlyphOrder = set(font.getGlyphOrder())
+ for name in font.strings.strings:
+ if name in fontGlyphOrder:
+ glyphOrderStrings.append(name)
+ cs = font.CharStrings
+ gs = table.cff.GlobalSubrs
+ log.debug("Font %d CharStrings: %d.", i, len(cs))
+ chrset.extend(font.charset)
+ if newcs.charStringsAreIndexed:
+ for i, name in enumerate(cs.charStrings, start=len(newcs)):
+ newcs.charStrings[name] = i
+ newcs.charStringsIndex.items.append(None)
+ for name in cs.charStrings:
+ if widthsDiffer:
+ c = cs[name]
+ defaultWidthXToken = object()
+ extractor = T2WidthExtractor([], [], nominalWidthX, defaultWidthXToken)
+ extractor.execute(c)
+ width = extractor.width
+ if width is not defaultWidthXToken:
+ # The following will be wrong if the width is added
+ # by a subroutine. Ouch!
+ c.program.pop(0)
+ else:
+ width = defaultWidthX
+ if width != newDefaultWidthX:
+ c.program.insert(0, width - newNominalWidthX)
+ newcs[name] = cs[name]
+
+ newfont.charset = chrset
+ newfont.numGlyphs = len(chrset)
+ newfont.strings.strings = glyphOrderStrings + storedNamesStrings
+
+ return newcff
+
+
+@add_method(ttLib.getTableClass("cmap"))
+def merge(self, m, tables):
+ if not hasattr(m, "cmap"):
+ computeMegaCmap(m, tables)
+ cmap = m.cmap
+
+ cmapBmpOnly = {uni: gid for uni, gid in cmap.items() if uni <= 0xFFFF}
+ self.tables = []
+ module = ttLib.getTableModule("cmap")
+ if len(cmapBmpOnly) != len(cmap):
+ # format-12 required.
+ cmapTable = module.cmap_classes[12](12)
+ cmapTable.platformID = 3
+ cmapTable.platEncID = 10
+ cmapTable.language = 0
+ cmapTable.cmap = cmap
+ self.tables.append(cmapTable)
+ # always create format-4
+ cmapTable = module.cmap_classes[4](4)
+ cmapTable.platformID = 3
+ cmapTable.platEncID = 1
+ cmapTable.language = 0
+ cmapTable.cmap = cmapBmpOnly
+ # ordered by platform then encoding
+ self.tables.insert(0, cmapTable)
+
+ uvsDict = m.uvsDict
+ if uvsDict:
+ # format-14
+ uvsTable = module.cmap_classes[14](14)
+ uvsTable.platformID = 0
+ uvsTable.platEncID = 5
+ uvsTable.language = 0
+ uvsTable.cmap = {}
+ uvsTable.uvsDict = uvsDict
+ # ordered by platform then encoding
+ self.tables.insert(0, uvsTable)
+ self.tableVersion = 0
+ self.numSubTables = len(self.tables)
+ return self
diff --git a/lib/python3.12/site-packages/fontTools/merge/unicode.py b/lib/python3.12/site-packages/fontTools/merge/unicode.py
new file mode 100644
index 0000000000000000000000000000000000000000..65ae6c49295ec25ce6e473e9a81e8a191c912d7f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/unicode.py
@@ -0,0 +1,78 @@
+# Copyright 2021 Behdad Esfahbod. All Rights Reserved.
+
+
+def is_Default_Ignorable(u):
+ # http://www.unicode.org/reports/tr44/#Default_Ignorable_Code_Point
+ #
+ # TODO Move me to unicodedata module and autogenerate.
+ #
+ # Unicode 14.0:
+ # $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/'
+ # 00AD # Cf SOFT HYPHEN
+ # 034F # Mn COMBINING GRAPHEME JOINER
+ # 061C # Cf ARABIC LETTER MARK
+ # 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
+ # 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+ # 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+ # 180E # Cf MONGOLIAN VOWEL SEPARATOR
+ # 180F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
+ # 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
+ # 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ # 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS
+ # 2065 # Cn
+ # 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
+ # 3164 # Lo HANGUL FILLER
+ # FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+ # FEFF # Cf ZERO WIDTH NO-BREAK SPACE
+ # FFA0 # Lo HALFWIDTH HANGUL FILLER
+ # FFF0..FFF8 # Cn [9] ..
+ # 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+ # 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+ # E0000 # Cn
+ # E0001 # Cf LANGUAGE TAG
+ # E0002..E001F # Cn [30] ..
+ # E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG
+ # E0080..E00FF # Cn [128] ..
+ # E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+ # E01F0..E0FFF # Cn [3600] ..
+ return (
+ u == 0x00AD
+ or u == 0x034F # Cf SOFT HYPHEN
+ or u == 0x061C # Mn COMBINING GRAPHEME JOINER
+ or 0x115F <= u <= 0x1160 # Cf ARABIC LETTER MARK
+ or 0x17B4 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
+ <= u
+ <= 0x17B5
+ or 0x180B # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+ <= u
+ <= 0x180D
+ or u # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+ == 0x180E
+ or u == 0x180F # Cf MONGOLIAN VOWEL SEPARATOR
+ or 0x200B <= u <= 0x200F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
+ or 0x202A <= u <= 0x202E # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
+ or 0x2060 # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ <= u
+ <= 0x2064
+ or u == 0x2065 # Cf [5] WORD JOINER..INVISIBLE PLUS
+ or 0x2066 <= u <= 0x206F # Cn
+ or u == 0x3164 # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
+ or 0xFE00 <= u <= 0xFE0F # Lo HANGUL FILLER
+ or u == 0xFEFF # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+ or u == 0xFFA0 # Cf ZERO WIDTH NO-BREAK SPACE
+ or 0xFFF0 <= u <= 0xFFF8 # Lo HALFWIDTH HANGUL FILLER
+ or 0x1BCA0 <= u <= 0x1BCA3 # Cn [9] ..
+ or 0x1D173 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+ <= u
+ <= 0x1D17A
+ or u == 0xE0000 # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+ or u == 0xE0001 # Cn
+ or 0xE0002 <= u <= 0xE001F # Cf LANGUAGE TAG
+ or 0xE0020 <= u <= 0xE007F # Cn [30] ..
+ or 0xE0080 <= u <= 0xE00FF # Cf [96] TAG SPACE..CANCEL TAG
+ or 0xE0100 <= u <= 0xE01EF # Cn [128] ..
+ or 0xE01F0 # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+ <= u
+ <= 0xE0FFF
+ or False # Cn [3600] ..
+ )
diff --git a/lib/python3.12/site-packages/fontTools/merge/util.py b/lib/python3.12/site-packages/fontTools/merge/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..42fe39d5f701e683f52ca7c4022b1bb85749fb6b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/merge/util.py
@@ -0,0 +1,143 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools.misc.timeTools import timestampNow
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+from functools import reduce
+import operator
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+# General utility functions for merging values from different fonts
+
+
+def equal(lst):
+ lst = list(lst)
+ t = iter(lst)
+ first = next(t)
+ assert all(item == first for item in t), "Expected all items to be equal: %s" % lst
+ return first
+
+
+def first(lst):
+ return next(iter(lst))
+
+
+def recalculate(lst):
+ return NotImplemented
+
+
+def current_time(lst):
+ return timestampNow()
+
+
+def bitwise_and(lst):
+ return reduce(operator.and_, lst)
+
+
+def bitwise_or(lst):
+ return reduce(operator.or_, lst)
+
+
+def avg_int(lst):
+ lst = list(lst)
+ return sum(lst) // len(lst)
+
+
+def onlyExisting(func):
+ """Returns a filter func that when called with a list,
+ only calls func on the non-NotImplemented items of the list,
+ and only so if there's at least one item remaining.
+ Otherwise returns NotImplemented."""
+
+ def wrapper(lst):
+ items = [item for item in lst if item is not NotImplemented]
+ return func(items) if items else NotImplemented
+
+ return wrapper
+
+
+def sumLists(lst):
+ l = []
+ for item in lst:
+ l.extend(item)
+ return l
+
+
+def sumDicts(lst):
+ d = {}
+ for item in lst:
+ d.update(item)
+ return d
+
+
+def mergeBits(bitmap):
+ def wrapper(lst):
+ lst = list(lst)
+ returnValue = 0
+ for bitNumber in range(bitmap["size"]):
+ try:
+ mergeLogic = bitmap[bitNumber]
+ except KeyError:
+ try:
+ mergeLogic = bitmap["*"]
+ except KeyError:
+ raise Exception("Don't know how to merge bit %s" % bitNumber)
+ shiftedBit = 1 << bitNumber
+ mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
+ returnValue |= mergedValue << bitNumber
+ return returnValue
+
+ return wrapper
+
+
+class AttendanceRecordingIdentityDict(object):
+ """A dictionary-like object that records indices of items actually accessed
+ from a list."""
+
+ def __init__(self, lst):
+ self.l = lst
+ self.d = {id(v): i for i, v in enumerate(lst)}
+ self.s = set()
+
+ def __getitem__(self, v):
+ self.s.add(self.d[id(v)])
+ return v
+
+
+class GregariousIdentityDict(object):
+ """A dictionary-like object that welcomes guests without reservations and
+ adds them to the end of the guest list."""
+
+ def __init__(self, lst):
+ self.l = lst
+ self.s = set(id(v) for v in lst)
+
+ def __getitem__(self, v):
+ if id(v) not in self.s:
+ self.s.add(id(v))
+ self.l.append(v)
+ return v
+
+
+class NonhashableDict(object):
+ """A dictionary-like object mapping objects to values."""
+
+ def __init__(self, keys, values=None):
+ if values is None:
+ self.d = {id(v): i for i, v in enumerate(keys)}
+ else:
+ self.d = {id(k): v for k, v in zip(keys, values)}
+
+ def __getitem__(self, k):
+ return self.d[id(k)]
+
+ def __setitem__(self, k, v):
+ self.d[id(k)] = v
+
+ def __delitem__(self, k):
+ del self.d[id(k)]
diff --git a/lib/python3.12/site-packages/fontTools/misc/__init__.py b/lib/python3.12/site-packages/fontTools/misc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..156cb232a7aa80eee1526c7598f72043de10473f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/__init__.py
@@ -0,0 +1 @@
+"""Empty __init__.py file to signal Python this directory is a package."""
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..75e2c085100534c03322ce119b278cad92250ed1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/arrayTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/arrayTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5e3201ec9c5116a2c3890d2e26eb0a4b223e5a2a
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/arrayTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/bezierTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/bezierTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5f81ee12abe02ba24d0b67e9ff48174c2671f476
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/bezierTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/classifyTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/classifyTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..85a8b3e25e30ddce63cd7cc702870885f724e8d3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/classifyTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/cliTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/cliTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e961a6423812c2d209404382edf03742e10c1895
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/cliTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/configTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/configTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..310c08816b7f120f57cfc0c33bd4bed3b6969587
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/configTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/cython.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/cython.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f24c399397199f791629b7845ff228127d3fdb4e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/cython.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/dictTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/dictTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8a1257f7f00afceaed3eed800e0d00889dc56a41
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/dictTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/eexec.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/eexec.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..091bcf89f381deeee9c4ede16798e69217edffba
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/eexec.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/encodingTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/encodingTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9ba2f7dfbefaddea643585bfc1bbee6fec4f0b61
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/encodingTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/enumTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/enumTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..446f82b1f430efbed508d3cc78684ffa6727f4af
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/enumTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/etree.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/etree.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d02bb4da2550c6c2f8c8116401bdefeee25a72e1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/etree.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/filenames.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/filenames.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e2c19042d559270e1f69db6d41e4a81ca7324fb7
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/filenames.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/fixedTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/fixedTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9ba01a973191b23953355900ce23096ccecd3fe5
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/fixedTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/intTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/intTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4ffd62736ba3de7cb8b40de57b266e38d6f4a88d
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/intTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/iterTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/iterTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9d9398bc5b6c5339d580d8c1934122b1fdde10c1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/iterTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/lazyTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/lazyTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5ea248f24bd27ad33dcde5951b4bda5bb78a2be4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/lazyTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/loggingTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/loggingTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..02da3f015aa060d0ab013660d068fd3ffd8628ed
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/loggingTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/macCreatorType.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/macCreatorType.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..64c68f104977a4832e0d4324f0f910c8c4a71141
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/macCreatorType.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/macRes.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/macRes.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bd7246a9996d494406adb61e1c6d63291985628f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/macRes.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/psCharStrings.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psCharStrings.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7d58200890d9810f12bfe2efeaf1d6a26e0ead5c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psCharStrings.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/psLib.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psLib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2e5195d6e5c9b2903b942510d72741750775de3b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psLib.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/psOperators.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psOperators.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ebd5f2d82ac541404b63d88fcf6fbcf73b43c281
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/psOperators.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/py23.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/py23.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4dfc803038830fa16fad2cfca972801dde4cf5ea
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/py23.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/roundTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/roundTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6df6fbf4d0fa36c96c7a9d745fe7dd7b4077cc6b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/roundTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/sstruct.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/sstruct.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9fd10f399023dad638cf3c115b07248f0001f3b5
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/sstruct.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/symfont.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/symfont.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ad16893b629cc8e5ee37560e8182767c593aa044
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/symfont.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/testTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/testTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4e464ae9cd3c6add0b3eeebcec06f568f99387e8
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/testTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/textTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/textTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2a68297e498f2fa45db3842183b8c3c93095a9b3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/textTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/timeTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/timeTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b65f2d433a5c78dba1bb9cee309dc08937177930
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/timeTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/transform.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/transform.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6b96168772e6d87439bc5bf20b53612f32d2021c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/transform.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/treeTools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/treeTools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f87faa96d2f736cfde91671c01daafef29fc85dd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/treeTools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/vector.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/vector.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9f394d628ec653ea8816a23f99721b1c519e3041
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/vector.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/visitor.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/visitor.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f1d91b8705b127b5b8535178548362ca337771ca
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/visitor.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlReader.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlReader.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c8475a4761af97be79987178315b8769936b482b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlReader.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlWriter.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlWriter.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b1395f398f7adba1d8458d4f94936c445381309f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/__pycache__/xmlWriter.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/arrayTools.py b/lib/python3.12/site-packages/fontTools/misc/arrayTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..ced8d87a613c1b43d3d6c6c822e053aae92a08cd
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/arrayTools.py
@@ -0,0 +1,424 @@
+"""Routines for calculating bounding boxes, point in rectangle calculations and
+so on.
+"""
+
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.vector import Vector as _Vector
+import math
+import warnings
+
+
+def calcBounds(array):
+ """Calculate the bounding rectangle of a 2D points array.
+
+ Args:
+ array: A sequence of 2D tuples.
+
+ Returns:
+ A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+ """
+ if not array:
+ return 0, 0, 0, 0
+ xs = [x for x, y in array]
+ ys = [y for x, y in array]
+ return min(xs), min(ys), max(xs), max(ys)
+
+
+def calcIntBounds(array, round=otRound):
+ """Calculate the integer bounding rectangle of a 2D points array.
+
+ Values are rounded to closest integer towards ``+Infinity`` using the
+ :func:`fontTools.misc.fixedTools.otRound` function by default, unless
+ an optional ``round`` function is passed.
+
+ Args:
+ array: A sequence of 2D tuples.
+ round: A rounding function of type ``f(x: float) -> int``.
+
+ Returns:
+ A four-item tuple of integers representing the bounding rectangle:
+ ``(xMin, yMin, xMax, yMax)``.
+ """
+ return tuple(round(v) for v in calcBounds(array))
+
+
+def updateBounds(bounds, p, min=min, max=max):
+ """Add a point to a bounding rectangle.
+
+ Args:
+ bounds: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax), or None``.
+ p: A 2D tuple representing a point.
+ min,max: functions to compute the minimum and maximum.
+
+ Returns:
+ The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+ """
+ (x, y) = p
+ if bounds is None:
+ return x, y, x, y
+ xMin, yMin, xMax, yMax = bounds
+ return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
+
+
+def pointInRect(p, rect):
+ """Test if a point is inside a bounding rectangle.
+
+ Args:
+ p: A 2D tuple representing a point.
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ ``True`` if the point is inside the rectangle, ``False`` otherwise.
+ """
+ (x, y) = p
+ xMin, yMin, xMax, yMax = rect
+ return (xMin <= x <= xMax) and (yMin <= y <= yMax)
+
+
+def pointsInRect(array, rect):
+ """Determine which points are inside a bounding rectangle.
+
+ Args:
+ array: A sequence of 2D tuples.
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ A list containing the points inside the rectangle.
+ """
+ if len(array) < 1:
+ return []
+ xMin, yMin, xMax, yMax = rect
+ return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
+
+
+def vectorLength(vector):
+ """Calculate the length of the given vector.
+
+ Args:
+ vector: A 2D tuple.
+
+ Returns:
+ The Euclidean length of the vector.
+ """
+ x, y = vector
+ return math.sqrt(x**2 + y**2)
+
+
+def asInt16(array):
+ """Round a list of floats to 16-bit signed integers.
+
+ Args:
+ array: List of float values.
+
+ Returns:
+ A list of rounded integers.
+ """
+ return [int(math.floor(i + 0.5)) for i in array]
+
+
+def normRect(rect):
+ """Normalize a bounding box rectangle.
+
+ This function "turns the rectangle the right way up", so that the following
+ holds::
+
+ xMin <= xMax and yMin <= yMax
+
+ Args:
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ A normalized bounding rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
+
+
+def scaleRect(rect, x, y):
+ """Scale a bounding box rectangle.
+
+ Args:
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+ x: Factor to scale the rectangle along the X axis.
+ Y: Factor to scale the rectangle along the Y axis.
+
+ Returns:
+ A scaled bounding rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return xMin * x, yMin * y, xMax * x, yMax * y
+
+
+def offsetRect(rect, dx, dy):
+ """Offset a bounding box rectangle.
+
+ Args:
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+ dx: Amount to offset the rectangle along the X axis.
+ dY: Amount to offset the rectangle along the Y axis.
+
+ Returns:
+ An offset bounding rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return xMin + dx, yMin + dy, xMax + dx, yMax + dy
+
+
+def insetRect(rect, dx, dy):
+ """Inset a bounding box rectangle on all sides.
+
+ Args:
+ rect: A bounding rectangle expressed as a tuple
+ ``(xMin, yMin, xMax, yMax)``.
+ dx: Amount to inset the rectangle along the X axis.
+ dY: Amount to inset the rectangle along the Y axis.
+
+ Returns:
+ An inset bounding rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return xMin + dx, yMin + dy, xMax - dx, yMax - dy
+
+
+def sectRect(rect1, rect2):
+ """Test for rectangle-rectangle intersection.
+
+ Args:
+ rect1: First bounding rectangle, expressed as tuples
+ ``(xMin, yMin, xMax, yMax)``.
+ rect2: Second bounding rectangle.
+
+ Returns:
+ A boolean and a rectangle.
+ If the input rectangles intersect, returns ``True`` and the intersecting
+ rectangle. Returns ``False`` and ``(0, 0, 0, 0)`` if the input
+ rectangles don't intersect.
+ """
+ (xMin1, yMin1, xMax1, yMax1) = rect1
+ (xMin2, yMin2, xMax2, yMax2) = rect2
+ xMin, yMin, xMax, yMax = (
+ max(xMin1, xMin2),
+ max(yMin1, yMin2),
+ min(xMax1, xMax2),
+ min(yMax1, yMax2),
+ )
+ if xMin >= xMax or yMin >= yMax:
+ return False, (0, 0, 0, 0)
+ return True, (xMin, yMin, xMax, yMax)
+
+
+def unionRect(rect1, rect2):
+ """Determine union of bounding rectangles.
+
+ Args:
+ rect1: First bounding rectangle, expressed as tuples
+ ``(xMin, yMin, xMax, yMax)``.
+ rect2: Second bounding rectangle.
+
+ Returns:
+ The smallest rectangle in which both input rectangles are fully
+ enclosed.
+ """
+ (xMin1, yMin1, xMax1, yMax1) = rect1
+ (xMin2, yMin2, xMax2, yMax2) = rect2
+ xMin, yMin, xMax, yMax = (
+ min(xMin1, xMin2),
+ min(yMin1, yMin2),
+ max(xMax1, xMax2),
+ max(yMax1, yMax2),
+ )
+ return (xMin, yMin, xMax, yMax)
+
+
+def rectCenter(rect):
+ """Determine rectangle center.
+
+ Args:
+ rect: Bounding rectangle, expressed as tuples
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ A 2D tuple representing the point at the center of the rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return (xMin + xMax) / 2, (yMin + yMax) / 2
+
+
+def rectArea(rect):
+ """Determine rectangle area.
+
+ Args:
+ rect: Bounding rectangle, expressed as tuples
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ The area of the rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ return (yMax - yMin) * (xMax - xMin)
+
+
+def intRect(rect):
+ """Round a rectangle to integer values.
+
+ Guarantees that the resulting rectangle is NOT smaller than the original.
+
+ Args:
+ rect: Bounding rectangle, expressed as tuples
+ ``(xMin, yMin, xMax, yMax)``.
+
+ Returns:
+ A rounded bounding rectangle.
+ """
+ (xMin, yMin, xMax, yMax) = rect
+ xMin = int(math.floor(xMin))
+ yMin = int(math.floor(yMin))
+ xMax = int(math.ceil(xMax))
+ yMax = int(math.ceil(yMax))
+ return (xMin, yMin, xMax, yMax)
+
+
+def quantizeRect(rect, factor=1):
+ """
+ >>> bounds = (72.3, -218.4, 1201.3, 919.1)
+ >>> quantizeRect(bounds)
+ (72, -219, 1202, 920)
+ >>> quantizeRect(bounds, factor=10)
+ (70, -220, 1210, 920)
+ >>> quantizeRect(bounds, factor=100)
+ (0, -300, 1300, 1000)
+ """
+ if factor < 1:
+ raise ValueError(f"Expected quantization factor >= 1, found: {factor!r}")
+ xMin, yMin, xMax, yMax = normRect(rect)
+ return (
+ int(math.floor(xMin / factor) * factor),
+ int(math.floor(yMin / factor) * factor),
+ int(math.ceil(xMax / factor) * factor),
+ int(math.ceil(yMax / factor) * factor),
+ )
+
+
+class Vector(_Vector):
+ def __init__(self, *args, **kwargs):
+ warnings.warn(
+ "fontTools.misc.arrayTools.Vector has been deprecated, please use "
+ "fontTools.misc.vector.Vector instead.",
+ DeprecationWarning,
+ )
+
+
+def pairwise(iterable, reverse=False):
+ """Iterate over current and next items in iterable.
+
+ Args:
+ iterable: An iterable
+ reverse: If true, iterate in reverse order.
+
+ Returns:
+ A iterable yielding two elements per iteration.
+
+ Example:
+
+ >>> tuple(pairwise([]))
+ ()
+ >>> tuple(pairwise([], reverse=True))
+ ()
+ >>> tuple(pairwise([0]))
+ ((0, 0),)
+ >>> tuple(pairwise([0], reverse=True))
+ ((0, 0),)
+ >>> tuple(pairwise([0, 1]))
+ ((0, 1), (1, 0))
+ >>> tuple(pairwise([0, 1], reverse=True))
+ ((1, 0), (0, 1))
+ >>> tuple(pairwise([0, 1, 2]))
+ ((0, 1), (1, 2), (2, 0))
+ >>> tuple(pairwise([0, 1, 2], reverse=True))
+ ((2, 1), (1, 0), (0, 2))
+ >>> tuple(pairwise(['a', 'b', 'c', 'd']))
+ (('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'))
+ >>> tuple(pairwise(['a', 'b', 'c', 'd'], reverse=True))
+ (('d', 'c'), ('c', 'b'), ('b', 'a'), ('a', 'd'))
+ """
+ if not iterable:
+ return
+ if reverse:
+ it = reversed(iterable)
+ else:
+ it = iter(iterable)
+ first = next(it, None)
+ a = first
+ for b in it:
+ yield (a, b)
+ a = b
+ yield (a, first)
+
+
+def _test():
+ """
+ >>> import math
+ >>> calcBounds([])
+ (0, 0, 0, 0)
+ >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
+ (0, 10, 80, 100)
+ >>> updateBounds((0, 0, 0, 0), (100, 100))
+ (0, 0, 100, 100)
+ >>> pointInRect((50, 50), (0, 0, 100, 100))
+ True
+ >>> pointInRect((0, 0), (0, 0, 100, 100))
+ True
+ >>> pointInRect((100, 100), (0, 0, 100, 100))
+ True
+ >>> not pointInRect((101, 100), (0, 0, 100, 100))
+ True
+ >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
+ [True, True, True, False]
+ >>> vectorLength((3, 4))
+ 5.0
+ >>> vectorLength((1, 1)) == math.sqrt(2)
+ True
+ >>> list(asInt16([0, 0.1, 0.5, 0.9]))
+ [0, 0, 1, 1]
+ >>> normRect((0, 10, 100, 200))
+ (0, 10, 100, 200)
+ >>> normRect((100, 200, 0, 10))
+ (0, 10, 100, 200)
+ >>> scaleRect((10, 20, 50, 150), 1.5, 2)
+ (15.0, 40, 75.0, 300)
+ >>> offsetRect((10, 20, 30, 40), 5, 6)
+ (15, 26, 35, 46)
+ >>> insetRect((10, 20, 50, 60), 5, 10)
+ (15, 30, 45, 50)
+ >>> insetRect((10, 20, 50, 60), -5, -10)
+ (5, 10, 55, 70)
+ >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
+ >>> not intersects
+ True
+ >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
+ >>> intersects
+ 1
+ >>> rect
+ (5, 20, 20, 30)
+ >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
+ (0, 10, 20, 50)
+ >>> rectCenter((0, 0, 100, 200))
+ (50.0, 100.0)
+ >>> rectCenter((0, 0, 100, 199.0))
+ (50.0, 99.5)
+ >>> intRect((0.9, 2.9, 3.1, 4.1))
+ (0, 2, 4, 5)
+ """
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/bezierTools.c b/lib/python3.12/site-packages/fontTools/misc/bezierTools.c
new file mode 100644
index 0000000000000000000000000000000000000000..5940e7dab9038d3110f7ebed4ce27493a306d7b3
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/bezierTools.c
@@ -0,0 +1,39731 @@
+/* Generated by Cython 3.2.2 */
+
+/* BEGIN: Cython Metadata
+{
+ "distutils": {
+ "name": "fontTools.misc.bezierTools",
+ "sources": [
+ "Lib/fontTools/misc/bezierTools.py"
+ ]
+ },
+ "module_name": "fontTools.misc.bezierTools"
+}
+END: Cython Metadata */
+
+#ifndef PY_SSIZE_T_CLEAN
+#define PY_SSIZE_T_CLEAN
+#endif /* PY_SSIZE_T_CLEAN */
+/* InitLimitedAPI */
+#if defined(Py_LIMITED_API)
+ #if !defined(CYTHON_LIMITED_API)
+ #define CYTHON_LIMITED_API 1
+ #endif
+#elif defined(CYTHON_LIMITED_API)
+ #ifdef _MSC_VER
+ #pragma message ("Limited API usage is enabled with 'CYTHON_LIMITED_API' but 'Py_LIMITED_API' does not define a Python target version. Consider setting 'Py_LIMITED_API' instead.")
+ #else
+ #warning Limited API usage is enabled with 'CYTHON_LIMITED_API' but 'Py_LIMITED_API' does not define a Python target version. Consider setting 'Py_LIMITED_API' instead.
+ #endif
+#endif
+
+#include "Python.h"
+#ifndef Py_PYTHON_H
+ #error Python headers needed to compile C extensions, please install development version of Python.
+#elif PY_VERSION_HEX < 0x03080000
+ #error Cython requires Python 3.8+.
+#else
+#define __PYX_ABI_VERSION "3_2_2"
+#define CYTHON_HEX_VERSION 0x030202F0
+#define CYTHON_FUTURE_DIVISION 1
+/* CModulePreamble */
+#include
+#ifndef offsetof
+ #define offsetof(type, member) ( (size_t) & ((type*)0) -> member )
+#endif
+#if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS)
+ #ifndef __stdcall
+ #define __stdcall
+ #endif
+ #ifndef __cdecl
+ #define __cdecl
+ #endif
+ #ifndef __fastcall
+ #define __fastcall
+ #endif
+#endif
+#ifndef DL_IMPORT
+ #define DL_IMPORT(t) t
+#endif
+#ifndef DL_EXPORT
+ #define DL_EXPORT(t) t
+#endif
+#define __PYX_COMMA ,
+#ifndef PY_LONG_LONG
+ #define PY_LONG_LONG LONG_LONG
+#endif
+#ifndef Py_HUGE_VAL
+ #define Py_HUGE_VAL HUGE_VAL
+#endif
+#define __PYX_LIMITED_VERSION_HEX PY_VERSION_HEX
+#if defined(GRAALVM_PYTHON)
+ /* For very preliminary testing purposes. Most variables are set the same as PyPy.
+ The existence of this section does not imply that anything works or is even tested */
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 1
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #undef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 1
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #undef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #undef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 1
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
+ #endif
+ #undef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 0
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#elif defined(PYPY_VERSION)
+ #define CYTHON_COMPILING_IN_PYPY 1
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #ifndef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #endif
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #undef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 1
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #ifndef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 1
+ #endif
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #if PY_VERSION_HEX < 0x03090000
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+ #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT)
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE (PYPY_VERSION_NUM >= 0x07030C00)
+ #endif
+ #undef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_NUM >= 0x07031100)
+ #endif
+ #undef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 0
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#elif defined(CYTHON_LIMITED_API)
+ #ifdef Py_LIMITED_API
+ #undef __PYX_LIMITED_VERSION_HEX
+ #define __PYX_LIMITED_VERSION_HEX Py_LIMITED_API
+ #endif
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 1
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 1
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #ifndef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #endif
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #ifndef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 0
+ #endif
+ #ifndef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #endif
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000)
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #ifndef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #endif
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #endif
+ #ifndef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND (__PYX_LIMITED_VERSION_HEX >= 0x030A0000)
+ #endif
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
+ #endif
+ #ifndef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 1
+ #endif
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#else
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 1
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #ifdef Py_GIL_DISABLED
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 1
+ #else
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #endif
+ #if PY_VERSION_HEX < 0x030A0000
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #elif !defined(CYTHON_USE_TYPE_SLOTS)
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #endif
+ #ifndef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #endif
+ #ifndef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 1
+ #endif
+ #ifndef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #elif !defined(CYTHON_USE_PYLIST_INTERNALS)
+ #define CYTHON_USE_PYLIST_INTERNALS 1
+ #endif
+ #ifndef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING || PY_VERSION_HEX >= 0x030B00A2
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #elif !defined(CYTHON_USE_UNICODE_WRITER)
+ #define CYTHON_USE_UNICODE_WRITER 1
+ #endif
+ #ifndef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 0
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1
+ #elif !defined(CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS)
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #endif
+ #ifndef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 1
+ #endif
+ #ifndef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 1
+ #endif
+ #ifndef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 1
+ #endif
+ #ifndef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #elif !defined(CYTHON_FAST_GIL)
+ #define CYTHON_FAST_GIL (PY_VERSION_HEX < 0x030C00A6)
+ #endif
+ #ifndef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 1
+ #endif
+ #ifndef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 1
+ #endif
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #ifndef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #endif
+ #ifndef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING (PY_VERSION_HEX >= 0x030d00B1)
+ #endif
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 1
+ #endif
+ #ifndef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #elif !defined(CYTHON_USE_DICT_VERSIONS)
+ #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX < 0x030C00A5 && !CYTHON_USE_MODULE_STATE)
+ #endif
+ #ifndef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 1
+ #endif
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 1
+ #endif
+ #ifndef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS (!CYTHON_COMPILING_IN_CPYTHON_FREETHREADING)
+ #endif
+ #if defined(CYTHON_IMMORTAL_CONSTANTS) && PY_VERSION_HEX < 0x030C0000
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0 // definitely won't work
+ #elif !defined(CYTHON_IMMORTAL_CONSTANTS)
+ #define CYTHON_IMMORTAL_CONSTANTS (PY_VERSION_HEX >= 0x030C0000 && !CYTHON_USE_MODULE_STATE && CYTHON_COMPILING_IN_CPYTHON_FREETHREADING)
+ #endif
+#endif
+#ifndef CYTHON_COMPRESS_STRINGS
+ #define CYTHON_COMPRESS_STRINGS 1
+#endif
+#ifndef CYTHON_FAST_PYCCALL
+#define CYTHON_FAST_PYCCALL CYTHON_FAST_PYCALL
+#endif
+#ifndef CYTHON_VECTORCALL
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define CYTHON_VECTORCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000)
+#else
+#define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL)
+#endif
+#endif
+#if CYTHON_USE_PYLONG_INTERNALS
+ #undef SHIFT
+ #undef BASE
+ #undef MASK
+ #ifdef SIZEOF_VOID_P
+ enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
+ #endif
+#endif
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+#ifndef __has_cpp_attribute
+ #define __has_cpp_attribute(x) 0
+#endif
+#ifndef CYTHON_RESTRICT
+ #if defined(__GNUC__)
+ #define CYTHON_RESTRICT __restrict__
+ #elif defined(_MSC_VER) && _MSC_VER >= 1400
+ #define CYTHON_RESTRICT __restrict
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_RESTRICT restrict
+ #else
+ #define CYTHON_RESTRICT
+ #endif
+#endif
+#ifndef CYTHON_UNUSED
+ #if defined(__cplusplus)
+ /* for clang __has_cpp_attribute(maybe_unused) is true even before C++17
+ * but leads to warnings with -pedantic, since it is a C++17 feature */
+ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+ #if __has_cpp_attribute(maybe_unused)
+ #define CYTHON_UNUSED [[maybe_unused]]
+ #endif
+ #endif
+ #endif
+#endif
+#ifndef CYTHON_UNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+#endif
+#ifndef CYTHON_UNUSED_VAR
+# if defined(__cplusplus)
+ template void CYTHON_UNUSED_VAR( const T& ) { }
+# else
+# define CYTHON_UNUSED_VAR(x) (void)(x)
+# endif
+#endif
+#ifndef CYTHON_MAYBE_UNUSED_VAR
+ #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x)
+#endif
+#ifndef CYTHON_NCP_UNUSED
+# if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+# define CYTHON_NCP_UNUSED
+# else
+# define CYTHON_NCP_UNUSED CYTHON_UNUSED
+# endif
+#endif
+#ifndef CYTHON_USE_CPP_STD_MOVE
+ #if defined(__cplusplus) && (\
+ __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600))
+ #define CYTHON_USE_CPP_STD_MOVE 1
+ #else
+ #define CYTHON_USE_CPP_STD_MOVE 0
+ #endif
+#endif
+#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None)
+#include
+typedef uintptr_t __pyx_uintptr_t;
+#ifndef CYTHON_FALLTHROUGH
+ #if defined(__cplusplus)
+ /* for clang __has_cpp_attribute(fallthrough) is true even before C++17
+ * but leads to warnings with -pedantic, since it is a C++17 feature */
+ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+ #if __has_cpp_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH [[fallthrough]]
+ #endif
+ #endif
+ #ifndef CYTHON_FALLTHROUGH
+ #if __has_cpp_attribute(clang::fallthrough)
+ #define CYTHON_FALLTHROUGH [[clang::fallthrough]]
+ #elif __has_cpp_attribute(gnu::fallthrough)
+ #define CYTHON_FALLTHROUGH [[gnu::fallthrough]]
+ #endif
+ #endif
+ #endif
+ #ifndef CYTHON_FALLTHROUGH
+ #if __has_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH __attribute__((fallthrough))
+ #else
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+ #if defined(__clang__) && defined(__apple_build_version__)
+ #if __apple_build_version__ < 7000000
+ #undef CYTHON_FALLTHROUGH
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+#endif
+#ifndef Py_UNREACHABLE
+ #define Py_UNREACHABLE() assert(0); abort()
+#endif
+#ifdef __cplusplus
+ template
+ struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);};
+ #define __PYX_IS_UNSIGNED(type) (__PYX_IS_UNSIGNED_IMPL::value)
+#else
+ #define __PYX_IS_UNSIGNED(type) (((type)-1) > 0)
+#endif
+#if CYTHON_COMPILING_IN_PYPY == 1
+ #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX < 0x030A0000)
+#else
+ #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX < 0x03090000)
+#endif
+#define __PYX_REINTERPRET_FUNCION(func_pointer, other_pointer) ((func_pointer)(void(*)(void))(other_pointer))
+
+/* CInitCode */
+#ifndef CYTHON_INLINE
+ #if defined(__clang__)
+ #define CYTHON_INLINE __inline__ __attribute__ ((__unused__))
+ #elif defined(__GNUC__)
+ #define CYTHON_INLINE __inline__
+ #elif defined(_MSC_VER)
+ #define CYTHON_INLINE __inline
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_INLINE inline
+ #else
+ #define CYTHON_INLINE
+ #endif
+#endif
+
+/* PythonCompatibility */
+#define __PYX_BUILD_PY_SSIZE_T "n"
+#define CYTHON_FORMAT_SSIZE_T "z"
+#define __Pyx_BUILTIN_MODULE_NAME "builtins"
+#define __Pyx_DefaultClassType PyType_Type
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #ifndef CO_OPTIMIZED
+ static int CO_OPTIMIZED;
+ #endif
+ #ifndef CO_NEWLOCALS
+ static int CO_NEWLOCALS;
+ #endif
+ #ifndef CO_VARARGS
+ static int CO_VARARGS;
+ #endif
+ #ifndef CO_VARKEYWORDS
+ static int CO_VARKEYWORDS;
+ #endif
+ #ifndef CO_ASYNC_GENERATOR
+ static int CO_ASYNC_GENERATOR;
+ #endif
+ #ifndef CO_GENERATOR
+ static int CO_GENERATOR;
+ #endif
+ #ifndef CO_COROUTINE
+ static int CO_COROUTINE;
+ #endif
+#else
+ #ifndef CO_COROUTINE
+ #define CO_COROUTINE 0x80
+ #endif
+ #ifndef CO_ASYNC_GENERATOR
+ #define CO_ASYNC_GENERATOR 0x200
+ #endif
+#endif
+static int __Pyx_init_co_variables(void);
+#if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE)
+ #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type)
+#else
+ #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type))
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_Is)
+ #define __Pyx_Py_Is(x, y) Py_Is(x, y)
+#else
+ #define __Pyx_Py_Is(x, y) ((x) == (y))
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsNone)
+ #define __Pyx_Py_IsNone(ob) Py_IsNone(ob)
+#else
+ #define __Pyx_Py_IsNone(ob) __Pyx_Py_Is((ob), Py_None)
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsTrue)
+ #define __Pyx_Py_IsTrue(ob) Py_IsTrue(ob)
+#else
+ #define __Pyx_Py_IsTrue(ob) __Pyx_Py_Is((ob), Py_True)
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsFalse)
+ #define __Pyx_Py_IsFalse(ob) Py_IsFalse(ob)
+#else
+ #define __Pyx_Py_IsFalse(ob) __Pyx_Py_Is((ob), Py_False)
+#endif
+#define __Pyx_NoneAsNull(obj) (__Pyx_Py_IsNone(obj) ? NULL : (obj))
+#if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY
+ #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o)
+#else
+ #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o)
+#endif
+#ifndef Py_TPFLAGS_CHECKTYPES
+ #define Py_TPFLAGS_CHECKTYPES 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_INDEX
+ #define Py_TPFLAGS_HAVE_INDEX 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
+ #define Py_TPFLAGS_HAVE_NEWBUFFER 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_FINALIZE
+ #define Py_TPFLAGS_HAVE_FINALIZE 0
+#endif
+#ifndef Py_TPFLAGS_SEQUENCE
+ #define Py_TPFLAGS_SEQUENCE 0
+#endif
+#ifndef Py_TPFLAGS_MAPPING
+ #define Py_TPFLAGS_MAPPING 0
+#endif
+#ifndef Py_TPFLAGS_IMMUTABLETYPE
+ #define Py_TPFLAGS_IMMUTABLETYPE (1UL << 8)
+#endif
+#ifndef Py_TPFLAGS_DISALLOW_INSTANTIATION
+ #define Py_TPFLAGS_DISALLOW_INSTANTIATION (1UL << 7)
+#endif
+#ifndef METH_STACKLESS
+ #define METH_STACKLESS 0
+#endif
+#ifndef METH_FASTCALL
+ #ifndef METH_FASTCALL
+ #define METH_FASTCALL 0x80
+ #endif
+ typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs);
+ typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args,
+ Py_ssize_t nargs, PyObject *kwnames);
+#else
+ #if PY_VERSION_HEX >= 0x030d00A4
+ # define __Pyx_PyCFunctionFast PyCFunctionFast
+ # define __Pyx_PyCFunctionFastWithKeywords PyCFunctionFastWithKeywords
+ #else
+ # define __Pyx_PyCFunctionFast _PyCFunctionFast
+ # define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords
+ #endif
+#endif
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_METH_FASTCALL METH_FASTCALL
+ #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast
+ #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords
+#else
+ #define __Pyx_METH_FASTCALL METH_VARARGS
+ #define __Pyx_PyCFunction_FastCall PyCFunction
+ #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords
+#endif
+#if CYTHON_VECTORCALL
+ #define __pyx_vectorcallfunc vectorcallfunc
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET
+ #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n))
+#else
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0
+ #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n))
+#endif
+#if PY_VERSION_HEX >= 0x030900B1
+#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func)
+#else
+#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func)
+#endif
+#define __Pyx_CyOrPyCFunction_Check(func) PyCFunction_Check(func)
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth)
+#elif !CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func)
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_CyOrPyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags)
+static CYTHON_INLINE PyObject* __Pyx_CyOrPyCFunction_GET_SELF(PyObject *func) {
+ return (__Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_STATIC) ? NULL : ((PyCFunctionObject*)func)->m_self;
+}
+#endif
+static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void (*cfunc)(void)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ return PyCFunction_Check(func) && PyCFunction_GetFunction(func) == (PyCFunction) cfunc;
+#else
+ return PyCFunction_Check(func) && PyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc;
+#endif
+}
+#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCFunction(func, cfunc)
+#if PY_VERSION_HEX < 0x03090000 || (CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000)
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b))
+ typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *);
+#else
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b)
+ #define __Pyx_PyCMethod PyCMethod
+#endif
+#ifndef METH_METHOD
+ #define METH_METHOD 0x200
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc)
+ #define PyObject_Malloc(s) PyMem_Malloc(s)
+ #define PyObject_Free(p) PyMem_Free(p)
+ #define PyObject_Realloc(p) PyMem_Realloc(p)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno)
+#elif CYTHON_COMPILING_IN_GRAAL && defined(GRAALPY_VERSION_NUM) && GRAALPY_VERSION_NUM > 0x19000000
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) GraalPyFrame_SetLineNumber((frame), (lineno))
+#elif CYTHON_COMPILING_IN_GRAAL
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) _PyFrame_SetLineNumber((frame), (lineno))
+#else
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyThreadState_Current PyThreadState_Get()
+#elif !CYTHON_FAST_THREAD_STATE
+ #define __Pyx_PyThreadState_Current PyThreadState_GET()
+#elif PY_VERSION_HEX >= 0x030d00A1
+ #define __Pyx_PyThreadState_Current PyThreadState_GetUnchecked()
+#else
+ #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet()
+#endif
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_INLINE void *__Pyx__PyModule_GetState(PyObject *op)
+{
+ void *result;
+ result = PyModule_GetState(op);
+ if (!result)
+ Py_FatalError("Couldn't find the module state");
+ return result;
+}
+#define __Pyx_PyModule_GetState(o) (__pyx_mstatetype *)__Pyx__PyModule_GetState(o)
+#else
+#define __Pyx_PyModule_GetState(op) ((void)op,__pyx_mstate_global)
+#endif
+#define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE((PyObject *) obj), name, func_ctype)
+#define __Pyx_PyObject_TryGetSlot(obj, name, func_ctype) __Pyx_PyType_TryGetSlot(Py_TYPE(obj), name, func_ctype)
+#define __Pyx_PyObject_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(Py_TYPE(obj), sub, name, func_ctype)
+#define __Pyx_PyObject_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSubSlot(Py_TYPE(obj), sub, name, func_ctype)
+#if CYTHON_USE_TYPE_SLOTS
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name)
+ #define __Pyx_PyType_TryGetSlot(type, name, func_ctype) __Pyx_PyType_GetSlot(type, name, func_ctype)
+ #define __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype) (((type)->sub) ? ((type)->sub->name) : NULL)
+ #define __Pyx_PyType_TryGetSubSlot(type, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype)
+#else
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name))
+ #define __Pyx_PyType_TryGetSlot(type, name, func_ctype)\
+ ((__PYX_LIMITED_VERSION_HEX >= 0x030A0000 ||\
+ (PyType_GetFlags(type) & Py_TPFLAGS_HEAPTYPE) || __Pyx_get_runtime_version() >= 0x030A0000) ?\
+ __Pyx_PyType_GetSlot(type, name, func_ctype) : NULL)
+ #define __Pyx_PyType_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSlot(obj, name, func_ctype)
+ #define __Pyx_PyType_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSlot(obj, name, func_ctype)
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON || defined(_PyDict_NewPresized)
+#define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n))
+#else
+#define __Pyx_PyDict_NewPresized(n) PyDict_New()
+#endif
+#define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)
+#define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)
+#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_UNICODE_INTERNALS
+#define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash)
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) {
+ PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name);
+ if (res == NULL) PyErr_Clear();
+ return res;
+}
+#elif !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000
+#define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#else
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) {
+#if CYTHON_COMPILING_IN_PYPY
+ return PyDict_GetItem(dict, name);
+#else
+ PyDictEntry *ep;
+ PyDictObject *mp = (PyDictObject*) dict;
+ long hash = ((PyStringObject *) name)->ob_shash;
+ assert(hash != -1);
+ ep = (mp->ma_lookup)(mp, name, hash);
+ if (ep == NULL) {
+ return NULL;
+ }
+ return ep->me_value;
+#endif
+}
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#endif
+#if CYTHON_USE_TYPE_SLOTS
+ #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags)
+ #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0)
+#else
+ #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp))
+ #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature)
+#endif
+#define __Pyx_PyObject_GetIterNextFunc(iterator) __Pyx_PyObject_GetSlot(iterator, tp_iternext, iternextfunc)
+#if CYTHON_USE_TYPE_SPECS
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) {\
+ PyTypeObject *type = Py_TYPE((PyObject*)obj);\
+ assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));\
+ PyObject_GC_Del(obj);\
+ Py_DECREF(type);\
+}
+#else
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i)
+ #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U)
+ #define __Pyx_PyUnicode_KIND(u) ((void)u, (0))
+ #define __Pyx_PyUnicode_DATA(u) ((void*)u)
+ #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i))
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u))
+#else
+ #if PY_VERSION_HEX >= 0x030C0000
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #else
+ #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\
+ 0 : _PyUnicode_Ready((PyObject *)(op)))
+ #endif
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
+ #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u)
+ #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u))
+ #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u)
+ #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
+ #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, (Py_UCS4) ch)
+ #if PY_VERSION_HEX >= 0x030C0000
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u))
+ #else
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
+ #else
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
+ #endif
+ #endif
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+ #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b)
+#else
+ #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\
+ PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b))
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+ #if !defined(PyUnicode_DecodeUnicodeEscape)
+ #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors)
+ #endif
+ #if !defined(PyUnicode_Contains)
+ #define PyUnicode_Contains(u, s) PySequence_Contains(u, s)
+ #endif
+ #if !defined(PyByteArray_Check)
+ #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type)
+ #endif
+ #if !defined(PyObject_Format)
+ #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
+ #endif
+#endif
+#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ #define __Pyx_PySequence_ListKeepNew(obj)\
+ (likely(PyList_CheckExact(obj) && PyUnstable_Object_IsUniquelyReferenced(obj)) ? __Pyx_NewRef(obj) : PySequence_List(obj))
+#elif CYTHON_COMPILING_IN_CPYTHON
+ #define __Pyx_PySequence_ListKeepNew(obj)\
+ (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj))
+#else
+ #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj)
+#endif
+#ifndef PySet_CheckExact
+ #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type)
+#endif
+#if PY_VERSION_HEX >= 0x030900A4
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size)
+#else
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size)
+#endif
+enum __Pyx_ReferenceSharing {
+ __Pyx_ReferenceSharing_DefinitelyUnique, // We created it so we know it's unshared - no need to check
+ __Pyx_ReferenceSharing_OwnStrongReference,
+ __Pyx_ReferenceSharing_FunctionArgument,
+ __Pyx_ReferenceSharing_SharedReference, // Never trust it to be unshared because it's a global or similar
+};
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && PY_VERSION_HEX >= 0x030E0000
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing)\
+ (sharing == __Pyx_ReferenceSharing_DefinitelyUnique ? 1 :\
+ (sharing == __Pyx_ReferenceSharing_FunctionArgument ? PyUnstable_Object_IsUniqueReferencedTemporary(o) :\
+ (sharing == __Pyx_ReferenceSharing_OwnStrongReference ? PyUnstable_Object_IsUniquelyReferenced(o) : 0)))
+#elif (CYTHON_COMPILING_IN_CPYTHON && !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING) || CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing) (((void)sharing), Py_REFCNT(o) == 1)
+#else
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing) (((void)o), ((void)sharing), 0)
+#endif
+#if CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
+ #elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyList_GetItemRef(o, i) (likely((i) >= 0) ? PySequence_GetItem(o, i) : (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL))
+ #else
+ #define __Pyx_PyList_GetItemRef(o, i) PySequence_ITEM(o, i)
+ #endif
+#elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
+ #else
+ #define __Pyx_PyList_GetItemRef(o, i) __Pyx_XNewRef(PyList_GetItem(o, i))
+ #endif
+#else
+ #define __Pyx_PyList_GetItemRef(o, i) __Pyx_NewRef(PyList_GET_ITEM(o, i))
+#endif
+#if CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS && !CYTHON_COMPILING_IN_LIMITED_API && CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyList_GetItemRefFast(o, i, unsafe_shared) (__Pyx_IS_UNIQUELY_REFERENCED(o, unsafe_shared) ?\
+ __Pyx_NewRef(PyList_GET_ITEM(o, i)) : __Pyx_PyList_GetItemRef(o, i))
+#else
+ #define __Pyx_PyList_GetItemRefFast(o, i, unsafe_shared) __Pyx_PyList_GetItemRef(o, i)
+#endif
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_PyDict_GetItemRef(dict, key, result) PyDict_GetItemRef(dict, key, result)
+#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
+ *result = PyObject_GetItem(dict, key);
+ if (*result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ return 0;
+ }
+ return -1;
+ }
+ return 1;
+}
+#else
+static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
+ *result = PyDict_GetItemWithError(dict, key);
+ if (*result == NULL) {
+ return PyErr_Occurred() ? -1 : 0;
+ }
+ Py_INCREF(*result);
+ return 1;
+}
+#endif
+#if defined(CYTHON_DEBUG_VISIT_CONST) && CYTHON_DEBUG_VISIT_CONST
+ #define __Pyx_VISIT_CONST(obj) Py_VISIT(obj)
+#else
+ #define __Pyx_VISIT_CONST(obj)
+#endif
+#if CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i)
+ #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq)
+ #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0))
+ #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GET_ITEM(o, i)
+ #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0))
+ #define __Pyx_PyList_GET_ITEM(o, i) PyList_GET_ITEM(o, i)
+#else
+ #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i)
+ #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq)
+ #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v)
+ #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GetItem(o, i)
+ #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v)
+ #define __Pyx_PyList_GET_ITEM(o, i) PyList_GetItem(o, i)
+#endif
+#if CYTHON_ASSUME_SAFE_SIZE
+ #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o)
+ #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o)
+ #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o)
+ #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
+ #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o)
+ #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GET_LENGTH(o)
+#else
+ #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o)
+ #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o)
+ #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o)
+ #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o)
+ #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o)
+ #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GetLength(o)
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_InternFromString)
+ #define PyUnicode_InternFromString(s) PyUnicode_FromString(s)
+#endif
+#define __Pyx_PyLong_FromHash_t PyLong_FromSsize_t
+#define __Pyx_PyLong_AsHash_t __Pyx_PyIndex_AsSsize_t
+#if __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+ #define __Pyx_PySendResult PySendResult
+#else
+ typedef enum {
+ PYGEN_RETURN = 0,
+ PYGEN_ERROR = -1,
+ PYGEN_NEXT = 1,
+ } __Pyx_PySendResult;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX < 0x030A00A3
+ typedef __Pyx_PySendResult (*__Pyx_pyiter_sendfunc)(PyObject *iter, PyObject *value, PyObject **result);
+#else
+ #define __Pyx_pyiter_sendfunc sendfunc
+#endif
+#if !CYTHON_USE_AM_SEND
+#define __PYX_HAS_PY_AM_SEND 0
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+#define __PYX_HAS_PY_AM_SEND 1
+#else
+#define __PYX_HAS_PY_AM_SEND 2 // our own backported implementation
+#endif
+#if __PYX_HAS_PY_AM_SEND < 2
+ #define __Pyx_PyAsyncMethodsStruct PyAsyncMethods
+#else
+ typedef struct {
+ unaryfunc am_await;
+ unaryfunc am_aiter;
+ unaryfunc am_anext;
+ __Pyx_pyiter_sendfunc am_send;
+ } __Pyx_PyAsyncMethodsStruct;
+ #define __Pyx_SlotTpAsAsync(s) ((PyAsyncMethods*)(s))
+#endif
+#if CYTHON_USE_AM_SEND && PY_VERSION_HEX < 0x030A00F0
+ #define __Pyx_TPFLAGS_HAVE_AM_SEND (1UL << 21)
+#else
+ #define __Pyx_TPFLAGS_HAVE_AM_SEND (0)
+#endif
+#if PY_VERSION_HEX >= 0x03090000
+#define __Pyx_PyInterpreterState_Get() PyInterpreterState_Get()
+#else
+#define __Pyx_PyInterpreterState_Get() PyThreadState_Get()->interp
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030A0000
+#ifdef __cplusplus
+extern "C"
+#endif
+PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize);
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_init_co_variable(PyObject *inspect, const char* name, int *write_to) {
+ int value;
+ PyObject *py_value = PyObject_GetAttrString(inspect, name);
+ if (!py_value) return 0;
+ value = (int) PyLong_AsLong(py_value);
+ Py_DECREF(py_value);
+ *write_to = value;
+ return value != -1 || !PyErr_Occurred();
+}
+static int __Pyx_init_co_variables(void) {
+ PyObject *inspect;
+ int result;
+ inspect = PyImport_ImportModule("inspect");
+ result =
+#if !defined(CO_OPTIMIZED)
+ __Pyx_init_co_variable(inspect, "CO_OPTIMIZED", &CO_OPTIMIZED) &&
+#endif
+#if !defined(CO_NEWLOCALS)
+ __Pyx_init_co_variable(inspect, "CO_NEWLOCALS", &CO_NEWLOCALS) &&
+#endif
+#if !defined(CO_VARARGS)
+ __Pyx_init_co_variable(inspect, "CO_VARARGS", &CO_VARARGS) &&
+#endif
+#if !defined(CO_VARKEYWORDS)
+ __Pyx_init_co_variable(inspect, "CO_VARKEYWORDS", &CO_VARKEYWORDS) &&
+#endif
+#if !defined(CO_ASYNC_GENERATOR)
+ __Pyx_init_co_variable(inspect, "CO_ASYNC_GENERATOR", &CO_ASYNC_GENERATOR) &&
+#endif
+#if !defined(CO_GENERATOR)
+ __Pyx_init_co_variable(inspect, "CO_GENERATOR", &CO_GENERATOR) &&
+#endif
+#if !defined(CO_COROUTINE)
+ __Pyx_init_co_variable(inspect, "CO_COROUTINE", &CO_COROUTINE) &&
+#endif
+ 1;
+ Py_DECREF(inspect);
+ return result ? 0 : -1;
+}
+#else
+static int __Pyx_init_co_variables(void) {
+ return 0; // It's a limited API-only feature
+}
+#endif
+
+/* MathInitCode */
+#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
+ #ifndef _USE_MATH_DEFINES
+ #define _USE_MATH_DEFINES
+ #endif
+#endif
+#include
+#if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL)
+#define __Pyx_truncl trunc
+#else
+#define __Pyx_truncl truncl
+#endif
+
+#ifndef CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME 0
+#endif
+#ifndef CYTHON_CLINE_IN_TRACEBACK
+#define CYTHON_CLINE_IN_TRACEBACK CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+#endif
+#if CYTHON_CLINE_IN_TRACEBACK
+#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; __pyx_clineno = __LINE__; (void) __pyx_clineno; }
+#else
+#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; (void) __pyx_clineno; }
+#endif
+#define __PYX_ERR(f_index, lineno, Ln_error) \
+ { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; }
+
+#ifdef CYTHON_EXTERN_C
+ #undef __PYX_EXTERN_C
+ #define __PYX_EXTERN_C CYTHON_EXTERN_C
+#elif defined(__PYX_EXTERN_C)
+ #ifdef _MSC_VER
+ #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
+ #else
+ #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
+ #endif
+#else
+ #ifdef __cplusplus
+ #define __PYX_EXTERN_C extern "C"
+ #else
+ #define __PYX_EXTERN_C extern
+ #endif
+#endif
+
+#define __PYX_HAVE__fontTools__misc__bezierTools
+#define __PYX_HAVE_API__fontTools__misc__bezierTools
+/* Early includes */
+#ifdef _OPENMP
+#include
+#endif /* _OPENMP */
+
+#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS)
+#define CYTHON_WITHOUT_ASSERTIONS
+#endif
+
+#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0
+#define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 0
+#define __PYX_DEFAULT_STRING_ENCODING ""
+#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString
+#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize
+#define __Pyx_uchar_cast(c) ((unsigned char)c)
+#define __Pyx_long_cast(x) ((long)x)
+#define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\
+ (sizeof(type) < sizeof(Py_ssize_t)) ||\
+ (sizeof(type) > sizeof(Py_ssize_t) &&\
+ likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX) &&\
+ (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\
+ v == (type)PY_SSIZE_T_MIN))) ||\
+ (sizeof(type) == sizeof(Py_ssize_t) &&\
+ (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX))) )
+static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) {
+ return (size_t) i < (size_t) limit;
+}
+#if defined (__cplusplus) && __cplusplus >= 201103L
+ #include
+ #define __Pyx_sst_abs(value) std::abs(value)
+#elif SIZEOF_INT >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) abs(value)
+#elif SIZEOF_LONG >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) labs(value)
+#elif defined (_MSC_VER)
+ #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value))
+#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define __Pyx_sst_abs(value) llabs(value)
+#elif defined (__GNUC__)
+ #define __Pyx_sst_abs(value) __builtin_llabs(value)
+#else
+ #define __Pyx_sst_abs(value) ((value<0) ? -value : value)
+#endif
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
+static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char*);
+#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l)
+#define __Pyx_PyBytes_FromString PyBytes_FromString
+#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*);
+#if CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyByteArray_AsString(s) PyByteArray_AS_STRING(s)
+#else
+ #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AsString(s))
+ #define __Pyx_PyByteArray_AsString(s) PyByteArray_AsString(s)
+#endif
+#define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
+#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
+#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
+#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s)
+#define __Pyx_PyUnicode_FromOrdinal(o) PyUnicode_FromOrdinal((int)o)
+#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
+static CYTHON_INLINE PyObject *__Pyx_NewRef(PyObject *obj) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_NewRef)
+ return Py_NewRef(obj);
+#else
+ Py_INCREF(obj);
+ return obj;
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_XNewRef(PyObject *obj) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_XNewRef)
+ return Py_XNewRef(obj);
+#else
+ Py_XINCREF(obj);
+ return obj;
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b);
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*);
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x);
+#define __Pyx_PySequence_Tuple(obj)\
+ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj))
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
+static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t);
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
+#if CYTHON_ASSUME_SAFE_MACROS
+#define __Pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
+#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AS_DOUBLE(x)
+#else
+#define __Pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x)
+#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AsDouble(x)
+#endif
+#define __Pyx_PyFloat_AsFloat(x) ((float) __Pyx_PyFloat_AsDouble(x))
+#define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x))
+#if CYTHON_USE_PYLONG_INTERNALS
+ #if PY_VERSION_HEX >= 0x030C00A7
+ #ifndef _PyLong_SIGN_MASK
+ #define _PyLong_SIGN_MASK 3
+ #endif
+ #ifndef _PyLong_NON_SIZE_BITS
+ #define _PyLong_NON_SIZE_BITS 3
+ #endif
+ #define __Pyx_PyLong_Sign(x) (((PyLongObject*)x)->long_value.lv_tag & _PyLong_SIGN_MASK)
+ #define __Pyx_PyLong_IsNeg(x) ((__Pyx_PyLong_Sign(x) & 2) != 0)
+ #define __Pyx_PyLong_IsNonNeg(x) (!__Pyx_PyLong_IsNeg(x))
+ #define __Pyx_PyLong_IsZero(x) (__Pyx_PyLong_Sign(x) & 1)
+ #define __Pyx_PyLong_IsPos(x) (__Pyx_PyLong_Sign(x) == 0)
+ #define __Pyx_PyLong_CompactValueUnsigned(x) (__Pyx_PyLong_Digits(x)[0])
+ #define __Pyx_PyLong_DigitCount(x) ((Py_ssize_t) (((PyLongObject*)x)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS))
+ #define __Pyx_PyLong_SignedDigitCount(x)\
+ ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * __Pyx_PyLong_DigitCount(x))
+ #if defined(PyUnstable_Long_IsCompact) && defined(PyUnstable_Long_CompactValue)
+ #define __Pyx_PyLong_IsCompact(x) PyUnstable_Long_IsCompact((PyLongObject*) x)
+ #define __Pyx_PyLong_CompactValue(x) PyUnstable_Long_CompactValue((PyLongObject*) x)
+ #else
+ #define __Pyx_PyLong_IsCompact(x) (((PyLongObject*)x)->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS))
+ #define __Pyx_PyLong_CompactValue(x) ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * (Py_ssize_t) __Pyx_PyLong_Digits(x)[0])
+ #endif
+ typedef Py_ssize_t __Pyx_compact_pylong;
+ typedef size_t __Pyx_compact_upylong;
+ #else
+ #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0)
+ #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0)
+ #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0)
+ #define __Pyx_PyLong_IsPos(x) (Py_SIZE(x) > 0)
+ #define __Pyx_PyLong_CompactValueUnsigned(x) ((Py_SIZE(x) == 0) ? 0 : __Pyx_PyLong_Digits(x)[0])
+ #define __Pyx_PyLong_DigitCount(x) __Pyx_sst_abs(Py_SIZE(x))
+ #define __Pyx_PyLong_SignedDigitCount(x) Py_SIZE(x)
+ #define __Pyx_PyLong_IsCompact(x) (Py_SIZE(x) == 0 || Py_SIZE(x) == 1 || Py_SIZE(x) == -1)
+ #define __Pyx_PyLong_CompactValue(x)\
+ ((Py_SIZE(x) == 0) ? (sdigit) 0 : ((Py_SIZE(x) < 0) ? -(sdigit)__Pyx_PyLong_Digits(x)[0] : (sdigit)__Pyx_PyLong_Digits(x)[0]))
+ typedef sdigit __Pyx_compact_pylong;
+ typedef digit __Pyx_compact_upylong;
+ #endif
+ #if PY_VERSION_HEX >= 0x030C00A5
+ #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->long_value.ob_digit)
+ #else
+ #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->ob_digit)
+ #endif
+#endif
+#if __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL)
+#elif __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeASCII(c_str, size, NULL)
+#else
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL)
+#endif
+
+
+/* Test for GCC > 2.95 */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)))
+ #define likely(x) __builtin_expect(!!(x), 1)
+ #define unlikely(x) __builtin_expect(!!(x), 0)
+#else /* !__GNUC__ or GCC < 2.95 */
+ #define likely(x) (x)
+ #define unlikely(x) (x)
+#endif /* __GNUC__ */
+/* PretendToInitialize */
+#ifdef __cplusplus
+#if __cplusplus > 201103L
+#include
+#endif
+template
+static void __Pyx_pretend_to_initialize(T* ptr) {
+#if __cplusplus > 201103L
+ if ((std::is_trivially_default_constructible::value))
+#endif
+ *ptr = T();
+ (void)ptr;
+}
+#else
+static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }
+#endif
+
+
+#if !CYTHON_USE_MODULE_STATE
+static PyObject *__pyx_m = NULL;
+#endif
+static int __pyx_lineno;
+static int __pyx_clineno = 0;
+static const char * const __pyx_cfilenm = __FILE__;
+static const char *__pyx_filename;
+
+/* Header.proto */
+#if !defined(CYTHON_CCOMPLEX)
+ #if defined(__cplusplus)
+ #define CYTHON_CCOMPLEX 1
+ #elif (defined(_Complex_I) && !defined(_MSC_VER)) || ((defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_COMPLEX__) && !defined(_MSC_VER))
+ #define CYTHON_CCOMPLEX 1
+ #else
+ #define CYTHON_CCOMPLEX 0
+ #endif
+#endif
+#if CYTHON_CCOMPLEX
+ #ifdef __cplusplus
+ #include
+ #else
+ #include
+ #endif
+#endif
+#if CYTHON_CCOMPLEX && !defined(__cplusplus) && defined(__sun__) && defined(__GNUC__)
+ #undef _Complex_I
+ #define _Complex_I 1.0fj
+#endif
+
+/* #### Code section: filename_table ### */
+
+static const char* const __pyx_f[] = {
+ "Lib/fontTools/misc/bezierTools.py",
+};
+/* #### Code section: utility_code_proto_before_types ### */
+/* Atomics.proto (used by UnpackUnboundCMethod) */
+#include
+#ifndef CYTHON_ATOMICS
+ #define CYTHON_ATOMICS 1
+#endif
+#define __PYX_CYTHON_ATOMICS_ENABLED() CYTHON_ATOMICS
+#define __PYX_GET_CYTHON_COMPILING_IN_CPYTHON_FREETHREADING() CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __pyx_atomic_int_type int
+#define __pyx_nonatomic_int_type int
+#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\
+ (__STDC_VERSION__ >= 201112L) &&\
+ !defined(__STDC_NO_ATOMICS__))
+ #include
+#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\
+ (__cplusplus >= 201103L) ||\
+ (defined(_MSC_VER) && _MSC_VER >= 1700)))
+ #include
+#endif
+#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\
+ (__STDC_VERSION__ >= 201112L) &&\
+ !defined(__STDC_NO_ATOMICS__) &&\
+ ATOMIC_INT_LOCK_FREE == 2)
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type atomic_int
+ #define __pyx_atomic_ptr_type atomic_uintptr_t
+ #define __pyx_nonatomic_ptr_type uintptr_t
+ #define __pyx_atomic_incr_relaxed(value) atomic_fetch_add_explicit(value, 1, memory_order_relaxed)
+ #define __pyx_atomic_incr_acq_rel(value) atomic_fetch_add_explicit(value, 1, memory_order_acq_rel)
+ #define __pyx_atomic_decr_acq_rel(value) atomic_fetch_sub_explicit(value, 1, memory_order_acq_rel)
+ #define __pyx_atomic_sub(value, arg) atomic_fetch_sub(value, arg)
+ #define __pyx_atomic_int_cmp_exchange(value, expected, desired) atomic_compare_exchange_strong(value, expected, desired)
+ #define __pyx_atomic_load(value) atomic_load(value)
+ #define __pyx_atomic_store(value, new_value) atomic_store(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) atomic_load_explicit(value, memory_order_relaxed)
+ #define __pyx_atomic_pointer_load_acquire(value) atomic_load_explicit(value, memory_order_acquire)
+ #define __pyx_atomic_pointer_exchange(value, new_value) atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value)
+ #define __pyx_atomic_pointer_cmp_exchange(value, expected, desired) atomic_compare_exchange_strong(value, expected, desired)
+ #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER)
+ #pragma message ("Using standard C atomics")
+ #elif defined(__PYX_DEBUG_ATOMICS)
+ #warning "Using standard C atomics"
+ #endif
+#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\
+ (__cplusplus >= 201103L) ||\
+\
+ (defined(_MSC_VER) && _MSC_VER >= 1700)) &&\
+ ATOMIC_INT_LOCK_FREE == 2)
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type std::atomic_int
+ #define __pyx_atomic_ptr_type std::atomic_uintptr_t
+ #define __pyx_nonatomic_ptr_type uintptr_t
+ #define __pyx_atomic_incr_relaxed(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_relaxed)
+ #define __pyx_atomic_incr_acq_rel(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_acq_rel)
+ #define __pyx_atomic_decr_acq_rel(value) std::atomic_fetch_sub_explicit(value, 1, std::memory_order_acq_rel)
+ #define __pyx_atomic_sub(value, arg) std::atomic_fetch_sub(value, arg)
+ #define __pyx_atomic_int_cmp_exchange(value, expected, desired) std::atomic_compare_exchange_strong(value, expected, desired)
+ #define __pyx_atomic_load(value) std::atomic_load(value)
+ #define __pyx_atomic_store(value, new_value) std::atomic_store(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) std::atomic_load_explicit(value, std::memory_order_relaxed)
+ #define __pyx_atomic_pointer_load_acquire(value) std::atomic_load_explicit(value, std::memory_order_acquire)
+ #define __pyx_atomic_pointer_exchange(value, new_value) std::atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value)
+ #define __pyx_atomic_pointer_cmp_exchange(value, expected, desired) std::atomic_compare_exchange_strong(value, expected, desired)
+ #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER)
+ #pragma message ("Using standard C++ atomics")
+ #elif defined(__PYX_DEBUG_ATOMICS)
+ #warning "Using standard C++ atomics"
+ #endif
+#elif CYTHON_ATOMICS && (__GNUC__ >= 5 || (__GNUC__ == 4 &&\
+ (__GNUC_MINOR__ > 1 ||\
+ (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 2))))
+ #define __pyx_atomic_ptr_type void*
+ #define __pyx_nonatomic_ptr_type void*
+ #define __pyx_atomic_incr_relaxed(value) __sync_fetch_and_add(value, 1)
+ #define __pyx_atomic_incr_acq_rel(value) __sync_fetch_and_add(value, 1)
+ #define __pyx_atomic_decr_acq_rel(value) __sync_fetch_and_sub(value, 1)
+ #define __pyx_atomic_sub(value, arg) __sync_fetch_and_sub(value, arg)
+ static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) {
+ __pyx_nonatomic_int_type old = __sync_val_compare_and_swap(value, *expected, desired);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #define __pyx_atomic_load(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_store(value, new_value) __sync_lock_test_and_set(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_pointer_load_acquire(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_pointer_exchange(value, new_value) __sync_lock_test_and_set(value, (__pyx_atomic_ptr_type)new_value)
+ static CYTHON_INLINE int __pyx_atomic_pointer_cmp_exchange(__pyx_atomic_ptr_type* value, __pyx_nonatomic_ptr_type* expected, __pyx_nonatomic_ptr_type desired) {
+ __pyx_nonatomic_ptr_type old = __sync_val_compare_and_swap(value, *expected, desired);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #ifdef __PYX_DEBUG_ATOMICS
+ #warning "Using GNU atomics"
+ #endif
+#elif CYTHON_ATOMICS && defined(_MSC_VER)
+ #include
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type long
+ #define __pyx_atomic_ptr_type void*
+ #undef __pyx_nonatomic_int_type
+ #define __pyx_nonatomic_int_type long
+ #define __pyx_nonatomic_ptr_type void*
+ #pragma intrinsic (_InterlockedExchangeAdd, _InterlockedExchange, _InterlockedCompareExchange, _InterlockedCompareExchangePointer, _InterlockedExchangePointer)
+ #define __pyx_atomic_incr_relaxed(value) _InterlockedExchangeAdd(value, 1)
+ #define __pyx_atomic_incr_acq_rel(value) _InterlockedExchangeAdd(value, 1)
+ #define __pyx_atomic_decr_acq_rel(value) _InterlockedExchangeAdd(value, -1)
+ #define __pyx_atomic_sub(value, arg) _InterlockedExchangeAdd(value, -arg)
+ static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) {
+ __pyx_nonatomic_int_type old = _InterlockedCompareExchange(value, desired, *expected);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #define __pyx_atomic_load(value) _InterlockedExchangeAdd(value, 0)
+ #define __pyx_atomic_store(value, new_value) _InterlockedExchange(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) *(void * volatile *)value
+ #define __pyx_atomic_pointer_load_acquire(value) _InterlockedCompareExchangePointer(value, 0, 0)
+ #define __pyx_atomic_pointer_exchange(value, new_value) _InterlockedExchangePointer(value, (__pyx_atomic_ptr_type)new_value)
+ static CYTHON_INLINE int __pyx_atomic_pointer_cmp_exchange(__pyx_atomic_ptr_type* value, __pyx_nonatomic_ptr_type* expected, __pyx_nonatomic_ptr_type desired) {
+ __pyx_atomic_ptr_type old = _InterlockedCompareExchangePointer(value, desired, *expected);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #ifdef __PYX_DEBUG_ATOMICS
+ #pragma message ("Using MSVC atomics")
+ #endif
+#else
+ #undef CYTHON_ATOMICS
+ #define CYTHON_ATOMICS 0
+ #ifdef __PYX_DEBUG_ATOMICS
+ #warning "Not using atomics"
+ #endif
+#endif
+
+/* CriticalSectionsDefinition.proto (used by CriticalSections) */
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __Pyx_PyCriticalSection void*
+#define __Pyx_PyCriticalSection2 void*
+#define __Pyx_PyCriticalSection_End(cs)
+#define __Pyx_PyCriticalSection2_End(cs)
+#else
+#define __Pyx_PyCriticalSection PyCriticalSection
+#define __Pyx_PyCriticalSection2 PyCriticalSection2
+#define __Pyx_PyCriticalSection_End PyCriticalSection_End
+#define __Pyx_PyCriticalSection2_End PyCriticalSection2_End
+#endif
+
+/* CriticalSections.proto (used by ParseKeywordsImpl) */
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __Pyx_PyCriticalSection_Begin(cs, arg) (void)(cs)
+#define __Pyx_PyCriticalSection2_Begin(cs, arg1, arg2) (void)(cs)
+#else
+#define __Pyx_PyCriticalSection_Begin PyCriticalSection_Begin
+#define __Pyx_PyCriticalSection2_Begin PyCriticalSection2_Begin
+#endif
+#if PY_VERSION_HEX < 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_BEGIN_CRITICAL_SECTION(o) {
+#define __Pyx_END_CRITICAL_SECTION() }
+#else
+#define __Pyx_BEGIN_CRITICAL_SECTION Py_BEGIN_CRITICAL_SECTION
+#define __Pyx_END_CRITICAL_SECTION Py_END_CRITICAL_SECTION
+#endif
+
+/* IncludeStructmemberH.proto (used by FixUpExtensionType) */
+#include
+
+/* #### Code section: numeric_typedefs ### */
+/* #### Code section: complex_type_declarations ### */
+/* Declarations.proto */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #ifdef __cplusplus
+ typedef ::std::complex< double > __pyx_t_double_complex;
+ #else
+ typedef double _Complex __pyx_t_double_complex;
+ #endif
+#else
+ typedef struct { double real, imag; } __pyx_t_double_complex;
+#endif
+static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double, double);
+
+/* #### Code section: type_declarations ### */
+
+/*--- Type declarations ---*/
+struct __pyx_defaults;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr;
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr;
+
+/* "fontTools/misc/bezierTools.py":815
+ *
+ *
+ * def solveQuadratic(a, b, c, sqrt=sqrt): # <<<<<<<<<<<<<<
+ * """Solve a quadratic equation.
+ *
+*/
+struct __pyx_defaults {
+ PyObject_HEAD
+ PyObject *arg0;
+};
+
+
+/* "fontTools/misc/bezierTools.py":546
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3)]
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_t;
+};
+
+
+/* "fontTools/misc/bezierTools.py":583
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3, pt4)]
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_t;
+};
+
+
+/* "fontTools/misc/bezierTools.py":644
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * pt1=cython.complex,
+ * pt2=cython.complex,
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC {
+ PyObject_HEAD
+ __pyx_t_double_complex __pyx_v_a;
+ __pyx_t_double_complex __pyx_v_b;
+ __pyx_t_double_complex __pyx_v_c;
+ __pyx_t_double_complex __pyx_v_d;
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ PyObject *__pyx_v_ts;
+};
+
+
+/* "fontTools/misc/bezierTools.py":770
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * a=cython.complex,
+ * b=cython.complex,
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC {
+ PyObject_HEAD
+ __pyx_t_double_complex __pyx_v_a;
+ __pyx_t_double_complex __pyx_v_a1;
+ __pyx_t_double_complex __pyx_v_b;
+ __pyx_t_double_complex __pyx_v_b1;
+ __pyx_t_double_complex __pyx_v_c;
+ __pyx_t_double_complex __pyx_v_c1;
+ __pyx_t_double_complex __pyx_v_d;
+ __pyx_t_double_complex __pyx_v_d1;
+ double __pyx_v_delta;
+ double __pyx_v_delta_2;
+ double __pyx_v_delta_3;
+ PyObject *__pyx_v_i;
+ PyObject *__pyx_v_pt1;
+ PyObject *__pyx_v_pt2;
+ PyObject *__pyx_v_pt3;
+ PyObject *__pyx_v_pt4;
+ double __pyx_v_t1;
+ double __pyx_v_t1_2;
+ double __pyx_v_t1_3;
+ double __pyx_v_t2;
+ PyObject *__pyx_v_ts;
+ PyObject *__pyx_t_0;
+ PyObject *(*__pyx_t_1)(PyObject *);
+};
+
+
+/* "fontTools/misc/bezierTools.py":1252
+ * else:
+ * raise ValueError("Unknown curve degree")
+ * return sorted(i for i in intersections if 0.0 <= i <= 1) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_i;
+};
+
+
+/* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t {
+ PyObject_HEAD
+ PyObject *__pyx_v_precision;
+};
+
+
+/* "fontTools/misc/bezierTools.py":1382
+ * def _is_linelike(segment):
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_p;
+};
+
+
+/* "fontTools/misc/bezierTools.py":1485
+ * return "%g" % obj
+ * else:
+ * return "(%s)" % ", ".join(_segmentrepr(x) for x in it) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_x;
+};
+
+/* #### Code section: utility_code_proto ### */
+
+/* --- Runtime support code (head) --- */
+/* Refnanny.proto */
+#ifndef CYTHON_REFNANNY
+ #define CYTHON_REFNANNY 0
+#endif
+#if CYTHON_REFNANNY
+ typedef struct {
+ void (*INCREF)(void*, PyObject*, Py_ssize_t);
+ void (*DECREF)(void*, PyObject*, Py_ssize_t);
+ void (*GOTREF)(void*, PyObject*, Py_ssize_t);
+ void (*GIVEREF)(void*, PyObject*, Py_ssize_t);
+ void* (*SetupContext)(const char*, Py_ssize_t, const char*);
+ void (*FinishContext)(void**);
+ } __Pyx_RefNannyAPIStruct;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname);
+ #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL;
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)\
+ if (acquire_gil) {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\
+ PyGILState_Release(__pyx_gilstate_save);\
+ } else {\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\
+ }
+ #define __Pyx_RefNannyFinishContextNogil() {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __Pyx_RefNannyFinishContext();\
+ PyGILState_Release(__pyx_gilstate_save);\
+ }
+ #define __Pyx_RefNannyFinishContextNogil() {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __Pyx_RefNannyFinishContext();\
+ PyGILState_Release(__pyx_gilstate_save);\
+ }
+ #define __Pyx_RefNannyFinishContext()\
+ __Pyx_RefNanny->FinishContext(&__pyx_refnanny)
+ #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0)
+ #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0)
+ #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0)
+ #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0)
+#else
+ #define __Pyx_RefNannyDeclarations
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)
+ #define __Pyx_RefNannyFinishContextNogil()
+ #define __Pyx_RefNannyFinishContext()
+ #define __Pyx_INCREF(r) Py_INCREF(r)
+ #define __Pyx_DECREF(r) Py_DECREF(r)
+ #define __Pyx_GOTREF(r)
+ #define __Pyx_GIVEREF(r)
+ #define __Pyx_XINCREF(r) Py_XINCREF(r)
+ #define __Pyx_XDECREF(r) Py_XDECREF(r)
+ #define __Pyx_XGOTREF(r)
+ #define __Pyx_XGIVEREF(r)
+#endif
+#define __Pyx_Py_XDECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; Py_XDECREF(tmp);\
+ } while (0)
+#define __Pyx_XDECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_XDECREF(tmp);\
+ } while (0)
+#define __Pyx_DECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_DECREF(tmp);\
+ } while (0)
+#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0)
+#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0)
+
+/* PyErrExceptionMatches.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyErr_ExceptionMatches(err) __Pyx_PyErr_ExceptionMatchesInState(__pyx_tstate, err)
+static CYTHON_INLINE int __Pyx_PyErr_ExceptionMatchesInState(PyThreadState* tstate, PyObject* err);
+#else
+#define __Pyx_PyErr_ExceptionMatches(err) PyErr_ExceptionMatches(err)
+#endif
+
+/* PyThreadStateGet.proto (used by PyErrFetchRestore) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate;
+#define __Pyx_PyThreadState_assign __pyx_tstate = __Pyx_PyThreadState_Current;
+#if PY_VERSION_HEX >= 0x030C00A6
+#define __Pyx_PyErr_Occurred() (__pyx_tstate->current_exception != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->current_exception ? (PyObject*) Py_TYPE(__pyx_tstate->current_exception) : (PyObject*) NULL)
+#else
+#define __Pyx_PyErr_Occurred() (__pyx_tstate->curexc_type != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->curexc_type)
+#endif
+#else
+#define __Pyx_PyThreadState_declare
+#define __Pyx_PyThreadState_assign
+#define __Pyx_PyErr_Occurred() (PyErr_Occurred() != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() PyErr_Occurred()
+#endif
+
+/* PyErrFetchRestore.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL)
+#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb);
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A6
+#define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL))
+#else
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
+#endif
+#else
+#define __Pyx_PyErr_Clear() PyErr_Clear()
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
+#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb)
+#endif
+
+/* PyObjectGetAttrStr.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);
+#else
+#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n)
+#endif
+
+/* PyObjectGetAttrStrNoError.proto (used by GetBuiltinName) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name);
+
+/* GetBuiltinName.proto */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name);
+
+/* TupleAndListFromArray.proto (used by fastcall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n);
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n);
+#endif
+
+/* IncludeStringH.proto (used by BytesEquals) */
+#include
+
+/* BytesEquals.proto (used by UnicodeEquals) */
+static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* UnicodeEquals.proto (used by fastcall) */
+static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* fastcall.proto */
+#if CYTHON_AVOID_BORROWED_REFS
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_PySequence_ITEM(args, i)
+#elif CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_NewRef(__Pyx_PyTuple_GET_ITEM(args, i))
+#else
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_XNewRef(PyTuple_GetItem(args, i))
+#endif
+#define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds)
+#define __Pyx_KwValues_VARARGS(args, nargs) NULL
+#define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s)
+#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw)
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_ArgRef_FASTCALL(args, i) __Pyx_NewRef(args[i])
+ #define __Pyx_NumKwargs_FASTCALL(kwds) __Pyx_PyTuple_GET_SIZE(kwds)
+ #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs))
+ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+ CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues);
+ #else
+ #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw)
+ #endif
+#else
+ #define __Pyx_ArgRef_FASTCALL __Pyx_ArgRef_VARARGS
+ #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS
+ #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS
+ #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS
+ #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS
+#endif
+#define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#if CYTHON_METH_FASTCALL || (CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(args + start, stop - start)
+#else
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#endif
+
+/* py_dict_items.proto (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d);
+
+/* CallCFunction.proto (used by CallUnboundCMethod0) */
+#define __Pyx_CallCFunction(cfunc, self, args)\
+ ((PyCFunction)(void(*)(void))(cfunc)->func)(self, args)
+#define __Pyx_CallCFunctionWithKeywords(cfunc, self, args, kwargs)\
+ ((PyCFunctionWithKeywords)(void(*)(void))(cfunc)->func)(self, args, kwargs)
+#define __Pyx_CallCFunctionFast(cfunc, self, args, nargs)\
+ ((__Pyx_PyCFunctionFast)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs)
+#define __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, nargs, kwnames)\
+ ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs, kwnames)
+
+/* PyObjectCall.proto (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw);
+#else
+#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw)
+#endif
+
+/* PyObjectCallMethO.proto (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg);
+#endif
+
+/* PyObjectFastCall.proto (used by PyObjectCallOneArg) */
+#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL)
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs);
+
+/* PyObjectCallOneArg.proto (used by CallUnboundCMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg);
+
+/* UnpackUnboundCMethod.proto (used by CallUnboundCMethod0) */
+typedef struct {
+ PyObject *type;
+ PyObject **method_name;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && CYTHON_ATOMICS
+ __pyx_atomic_int_type initialized;
+#endif
+ PyCFunction func;
+ PyObject *method;
+ int flag;
+} __Pyx_CachedCFunction;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+static CYTHON_INLINE int __Pyx_CachedCFunction_GetAndSetInitializing(__Pyx_CachedCFunction *cfunc) {
+#if !CYTHON_ATOMICS
+ return 1;
+#else
+ __pyx_nonatomic_int_type expected = 0;
+ if (__pyx_atomic_int_cmp_exchange(&cfunc->initialized, &expected, 1)) {
+ return 0;
+ }
+ return expected;
+#endif
+}
+static CYTHON_INLINE void __Pyx_CachedCFunction_SetFinishedInitializing(__Pyx_CachedCFunction *cfunc) {
+#if CYTHON_ATOMICS
+ __pyx_atomic_store(&cfunc->initialized, 2);
+#endif
+}
+#else
+#define __Pyx_CachedCFunction_GetAndSetInitializing(cfunc) 2
+#define __Pyx_CachedCFunction_SetFinishedInitializing(cfunc)
+#endif
+
+/* CallUnboundCMethod0.proto */
+CYTHON_UNUSED
+static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self);
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self);
+#else
+#define __Pyx_CallUnboundCMethod0(cfunc, self) __Pyx__CallUnboundCMethod0(cfunc, self)
+#endif
+
+/* py_dict_values.proto (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d);
+
+/* OwnedDictNext.proto (used by ParseKeywordsImpl) */
+#if CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, PyObject **ppos, PyObject **pkey, PyObject **pvalue);
+#else
+CYTHON_INLINE
+static int __Pyx_PyDict_NextRef(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue);
+#endif
+
+/* RaiseDoubleKeywords.proto (used by ParseKeywordsImpl) */
+static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name);
+
+/* ParseKeywordsImpl.export */
+static int __Pyx_ParseKeywordsTuple(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+static int __Pyx_ParseKeywordDictToDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ const char* function_name
+);
+static int __Pyx_ParseKeywordDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+
+/* CallUnboundCMethod2.proto */
+CYTHON_UNUSED
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2);
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2);
+#else
+#define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2)
+#endif
+
+/* ParseKeywords.proto */
+static CYTHON_INLINE int __Pyx_ParseKeywords(
+ PyObject *kwds, PyObject *const *kwvalues, PyObject ** const argnames[],
+ PyObject *kwds2, PyObject *values[],
+ Py_ssize_t num_pos_args, Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+
+/* RaiseArgTupleInvalid.proto */
+static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
+ Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found);
+
+/* PyDictVersioning.proto (used by GetModuleGlobalName) */
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1)
+#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)\
+ (version_var) = __PYX_GET_DICT_VERSION(dict);\
+ (cache_var) = (value);
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) {\
+ static PY_UINT64_T __pyx_dict_version = 0;\
+ static PyObject *__pyx_dict_cached_value = NULL;\
+ if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) {\
+ (VAR) = __Pyx_XNewRef(__pyx_dict_cached_value);\
+ } else {\
+ (VAR) = __pyx_dict_cached_value = (LOOKUP);\
+ __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT);\
+ }\
+}
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj);
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj);
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version);
+#else
+#define __PYX_GET_DICT_VERSION(dict) (0)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP);
+#endif
+
+/* GetModuleGlobalName.proto */
+#if CYTHON_USE_DICT_VERSIONS
+#define __Pyx_GetModuleGlobalName(var, name) do {\
+ static PY_UINT64_T __pyx_dict_version = 0;\
+ static PyObject *__pyx_dict_cached_value = NULL;\
+ (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION(__pyx_mstate_global->__pyx_d))) ?\
+ (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) :\
+ __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\
+} while(0)
+#define __Pyx_GetModuleGlobalNameUncached(var, name) do {\
+ PY_UINT64_T __pyx_dict_version;\
+ PyObject *__pyx_dict_cached_value;\
+ (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\
+} while(0)
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value);
+#else
+#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name);
+#endif
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_MultiplyCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_MultiplyCObj(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceMultiply(op1, op2) : PyNumber_Multiply(op1, op2))
+#endif
+
+/* RaiseTooManyValuesToUnpack.proto */
+static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected);
+
+/* RaiseNeedMoreValuesToUnpack.proto */
+static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
+
+/* IterFinish.proto */
+static CYTHON_INLINE int __Pyx_IterFinish(void);
+
+/* UnpackItemEndCheck.proto */
+static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected);
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_TrueDivideObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_TrueDivideObjC(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceTrueDivide(op1, op2) : PyNumber_TrueDivide(op1, op2))
+#endif
+
+/* IncludeStdlibH.proto */
+#include
+
+/* PyLongCompare.proto */
+static CYTHON_INLINE int __Pyx_PyLong_BoolNeObjC(PyObject *op1, PyObject *op2, long intval, long inplace);
+
+/* ListAppend.proto */
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS
+static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) {
+ PyListObject* L = (PyListObject*) list;
+ Py_ssize_t len = Py_SIZE(list);
+ if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) {
+ Py_INCREF(x);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
+ L->ob_item[len] = x;
+ #else
+ PyList_SET_ITEM(list, len, x);
+ #endif
+ __Pyx_SET_SIZE(list, len + 1);
+ return 0;
+ }
+ return PyList_Append(list, x);
+}
+#else
+#define __Pyx_PyList_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* ListCompAppend.proto */
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS
+static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) {
+ PyListObject* L = (PyListObject*) list;
+ Py_ssize_t len = Py_SIZE(list);
+ if (likely(L->allocated > len)) {
+ Py_INCREF(x);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
+ L->ob_item[len] = x;
+ #else
+ PyList_SET_ITEM(list, len, x);
+ #endif
+ __Pyx_SET_SIZE(list, len + 1);
+ return 0;
+ }
+ return PyList_Append(list, x);
+}
+#else
+#define __Pyx_ListComp_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* GetItemInt.proto */
+#define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck, unsafe_shared) :\
+ (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) :\
+ __Pyx_GetItemInt_Generic(o, to_py_func(i))))
+#define __Pyx_GetItemInt_List(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_List_Fast(o, (Py_ssize_t)i, wraparound, boundscheck, unsafe_shared) :\
+ (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL))
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared);
+#define __Pyx_GetItemInt_Tuple(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_Tuple_Fast(o, (Py_ssize_t)i, wraparound, boundscheck, unsafe_shared) :\
+ (PyErr_SetString(PyExc_IndexError, "tuple index out of range"), (PyObject*)NULL))
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared);
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j);
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
+ int is_list, int wraparound, int boundscheck, int unsafe_shared);
+
+/* ObjectGetItem.proto */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key);
+#else
+#define __Pyx_PyObject_GetItem(obj, key) PyObject_GetItem(obj, key)
+#endif
+
+/* PyLongCompare.proto */
+static CYTHON_INLINE int __Pyx_PyLong_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace);
+
+/* RaiseUnboundLocalError.proto */
+static void __Pyx_RaiseUnboundLocalError(const char *varname);
+
+/* GetException.proto (used by pep479) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_GetException(type, value, tb) __Pyx__GetException(__pyx_tstate, type, value, tb)
+static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#else
+static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb);
+#endif
+
+/* pep479.proto */
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen);
+
+/* SliceObject.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(
+ PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop,
+ PyObject** py_start, PyObject** py_stop, PyObject** py_slice,
+ int has_cstart, int has_cstop, int wraparound);
+
+/* ListExtend.proto */
+static CYTHON_INLINE int __Pyx_PyList_Extend(PyObject* L, PyObject* v) {
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00a2
+ return PyList_Extend(L, v);
+#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000
+ PyObject* none = _PyList_Extend((PyListObject*)L, v);
+ if (unlikely(!none))
+ return -1;
+ Py_DECREF(none);
+ return 0;
+#else
+ return PyList_SetSlice(L, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, v);
+#endif
+}
+
+/* SetItemInt.proto */
+#define __Pyx_SetItemInt(o, i, v, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_SetItemInt_Fast(o, (Py_ssize_t)i, v, is_list, wraparound, boundscheck, unsafe_shared) :\
+ (is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) :\
+ __Pyx_SetItemInt_Generic(o, to_py_func(i), v)))
+static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v);
+static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v,
+ int is_list, int wraparound, int boundscheck, int unsafe_shared);
+
+/* dict_setdefault.proto (used by FetchCommonType) */
+static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value);
+
+/* LimitedApiGetTypeDict.proto (used by SetItemOnTypeDict) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp);
+#endif
+
+/* SetItemOnTypeDict.proto (used by FixUpExtensionType) */
+static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v);
+#define __Pyx_SetItemOnTypeDict(tp, k, v) __Pyx__SetItemOnTypeDict((PyTypeObject*)tp, k, v)
+
+/* FixUpExtensionType.proto (used by FetchCommonType) */
+static CYTHON_INLINE int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type);
+
+/* AddModuleRef.proto (used by FetchSharedCythonModule) */
+#if ((CYTHON_COMPILING_IN_CPYTHON_FREETHREADING ) ||\
+ __PYX_LIMITED_VERSION_HEX < 0x030d0000)
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name);
+#else
+ #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name)
+#endif
+
+/* FetchSharedCythonModule.proto (used by FetchCommonType) */
+static PyObject *__Pyx_FetchSharedCythonABIModule(void);
+
+/* FetchCommonType.proto (used by CommonTypesMetaclass) */
+static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases);
+
+/* CommonTypesMetaclass.proto (used by CoroutineBase) */
+static int __pyx_CommonTypesMetaclass_init(PyObject *module);
+#define __Pyx_CommonTypesMetaclass_USED
+
+/* RaiseException.export */
+static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause);
+
+/* GetTopmostException.proto (used by SaveResetException) */
+#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE
+static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate);
+#endif
+
+/* SaveResetException.proto (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_ExceptionSave(type, value, tb) __Pyx__ExceptionSave(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#define __Pyx_ExceptionReset(type, value, tb) __Pyx__ExceptionReset(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb);
+#else
+#define __Pyx_ExceptionSave(type, value, tb) PyErr_GetExcInfo(type, value, tb)
+#define __Pyx_ExceptionReset(type, value, tb) PyErr_SetExcInfo(type, value, tb)
+#endif
+
+/* SwapException.proto (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_ExceptionSwap(type, value, tb) __Pyx__ExceptionSwap(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#else
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb);
+#endif
+
+/* CallTypeTraverse.proto (used by CoroutineBase) */
+#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000)
+#define __Pyx_call_type_traverse(o, always_call, visit, arg) 0
+#else
+static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg);
+#endif
+
+/* IterNextPlain.proto (used by CoroutineBase) */
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator);
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void);
+#endif
+
+/* PyObjectCall2Args.proto (used by PyObjectCallMethod1) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2);
+
+/* PyObjectGetMethod.proto (used by PyObjectCallMethod1) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);
+#endif
+
+/* PyObjectCallMethod1.proto (used by CoroutineBase) */
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg);
+
+/* PyObjectCallNoArg.proto (used by CoroutineBase) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func);
+
+/* ReturnWithStopIteration.proto (used by CoroutineBase) */
+static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext);
+
+/* CoroutineBase.proto (used by Generator) */
+struct __pyx_CoroutineObject;
+typedef PyObject *(*__pyx_coroutine_body_t)(struct __pyx_CoroutineObject *, PyThreadState *, PyObject *);
+#if CYTHON_USE_EXC_INFO_STACK
+#define __Pyx_ExcInfoStruct _PyErr_StackItem
+#else
+typedef struct {
+ PyObject *exc_type;
+ PyObject *exc_value;
+ PyObject *exc_traceback;
+} __Pyx_ExcInfoStruct;
+#endif
+typedef struct __pyx_CoroutineObject {
+ PyObject_HEAD
+ __pyx_coroutine_body_t body;
+ PyObject *closure;
+ __Pyx_ExcInfoStruct gi_exc_state;
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *gi_weakreflist;
+#endif
+ PyObject *classobj;
+ PyObject *yieldfrom;
+ __Pyx_pyiter_sendfunc yieldfrom_am_send;
+ PyObject *gi_name;
+ PyObject *gi_qualname;
+ PyObject *gi_modulename;
+ PyObject *gi_code;
+ PyObject *gi_frame;
+#if CYTHON_USE_SYS_MONITORING && (CYTHON_PROFILE || CYTHON_TRACE)
+ PyMonitoringState __pyx_pymonitoring_state[__Pyx_MonitoringEventTypes_CyGen_count];
+ uint64_t __pyx_pymonitoring_version;
+#endif
+ int resume_label;
+ char is_running;
+} __pyx_CoroutineObject;
+static __pyx_CoroutineObject *__Pyx__Coroutine_New(
+ PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name);
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name);
+static CYTHON_INLINE void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *self);
+static int __Pyx_Coroutine_clear(PyObject *self);
+static __Pyx_PySendResult __Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval);
+static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value);
+static __Pyx_PySendResult __Pyx_Coroutine_Close(PyObject *self, PyObject **retval);
+static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args);
+#if CYTHON_USE_EXC_INFO_STACK
+#define __Pyx_Coroutine_SwapException(self)
+#define __Pyx_Coroutine_ResetAndClearException(self) __Pyx_Coroutine_ExceptionClear(&(self)->gi_exc_state)
+#else
+#define __Pyx_Coroutine_SwapException(self) {\
+ __Pyx_ExceptionSwap(&(self)->gi_exc_state.exc_type, &(self)->gi_exc_state.exc_value, &(self)->gi_exc_state.exc_traceback);\
+ __Pyx_Coroutine_ResetFrameBackpointer(&(self)->gi_exc_state);\
+ }
+#define __Pyx_Coroutine_ResetAndClearException(self) {\
+ __Pyx_ExceptionReset((self)->gi_exc_state.exc_type, (self)->gi_exc_state.exc_value, (self)->gi_exc_state.exc_traceback);\
+ (self)->gi_exc_state.exc_type = (self)->gi_exc_state.exc_value = (self)->gi_exc_state.exc_traceback = NULL;\
+ }
+#endif
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\
+ __Pyx_PyGen__FetchStopIterationValue(__pyx_tstate, pvalue)
+#else
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue)
+#endif
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue);
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state);
+static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen);
+static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen);
+static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen);
+static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure);
+#if __PYX_HAS_PY_AM_SEND == 2
+static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send);
+#endif
+static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg);
+
+/* Generator.proto (used by GeneratorYieldFrom) */
+#define __Pyx_Generator_USED
+#define __Pyx_Generator_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_GeneratorType)
+#define __Pyx_Generator_New(body, code, closure, name, qualname, module_name)\
+ __Pyx__Coroutine_New(__pyx_mstate_global->__pyx_GeneratorType, body, code, closure, name, qualname, module_name)
+static PyObject *__Pyx_Generator_Next(PyObject *self);
+static int __pyx_Generator_init(PyObject *module);
+static CYTHON_INLINE PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self);
+
+/* GeneratorYieldFrom.proto */
+static CYTHON_INLINE __Pyx_PySendResult __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source, PyObject **retval);
+
+/* append.proto */
+static CYTHON_INLINE int __Pyx_PyObject_Append(PyObject* L, PyObject* x);
+
+/* PyRange_Check.proto */
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyRange_Check)
+ #define PyRange_Check(obj) __Pyx_TypeCheck((obj), &PyRange_Type)
+#endif
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceAdd(op1, op2) : PyNumber_Add(op1, op2))
+#endif
+
+/* py_abs.proto */
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *num);
+#define __Pyx_PyNumber_Absolute(x)\
+ ((likely(PyLong_CheckExact(x))) ?\
+ (likely(__Pyx_PyLong_IsNonNeg(x)) ? __Pyx_NewRef(x) : __Pyx_PyLong_AbsNeg(x)) :\
+ PyNumber_Absolute(x))
+#else
+#define __Pyx_PyNumber_Absolute(x) PyNumber_Absolute(x)
+#endif
+
+/* PyFloatBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_PyFloat_TrueDivideObjC(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyFloat_TrueDivideObjC(op1, op2, floatval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceTrueDivide(op1, op2) : PyNumber_TrueDivide(op1, op2))
+#endif
+
+/* pybytes_as_double.proto (used by pynumber_float) */
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj);
+static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length);
+static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) {
+ char* as_c_string;
+ Py_ssize_t size;
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ as_c_string = PyBytes_AS_STRING(obj);
+ size = PyBytes_GET_SIZE(obj);
+#else
+ if (PyBytes_AsStringAndSize(obj, &as_c_string, &size) < 0) {
+ return (double)-1;
+ }
+#endif
+ return __Pyx__PyBytes_AsDouble(obj, as_c_string, size);
+}
+static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) {
+ char* as_c_string;
+ Py_ssize_t size;
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ as_c_string = PyByteArray_AS_STRING(obj);
+ size = PyByteArray_GET_SIZE(obj);
+#else
+ as_c_string = PyByteArray_AsString(obj);
+ if (as_c_string == NULL) {
+ return (double)-1;
+ }
+ size = PyByteArray_Size(obj);
+#endif
+ return __Pyx__PyBytes_AsDouble(obj, as_c_string, size);
+}
+
+/* pyunicode_as_double.proto (used by pynumber_float) */
+#if !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS
+static const char* __Pyx__PyUnicode_AsDouble_Copy(const void* data, const int kind, char* buffer, Py_ssize_t start, Py_ssize_t end) {
+ int last_was_punctuation;
+ Py_ssize_t i;
+ last_was_punctuation = 1;
+ for (i=start; i <= end; i++) {
+ Py_UCS4 chr = PyUnicode_READ(kind, data, i);
+ int is_punctuation = (chr == '_') | (chr == '.');
+ *buffer = (char)chr;
+ buffer += (chr != '_');
+ if (unlikely(chr > 127)) goto parse_failure;
+ if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure;
+ last_was_punctuation = is_punctuation;
+ }
+ if (unlikely(last_was_punctuation)) goto parse_failure;
+ *buffer = '\0';
+ return buffer;
+parse_failure:
+ return NULL;
+}
+static double __Pyx__PyUnicode_AsDouble_inf_nan(const void* data, int kind, Py_ssize_t start, Py_ssize_t length) {
+ int matches = 1;
+ Py_UCS4 chr;
+ Py_UCS4 sign = PyUnicode_READ(kind, data, start);
+ int is_signed = (sign == '-') | (sign == '+');
+ start += is_signed;
+ length -= is_signed;
+ switch (PyUnicode_READ(kind, data, start)) {
+ #ifdef Py_NAN
+ case 'n':
+ case 'N':
+ if (unlikely(length != 3)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+1);
+ matches &= (chr == 'a') | (chr == 'A');
+ chr = PyUnicode_READ(kind, data, start+2);
+ matches &= (chr == 'n') | (chr == 'N');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_NAN : Py_NAN;
+ #endif
+ case 'i':
+ case 'I':
+ if (unlikely(length < 3)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+1);
+ matches &= (chr == 'n') | (chr == 'N');
+ chr = PyUnicode_READ(kind, data, start+2);
+ matches &= (chr == 'f') | (chr == 'F');
+ if (likely(length == 3 && matches))
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ if (unlikely(length != 8)) goto parse_failure;
+ chr = PyUnicode_READ(kind, data, start+3);
+ matches &= (chr == 'i') | (chr == 'I');
+ chr = PyUnicode_READ(kind, data, start+4);
+ matches &= (chr == 'n') | (chr == 'N');
+ chr = PyUnicode_READ(kind, data, start+5);
+ matches &= (chr == 'i') | (chr == 'I');
+ chr = PyUnicode_READ(kind, data, start+6);
+ matches &= (chr == 't') | (chr == 'T');
+ chr = PyUnicode_READ(kind, data, start+7);
+ matches &= (chr == 'y') | (chr == 'Y');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ break;
+ default:
+ goto parse_failure;
+ }
+ return 0.0;
+parse_failure:
+ return -1.0;
+}
+static double __Pyx_PyUnicode_AsDouble_WithSpaces(PyObject *obj) {
+ double value;
+ const char *last;
+ char *end;
+ Py_ssize_t start, length = PyUnicode_GET_LENGTH(obj);
+ const int kind = PyUnicode_KIND(obj);
+ const void* data = PyUnicode_DATA(obj);
+ start = 0;
+ while (Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, start)))
+ start++;
+ while (start < length - 1 && Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, length - 1)))
+ length--;
+ length -= start;
+ if (unlikely(length <= 0)) goto fallback;
+ value = __Pyx__PyUnicode_AsDouble_inf_nan(data, kind, start, length);
+ if (unlikely(value == -1.0)) goto fallback;
+ if (value != 0.0) return value;
+ if (length < 40) {
+ char number[40];
+ last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+ if (unlikely(!last)) goto fallback;
+ value = PyOS_string_to_double(number, &end, NULL);
+ } else {
+ char *number = (char*) PyMem_Malloc((length + 1) * sizeof(char));
+ if (unlikely(!number)) goto fallback;
+ last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length);
+ if (unlikely(!last)) {
+ PyMem_Free(number);
+ goto fallback;
+ }
+ value = PyOS_string_to_double(number, &end, NULL);
+ PyMem_Free(number);
+ }
+ if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+ return value;
+ }
+fallback:
+ return __Pyx_SlowPyString_AsDouble(obj);
+}
+#endif
+static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) {
+#if !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(__Pyx_PyUnicode_READY(obj) == -1))
+ return (double)-1;
+ if (likely(PyUnicode_IS_ASCII(obj))) {
+ const char *s;
+ Py_ssize_t length;
+ s = PyUnicode_AsUTF8AndSize(obj, &length);
+ return __Pyx__PyBytes_AsDouble(obj, s, length);
+ }
+ return __Pyx_PyUnicode_AsDouble_WithSpaces(obj);
+#else
+ return __Pyx_SlowPyString_AsDouble(obj);
+#endif
+}
+
+/* pynumber_float.proto */
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj);
+#define __Pyx_PyNumber_Float(x) (PyFloat_CheckExact(x) ? __Pyx_NewRef(x) : __Pyx__PyNumber_Float(x))
+
+/* PyFloatBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static int __Pyx_PyFloat_BoolEqObjC(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyFloat_BoolEqObjC(op1, op2, floatval, inplace, zerodivision_check)\
+ __Pyx_PyObject_IsTrueAndDecref(PyObject_RichCompare(op1, op2, Py_EQ))
+ #endif
+
+/* pow2.proto */
+#define __Pyx_PyNumber_Power2(a, b) PyNumber_Power(a, b, Py_None)
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_SubtractCObj(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceSubtract(op1, op2) : PyNumber_Subtract(op1, op2))
+#endif
+
+/* PyValueError_Check.proto */
+#define __Pyx_PyExc_ValueError_Check(obj) __Pyx_TypeCheck(obj, PyExc_ValueError)
+
+/* PyObjectVectorCallKwBuilder.proto */
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+#if CYTHON_VECTORCALL
+#if PY_VERSION_HEX >= 0x03090000
+#define __Pyx_Object_Vectorcall_CallFromBuilder PyObject_Vectorcall
+#else
+#define __Pyx_Object_Vectorcall_CallFromBuilder _PyObject_Vectorcall
+#endif
+#define __Pyx_MakeVectorcallBuilderKwds(n) PyTuple_New(n)
+static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+#else
+#define __Pyx_Object_Vectorcall_CallFromBuilder __Pyx_PyObject_FastCallDict
+#define __Pyx_MakeVectorcallBuilderKwds(n) __Pyx_PyDict_NewPresized(n)
+#define __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n) PyDict_SetItem(builder, key, value)
+#define __Pyx_VectorcallBuilder_AddArgStr(key, value, builder, args, n) PyDict_SetItemString(builder, key, value)
+#endif
+
+/* PyObjectFastCallMethod.proto */
+#if CYTHON_VECTORCALL && PY_VERSION_HEX >= 0x03090000
+#define __Pyx_PyObject_FastCallMethod(name, args, nargsf) PyObject_VectorcallMethod(name, args, nargsf, NULL)
+#else
+static PyObject *__Pyx_PyObject_FastCallMethod(PyObject *name, PyObject *const *args, size_t nargsf);
+#endif
+
+/* RaiseClosureNameError.proto */
+static void __Pyx_RaiseClosureNameError(const char *varname);
+
+/* PyMethodNew.proto (used by CythonFunctionShared) */
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ);
+
+/* PyVectorcallFastCallDict.proto (used by CythonFunctionShared) */
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw);
+#endif
+
+/* CythonFunctionShared.proto (used by CythonFunction) */
+#define __Pyx_CyFunction_USED
+#define __Pyx_CYFUNCTION_STATICMETHOD 0x01
+#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02
+#define __Pyx_CYFUNCTION_CCLASS 0x04
+#define __Pyx_CYFUNCTION_COROUTINE 0x08
+#define __Pyx_CyFunction_GetClosure(f)\
+ (((__pyx_CyFunctionObject *) (f))->func_closure)
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_CyFunction_GetClassObj(f)\
+ (((__pyx_CyFunctionObject *) (f))->func_classobj)
+#else
+ #define __Pyx_CyFunction_GetClassObj(f)\
+ ((PyObject*) ((PyCMethodObject *) (f))->mm_class)
+#endif
+#define __Pyx_CyFunction_SetClassObj(f, classobj)\
+ __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj))
+#define __Pyx_CyFunction_Defaults(type, f)\
+ ((type *)(((__pyx_CyFunctionObject *) (f))->defaults))
+#define __Pyx_CyFunction_SetDefaultsGetter(f, g)\
+ ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g)
+typedef struct {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject_HEAD
+ PyObject *func;
+#elif PY_VERSION_HEX < 0x030900B1
+ PyCFunctionObject func;
+#else
+ PyCMethodObject func;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API && CYTHON_METH_FASTCALL
+ __pyx_vectorcallfunc func_vectorcall;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_weakreflist;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_dict;
+#endif
+ PyObject *func_name;
+ PyObject *func_qualname;
+ PyObject *func_doc;
+ PyObject *func_globals;
+ PyObject *func_code;
+ PyObject *func_closure;
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_classobj;
+#endif
+ PyObject *defaults;
+ int flags;
+ PyObject *defaults_tuple;
+ PyObject *defaults_kwdict;
+ PyObject *(*defaults_getter)(PyObject *);
+ PyObject *func_annotations;
+ PyObject *func_is_coroutine;
+} __pyx_CyFunctionObject;
+#undef __Pyx_CyOrPyCFunction_Check
+#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_mstate_global->__pyx_CyFunctionType)
+#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_mstate_global->__pyx_CyFunctionType, &PyCFunction_Type)
+#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_CyFunctionType)
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void));
+#undef __Pyx_IsSameCFunction
+#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc)
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml,
+ int flags, PyObject* qualname,
+ PyObject *closure,
+ PyObject *module, PyObject *globals,
+ PyObject* code);
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj);
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func,
+ PyTypeObject *defaults_type);
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m,
+ PyObject *tuple);
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m,
+ PyObject *dict);
+static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m,
+ PyObject *dict);
+static int __pyx_CyFunction_init(PyObject *module);
+#if CYTHON_METH_FASTCALL
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall)
+#else
+#define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall)
+#endif
+#endif
+
+/* CythonFunction.proto */
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml,
+ int flags, PyObject* qualname,
+ PyObject *closure,
+ PyObject *module, PyObject *globals,
+ PyObject* code);
+
+/* pyfrozenset_new.proto (used by PySetContains) */
+static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it);
+
+/* PySetContains.proto */
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq);
+
+/* PyTypeError_Check.proto */
+#define __Pyx_PyExc_TypeError_Check(obj) __Pyx_TypeCheck(obj, PyExc_TypeError)
+
+/* AllocateExtensionType.proto */
+static PyObject *__Pyx_AllocateExtensionType(PyTypeObject *t, int is_final);
+
+/* CheckTypeForFreelists.proto */
+#if CYTHON_USE_FREELISTS
+#if CYTHON_USE_TYPE_SPECS
+#define __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, expected_tp, expected_size) ((int) ((t) == (expected_tp)))
+#define __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS Py_TPFLAGS_IS_ABSTRACT
+#else
+#define __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, expected_tp, expected_size) ((int) ((t)->tp_basicsize == (expected_size)))
+#define __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)
+#endif
+#define __PYX_CHECK_TYPE_FOR_FREELISTS(t, expected_tp, expected_size)\
+ (__PYX_CHECK_FINAL_TYPE_FOR_FREELISTS((t), (expected_tp), (expected_size)) &\
+ (int) (!__Pyx_PyType_HasFeature((t), __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS)))
+#endif
+
+/* PyObjectCallMethod0.proto (used by PyType_Ready) */
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name);
+
+/* ValidateBasesTuple.proto (used by PyType_Ready) */
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases);
+#endif
+
+/* PyType_Ready.proto */
+CYTHON_UNUSED static int __Pyx_PyType_Ready(PyTypeObject *t);
+
+/* HasAttr.proto (used by ImportImpl) */
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_HasAttr(o, n) PyObject_HasAttrWithError(o, n)
+#else
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *, PyObject *);
+#endif
+
+/* ImportImpl.export */
+static PyObject *__Pyx__Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, PyObject *moddict, int level);
+
+/* Import.proto */
+static CYTHON_INLINE PyObject *__Pyx_Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, int level);
+
+/* ImportFrom.proto */
+static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name);
+
+/* ListPack.proto */
+static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...);
+
+/* CLineInTraceback.proto (used by AddTraceback) */
+#if CYTHON_CLINE_IN_TRACEBACK && CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);
+#else
+#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0)
+#endif
+
+/* CodeObjectCache.proto (used by AddTraceback) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+typedef PyObject __Pyx_CachedCodeObjectType;
+#else
+typedef PyCodeObject __Pyx_CachedCodeObjectType;
+#endif
+typedef struct {
+ __Pyx_CachedCodeObjectType* code_object;
+ int code_line;
+} __Pyx_CodeObjectCacheEntry;
+struct __Pyx_CodeObjectCache {
+ int count;
+ int max_count;
+ __Pyx_CodeObjectCacheEntry* entries;
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_int_type accessor_count;
+ #endif
+};
+static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line);
+static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line);
+static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object);
+
+/* AddTraceback.proto */
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename);
+
+/* RealImag.proto */
+#if CYTHON_CCOMPLEX
+ #ifdef __cplusplus
+ #define __Pyx_CREAL(z) ((z).real())
+ #define __Pyx_CIMAG(z) ((z).imag())
+ #else
+ #define __Pyx_CREAL(z) (__real__(z))
+ #define __Pyx_CIMAG(z) (__imag__(z))
+ #endif
+#else
+ #define __Pyx_CREAL(z) ((z).real)
+ #define __Pyx_CIMAG(z) ((z).imag)
+#endif
+#if defined(__cplusplus) && CYTHON_CCOMPLEX\
+ && (defined(_WIN32) || defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5 || __GNUC__ == 4 && __GNUC_MINOR__ >= 4 )) || __cplusplus >= 201103)
+ #define __Pyx_SET_CREAL(z,x) ((z).real(x))
+ #define __Pyx_SET_CIMAG(z,y) ((z).imag(y))
+#else
+ #define __Pyx_SET_CREAL(z,x) __Pyx_CREAL(z) = (x)
+ #define __Pyx_SET_CIMAG(z,y) __Pyx_CIMAG(z) = (y)
+#endif
+
+/* Arithmetic.proto */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #define __Pyx_c_eq_double(a, b) ((a)==(b))
+ #define __Pyx_c_sum_double(a, b) ((a)+(b))
+ #define __Pyx_c_diff_double(a, b) ((a)-(b))
+ #define __Pyx_c_prod_double(a, b) ((a)*(b))
+ #define __Pyx_c_quot_double(a, b) ((a)/(b))
+ #define __Pyx_c_neg_double(a) (-(a))
+ #ifdef __cplusplus
+ #define __Pyx_c_is_zero_double(z) ((z)==(double)0)
+ #define __Pyx_c_conj_double(z) (::std::conj(z))
+ #if 1
+ #define __Pyx_c_abs_double(z) (::std::abs(z))
+ #define __Pyx_c_pow_double(a, b) (::std::pow(a, b))
+ #endif
+ #else
+ #define __Pyx_c_is_zero_double(z) ((z)==0)
+ #define __Pyx_c_conj_double(z) (conj(z))
+ #if 1
+ #define __Pyx_c_abs_double(z) (cabs(z))
+ #define __Pyx_c_pow_double(a, b) (cpow(a, b))
+ #endif
+ #endif
+#else
+ static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex);
+ static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex);
+ #if 1
+ static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ #endif
+#endif
+
+/* FromPy.proto */
+static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject*);
+
+/* ToPy.proto */
+#define __pyx_PyComplex_FromComplex(z)\
+ PyComplex_FromDoubles((double)__Pyx_CREAL(z),\
+ (double)__Pyx_CIMAG(z))
+
+/* GCCDiagnostics.proto */
+#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define __Pyx_HAS_GCC_DIAGNOSTIC
+#endif
+
+/* CIntToPy.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value);
+
+/* FormatTypeName.proto */
+#if CYTHON_COMPILING_IN_LIMITED_API
+typedef PyObject *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%U"
+#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj)
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_PyType_GetFullyQualifiedName PyType_GetFullyQualifiedName
+#else
+static __Pyx_TypeName __Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp);
+#endif
+#else // !LIMITED_API
+typedef const char *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%.200s"
+#define __Pyx_PyType_GetFullyQualifiedName(tp) ((tp)->tp_name)
+#define __Pyx_DECREF_TypeName(obj)
+#endif
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *);
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *);
+
+/* FastTypeChecks.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2)
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b);
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b);
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type);
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2);
+#else
+#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2))
+#define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type)
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2) {
+ return PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2);
+}
+#endif
+#define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_CurrentExceptionType(), err1, err2)
+#define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception)
+#ifdef PyExceptionInstance_Check
+ #define __Pyx_PyBaseException_Check(obj) PyExceptionInstance_Check(obj)
+#else
+ #define __Pyx_PyBaseException_Check(obj) __Pyx_TypeCheck(obj, PyExc_BaseException)
+#endif
+
+/* GetRuntimeVersion.proto */
+#if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+static unsigned long __Pyx_cached_runtime_version = 0;
+static void __Pyx_init_runtime_version(void);
+#else
+#define __Pyx_init_runtime_version()
+#endif
+static unsigned long __Pyx_get_runtime_version(void);
+
+/* CheckBinaryVersion.proto */
+static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer);
+
+/* DecompressString.proto */
+static PyObject *__Pyx_DecompressString(const char *s, Py_ssize_t length, int algo);
+
+/* MultiPhaseInitModuleState.proto */
+#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE
+static PyObject *__Pyx_State_FindModule(void*);
+static int __Pyx_State_AddModule(PyObject* module, void*);
+static int __Pyx_State_RemoveModule(void*);
+#elif CYTHON_USE_MODULE_STATE
+#define __Pyx_State_FindModule PyState_FindModule
+#define __Pyx_State_AddModule PyState_AddModule
+#define __Pyx_State_RemoveModule PyState_RemoveModule
+#endif
+
+/* #### Code section: module_declarations ### */
+/* CythonABIVersion.proto */
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #if CYTHON_METH_FASTCALL
+ #define __PYX_FASTCALL_ABI_SUFFIX "_fastcall"
+ #else
+ #define __PYX_FASTCALL_ABI_SUFFIX
+ #endif
+ #define __PYX_LIMITED_ABI_SUFFIX "limited" __PYX_FASTCALL_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX
+#else
+ #define __PYX_LIMITED_ABI_SUFFIX
+#endif
+#if __PYX_HAS_PY_AM_SEND == 1
+ #define __PYX_AM_SEND_ABI_SUFFIX
+#elif __PYX_HAS_PY_AM_SEND == 2
+ #define __PYX_AM_SEND_ABI_SUFFIX "amsendbackport"
+#else
+ #define __PYX_AM_SEND_ABI_SUFFIX "noamsend"
+#endif
+#ifndef __PYX_MONITORING_ABI_SUFFIX
+ #define __PYX_MONITORING_ABI_SUFFIX
+#endif
+#if CYTHON_USE_TP_FINALIZE
+ #define __PYX_TP_FINALIZE_ABI_SUFFIX
+#else
+ #define __PYX_TP_FINALIZE_ABI_SUFFIX "nofinalize"
+#endif
+#if CYTHON_USE_FREELISTS || !defined(__Pyx_AsyncGen_USED)
+ #define __PYX_FREELISTS_ABI_SUFFIX
+#else
+ #define __PYX_FREELISTS_ABI_SUFFIX "nofreelists"
+#endif
+#define CYTHON_ABI __PYX_ABI_VERSION __PYX_LIMITED_ABI_SUFFIX __PYX_MONITORING_ABI_SUFFIX __PYX_TP_FINALIZE_ABI_SUFFIX __PYX_FREELISTS_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX
+#define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI
+#define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "."
+
+
+/* Module declarations from "cython" */
+
+/* Module declarations from "fontTools.misc.bezierTools" */
+static CYTHON_INLINE double __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_t_double_complex, __pyx_t_double_complex); /*proto*/
+static CYTHON_INLINE double __pyx_f_9fontTools_4misc_11bezierTools__intSecAtan(double); /*proto*/
+static CYTHON_INLINE PyObject *__pyx_f_9fontTools_4misc_11bezierTools_calcCubicParametersC(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/
+static CYTHON_INLINE PyObject *__pyx_f_9fontTools_4misc_11bezierTools_calcCubicPointsC(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/
+/* #### Code section: typeinfo ### */
+/* #### Code section: before_global_var ### */
+#define __Pyx_MODULE_NAME "fontTools.misc.bezierTools"
+extern int __pyx_module_is_main_fontTools__misc__bezierTools;
+int __pyx_module_is_main_fontTools__misc__bezierTools = 0;
+
+/* Implementation of "fontTools.misc.bezierTools" */
+/* #### Code section: global_var ### */
+static PyObject *__pyx_builtin_round;
+static PyObject *__pyx_builtin_print;
+/* #### Code section: string_decls ### */
+static const char __pyx_k_fontTools_misc_bezierTools_py_to[] = "fontTools.misc.bezierTools.py -- tools for working with Bezier path segments.\n";
+/* #### Code section: decls ### */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_calcCubicArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_tolerance); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_2_split_cubic_into_two(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_p0, PyObject *__pyx_v_p1, PyObject *__pyx_v_p2, PyObject *__pyx_v_p3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_4_calcCubicArcLengthCRecurse(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_mult, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_6calcCubicArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_tolerance); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_8calcQuadraticArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_10calcQuadraticArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12approximateQuadraticArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_14approximateQuadraticArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_16calcQuadraticBounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_18approximateCubicArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_20approximateCubicArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_22calcCubicBounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_24splitLine(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_14splitQuadratic_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_26splitQuadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_10splitCubic_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_28splitCubic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_30splitQuadraticAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_32splitCubicAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_34splitCubicAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_37splitCubicIntoTwoAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_39_splitQuadraticAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_41_splitCubicAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_43_splitCubicAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_96__defaults__(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_46solveQuadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_sqrt); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_48solveCubic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_50calcQuadraticParameters(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_52calcCubicParameters(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_54calcQuadraticPoints(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_56calcCubicPoints(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_58linePointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_60quadraticPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_62cubicPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_64cubicPointAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_66segmentPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_seg, PyObject *__pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_68_line_t_of_pt(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_s, PyObject *__pyx_v_e, PyObject *__pyx_v_pt); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_70_both_points_are_on_same_side_of_origin(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_origin); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_72lineLineIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_s1, PyObject *__pyx_v_e1, PyObject *__pyx_v_s2, PyObject *__pyx_v_e2); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_74_alignment_transformation(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segment); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_76_curve_line_intersections_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, PyObject *__pyx_v_line); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_78curveLineIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, PyObject *__pyx_v_line); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_80_curve_bounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_c); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_82_split_segment_at_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_c, PyObject *__pyx_v_t); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_r); /* proto */
+static PyObject *__pyx_lambda_funcdef_lambda3(PyObject *__pyx_self, PyObject *__pyx_v_ts); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_84_curve_curve_intersections_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve1, PyObject *__pyx_v_curve2, PyObject *__pyx_v_precision, PyObject *__pyx_v_range1, PyObject *__pyx_v_range2); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12_is_linelike_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_86_is_linelike(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segment); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_88curveCurveIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve1, PyObject *__pyx_v_curve2); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_90segmentSegmentIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_seg1, PyObject *__pyx_v_seg2); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12_segmentrepr_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_92_segmentrepr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_obj); /* proto */
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_94printSegments(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segments); /* proto */
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_defaults(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+/* #### Code section: late_includes ### */
+/* #### Code section: module_state ### */
+/* SmallCodeConfig */
+#ifndef CYTHON_SMALL_CODE
+#if defined(__clang__)
+ #define CYTHON_SMALL_CODE
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define CYTHON_SMALL_CODE __attribute__((cold))
+#else
+ #define CYTHON_SMALL_CODE
+#endif
+#endif
+
+typedef struct {
+ PyObject *__pyx_d;
+ PyObject *__pyx_b;
+ PyObject *__pyx_cython_runtime;
+ PyObject *__pyx_empty_tuple;
+ PyObject *__pyx_empty_bytes;
+ PyObject *__pyx_empty_unicode;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr;
+ PyObject *__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_items;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_pop;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_values;
+ PyObject *__pyx_slice[2];
+ PyObject *__pyx_tuple[4];
+ PyObject *__pyx_codeobj_tab[54];
+ PyObject *__pyx_string_tab[387];
+ PyObject *__pyx_number_tab[21];
+/* #### Code section: module_state_contents ### */
+/* CommonTypesMetaclass.module_state_decls */
+PyTypeObject *__pyx_CommonTypesMetaclassType;
+
+/* IterNextPlain.module_state_decls */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+PyObject *__Pyx_GetBuiltinNext_LimitedAPI_cache;
+#endif
+
+/* Generator.module_state_decls */
+PyTypeObject *__pyx_GeneratorType;
+
+/* CachedMethodType.module_state_decls */
+#if CYTHON_COMPILING_IN_LIMITED_API
+PyObject *__Pyx_CachedMethodType;
+#endif
+
+/* CythonFunctionShared.module_state_decls */
+PyTypeObject *__pyx_CyFunctionType;
+
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr[8];
+int __pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr;
+#endif
+/* CodeObjectCache.module_state_decls */
+struct __Pyx_CodeObjectCache __pyx_code_cache;
+
+/* #### Code section: module_state_end ### */
+} __pyx_mstatetype;
+
+#if CYTHON_USE_MODULE_STATE
+#ifdef __cplusplus
+namespace {
+extern struct PyModuleDef __pyx_moduledef;
+} /* anonymous namespace */
+#else
+static struct PyModuleDef __pyx_moduledef;
+#endif
+
+#define __pyx_mstate_global (__Pyx_PyModule_GetState(__Pyx_State_FindModule(&__pyx_moduledef)))
+
+#define __pyx_m (__Pyx_State_FindModule(&__pyx_moduledef))
+#else
+static __pyx_mstatetype __pyx_mstate_global_static =
+#ifdef __cplusplus
+ {};
+#else
+ {0};
+#endif
+static __pyx_mstatetype * const __pyx_mstate_global = &__pyx_mstate_global_static;
+#endif
+/* #### Code section: constant_name_defines ### */
+#define __pyx_kp_u_ __pyx_string_tab[0]
+#define __pyx_kp_u_Approximates_the_arc_length_for __pyx_string_tab[1]
+#define __pyx_kp_u_Calculates_the_arc_length_for_a __pyx_string_tab[2]
+#define __pyx_kp_u_Calculates_the_bounding_rectangl __pyx_string_tab[3]
+#define __pyx_kp_u_Calculates_the_bounding_rectangl_2 __pyx_string_tab[4]
+#define __pyx_kp_u_Couldn_t_work_out_which_intersec __pyx_string_tab[5]
+#define __pyx_kp_u_Finds_intersections_between_a_cu __pyx_string_tab[6]
+#define __pyx_kp_u_Finds_intersections_between_a_cu_2 __pyx_string_tab[7]
+#define __pyx_kp_u_Finds_intersections_between_two __pyx_string_tab[8]
+#define __pyx_kp_u_Finds_intersections_between_two_2 __pyx_string_tab[9]
+#define __pyx_kp_u_Lib_fontTools_misc_bezierTools_p __pyx_string_tab[10]
+#define __pyx_kp_u_Solve_a_cubic_equation_Solves_a __pyx_string_tab[11]
+#define __pyx_kp_u_Split_a_cubic_Bezier_curve_at_a __pyx_string_tab[12]
+#define __pyx_kp_u_Split_a_cubic_Bezier_curve_at_on __pyx_string_tab[13]
+#define __pyx_kp_u_Split_a_line_at_a_given_coordina __pyx_string_tab[14]
+#define __pyx_kp_u_Split_a_quadratic_Bezier_curve_a __pyx_string_tab[15]
+#define __pyx_kp_u_Split_a_quadratic_Bezier_curve_a_2 __pyx_string_tab[16]
+#define __pyx_kp_u_Unknown_curve_degree __pyx_string_tab[17]
+#define __pyx_kp_u__2 __pyx_string_tab[18]
+#define __pyx_kp_u__3 __pyx_string_tab[19]
+#define __pyx_kp_u_approximateCubicArcLength_line_3 __pyx_string_tab[20]
+#define __pyx_kp_u_calcCubicBounds_line_412 __pyx_string_tab[21]
+#define __pyx_kp_u_calcQuadraticArcLength_line_151 __pyx_string_tab[22]
+#define __pyx_kp_u_calcQuadraticBounds_line_298 __pyx_string_tab[23]
+#define __pyx_kp_u_curveCurveIntersections_line_138 __pyx_string_tab[24]
+#define __pyx_kp_u_curveLineIntersections_line_1255 __pyx_string_tab[25]
+#define __pyx_kp_u_disable __pyx_string_tab[26]
+#define __pyx_kp_u_enable __pyx_string_tab[27]
+#define __pyx_kp_u_g __pyx_string_tab[28]
+#define __pyx_kp_u_gc __pyx_string_tab[29]
+#define __pyx_kp_u_isenabled __pyx_string_tab[30]
+#define __pyx_kp_u_lineLineIntersections_line_1154 __pyx_string_tab[31]
+#define __pyx_kp_u_s_2 __pyx_string_tab[32]
+#define __pyx_kp_u_segmentSegmentIntersections_line __pyx_string_tab[33]
+#define __pyx_kp_u_segmentrepr_1_2_3_2_3_4_0_1_2 __pyx_string_tab[34]
+#define __pyx_kp_u_segmentrepr_line_1475 __pyx_string_tab[35]
+#define __pyx_kp_u_solveCubic_line_848 __pyx_string_tab[36]
+#define __pyx_kp_u_splitCubicAtT_line_613 __pyx_string_tab[37]
+#define __pyx_kp_u_splitCubic_line_552 __pyx_string_tab[38]
+#define __pyx_kp_u_splitLine_line_450 __pyx_string_tab[39]
+#define __pyx_kp_u_splitQuadraticAtT_line_589 __pyx_string_tab[40]
+#define __pyx_kp_u_splitQuadratic_line_507 __pyx_string_tab[41]
+#define __pyx_n_u_1_t __pyx_string_tab[42]
+#define __pyx_n_u_1_t_2 __pyx_string_tab[43]
+#define __pyx_n_u_2_t_1_t __pyx_string_tab[44]
+#define __pyx_n_u_COMPILED __pyx_string_tab[45]
+#define __pyx_n_u_DD __pyx_string_tab[46]
+#define __pyx_n_u_EPSILON __pyx_string_tab[47]
+#define __pyx_n_u_Identity __pyx_string_tab[48]
+#define __pyx_n_u_Intersection __pyx_string_tab[49]
+#define __pyx_n_u_Len __pyx_string_tab[50]
+#define __pyx_n_u_Pyx_PyDict_NextRef __pyx_string_tab[51]
+#define __pyx_n_u_Q __pyx_string_tab[52]
+#define __pyx_n_u_Q3 __pyx_string_tab[53]
+#define __pyx_n_u_R __pyx_string_tab[54]
+#define __pyx_n_u_R2 __pyx_string_tab[55]
+#define __pyx_n_u_R2_Q3 __pyx_string_tab[56]
+#define __pyx_n_u__6 __pyx_string_tab[57]
+#define __pyx_n_u_a __pyx_string_tab[58]
+#define __pyx_n_u_a1 __pyx_string_tab[59]
+#define __pyx_n_u_a1_3 __pyx_string_tab[60]
+#define __pyx_n_u_a1x __pyx_string_tab[61]
+#define __pyx_n_u_a1y __pyx_string_tab[62]
+#define __pyx_n_u_a2 __pyx_string_tab[63]
+#define __pyx_n_u_a3 __pyx_string_tab[64]
+#define __pyx_n_u_acos __pyx_string_tab[65]
+#define __pyx_n_u_aligned_curve __pyx_string_tab[66]
+#define __pyx_n_u_alignment_transformation __pyx_string_tab[67]
+#define __pyx_n_u_all __pyx_string_tab[68]
+#define __pyx_n_u_angle __pyx_string_tab[69]
+#define __pyx_n_u_append __pyx_string_tab[70]
+#define __pyx_n_u_approximateCubicArcLength __pyx_string_tab[71]
+#define __pyx_n_u_approximateCubicArcLengthC __pyx_string_tab[72]
+#define __pyx_n_u_approximateQuadraticArcLength __pyx_string_tab[73]
+#define __pyx_n_u_approximateQuadraticArcLengthC __pyx_string_tab[74]
+#define __pyx_n_u_arch __pyx_string_tab[75]
+#define __pyx_n_u_asinh __pyx_string_tab[76]
+#define __pyx_n_u_asyncio_coroutines __pyx_string_tab[77]
+#define __pyx_n_u_atan2 __pyx_string_tab[78]
+#define __pyx_n_u_ax __pyx_string_tab[79]
+#define __pyx_n_u_ax2 __pyx_string_tab[80]
+#define __pyx_n_u_ax3 __pyx_string_tab[81]
+#define __pyx_n_u_ay __pyx_string_tab[82]
+#define __pyx_n_u_ay2 __pyx_string_tab[83]
+#define __pyx_n_u_ay3 __pyx_string_tab[84]
+#define __pyx_n_u_b __pyx_string_tab[85]
+#define __pyx_n_u_b1 __pyx_string_tab[86]
+#define __pyx_n_u_b1x __pyx_string_tab[87]
+#define __pyx_n_u_b1y __pyx_string_tab[88]
+#define __pyx_n_u_both_points_are_on_same_side_of __pyx_string_tab[89]
+#define __pyx_n_u_bounds1 __pyx_string_tab[90]
+#define __pyx_n_u_bounds2 __pyx_string_tab[91]
+#define __pyx_n_u_box __pyx_string_tab[92]
+#define __pyx_n_u_bx __pyx_string_tab[93]
+#define __pyx_n_u_bx2 __pyx_string_tab[94]
+#define __pyx_n_u_by __pyx_string_tab[95]
+#define __pyx_n_u_by2 __pyx_string_tab[96]
+#define __pyx_n_u_c __pyx_string_tab[97]
+#define __pyx_n_u_c1 __pyx_string_tab[98]
+#define __pyx_n_u_c11 __pyx_string_tab[99]
+#define __pyx_n_u_c11_range __pyx_string_tab[100]
+#define __pyx_n_u_c12 __pyx_string_tab[101]
+#define __pyx_n_u_c12_range __pyx_string_tab[102]
+#define __pyx_n_u_c1x __pyx_string_tab[103]
+#define __pyx_n_u_c1y __pyx_string_tab[104]
+#define __pyx_n_u_c21 __pyx_string_tab[105]
+#define __pyx_n_u_c21_range __pyx_string_tab[106]
+#define __pyx_n_u_c22 __pyx_string_tab[107]
+#define __pyx_n_u_c22_range __pyx_string_tab[108]
+#define __pyx_n_u_calcBounds __pyx_string_tab[109]
+#define __pyx_n_u_calcCubicArcLength __pyx_string_tab[110]
+#define __pyx_n_u_calcCubicArcLengthC __pyx_string_tab[111]
+#define __pyx_n_u_calcCubicArcLengthCRecurse __pyx_string_tab[112]
+#define __pyx_n_u_calcCubicBounds __pyx_string_tab[113]
+#define __pyx_n_u_calcCubicParameters __pyx_string_tab[114]
+#define __pyx_n_u_calcCubicPoints __pyx_string_tab[115]
+#define __pyx_n_u_calcQuadraticArcLength __pyx_string_tab[116]
+#define __pyx_n_u_calcQuadraticArcLengthC __pyx_string_tab[117]
+#define __pyx_n_u_calcQuadraticBounds __pyx_string_tab[118]
+#define __pyx_n_u_calcQuadraticParameters __pyx_string_tab[119]
+#define __pyx_n_u_calcQuadraticPoints __pyx_string_tab[120]
+#define __pyx_n_u_class_getitem __pyx_string_tab[121]
+#define __pyx_n_u_cline_in_traceback __pyx_string_tab[122]
+#define __pyx_n_u_close __pyx_string_tab[123]
+#define __pyx_n_u_collections __pyx_string_tab[124]
+#define __pyx_n_u_cos __pyx_string_tab[125]
+#define __pyx_n_u_cubicPointAtT __pyx_string_tab[126]
+#define __pyx_n_u_cubicPointAtTC __pyx_string_tab[127]
+#define __pyx_n_u_curve __pyx_string_tab[128]
+#define __pyx_n_u_curve1 __pyx_string_tab[129]
+#define __pyx_n_u_curve2 __pyx_string_tab[130]
+#define __pyx_n_u_curveCurveIntersections __pyx_string_tab[131]
+#define __pyx_n_u_curveLineIntersections __pyx_string_tab[132]
+#define __pyx_n_u_curve_bounds __pyx_string_tab[133]
+#define __pyx_n_u_curve_curve_intersections_t __pyx_string_tab[134]
+#define __pyx_n_u_curve_curve_intersections_t_loc __pyx_string_tab[135]
+#define __pyx_n_u_curve_curve_intersections_t_loc_2 __pyx_string_tab[136]
+#define __pyx_n_u_curve_line_intersections_t __pyx_string_tab[137]
+#define __pyx_n_u_curve_line_intersections_t_loca __pyx_string_tab[138]
+#define __pyx_n_u_cx __pyx_string_tab[139]
+#define __pyx_n_u_cy __pyx_string_tab[140]
+#define __pyx_n_u_d __pyx_string_tab[141]
+#define __pyx_n_u_d0 __pyx_string_tab[142]
+#define __pyx_n_u_d1 __pyx_string_tab[143]
+#define __pyx_n_u_d1x __pyx_string_tab[144]
+#define __pyx_n_u_d1y __pyx_string_tab[145]
+#define __pyx_n_u_delta __pyx_string_tab[146]
+#define __pyx_n_u_delta_2 __pyx_string_tab[147]
+#define __pyx_n_u_delta_3 __pyx_string_tab[148]
+#define __pyx_n_u_deriv3 __pyx_string_tab[149]
+#define __pyx_n_u_doctest __pyx_string_tab[150]
+#define __pyx_n_u_dx __pyx_string_tab[151]
+#define __pyx_n_u_dy __pyx_string_tab[152]
+#define __pyx_n_u_e __pyx_string_tab[153]
+#define __pyx_n_u_e1 __pyx_string_tab[154]
+#define __pyx_n_u_e1x __pyx_string_tab[155]
+#define __pyx_n_u_e1y __pyx_string_tab[156]
+#define __pyx_n_u_e2 __pyx_string_tab[157]
+#define __pyx_n_u_e2x __pyx_string_tab[158]
+#define __pyx_n_u_e2y __pyx_string_tab[159]
+#define __pyx_n_u_end __pyx_string_tab[160]
+#define __pyx_n_u_epsilon __pyx_string_tab[161]
+#define __pyx_n_u_epsilonDigits __pyx_string_tab[162]
+#define __pyx_n_u_ex __pyx_string_tab[163]
+#define __pyx_n_u_exit __pyx_string_tab[164]
+#define __pyx_n_u_ey __pyx_string_tab[165]
+#define __pyx_n_u_failed __pyx_string_tab[166]
+#define __pyx_n_u_fontTools_misc_arrayTools __pyx_string_tab[167]
+#define __pyx_n_u_fontTools_misc_bezierTools __pyx_string_tab[168]
+#define __pyx_n_u_fontTools_misc_transform __pyx_string_tab[169]
+#define __pyx_n_u_found __pyx_string_tab[170]
+#define __pyx_n_u_func __pyx_string_tab[171]
+#define __pyx_n_u_genexpr __pyx_string_tab[172]
+#define __pyx_n_u_hits __pyx_string_tab[173]
+#define __pyx_n_u_i __pyx_string_tab[174]
+#define __pyx_n_u_insert __pyx_string_tab[175]
+#define __pyx_n_u_intersection_ts __pyx_string_tab[176]
+#define __pyx_n_u_intersections __pyx_string_tab[177]
+#define __pyx_n_u_intersects __pyx_string_tab[178]
+#define __pyx_n_u_isHorizontal __pyx_string_tab[179]
+#define __pyx_n_u_is_coroutine __pyx_string_tab[180]
+#define __pyx_n_u_is_linelike __pyx_string_tab[181]
+#define __pyx_n_u_is_linelike_locals_genexpr __pyx_string_tab[182]
+#define __pyx_n_u_isclose __pyx_string_tab[183]
+#define __pyx_n_u_it __pyx_string_tab[184]
+#define __pyx_n_u_items __pyx_string_tab[185]
+#define __pyx_n_u_key __pyx_string_tab[186]
+#define __pyx_n_u_lambda __pyx_string_tab[187]
+#define __pyx_n_u_line __pyx_string_tab[188]
+#define __pyx_n_u_line1 __pyx_string_tab[189]
+#define __pyx_n_u_line2 __pyx_string_tab[190]
+#define __pyx_n_u_lineLineIntersections __pyx_string_tab[191]
+#define __pyx_n_u_linePointAtT __pyx_string_tab[192]
+#define __pyx_n_u_line_t __pyx_string_tab[193]
+#define __pyx_n_u_line_t_of_pt __pyx_string_tab[194]
+#define __pyx_n_u_main __pyx_string_tab[195]
+#define __pyx_n_u_math __pyx_string_tab[196]
+#define __pyx_n_u_maybeline __pyx_string_tab[197]
+#define __pyx_n_u_mid __pyx_string_tab[198]
+#define __pyx_n_u_midPt __pyx_string_tab[199]
+#define __pyx_n_u_midpoint __pyx_string_tab[200]
+#define __pyx_n_u_module __pyx_string_tab[201]
+#define __pyx_n_u_mult __pyx_string_tab[202]
+#define __pyx_n_u_n __pyx_string_tab[203]
+#define __pyx_n_u_name __pyx_string_tab[204]
+#define __pyx_n_u_namedtuple __pyx_string_tab[205]
+#define __pyx_n_u_next __pyx_string_tab[206]
+#define __pyx_n_u_obj __pyx_string_tab[207]
+#define __pyx_n_u_off1 __pyx_string_tab[208]
+#define __pyx_n_u_off2 __pyx_string_tab[209]
+#define __pyx_n_u_one __pyx_string_tab[210]
+#define __pyx_n_u_origDist __pyx_string_tab[211]
+#define __pyx_n_u_origin __pyx_string_tab[212]
+#define __pyx_n_u_p __pyx_string_tab[213]
+#define __pyx_n_u_p0 __pyx_string_tab[214]
+#define __pyx_n_u_p1 __pyx_string_tab[215]
+#define __pyx_n_u_p2 __pyx_string_tab[216]
+#define __pyx_n_u_p3 __pyx_string_tab[217]
+#define __pyx_n_u_pi __pyx_string_tab[218]
+#define __pyx_n_u_pointAtT __pyx_string_tab[219]
+#define __pyx_n_u_pointFinder __pyx_string_tab[220]
+#define __pyx_n_u_points __pyx_string_tab[221]
+#define __pyx_n_u_pop __pyx_string_tab[222]
+#define __pyx_n_u_precision __pyx_string_tab[223]
+#define __pyx_n_u_print __pyx_string_tab[224]
+#define __pyx_n_u_printSegments __pyx_string_tab[225]
+#define __pyx_n_u_pt __pyx_string_tab[226]
+#define __pyx_n_u_pt1 __pyx_string_tab[227]
+#define __pyx_n_u_pt1x __pyx_string_tab[228]
+#define __pyx_n_u_pt1y __pyx_string_tab[229]
+#define __pyx_n_u_pt2 __pyx_string_tab[230]
+#define __pyx_n_u_pt2x __pyx_string_tab[231]
+#define __pyx_n_u_pt2y __pyx_string_tab[232]
+#define __pyx_n_u_pt3 __pyx_string_tab[233]
+#define __pyx_n_u_pt4 __pyx_string_tab[234]
+#define __pyx_n_u_px __pyx_string_tab[235]
+#define __pyx_n_u_py __pyx_string_tab[236]
+#define __pyx_n_u_quadraticPointAtT __pyx_string_tab[237]
+#define __pyx_n_u_qualname __pyx_string_tab[238]
+#define __pyx_n_u_r __pyx_string_tab[239]
+#define __pyx_n_u_rDD __pyx_string_tab[240]
+#define __pyx_n_u_rQ2 __pyx_string_tab[241]
+#define __pyx_n_u_range1 __pyx_string_tab[242]
+#define __pyx_n_u_range2 __pyx_string_tab[243]
+#define __pyx_n_u_rectArea __pyx_string_tab[244]
+#define __pyx_n_u_roots __pyx_string_tab[245]
+#define __pyx_n_u_rotate __pyx_string_tab[246]
+#define __pyx_n_u_round __pyx_string_tab[247]
+#define __pyx_n_u_s __pyx_string_tab[248]
+#define __pyx_n_u_s1 __pyx_string_tab[249]
+#define __pyx_n_u_s1x __pyx_string_tab[250]
+#define __pyx_n_u_s1y __pyx_string_tab[251]
+#define __pyx_n_u_s2 __pyx_string_tab[252]
+#define __pyx_n_u_s2x __pyx_string_tab[253]
+#define __pyx_n_u_s2y __pyx_string_tab[254]
+#define __pyx_n_u_scale __pyx_string_tab[255]
+#define __pyx_n_u_sectRect __pyx_string_tab[256]
+#define __pyx_n_u_seen __pyx_string_tab[257]
+#define __pyx_n_u_seg __pyx_string_tab[258]
+#define __pyx_n_u_seg1 __pyx_string_tab[259]
+#define __pyx_n_u_seg2 __pyx_string_tab[260]
+#define __pyx_n_u_segment __pyx_string_tab[261]
+#define __pyx_n_u_segmentPointAtT __pyx_string_tab[262]
+#define __pyx_n_u_segmentSegmentIntersections __pyx_string_tab[263]
+#define __pyx_n_u_segmentrepr __pyx_string_tab[264]
+#define __pyx_n_u_segmentrepr_locals_genexpr __pyx_string_tab[265]
+#define __pyx_n_u_segments __pyx_string_tab[266]
+#define __pyx_n_u_send __pyx_string_tab[267]
+#define __pyx_n_u_set_name __pyx_string_tab[268]
+#define __pyx_n_u_setdefault __pyx_string_tab[269]
+#define __pyx_n_u_slope12 __pyx_string_tab[270]
+#define __pyx_n_u_slope34 __pyx_string_tab[271]
+#define __pyx_n_u_solutions __pyx_string_tab[272]
+#define __pyx_n_u_solveCubic __pyx_string_tab[273]
+#define __pyx_n_u_solveQuadratic __pyx_string_tab[274]
+#define __pyx_n_u_split __pyx_string_tab[275]
+#define __pyx_n_u_splitCubic __pyx_string_tab[276]
+#define __pyx_n_u_splitCubicAtT __pyx_string_tab[277]
+#define __pyx_n_u_splitCubicAtTC __pyx_string_tab[278]
+#define __pyx_n_u_splitCubicAtTC_2 __pyx_string_tab[279]
+#define __pyx_n_u_splitCubicAtT_2 __pyx_string_tab[280]
+#define __pyx_n_u_splitCubicIntoTwoAtTC __pyx_string_tab[281]
+#define __pyx_n_u_splitCubic_locals_genexpr __pyx_string_tab[282]
+#define __pyx_n_u_splitLine __pyx_string_tab[283]
+#define __pyx_n_u_splitQuadratic __pyx_string_tab[284]
+#define __pyx_n_u_splitQuadraticAtT __pyx_string_tab[285]
+#define __pyx_n_u_splitQuadraticAtT_2 __pyx_string_tab[286]
+#define __pyx_n_u_splitQuadratic_locals_genexpr __pyx_string_tab[287]
+#define __pyx_n_u_split_cubic_into_two __pyx_string_tab[288]
+#define __pyx_n_u_split_segment_at_t __pyx_string_tab[289]
+#define __pyx_n_u_sqrt __pyx_string_tab[290]
+#define __pyx_n_u_start __pyx_string_tab[291]
+#define __pyx_n_u_swapped __pyx_string_tab[292]
+#define __pyx_n_u_sx __pyx_string_tab[293]
+#define __pyx_n_u_sy __pyx_string_tab[294]
+#define __pyx_n_u_sys __pyx_string_tab[295]
+#define __pyx_n_u_t __pyx_string_tab[296]
+#define __pyx_n_u_t1 __pyx_string_tab[297]
+#define __pyx_n_u_t1_2 __pyx_string_tab[298]
+#define __pyx_n_u_t1_3 __pyx_string_tab[299]
+#define __pyx_n_u_t2 __pyx_string_tab[300]
+#define __pyx_n_u_test __pyx_string_tab[301]
+#define __pyx_n_u_testmod __pyx_string_tab[302]
+#define __pyx_n_u_theta __pyx_string_tab[303]
+#define __pyx_n_u_throw __pyx_string_tab[304]
+#define __pyx_n_u_tolerance __pyx_string_tab[305]
+#define __pyx_n_u_transformPoints __pyx_string_tab[306]
+#define __pyx_n_u_translate __pyx_string_tab[307]
+#define __pyx_n_u_ts __pyx_string_tab[308]
+#define __pyx_n_u_two __pyx_string_tab[309]
+#define __pyx_n_u_unique_key __pyx_string_tab[310]
+#define __pyx_n_u_unique_values __pyx_string_tab[311]
+#define __pyx_n_u_v0 __pyx_string_tab[312]
+#define __pyx_n_u_v1 __pyx_string_tab[313]
+#define __pyx_n_u_v2 __pyx_string_tab[314]
+#define __pyx_n_u_v3 __pyx_string_tab[315]
+#define __pyx_n_u_v4 __pyx_string_tab[316]
+#define __pyx_n_u_value __pyx_string_tab[317]
+#define __pyx_n_u_values __pyx_string_tab[318]
+#define __pyx_n_u_where __pyx_string_tab[319]
+#define __pyx_n_u_x __pyx_string_tab[320]
+#define __pyx_n_u_x0 __pyx_string_tab[321]
+#define __pyx_n_u_x1 __pyx_string_tab[322]
+#define __pyx_n_u_x2 __pyx_string_tab[323]
+#define __pyx_n_u_x3 __pyx_string_tab[324]
+#define __pyx_n_u_x4 __pyx_string_tab[325]
+#define __pyx_n_u_xDiff __pyx_string_tab[326]
+#define __pyx_n_u_xRoots __pyx_string_tab[327]
+#define __pyx_n_u_y __pyx_string_tab[328]
+#define __pyx_n_u_y1 __pyx_string_tab[329]
+#define __pyx_n_u_y2 __pyx_string_tab[330]
+#define __pyx_n_u_y3 __pyx_string_tab[331]
+#define __pyx_n_u_y4 __pyx_string_tab[332]
+#define __pyx_n_u_yDiff __pyx_string_tab[333]
+#define __pyx_n_u_yRoots __pyx_string_tab[334]
+#define __pyx_kp_b_iso88591_0_2Q_2Rq_U_A_r_2Rq_r_b_Bb_7_Bb __pyx_string_tab[335]
+#define __pyx_kp_b_iso88591_1 __pyx_string_tab[336]
+#define __pyx_kp_b_iso88591_1A_at6_1_q_F_4vRq_1L_AU_Qe3auD __pyx_string_tab[337]
+#define __pyx_kp_b_iso88591_1_Ql_1 __pyx_string_tab[338]
+#define __pyx_kp_b_iso88591_1_a_r_wb_gRvWBfA __pyx_string_tab[339]
+#define __pyx_kp_b_iso88591_2B_1_1 __pyx_string_tab[340]
+#define __pyx_kp_b_iso88591_2Q_2Rq_U_A_7_E_4r_3gRr_4r_b_2U __pyx_string_tab[341]
+#define __pyx_kp_b_iso88591_2Q_2Rq_U_A_r_b_1A_Cwb_Cq_2U_Cr __pyx_string_tab[342]
+#define __pyx_kp_b_iso88591_2S_2Rs_Cq_2Rs_Bc_2Rs_3b_Bb_1A_2 __pyx_string_tab[343]
+#define __pyx_kp_b_iso88591_2_Q_r_wb_gRvWBa __pyx_string_tab[344]
+#define __pyx_kp_b_iso88591_3as_A_Qc_4r_AS_b_1Cr_uBe2XS_RuB __pyx_string_tab[345]
+#define __pyx_kp_b_iso88591_3b_S_b_Ba_c_3b_2T_1_c_4r_d_HA_d __pyx_string_tab[346]
+#define __pyx_kp_b_iso88591_4r_Rq_avU_uA __pyx_string_tab[347]
+#define __pyx_kp_b_iso88591_6_rQR __pyx_string_tab[348]
+#define __pyx_kp_b_iso88591_6_s_T_AU_uA_1O1A_2Q_aq_t1_r_e5 __pyx_string_tab[349]
+#define __pyx_kp_b_iso88591_7_F_6 __pyx_string_tab[350]
+#define __pyx_kp_b_iso88591_AS_Cr_3b_1Cr_c_S_Cr_AS __pyx_string_tab[351]
+#define __pyx_kp_b_iso88591_AT_5_A_2Q_Ba_Ba_Ba_AT_4r_RuBa_A __pyx_string_tab[352]
+#define __pyx_kp_b_iso88591_A_t3aq_2Qaq __pyx_string_tab[353]
+#define __pyx_kp_b_iso88591_B_A_2T_5Rt2_PPRRS_AT_5_A_Bd_6b __pyx_string_tab[354]
+#define __pyx_kp_b_iso88591_F_s_Qe5_a_1N_A_aq_t1_r_e1_Qc_D __pyx_string_tab[355]
+#define __pyx_kp_b_iso88591_G1A_1_D_as_3b_Qd_Qc_5_87_1F_AQe __pyx_string_tab[356]
+#define __pyx_kp_b_iso88591_H_b_b_S_r_A_r_a_s_c_1_r_D_2Rr_S __pyx_string_tab[357]
+#define __pyx_kp_b_iso88591_L_s_3b_Qc_A_Qa_2Q_2Q_2Q_Bc_4r_R __pyx_string_tab[358]
+#define __pyx_kp_b_iso88591_M_m1A_m1A_t1_q_t1_q_HAYa_t1_q_x __pyx_string_tab[359]
+#define __pyx_kp_b_iso88591_Q_Q_Q_Q_Rt2Q_Rt2Q_Rt2T_1_Rt2T_1 __pyx_string_tab[360]
+#define __pyx_kp_b_iso88591_Q_Q_Q_Q_RuBa_RuBa_Rt2T_1_Rt2T_1 __pyx_string_tab[361]
+#define __pyx_kp_b_iso88591_Q_Q_Q_Rt2Q_Rt2Q_Bc_1_Bc_1_1D_d __pyx_string_tab[362]
+#define __pyx_kp_b_iso88591_Q_Q_Q_RuBa_RuBa_Bc_1_Bc_1_1D_d __pyx_string_tab[363]
+#define __pyx_kp_b_iso88591_Q_Q_Q_s_3b_Bhd_Qc_4r_s_3b_Bc_Rq __pyx_string_tab[364]
+#define __pyx_kp_b_iso88591_Qa_gQc_gQa_q_Q_Q_Q_Q_U_3at2Q_Rq __pyx_string_tab[365]
+#define __pyx_kp_b_iso88591_Qa_q_gQc_gQa_Q_Q_Q_U_3at2Q_Rq_R __pyx_string_tab[366]
+#define __pyx_kp_b_iso88591_Qas_F_4s_1Cr_q_Qas_F_4s_1Cr_q_5 __pyx_string_tab[367]
+#define __pyx_kp_b_iso88591_Qe3C1A_s_7_Q_3d_1_aq_Qat1AQ_AWC __pyx_string_tab[368]
+#define __pyx_kp_b_iso88591_Rq_Rq_2Q_A_Cq_vS_s_4r_t1Cq_s_Rq __pyx_string_tab[369]
+#define __pyx_kp_b_iso88591_S_1Cr_S_1Cr __pyx_string_tab[370]
+#define __pyx_kp_b_iso88591_T_t6_V4v_e5PQ_Rq_Rq_Rq_Rq_Qb_E __pyx_string_tab[371]
+#define __pyx_kp_b_iso88591_T_t6_V_5_Q_Rq_Rq_A_t3a_WAQc_1_t __pyx_string_tab[372]
+#define __pyx_kp_b_iso88591_T_uBa_wb_E_a __pyx_string_tab[373]
+#define __pyx_kp_b_iso88591__4 __pyx_string_tab[374]
+#define __pyx_kp_b_iso88591__5 __pyx_string_tab[375]
+#define __pyx_kp_b_iso88591_a_2 __pyx_string_tab[376]
+#define __pyx_kp_b_iso88591_a_s_6_3aq_gV1_s_6_1_3avRq_31F_2 __pyx_string_tab[377]
+#define __pyx_kp_b_iso88591_q __pyx_string_tab[378]
+#define __pyx_kp_b_iso88591_q_q_q_q_HAU_t4xq_U_d_hauTU_q_HA __pyx_string_tab[379]
+#define __pyx_kp_b_iso88591_s_3b_3as_A_A_AQb_Rr_2T_2Rq_3c_a __pyx_string_tab[380]
+#define __pyx_kp_b_iso88591_s_3c_4q_q_3a_r_L_s_3c_Q_AS_1_Bc __pyx_string_tab[381]
+#define __pyx_kp_b_iso88591_s_3c_A_AS_1_b_AQ __pyx_string_tab[382]
+#define __pyx_kp_b_iso88591_s_5_1_2U_AU_Q_q_AU_Q_Be1_AQ __pyx_string_tab[383]
+#define __pyx_kp_b_iso88591_s_7_Q_a_AWCq_a_j_A_b_a_6_WA_T_C __pyx_string_tab[384]
+#define __pyx_kp_b_iso88591_s_Qe5_Qc_D __pyx_string_tab[385]
+#define __pyx_kp_b_iso88591_s_T_AU_uA_N_3c_D_e1Baq_6_r_3d_1 __pyx_string_tab[386]
+#define __pyx_float_0_0 __pyx_number_tab[0]
+#define __pyx_float_0_5 __pyx_number_tab[1]
+#define __pyx_float_1_0 __pyx_number_tab[2]
+#define __pyx_float_neg_2_0 __pyx_number_tab[3]
+#define __pyx_float_2_0 __pyx_number_tab[4]
+#define __pyx_float_3_0 __pyx_number_tab[5]
+#define __pyx_float_4_0 __pyx_number_tab[6]
+#define __pyx_float_9_0 __pyx_number_tab[7]
+#define __pyx_float_1eneg_3 __pyx_number_tab[8]
+#define __pyx_float_1eneg_9 __pyx_number_tab[9]
+#define __pyx_float_27_0 __pyx_number_tab[10]
+#define __pyx_float_54_0 __pyx_number_tab[11]
+#define __pyx_float_0_005 __pyx_number_tab[12]
+#define __pyx_float_0_125 __pyx_number_tab[13]
+#define __pyx_float_1eneg_10 __pyx_number_tab[14]
+#define __pyx_int_0 __pyx_number_tab[15]
+#define __pyx_int_neg_1 __pyx_number_tab[16]
+#define __pyx_int_1 __pyx_number_tab[17]
+#define __pyx_int_2 __pyx_number_tab[18]
+#define __pyx_int_3 __pyx_number_tab[19]
+#define __pyx_int_6 __pyx_number_tab[20]
+/* #### Code section: module_state_clear ### */
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __pyx_m_clear(PyObject *m) {
+ __pyx_mstatetype *clear_module_state = __Pyx_PyModule_GetState(m);
+ if (!clear_module_state) return 0;
+ Py_CLEAR(clear_module_state->__pyx_d);
+ Py_CLEAR(clear_module_state->__pyx_b);
+ Py_CLEAR(clear_module_state->__pyx_cython_runtime);
+ Py_CLEAR(clear_module_state->__pyx_empty_tuple);
+ Py_CLEAR(clear_module_state->__pyx_empty_bytes);
+ Py_CLEAR(clear_module_state->__pyx_empty_unicode);
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __Pyx_State_RemoveModule(NULL);
+ #endif
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr);
+ for (int i=0; i<2; ++i) { Py_CLEAR(clear_module_state->__pyx_slice[i]); }
+ for (int i=0; i<4; ++i) { Py_CLEAR(clear_module_state->__pyx_tuple[i]); }
+ for (int i=0; i<54; ++i) { Py_CLEAR(clear_module_state->__pyx_codeobj_tab[i]); }
+ for (int i=0; i<387; ++i) { Py_CLEAR(clear_module_state->__pyx_string_tab[i]); }
+ for (int i=0; i<21; ++i) { Py_CLEAR(clear_module_state->__pyx_number_tab[i]); }
+/* #### Code section: module_state_clear_contents ### */
+/* CommonTypesMetaclass.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_CommonTypesMetaclassType);
+
+/* Generator.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_GeneratorType);
+
+/* CythonFunctionShared.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_CyFunctionType);
+
+/* #### Code section: module_state_clear_end ### */
+return 0;
+}
+#endif
+/* #### Code section: module_state_traverse ### */
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
+ __pyx_mstatetype *traverse_module_state = __Pyx_PyModule_GetState(m);
+ if (!traverse_module_state) return 0;
+ Py_VISIT(traverse_module_state->__pyx_d);
+ Py_VISIT(traverse_module_state->__pyx_b);
+ Py_VISIT(traverse_module_state->__pyx_cython_runtime);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_tuple);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_bytes);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_unicode);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr);
+ for (int i=0; i<2; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_slice[i]); }
+ for (int i=0; i<4; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_tuple[i]); }
+ for (int i=0; i<54; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_codeobj_tab[i]); }
+ for (int i=0; i<387; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_string_tab[i]); }
+ for (int i=0; i<21; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_number_tab[i]); }
+/* #### Code section: module_state_traverse_contents ### */
+/* CommonTypesMetaclass.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_CommonTypesMetaclassType);
+
+/* Generator.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_GeneratorType);
+
+/* CythonFunctionShared.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_CyFunctionType);
+
+/* #### Code section: module_state_traverse_end ### */
+return 0;
+}
+#endif
+/* #### Code section: module_code ### */
+
+/* "fontTools/misc/bezierTools.py":56
+ *
+ *
+ * def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a cubic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_1calcCubicArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_calcCubicArcLength, "calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005)\n\nCalculates the arc length for a cubic Bezier segment.\n\nWhereas :func:`approximateCubicArcLength` approximates the length, this\nfunction calculates it by \"measuring\", recursively dividing the curve\nuntil the divided segments are shorter than ``tolerance``.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n tolerance: Controls the precision of the calcuation.\n\nReturns:\n Arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_1calcCubicArcLength = {"calcCubicArcLength", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_1calcCubicArcLength, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_calcCubicArcLength};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_1calcCubicArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ PyObject *__pyx_v_tolerance = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcCubicArcLength (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_tolerance,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 56, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcCubicArcLength", 0) < (0)) __PYX_ERR(0, 56, __pyx_L3_error)
+ if (!values[4]) values[4] = __Pyx_NewRef(((PyObject *)((PyObject*)__pyx_mstate_global->__pyx_float_0_005)));
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcCubicArcLength", 0, 4, 5, i); __PYX_ERR(0, 56, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 56, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 56, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 56, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 56, __pyx_L3_error)
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 56, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ if (!values[4]) values[4] = __Pyx_NewRef(((PyObject *)((PyObject*)__pyx_mstate_global->__pyx_float_0_005)));
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ __pyx_v_tolerance = values[4];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcCubicArcLength", 0, 4, 5, __pyx_nargs); __PYX_ERR(0, 56, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_calcCubicArcLength(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_tolerance);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_calcCubicArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_tolerance) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicArcLength", 0);
+
+ /* "fontTools/misc/bezierTools.py":70
+ * Arc length value.
+ * """
+ * return calcCubicArcLengthC( # <<<<<<<<<<<<<<
+ * complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance
+ * )
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthC); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 70, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+
+ /* "fontTools/misc/bezierTools.py":71
+ * """
+ * return calcCubicArcLengthC(
+ * complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 71, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[6] = {__pyx_t_2, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_8, __pyx_v_tolerance};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_9, (6-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 70, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":56
+ *
+ *
+ * def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a cubic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":75
+ *
+ *
+ * def _split_cubic_into_two(p0, p1, p2, p3): # <<<<<<<<<<<<<<
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_3_split_cubic_into_two(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_2_split_cubic_into_two, "_split_cubic_into_two(p0, p1, p2, p3)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_3_split_cubic_into_two = {"_split_cubic_into_two", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_3_split_cubic_into_two, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_2_split_cubic_into_two};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_3_split_cubic_into_two(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_p0 = 0;
+ PyObject *__pyx_v_p1 = 0;
+ PyObject *__pyx_v_p2 = 0;
+ PyObject *__pyx_v_p3 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_split_cubic_into_two (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_p0,&__pyx_mstate_global->__pyx_n_u_p1,&__pyx_mstate_global->__pyx_n_u_p2,&__pyx_mstate_global->__pyx_n_u_p3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 75, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 75, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 75, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 75, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 75, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_split_cubic_into_two", 0) < (0)) __PYX_ERR(0, 75, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_split_cubic_into_two", 1, 4, 4, i); __PYX_ERR(0, 75, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 75, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 75, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 75, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 75, __pyx_L3_error)
+ }
+ __pyx_v_p0 = values[0];
+ __pyx_v_p1 = values[1];
+ __pyx_v_p2 = values[2];
+ __pyx_v_p3 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_split_cubic_into_two", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 75, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._split_cubic_into_two", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_2_split_cubic_into_two(__pyx_self, __pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_2_split_cubic_into_two(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_p0, PyObject *__pyx_v_p1, PyObject *__pyx_v_p2, PyObject *__pyx_v_p3) {
+ PyObject *__pyx_v_mid = NULL;
+ PyObject *__pyx_v_deriv3 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_split_cubic_into_two", 0);
+
+ /* "fontTools/misc/bezierTools.py":76
+ *
+ * def _split_cubic_into_two(p0, p1, p2, p3):
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<<
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return (
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_p1, __pyx_v_p2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 76, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_t_1, 3, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 76, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_v_p0, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 76, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_p3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 76, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_mstate_global->__pyx_float_0_125); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 76, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_mid = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":77
+ * def _split_cubic_into_two(p0, p1, p2, p3):
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<<
+ * return (
+ * (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_p3, __pyx_v_p2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 77, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_v_p1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 77, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 77, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_0_125); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 77, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_deriv3 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":78
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return ( # <<<<<<<<<<<<<<
+ * (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
+ * (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
+*/
+ __Pyx_XDECREF(__pyx_r);
+
+ /* "fontTools/misc/bezierTools.py":79
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return (
+ * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<<
+ * (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
+ * )
+*/
+ __pyx_t_2 = PyNumber_Add(__pyx_v_p0, __pyx_v_p1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 79, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_mstate_global->__pyx_float_0_5); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 79, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_mid, __pyx_v_deriv3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 79, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyTuple_New(4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 79, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_p0);
+ __Pyx_GIVEREF(__pyx_v_p0);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_p0) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 2, __pyx_t_2) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_mid);
+ __Pyx_GIVEREF(__pyx_v_mid);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 3, __pyx_v_mid) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":80
+ * return (
+ * (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
+ * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_2 = PyNumber_Add(__pyx_v_mid, __pyx_v_deriv3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 80, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 80, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_0_5); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 80, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyTuple_New(4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 80, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_mid);
+ __Pyx_GIVEREF(__pyx_v_mid);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_mid) != (0)) __PYX_ERR(0, 80, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 80, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_t_4) != (0)) __PYX_ERR(0, 80, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_p3);
+ __Pyx_GIVEREF(__pyx_v_p3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 3, __pyx_v_p3) != (0)) __PYX_ERR(0, 80, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":79
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return (
+ * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<<
+ * (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
+ * )
+*/
+ __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 79, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 79, __pyx_L1_error);
+ __pyx_t_3 = 0;
+ __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":75
+ *
+ *
+ * def _split_cubic_into_two(p0, p1, p2, p3): # <<<<<<<<<<<<<<
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._split_cubic_into_two", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_mid);
+ __Pyx_XDECREF(__pyx_v_deriv3);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":84
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * p0=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_5_calcCubicArcLengthCRecurse(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_4_calcCubicArcLengthCRecurse, "_calcCubicArcLengthCRecurse(double mult, double complex p0, double complex p1, double complex p2, double complex p3)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_5_calcCubicArcLengthCRecurse = {"_calcCubicArcLengthCRecurse", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_5_calcCubicArcLengthCRecurse, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_4_calcCubicArcLengthCRecurse};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_5_calcCubicArcLengthCRecurse(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ double __pyx_v_mult;
+ __pyx_t_double_complex __pyx_v_p0;
+ __pyx_t_double_complex __pyx_v_p1;
+ __pyx_t_double_complex __pyx_v_p2;
+ __pyx_t_double_complex __pyx_v_p3;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_calcCubicArcLengthCRecurse (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_mult,&__pyx_mstate_global->__pyx_n_u_p0,&__pyx_mstate_global->__pyx_n_u_p1,&__pyx_mstate_global->__pyx_n_u_p2,&__pyx_mstate_global->__pyx_n_u_p3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 84, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 84, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 84, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 84, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 84, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 84, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_calcCubicArcLengthCRecurse", 0) < (0)) __PYX_ERR(0, 84, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 5; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_calcCubicArcLengthCRecurse", 1, 5, 5, i); __PYX_ERR(0, 84, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 5)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 84, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 84, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 84, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 84, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 84, __pyx_L3_error)
+ }
+ __pyx_v_mult = __Pyx_PyFloat_AsDouble(values[0]); if (unlikely((__pyx_v_mult == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_p0 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_p1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_p2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_p3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[4]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_calcCubicArcLengthCRecurse", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 84, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._calcCubicArcLengthCRecurse", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_4_calcCubicArcLengthCRecurse(__pyx_self, __pyx_v_mult, __pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_4_calcCubicArcLengthCRecurse(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_mult, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) {
+ double __pyx_v_arch;
+ double __pyx_v_box;
+ PyObject *__pyx_v_one = NULL;
+ PyObject *__pyx_v_two = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ PyObject *(*__pyx_t_10)(PyObject *);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_calcCubicArcLengthCRecurse", 0);
+
+ /* "fontTools/misc/bezierTools.py":93
+ * @cython.locals(mult=cython.double, arch=cython.double, box=cython.double)
+ * def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
+ * arch = abs(p0 - p3) # <<<<<<<<<<<<<<
+ * box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
+ * if arch * mult + EPSILON >= box:
+*/
+ __pyx_v_arch = __Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p0, __pyx_v_p3));
+
+ /* "fontTools/misc/bezierTools.py":94
+ * def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
+ * arch = abs(p0 - p3)
+ * box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3) # <<<<<<<<<<<<<<
+ * if arch * mult + EPSILON >= box:
+ * return (arch + box) * 0.5
+*/
+ __pyx_v_box = ((__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p0, __pyx_v_p1)) + __Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p2))) + __Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p3)));
+
+ /* "fontTools/misc/bezierTools.py":95
+ * arch = abs(p0 - p3)
+ * box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
+ * if arch * mult + EPSILON >= box: # <<<<<<<<<<<<<<
+ * return (arch + box) * 0.5
+ * else:
+*/
+ __pyx_t_1 = PyFloat_FromDouble((__pyx_v_arch * __pyx_v_mult)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_EPSILON); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyFloat_FromDouble(__pyx_v_box); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_3, __pyx_t_2, Py_GE); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":96
+ * box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
+ * if arch * mult + EPSILON >= box:
+ * return (arch + box) * 0.5 # <<<<<<<<<<<<<<
+ * else:
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyFloat_FromDouble(((__pyx_v_arch + __pyx_v_box) * 0.5)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 96, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":95
+ * arch = abs(p0 - p3)
+ * box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
+ * if arch * mult + EPSILON >= box: # <<<<<<<<<<<<<<
+ * return (arch + box) * 0.5
+ * else:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":98
+ * return (arch + box) * 0.5
+ * else:
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3) # <<<<<<<<<<<<<<
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(
+ * mult, *two
+*/
+ /*else*/ {
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_split_cubic_into_two); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_v_p1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_8};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_9, (5-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 98, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_8 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_8);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_8);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_8 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_10 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ index = 0; __pyx_t_3 = __pyx_t_10(__pyx_t_7); if (unlikely(!__pyx_t_3)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_8 = __pyx_t_10(__pyx_t_7); if (unlikely(!__pyx_t_8)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_10(__pyx_t_7), 2) < (0)) __PYX_ERR(0, 98, __pyx_L1_error)
+ __pyx_t_10 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L5_unpacking_done;
+ __pyx_L4_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_10 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 98, __pyx_L1_error)
+ __pyx_L5_unpacking_done:;
+ }
+ __pyx_v_one = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_two = __pyx_t_8;
+ __pyx_t_8 = 0;
+
+ /* "fontTools/misc/bezierTools.py":99
+ * else:
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse( # <<<<<<<<<<<<<<
+ * mult, *two
+ * )
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthCRecurse); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_8 = PyFloat_FromDouble(__pyx_v_mult); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_8);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_8) != (0)) __PYX_ERR(0, 99, __pyx_L1_error);
+ __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PySequence_Tuple(__pyx_v_one); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_7 = PyNumber_Add(__pyx_t_3, __pyx_t_8); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_7, NULL); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthCRecurse); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+
+ /* "fontTools/misc/bezierTools.py":100
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(
+ * mult, *two # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_1 = PyFloat_FromDouble(__pyx_v_mult); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 100, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":99
+ * else:
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse( # <<<<<<<<<<<<<<
+ * mult, *two
+ * )
+*/
+ __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 99, __pyx_L1_error);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":100
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(
+ * mult, *two # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_1 = __Pyx_PySequence_Tuple(__pyx_v_two); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":99
+ * else:
+ * one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ * return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse( # <<<<<<<<<<<<<<
+ * mult, *two
+ * )
+*/
+ __pyx_t_6 = PyNumber_Add(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_7, __pyx_t_6, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Add(__pyx_t_8, __pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 99, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":84
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * p0=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._calcCubicArcLengthCRecurse", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_one);
+ __Pyx_XDECREF(__pyx_v_two);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":104
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_7calcCubicArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_6calcCubicArcLengthC, "calcCubicArcLengthC(double complex pt1, double complex pt2, double complex pt3, double complex pt4, double tolerance=0.005)\n\nCalculates the arc length for a cubic Bezier segment.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.\n tolerance: Controls the precision of the calcuation.\n\nReturns:\n Arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_7calcCubicArcLengthC = {"calcCubicArcLengthC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_7calcCubicArcLengthC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_6calcCubicArcLengthC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_7calcCubicArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ double __pyx_v_tolerance;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcCubicArcLengthC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_tolerance,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 104, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcCubicArcLengthC", 0) < (0)) __PYX_ERR(0, 104, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcCubicArcLengthC", 0, 4, 5, i); __PYX_ERR(0, 104, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 104, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 104, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 104, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 104, __pyx_L3_error)
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 104, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L3_error)
+ __pyx_v_pt4 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L3_error)
+ if (values[4]) {
+ __pyx_v_tolerance = __Pyx_PyFloat_AsDouble(values[4]); if (unlikely((__pyx_v_tolerance == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L3_error)
+ } else {
+ __pyx_v_tolerance = ((double)((double)0.005));
+ }
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcCubicArcLengthC", 0, 4, 5, __pyx_nargs); __PYX_ERR(0, 104, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_6calcCubicArcLengthC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_tolerance);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_6calcCubicArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_tolerance) {
+ double __pyx_v_mult;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicArcLengthC", 0);
+
+ /* "fontTools/misc/bezierTools.py":125
+ * Arc length value.
+ * """
+ * mult = 1.0 + 1.5 * tolerance # The 1.5 is a empirical hack; no math # <<<<<<<<<<<<<<
+ * return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
+ *
+*/
+ __pyx_v_mult = (1.0 + (1.5 * __pyx_v_tolerance));
+
+ /* "fontTools/misc/bezierTools.py":126
+ * """
+ * mult = 1.0 + 1.5 * tolerance # The 1.5 is a empirical hack; no math
+ * return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthCRecurse); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyFloat_FromDouble(__pyx_v_mult); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_pt1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_v_pt2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_pt3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_pt4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[6] = {__pyx_t_2, __pyx_t_4, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_8};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_9, (6-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":104
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":133
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.returns(cython.double)
+*/
+
+static CYTHON_INLINE double __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_t_double_complex __pyx_v_v1, __pyx_t_double_complex __pyx_v_v2) {
+ double __pyx_r;
+
+ /* "fontTools/misc/bezierTools.py":138
+ * @cython.locals(v1=cython.complex, v2=cython.complex)
+ * def _dot(v1, v2):
+ * return (v1 * v2.conjugate()).real # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_r = __Pyx_CREAL(__Pyx_c_prod_double(__pyx_v_v1, __Pyx_c_conj_double(__pyx_v_v2)));
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":133
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.returns(cython.double)
+*/
+
+ /* function exit code */
+ __pyx_L0:;
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":141
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.returns(cython.double)
+*/
+
+static CYTHON_INLINE double __pyx_f_9fontTools_4misc_11bezierTools__intSecAtan(double __pyx_v_x) {
+ double __pyx_r;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ double __pyx_t_7;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_intSecAtan", 0);
+
+ /* "fontTools/misc/bezierTools.py":148
+ * # In : sympy.integrate(sp.sec(sp.atan(x)))
+ * # Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
+ * return x * math.sqrt(x**2 + 1) / 2 + math.asinh(x) / 2 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_1 = PyFloat_FromDouble(__pyx_v_x); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_sqrt); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyFloat_FromDouble((pow(__pyx_v_x, 2.0) + 1.0)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_t_4};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyLong_TrueDivideObjC(__pyx_t_5, __pyx_mstate_global->__pyx_int_2, 2, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_asinh); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyFloat_FromDouble(__pyx_v_x); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_t_4};
+ __pyx_t_5 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ }
+ __pyx_t_3 = __Pyx_PyLong_TrueDivideObjC(__pyx_t_5, __pyx_mstate_global->__pyx_int_2, 2, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = PyNumber_Add(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = __Pyx_PyFloat_AsDouble(__pyx_t_5); if (unlikely((__pyx_t_7 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 148, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_7;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":141
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.returns(cython.double)
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._intSecAtan", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":151
+ *
+ *
+ * def calcQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_9calcQuadraticArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_8calcQuadraticArcLength, "calcQuadraticArcLength(pt1, pt2, pt3)\n\nCalculates the arc length for a quadratic Bezier segment.\n\nArgs:\n pt1: Start point of the Bezier as 2D tuple.\n pt2: Handle point of the Bezier as 2D tuple.\n pt3: End point of the Bezier as 2D tuple.\n\nReturns:\n Arc length value.\n\nExample::\n\n >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment\n 0.0\n >>> calcQuadraticArcLength((0, 0), (50, 0), (80, 0)) # collinear points\n 80.0\n >>> calcQuadraticArcLength((0, 0), (0, 50), (0, 80)) # collinear points vertical\n 80.0\n >>> calcQuadraticArcLength((0, 0), (50, 20), (100, 40)) # collinear points\n 107.70329614269008\n >>> calcQuadraticArcLength((0, 0), (0, 100), (100, 0))\n 154.02976155645263\n >>> calcQuadraticArcLength((0, 0), (0, 50), (100, 0))\n 120.21581243984076\n >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))\n 102.53273816445825\n >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0)) # collinear points, control point outside\n 66.66666666666667\n >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0)) # collinear points, looping back\n 40.0");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_9calcQuadraticArcLength = {"calcQuadraticArcLength", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_9calcQuadraticArcLength, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_8calcQuadraticArcLength};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_9calcQuadraticArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcQuadraticArcLength (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 151, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 151, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 151, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 151, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcQuadraticArcLength", 0) < (0)) __PYX_ERR(0, 151, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcQuadraticArcLength", 1, 3, 3, i); __PYX_ERR(0, 151, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 151, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 151, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 151, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcQuadraticArcLength", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 151, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_8calcQuadraticArcLength(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_8calcQuadraticArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ size_t __pyx_t_8;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcQuadraticArcLength", 0);
+
+ /* "fontTools/misc/bezierTools.py":183
+ * 40.0
+ * """
+ * return calcQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3)) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLengthC); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_8 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_8 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_t_5, __pyx_t_6, __pyx_t_7};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_8, (4-__pyx_t_8) | (__pyx_t_8*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 183, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":151
+ *
+ *
+ * def calcQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":186
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_11calcQuadraticArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_10calcQuadraticArcLengthC, "calcQuadraticArcLengthC(double complex pt1, double complex pt2, double complex pt3)\n\nCalculates the arc length for a quadratic Bezier segment.\n\nArgs:\n pt1: Start point of the Bezier as a complex number.\n pt2: Handle point of the Bezier as a complex number.\n pt3: End point of the Bezier as a complex number.\n\nReturns:\n Arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_11calcQuadraticArcLengthC = {"calcQuadraticArcLengthC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_11calcQuadraticArcLengthC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_10calcQuadraticArcLengthC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_11calcQuadraticArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcQuadraticArcLengthC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 186, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 186, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 186, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 186, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcQuadraticArcLengthC", 0) < (0)) __PYX_ERR(0, 186, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcQuadraticArcLengthC", 1, 3, 3, i); __PYX_ERR(0, 186, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 186, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 186, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 186, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 205, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 205, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 205, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcQuadraticArcLengthC", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 186, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_10calcQuadraticArcLengthC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_10calcQuadraticArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3) {
+ double __pyx_v_scale;
+ double __pyx_v_origDist;
+ double __pyx_v_a;
+ double __pyx_v_b;
+ double __pyx_v_x0;
+ double __pyx_v_x1;
+ double __pyx_v_Len;
+ __pyx_t_double_complex __pyx_v_d0;
+ __pyx_t_double_complex __pyx_v_d1;
+ __pyx_t_double_complex __pyx_v_d;
+ __pyx_t_double_complex __pyx_v_n;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ PyObject *__pyx_t_2 = NULL;
+ double __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ double __pyx_t_6;
+ double __pyx_t_7;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcQuadraticArcLengthC", 0);
+
+ /* "fontTools/misc/bezierTools.py":218
+ * # Analytical solution to the length of a quadratic bezier.
+ * # Documentation: https://github.com/fonttools/fonttools/issues/3055
+ * d0 = pt2 - pt1 # <<<<<<<<<<<<<<
+ * d1 = pt3 - pt2
+ * d = d1 - d0
+*/
+ __pyx_v_d0 = __Pyx_c_diff_double(__pyx_v_pt2, __pyx_v_pt1);
+
+ /* "fontTools/misc/bezierTools.py":219
+ * # Documentation: https://github.com/fonttools/fonttools/issues/3055
+ * d0 = pt2 - pt1
+ * d1 = pt3 - pt2 # <<<<<<<<<<<<<<
+ * d = d1 - d0
+ * n = d * 1j
+*/
+ __pyx_v_d1 = __Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt2);
+
+ /* "fontTools/misc/bezierTools.py":220
+ * d0 = pt2 - pt1
+ * d1 = pt3 - pt2
+ * d = d1 - d0 # <<<<<<<<<<<<<<
+ * n = d * 1j
+ * scale = abs(n)
+*/
+ __pyx_v_d = __Pyx_c_diff_double(__pyx_v_d1, __pyx_v_d0);
+
+ /* "fontTools/misc/bezierTools.py":221
+ * d1 = pt3 - pt2
+ * d = d1 - d0
+ * n = d * 1j # <<<<<<<<<<<<<<
+ * scale = abs(n)
+ * if scale == 0.0:
+*/
+ __pyx_v_n = __Pyx_c_prod_double(__pyx_v_d, __pyx_t_double_complex_from_parts(0, 1.0));
+
+ /* "fontTools/misc/bezierTools.py":222
+ * d = d1 - d0
+ * n = d * 1j
+ * scale = abs(n) # <<<<<<<<<<<<<<
+ * if scale == 0.0:
+ * return abs(pt3 - pt1)
+*/
+ __pyx_v_scale = __Pyx_c_abs_double(__pyx_v_n);
+
+ /* "fontTools/misc/bezierTools.py":223
+ * n = d * 1j
+ * scale = abs(n)
+ * if scale == 0.0: # <<<<<<<<<<<<<<
+ * return abs(pt3 - pt1)
+ * origDist = _dot(n, d0)
+*/
+ __pyx_t_1 = (__pyx_v_scale == 0.0);
+ if (__pyx_t_1) {
+
+ /* "fontTools/misc/bezierTools.py":224
+ * scale = abs(n)
+ * if scale == 0.0:
+ * return abs(pt3 - pt1) # <<<<<<<<<<<<<<
+ * origDist = _dot(n, d0)
+ * if abs(origDist) < epsilon:
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyFloat_FromDouble(__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt1))); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 224, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":223
+ * n = d * 1j
+ * scale = abs(n)
+ * if scale == 0.0: # <<<<<<<<<<<<<<
+ * return abs(pt3 - pt1)
+ * origDist = _dot(n, d0)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":225
+ * if scale == 0.0:
+ * return abs(pt3 - pt1)
+ * origDist = _dot(n, d0) # <<<<<<<<<<<<<<
+ * if abs(origDist) < epsilon:
+ * if _dot(d0, d1) >= 0:
+*/
+ __pyx_t_3 = __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_v_n, __pyx_v_d0); if (unlikely(__pyx_t_3 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 225, __pyx_L1_error)
+ __pyx_v_origDist = __pyx_t_3;
+
+ /* "fontTools/misc/bezierTools.py":226
+ * return abs(pt3 - pt1)
+ * origDist = _dot(n, d0)
+ * if abs(origDist) < epsilon: # <<<<<<<<<<<<<<
+ * if _dot(d0, d1) >= 0:
+ * return abs(pt3 - pt1)
+*/
+ __pyx_t_3 = fabs(__pyx_v_origDist);
+ __pyx_t_2 = PyFloat_FromDouble(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyObject_RichCompare(__pyx_t_2, __pyx_t_4, Py_LT); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_1 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (__pyx_t_1) {
+
+ /* "fontTools/misc/bezierTools.py":227
+ * origDist = _dot(n, d0)
+ * if abs(origDist) < epsilon:
+ * if _dot(d0, d1) >= 0: # <<<<<<<<<<<<<<
+ * return abs(pt3 - pt1)
+ * a, b = abs(d0), abs(d1)
+*/
+ __pyx_t_3 = __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_v_d0, __pyx_v_d1); if (unlikely(__pyx_t_3 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 227, __pyx_L1_error)
+ __pyx_t_1 = (__pyx_t_3 >= 0.0);
+ if (__pyx_t_1) {
+
+ /* "fontTools/misc/bezierTools.py":228
+ * if abs(origDist) < epsilon:
+ * if _dot(d0, d1) >= 0:
+ * return abs(pt3 - pt1) # <<<<<<<<<<<<<<
+ * a, b = abs(d0), abs(d1)
+ * return (a * a + b * b) / (a + b)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = PyFloat_FromDouble(__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt1))); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 228, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":227
+ * origDist = _dot(n, d0)
+ * if abs(origDist) < epsilon:
+ * if _dot(d0, d1) >= 0: # <<<<<<<<<<<<<<
+ * return abs(pt3 - pt1)
+ * a, b = abs(d0), abs(d1)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":229
+ * if _dot(d0, d1) >= 0:
+ * return abs(pt3 - pt1)
+ * a, b = abs(d0), abs(d1) # <<<<<<<<<<<<<<
+ * return (a * a + b * b) / (a + b)
+ * x0 = _dot(d, d0) / origDist
+*/
+ __pyx_t_3 = __Pyx_c_abs_double(__pyx_v_d0);
+ __pyx_t_6 = __Pyx_c_abs_double(__pyx_v_d1);
+ __pyx_v_a = __pyx_t_3;
+ __pyx_v_b = __pyx_t_6;
+
+ /* "fontTools/misc/bezierTools.py":230
+ * return abs(pt3 - pt1)
+ * a, b = abs(d0), abs(d1)
+ * return (a * a + b * b) / (a + b) # <<<<<<<<<<<<<<
+ * x0 = _dot(d, d0) / origDist
+ * x1 = _dot(d, d1) / origDist
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_6 = ((__pyx_v_a * __pyx_v_a) + (__pyx_v_b * __pyx_v_b));
+ __pyx_t_3 = (__pyx_v_a + __pyx_v_b);
+ if (unlikely(__pyx_t_3 == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 230, __pyx_L1_error)
+ }
+ __pyx_t_5 = PyFloat_FromDouble((__pyx_t_6 / __pyx_t_3)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 230, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":226
+ * return abs(pt3 - pt1)
+ * origDist = _dot(n, d0)
+ * if abs(origDist) < epsilon: # <<<<<<<<<<<<<<
+ * if _dot(d0, d1) >= 0:
+ * return abs(pt3 - pt1)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":231
+ * a, b = abs(d0), abs(d1)
+ * return (a * a + b * b) / (a + b)
+ * x0 = _dot(d, d0) / origDist # <<<<<<<<<<<<<<
+ * x1 = _dot(d, d1) / origDist
+ * Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
+*/
+ __pyx_t_3 = __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_v_d, __pyx_v_d0); if (unlikely(__pyx_t_3 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 231, __pyx_L1_error)
+ if (unlikely(__pyx_v_origDist == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 231, __pyx_L1_error)
+ }
+ __pyx_v_x0 = (__pyx_t_3 / __pyx_v_origDist);
+
+ /* "fontTools/misc/bezierTools.py":232
+ * return (a * a + b * b) / (a + b)
+ * x0 = _dot(d, d0) / origDist
+ * x1 = _dot(d, d1) / origDist # <<<<<<<<<<<<<<
+ * Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
+ * return Len
+*/
+ __pyx_t_3 = __pyx_f_9fontTools_4misc_11bezierTools__dot(__pyx_v_d, __pyx_v_d1); if (unlikely(__pyx_t_3 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 232, __pyx_L1_error)
+ if (unlikely(__pyx_v_origDist == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 232, __pyx_L1_error)
+ }
+ __pyx_v_x1 = (__pyx_t_3 / __pyx_v_origDist);
+
+ /* "fontTools/misc/bezierTools.py":233
+ * x0 = _dot(d, d0) / origDist
+ * x1 = _dot(d, d1) / origDist
+ * Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0))) # <<<<<<<<<<<<<<
+ * return Len
+ *
+*/
+ __pyx_t_3 = __pyx_f_9fontTools_4misc_11bezierTools__intSecAtan(__pyx_v_x1); if (unlikely(__pyx_t_3 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 233, __pyx_L1_error)
+ __pyx_t_6 = __pyx_f_9fontTools_4misc_11bezierTools__intSecAtan(__pyx_v_x0); if (unlikely(__pyx_t_6 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 233, __pyx_L1_error)
+ __pyx_t_7 = ((2.0 * (__pyx_t_3 - __pyx_t_6)) * __pyx_v_origDist);
+ __pyx_t_6 = (__pyx_v_scale * (__pyx_v_x1 - __pyx_v_x0));
+ if (unlikely(__pyx_t_6 == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 233, __pyx_L1_error)
+ }
+ __pyx_t_3 = fabs((__pyx_t_7 / __pyx_t_6));
+ __pyx_v_Len = __pyx_t_3;
+
+ /* "fontTools/misc/bezierTools.py":234
+ * x1 = _dot(d, d1) / origDist
+ * Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
+ * return Len # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = PyFloat_FromDouble(__pyx_v_Len); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 234, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":186
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":237
+ *
+ *
+ * def approximateQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_13approximateQuadraticArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_12approximateQuadraticArcLength, "approximateQuadraticArcLength(pt1, pt2, pt3)\n\nCalculates the arc length for a quadratic Bezier segment.\n\nUses Gauss-Legendre quadrature for a branch-free approximation.\nSee :func:`calcQuadraticArcLength` for a slower but more accurate result.\n\nArgs:\n pt1: Start point of the Bezier as 2D tuple.\n pt2: Handle point of the Bezier as 2D tuple.\n pt3: End point of the Bezier as 2D tuple.\n\nReturns:\n Approximate arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_13approximateQuadraticArcLength = {"approximateQuadraticArcLength", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_13approximateQuadraticArcLength, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_12approximateQuadraticArcLength};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_13approximateQuadraticArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("approximateQuadraticArcLength (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 237, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 237, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 237, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 237, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "approximateQuadraticArcLength", 0) < (0)) __PYX_ERR(0, 237, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("approximateQuadraticArcLength", 1, 3, 3, i); __PYX_ERR(0, 237, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 237, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 237, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 237, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("approximateQuadraticArcLength", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 237, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateQuadraticArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_12approximateQuadraticArcLength(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12approximateQuadraticArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ size_t __pyx_t_8;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("approximateQuadraticArcLength", 0);
+
+ /* "fontTools/misc/bezierTools.py":251
+ * Approximate arc length value.
+ * """
+ * return approximateQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3)) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLengthC); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_8 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_8 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_t_5, __pyx_t_6, __pyx_t_7};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_8, (4-__pyx_t_8) | (__pyx_t_8*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":237
+ *
+ *
+ * def approximateQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateQuadraticArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":254
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_15approximateQuadraticArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_14approximateQuadraticArcLengthC, "approximateQuadraticArcLengthC(double complex pt1, double complex pt2, double complex pt3)\n\nCalculates the arc length for a quadratic Bezier segment.\n\nUses Gauss-Legendre quadrature for a branch-free approximation.\nSee :func:`calcQuadraticArcLength` for a slower but more accurate result.\n\nArgs:\n pt1: Start point of the Bezier as a complex number.\n pt2: Handle point of the Bezier as a complex number.\n pt3: End point of the Bezier as a complex number.\n\nReturns:\n Approximate arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_15approximateQuadraticArcLengthC = {"approximateQuadraticArcLengthC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_15approximateQuadraticArcLengthC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_14approximateQuadraticArcLengthC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_15approximateQuadraticArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("approximateQuadraticArcLengthC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 254, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 254, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 254, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 254, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "approximateQuadraticArcLengthC", 0) < (0)) __PYX_ERR(0, 254, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("approximateQuadraticArcLengthC", 1, 3, 3, i); __PYX_ERR(0, 254, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 254, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 254, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 254, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 265, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 265, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 265, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("approximateQuadraticArcLengthC", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 254, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateQuadraticArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_14approximateQuadraticArcLengthC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_14approximateQuadraticArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3) {
+ double __pyx_v_v0;
+ double __pyx_v_v1;
+ double __pyx_v_v2;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("approximateQuadraticArcLengthC", 0);
+
+ /* "fontTools/misc/bezierTools.py":287
+ * # abs(BezierCurveC[2].diff(t).subs({t:T})) for T in sorted(.5, .5sqrt(3/5)/2),
+ * # weighted 5/18, 8/18, 5/18 respectively.
+ * v0 = abs( # <<<<<<<<<<<<<<
+ * -0.492943519233745 * pt1 + 0.430331482911935 * pt2 + 0.0626120363218102 * pt3
+ * )
+*/
+ __pyx_v_v0 = __Pyx_c_abs_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(-0.492943519233745, 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.430331482911935, 0), __pyx_v_pt2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.0626120363218102, 0), __pyx_v_pt3)));
+
+ /* "fontTools/misc/bezierTools.py":290
+ * -0.492943519233745 * pt1 + 0.430331482911935 * pt2 + 0.0626120363218102 * pt3
+ * )
+ * v1 = abs(pt3 - pt1) * 0.4444444444444444 # <<<<<<<<<<<<<<
+ * v2 = abs(
+ * -0.0626120363218102 * pt1 - 0.430331482911935 * pt2 + 0.492943519233745 * pt3
+*/
+ __pyx_v_v1 = (__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt1)) * 0.4444444444444444);
+
+ /* "fontTools/misc/bezierTools.py":291
+ * )
+ * v1 = abs(pt3 - pt1) * 0.4444444444444444
+ * v2 = abs( # <<<<<<<<<<<<<<
+ * -0.0626120363218102 * pt1 - 0.430331482911935 * pt2 + 0.492943519233745 * pt3
+ * )
+*/
+ __pyx_v_v2 = __Pyx_c_abs_double(__Pyx_c_sum_double(__Pyx_c_diff_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(-0.0626120363218102, 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.430331482911935, 0), __pyx_v_pt2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.492943519233745, 0), __pyx_v_pt3)));
+
+ /* "fontTools/misc/bezierTools.py":295
+ * )
+ *
+ * return v0 + v1 + v2 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyFloat_FromDouble(((__pyx_v_v0 + __pyx_v_v1) + __pyx_v_v2)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 295, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":254
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateQuadraticArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":298
+ *
+ *
+ * def calcQuadraticBounds(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_17calcQuadraticBounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_16calcQuadraticBounds, "calcQuadraticBounds(pt1, pt2, pt3)\n\nCalculates the bounding rectangle for a quadratic Bezier segment.\n\nArgs:\n pt1: Start point of the Bezier as a 2D tuple.\n pt2: Handle point of the Bezier as a 2D tuple.\n pt3: End point of the Bezier as a 2D tuple.\n\nReturns:\n A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.\n\nExample::\n\n >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))\n (0, 0, 100, 50.0)\n >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))\n (0.0, 0.0, 100, 100)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_17calcQuadraticBounds = {"calcQuadraticBounds", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_17calcQuadraticBounds, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_16calcQuadraticBounds};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_17calcQuadraticBounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcQuadraticBounds (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 298, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 298, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 298, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 298, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcQuadraticBounds", 0) < (0)) __PYX_ERR(0, 298, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcQuadraticBounds", 1, 3, 3, i); __PYX_ERR(0, 298, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 298, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 298, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 298, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcQuadraticBounds", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 298, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticBounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_16calcQuadraticBounds(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_16calcQuadraticBounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3) {
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_ax2 = NULL;
+ PyObject *__pyx_v_ay2 = NULL;
+ PyObject *__pyx_v_roots = NULL;
+ PyObject *__pyx_v_points = NULL;
+ PyObject *__pyx_7genexpr__pyx_v_t = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ PyObject *__pyx_t_8 = NULL;
+ PyObject *__pyx_t_9 = NULL;
+ int __pyx_t_10;
+ int __pyx_t_11;
+ Py_ssize_t __pyx_t_12;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcQuadraticBounds", 0);
+
+ /* "fontTools/misc/bezierTools.py":316
+ * (0.0, 0.0, 100, 100)
+ * """
+ * (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3) # <<<<<<<<<<<<<<
+ * ax2 = ax * 2.0
+ * ay2 = ay * 2.0
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6);
+ index = 0; __pyx_t_3 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_3)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_2 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 2; __pyx_t_5 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_5)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_5);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 3) < (0)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_3))) || (PyList_CheckExact(__pyx_t_3))) {
+ PyObject* sequence = __pyx_t_3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_8 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_8);
+ } else {
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_8);
+ }
+ #else
+ __pyx_t_6 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ #endif
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_9 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_9);
+ index = 0; __pyx_t_6 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_6)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_6);
+ index = 1; __pyx_t_8 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_8)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_9), 2) < (0)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_6;
+ __pyx_t_6 = 0;
+ __pyx_v_ay = __pyx_t_8;
+ __pyx_t_8 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_2))) || (PyList_CheckExact(__pyx_t_2))) {
+ PyObject* sequence = __pyx_t_2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_8 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_8);
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_6);
+ } else {
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_8);
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ }
+ #else
+ __pyx_t_8 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_6 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ #endif
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_9 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_9);
+ index = 0; __pyx_t_8 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_8)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_8);
+ index = 1; __pyx_t_6 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_6)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_6);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_9), 2) < (0)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_8;
+ __pyx_t_8 = 0;
+ __pyx_v_by = __pyx_t_6;
+ __pyx_t_6 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_5))) || (PyList_CheckExact(__pyx_t_5))) {
+ PyObject* sequence = __pyx_t_5;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_8 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_8);
+ } else {
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_8);
+ }
+ #else
+ __pyx_t_6 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ #endif
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_9 = PyObject_GetIter(__pyx_t_5); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_9);
+ index = 0; __pyx_t_6 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_6)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_6);
+ index = 1; __pyx_t_8 = __pyx_t_7(__pyx_t_9); if (unlikely(!__pyx_t_8)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_9), 2) < (0)) __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 316, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_6;
+ __pyx_t_6 = 0;
+ __pyx_v_cy = __pyx_t_8;
+ __pyx_t_8 = 0;
+
+ /* "fontTools/misc/bezierTools.py":317
+ * """
+ * (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
+ * ax2 = ax * 2.0 # <<<<<<<<<<<<<<
+ * ay2 = ay * 2.0
+ * roots = []
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ax, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 317, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ax2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":318
+ * (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
+ * ax2 = ax * 2.0
+ * ay2 = ay * 2.0 # <<<<<<<<<<<<<<
+ * roots = []
+ * if ax2 != 0:
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ay, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 318, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ay2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":319
+ * ax2 = ax * 2.0
+ * ay2 = ay * 2.0
+ * roots = [] # <<<<<<<<<<<<<<
+ * if ax2 != 0:
+ * roots.append(-bx / ax2)
+*/
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 319, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_roots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":320
+ * ay2 = ay * 2.0
+ * roots = []
+ * if ax2 != 0: # <<<<<<<<<<<<<<
+ * roots.append(-bx / ax2)
+ * if ay2 != 0:
+*/
+ __pyx_t_10 = (__Pyx_PyLong_BoolNeObjC(__pyx_v_ax2, __pyx_mstate_global->__pyx_int_0, 0, 0)); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 320, __pyx_L1_error)
+ if (__pyx_t_10) {
+
+ /* "fontTools/misc/bezierTools.py":321
+ * roots = []
+ * if ax2 != 0:
+ * roots.append(-bx / ax2) # <<<<<<<<<<<<<<
+ * if ay2 != 0:
+ * roots.append(-by / ay2)
+*/
+ __pyx_t_1 = PyNumber_Negative(__pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_v_ax2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_roots, __pyx_t_5); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":320
+ * ay2 = ay * 2.0
+ * roots = []
+ * if ax2 != 0: # <<<<<<<<<<<<<<
+ * roots.append(-bx / ax2)
+ * if ay2 != 0:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":322
+ * if ax2 != 0:
+ * roots.append(-bx / ax2)
+ * if ay2 != 0: # <<<<<<<<<<<<<<
+ * roots.append(-by / ay2)
+ * points = [
+*/
+ __pyx_t_10 = (__Pyx_PyLong_BoolNeObjC(__pyx_v_ay2, __pyx_mstate_global->__pyx_int_0, 0, 0)); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 322, __pyx_L1_error)
+ if (__pyx_t_10) {
+
+ /* "fontTools/misc/bezierTools.py":323
+ * roots.append(-bx / ax2)
+ * if ay2 != 0:
+ * roots.append(-by / ay2) # <<<<<<<<<<<<<<
+ * points = [
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+*/
+ __pyx_t_5 = PyNumber_Negative(__pyx_v_by); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 323, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_t_5, __pyx_v_ay2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 323, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_roots, __pyx_t_1); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 323, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":322
+ * if ax2 != 0:
+ * roots.append(-bx / ax2)
+ * if ay2 != 0: # <<<<<<<<<<<<<<
+ * roots.append(-by / ay2)
+ * points = [
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":328
+ * for t in roots
+ * if 0 <= t < 1
+ * ] + [pt1, pt3] # <<<<<<<<<<<<<<
+ * return calcBounds(points)
+ *
+*/
+ { /* enter inner scope */
+
+ /* "fontTools/misc/bezierTools.py":324
+ * if ay2 != 0:
+ * roots.append(-by / ay2)
+ * points = [ # <<<<<<<<<<<<<<
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ * for t in roots
+*/
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 324, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":326
+ * points = [
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ * for t in roots # <<<<<<<<<<<<<<
+ * if 0 <= t < 1
+ * ] + [pt1, pt3]
+*/
+ __pyx_t_5 = __pyx_v_roots; __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_12 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_5);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 326, __pyx_L15_error)
+ #endif
+ if (__pyx_t_12 >= __pyx_temp) break;
+ }
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(__pyx_t_5, __pyx_t_12, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_12;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 326, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_7genexpr__pyx_v_t, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":327
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ * for t in roots
+ * if 0 <= t < 1 # <<<<<<<<<<<<<<
+ * ] + [pt1, pt3]
+ * return calcBounds(points)
+*/
+ __pyx_t_2 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_7genexpr__pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 327, __pyx_L15_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_2)) {
+ __Pyx_DECREF(__pyx_t_2);
+ __pyx_t_2 = PyObject_RichCompare(__pyx_7genexpr__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 327, __pyx_L15_error)
+ }
+ __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 327, __pyx_L15_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_10) {
+
+ /* "fontTools/misc/bezierTools.py":325
+ * roots.append(-by / ay2)
+ * points = [
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy) # <<<<<<<<<<<<<<
+ * for t in roots
+ * if 0 <= t < 1
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ax, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_2, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_bx, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_8 = PyNumber_Add(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_8, __pyx_v_cx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = PyNumber_Multiply(__pyx_v_ay, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_8, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = PyNumber_Multiply(__pyx_v_by, __pyx_7genexpr__pyx_v_t); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_3, __pyx_t_8); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = PyNumber_Add(__pyx_t_6, __pyx_v_cy); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyTuple_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 325, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 325, __pyx_L15_error);
+ __Pyx_GIVEREF(__pyx_t_8);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_8) != (0)) __PYX_ERR(0, 325, __pyx_L15_error);
+ __pyx_t_2 = 0;
+ __pyx_t_8 = 0;
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_6))) __PYX_ERR(0, 324, __pyx_L15_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":327
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ * for t in roots
+ * if 0 <= t < 1 # <<<<<<<<<<<<<<
+ * ] + [pt1, pt3]
+ * return calcBounds(points)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":326
+ * points = [
+ * (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ * for t in roots # <<<<<<<<<<<<<<
+ * if 0 <= t < 1
+ * ] + [pt1, pt3]
+*/
+ }
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_XDECREF(__pyx_7genexpr__pyx_v_t); __pyx_7genexpr__pyx_v_t = 0;
+ goto __pyx_L20_exit_scope;
+ __pyx_L15_error:;
+ __Pyx_XDECREF(__pyx_7genexpr__pyx_v_t); __pyx_7genexpr__pyx_v_t = 0;
+ goto __pyx_L1_error;
+ __pyx_L20_exit_scope:;
+ } /* exit inner scope */
+
+ /* "fontTools/misc/bezierTools.py":328
+ * for t in roots
+ * if 0 <= t < 1
+ * ] + [pt1, pt3] # <<<<<<<<<<<<<<
+ * return calcBounds(points)
+ *
+*/
+ __pyx_t_5 = PyList_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_5, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 328, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_v_pt3);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_5, 1, __pyx_v_pt3) != (0)) __PYX_ERR(0, 328, __pyx_L1_error);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_1, __pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_v_points = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":329
+ * if 0 <= t < 1
+ * ] + [pt1, pt3]
+ * return calcBounds(points) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_calcBounds); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 329, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_v_points};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 329, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":298
+ *
+ *
+ * def calcQuadraticBounds(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticBounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_ax2);
+ __Pyx_XDECREF(__pyx_v_ay2);
+ __Pyx_XDECREF(__pyx_v_roots);
+ __Pyx_XDECREF(__pyx_v_points);
+ __Pyx_XDECREF(__pyx_7genexpr__pyx_v_t);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":332
+ *
+ *
+ * def approximateCubicArcLength(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Approximates the arc length for a cubic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_19approximateCubicArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_18approximateCubicArcLength, "approximateCubicArcLength(pt1, pt2, pt3, pt4)\n\nApproximates the arc length for a cubic Bezier segment.\n\nUses Gauss-Lobatto quadrature with n=5 points to approximate arc length.\nSee :func:`calcCubicArcLength` for a slower but more accurate result.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n\nReturns:\n Arc length value.\n\nExample::\n\n >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))\n 190.04332968932817\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 50), (100, 100))\n 154.8852074945903\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (150, 0)) # line; exact result should be 150.\n 149.99999999999991\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (-50, 0)) # cusp; exact result should be 150.\n 136.9267662156362\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp\n 154.80848416537057");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_19approximateCubicArcLength = {"approximateCubicArcLength", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_19approximateCubicArcLength, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_18approximateCubicArcLength};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_19approximateCubicArcLength(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("approximateCubicArcLength (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 332, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 332, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 332, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 332, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 332, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "approximateCubicArcLength", 0) < (0)) __PYX_ERR(0, 332, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("approximateCubicArcLength", 1, 4, 4, i); __PYX_ERR(0, 332, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 332, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 332, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 332, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 332, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("approximateCubicArcLength", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 332, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateCubicArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_18approximateCubicArcLength(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_18approximateCubicArcLength(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("approximateCubicArcLength", 0);
+
+ /* "fontTools/misc/bezierTools.py":357
+ * 154.80848416537057
+ * """
+ * return approximateCubicArcLengthC( # <<<<<<<<<<<<<<
+ * complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4)
+ * )
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLengthC); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 357, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+
+ /* "fontTools/misc/bezierTools.py":358
+ * """
+ * return approximateCubicArcLengthC(
+ * complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4) # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_pt4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_4, NULL); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 358, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_8};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_9, (5-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 357, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":332
+ *
+ *
+ * def approximateCubicArcLength(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Approximates the arc length for a cubic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateCubicArcLength", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":362
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_21approximateCubicArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_20approximateCubicArcLengthC, "approximateCubicArcLengthC(double complex pt1, double complex pt2, double complex pt3, double complex pt4)\n\nApproximates the arc length for a cubic Bezier segment.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.\n\nReturns:\n Arc length value.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_21approximateCubicArcLengthC = {"approximateCubicArcLengthC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_21approximateCubicArcLengthC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_20approximateCubicArcLengthC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_21approximateCubicArcLengthC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("approximateCubicArcLengthC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 362, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 362, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 362, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 362, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 362, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "approximateCubicArcLengthC", 0) < (0)) __PYX_ERR(0, 362, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("approximateCubicArcLengthC", 1, 4, 4, i); __PYX_ERR(0, 362, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 362, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 362, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 362, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 362, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 376, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 376, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 376, __pyx_L3_error)
+ __pyx_v_pt4 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 376, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("approximateCubicArcLengthC", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 362, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateCubicArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_20approximateCubicArcLengthC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_20approximateCubicArcLengthC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4) {
+ double __pyx_v_v0;
+ double __pyx_v_v1;
+ double __pyx_v_v2;
+ double __pyx_v_v3;
+ double __pyx_v_v4;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("approximateCubicArcLengthC", 0);
+
+ /* "fontTools/misc/bezierTools.py":393
+ * # abs(BezierCurveC[3].diff(t).subs({t:T})) for T in sorted(0, .5(3/7)**.5/2, .5, 1),
+ * # weighted 1/20, 49/180, 32/90, 49/180, 1/20 respectively.
+ * v0 = abs(pt2 - pt1) * 0.15 # <<<<<<<<<<<<<<
+ * v1 = abs(
+ * -0.558983582205757 * pt1
+*/
+ __pyx_v_v0 = (__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_pt2, __pyx_v_pt1)) * 0.15);
+
+ /* "fontTools/misc/bezierTools.py":394
+ * # weighted 1/20, 49/180, 32/90, 49/180, 1/20 respectively.
+ * v0 = abs(pt2 - pt1) * 0.15
+ * v1 = abs( # <<<<<<<<<<<<<<
+ * -0.558983582205757 * pt1
+ * + 0.325650248872424 * pt2
+*/
+ __pyx_v_v1 = __Pyx_c_abs_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(-0.558983582205757, 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.325650248872424, 0), __pyx_v_pt2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.208983582205757, 0), __pyx_v_pt3)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.024349751127576, 0), __pyx_v_pt4)));
+
+ /* "fontTools/misc/bezierTools.py":400
+ * + 0.024349751127576 * pt4
+ * )
+ * v2 = abs(pt4 - pt1 + pt3 - pt2) * 0.26666666666666666 # <<<<<<<<<<<<<<
+ * v3 = abs(
+ * -0.024349751127576 * pt1
+*/
+ __pyx_v_v2 = (__Pyx_c_abs_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__Pyx_c_diff_double(__pyx_v_pt4, __pyx_v_pt1), __pyx_v_pt3), __pyx_v_pt2)) * 0.26666666666666666);
+
+ /* "fontTools/misc/bezierTools.py":401
+ * )
+ * v2 = abs(pt4 - pt1 + pt3 - pt2) * 0.26666666666666666
+ * v3 = abs( # <<<<<<<<<<<<<<
+ * -0.024349751127576 * pt1
+ * - 0.208983582205757 * pt2
+*/
+ __pyx_v_v3 = __Pyx_c_abs_double(__Pyx_c_sum_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(-0.024349751127576, 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.208983582205757, 0), __pyx_v_pt2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.325650248872424, 0), __pyx_v_pt3)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(0.558983582205757, 0), __pyx_v_pt4)));
+
+ /* "fontTools/misc/bezierTools.py":407
+ * + 0.558983582205757 * pt4
+ * )
+ * v4 = abs(pt4 - pt3) * 0.15 # <<<<<<<<<<<<<<
+ *
+ * return v0 + v1 + v2 + v3 + v4
+*/
+ __pyx_v_v4 = (__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_pt4, __pyx_v_pt3)) * 0.15);
+
+ /* "fontTools/misc/bezierTools.py":409
+ * v4 = abs(pt4 - pt3) * 0.15
+ *
+ * return v0 + v1 + v2 + v3 + v4 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyFloat_FromDouble(((((__pyx_v_v0 + __pyx_v_v1) + __pyx_v_v2) + __pyx_v_v3) + __pyx_v_v4)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 409, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":362
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.approximateCubicArcLengthC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":412
+ *
+ *
+ * def calcCubicBounds(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_23calcCubicBounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_22calcCubicBounds, "calcCubicBounds(pt1, pt2, pt3, pt4)\n\nCalculates the bounding rectangle for a quadratic Bezier segment.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n\nReturns:\n A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.\n\nExample::\n\n >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))\n (0, 0, 100, 75.0)\n >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))\n (0.0, 0.0, 100, 100)\n >>> print(\"%f %f %f %f\" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))\n 35.566243 0.000000 64.433757 75.000000");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_23calcCubicBounds = {"calcCubicBounds", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_23calcCubicBounds, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_22calcCubicBounds};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_23calcCubicBounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcCubicBounds (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 412, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 412, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 412, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 412, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 412, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcCubicBounds", 0) < (0)) __PYX_ERR(0, 412, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcCubicBounds", 1, 4, 4, i); __PYX_ERR(0, 412, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 412, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 412, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 412, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 412, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcCubicBounds", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 412, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicBounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_22calcCubicBounds(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_22calcCubicBounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4) {
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_dx = NULL;
+ PyObject *__pyx_v_dy = NULL;
+ PyObject *__pyx_v_ax3 = NULL;
+ PyObject *__pyx_v_ay3 = NULL;
+ PyObject *__pyx_v_bx2 = NULL;
+ PyObject *__pyx_v_by2 = NULL;
+ PyObject *__pyx_v_xRoots = NULL;
+ PyObject *__pyx_v_yRoots = NULL;
+ PyObject *__pyx_v_roots = NULL;
+ PyObject *__pyx_v_points = NULL;
+ PyObject *__pyx_8genexpr1__pyx_v_t = NULL;
+ PyObject *__pyx_8genexpr2__pyx_v_t = NULL;
+ PyObject *__pyx_8genexpr3__pyx_v_t = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ Py_ssize_t __pyx_t_11;
+ PyObject *(*__pyx_t_12)(PyObject *);
+ int __pyx_t_13;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicBounds", 0);
+
+ /* "fontTools/misc/bezierTools.py":430
+ * 35.566243 0.000000 64.433757 75.000000
+ * """
+ * (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ * # calc first derivative
+ * ax3 = ax * 3.0
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcCubicParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (5-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_6);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_8(__pyx_t_7); if (unlikely(!item)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 4) < (0)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_3))) || (PyList_CheckExact(__pyx_t_3))) {
+ PyObject* sequence = __pyx_t_3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_7 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_7);
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_9);
+ } else {
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_7);
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ }
+ #else
+ __pyx_t_7 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_9 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ #endif
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ index = 0; __pyx_t_7 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_7)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_7);
+ index = 1; __pyx_t_9 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_9)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_9);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_10), 2) < (0)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_7;
+ __pyx_t_7 = 0;
+ __pyx_v_ay = __pyx_t_9;
+ __pyx_t_9 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_2))) || (PyList_CheckExact(__pyx_t_2))) {
+ PyObject* sequence = __pyx_t_2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_9);
+ __pyx_t_7 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_7);
+ } else {
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_7);
+ }
+ #else
+ __pyx_t_9 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_7 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ #endif
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ index = 0; __pyx_t_9 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_9)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_9);
+ index = 1; __pyx_t_7 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_7)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_10), 2) < (0)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_9;
+ __pyx_t_9 = 0;
+ __pyx_v_by = __pyx_t_7;
+ __pyx_t_7 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_5))) || (PyList_CheckExact(__pyx_t_5))) {
+ PyObject* sequence = __pyx_t_5;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_7 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_7);
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_9);
+ } else {
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_7);
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ }
+ #else
+ __pyx_t_7 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_9 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ #endif
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_5); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ index = 0; __pyx_t_7 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_7)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_7);
+ index = 1; __pyx_t_9 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_9)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_9);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_10), 2) < (0)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_7;
+ __pyx_t_7 = 0;
+ __pyx_v_cy = __pyx_t_9;
+ __pyx_t_9 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_6))) || (PyList_CheckExact(__pyx_t_6))) {
+ PyObject* sequence = __pyx_t_6;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_9);
+ __pyx_t_7 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_7);
+ } else {
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_7);
+ }
+ #else
+ __pyx_t_9 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_7 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ #endif
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_6); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ index = 0; __pyx_t_9 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_9)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_9);
+ index = 1; __pyx_t_7 = __pyx_t_8(__pyx_t_10); if (unlikely(!__pyx_t_7)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_10), 2) < (0)) __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L12_unpacking_done;
+ __pyx_L11_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 430, __pyx_L1_error)
+ __pyx_L12_unpacking_done:;
+ }
+ __pyx_v_dx = __pyx_t_9;
+ __pyx_t_9 = 0;
+ __pyx_v_dy = __pyx_t_7;
+ __pyx_t_7 = 0;
+
+ /* "fontTools/misc/bezierTools.py":432
+ * (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
+ * # calc first derivative
+ * ax3 = ax * 3.0 # <<<<<<<<<<<<<<
+ * ay3 = ay * 3.0
+ * bx2 = bx * 2.0
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ax, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 432, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ax3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":433
+ * # calc first derivative
+ * ax3 = ax * 3.0
+ * ay3 = ay * 3.0 # <<<<<<<<<<<<<<
+ * bx2 = bx * 2.0
+ * by2 = by * 2.0
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ay, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 433, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ay3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":434
+ * ax3 = ax * 3.0
+ * ay3 = ay * 3.0
+ * bx2 = bx * 2.0 # <<<<<<<<<<<<<<
+ * by2 = by * 2.0
+ * xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_bx, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 434, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_bx2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":435
+ * ay3 = ay * 3.0
+ * bx2 = bx * 2.0
+ * by2 = by * 2.0 # <<<<<<<<<<<<<<
+ * xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
+ * yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_by, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 435, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_by2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":436
+ * bx2 = bx * 2.0
+ * by2 = by * 2.0
+ * xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1] # <<<<<<<<<<<<<<
+ * yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
+ * roots = xRoots + yRoots
+*/
+ { /* enter inner scope */
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 436, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_solveQuadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 436, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_5, __pyx_v_ax3, __pyx_v_bx2, __pyx_v_cx};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 436, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ if (likely(PyList_CheckExact(__pyx_t_6)) || PyTuple_CheckExact(__pyx_t_6)) {
+ __pyx_t_2 = __pyx_t_6; __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_11 = 0;
+ __pyx_t_12 = NULL;
+ } else {
+ __pyx_t_11 = -1; __pyx_t_2 = PyObject_GetIter(__pyx_t_6); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 436, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_12 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_2); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 436, __pyx_L15_error)
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ for (;;) {
+ if (likely(!__pyx_t_12)) {
+ if (likely(PyList_CheckExact(__pyx_t_2))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 436, __pyx_L15_error)
+ #endif
+ if (__pyx_t_11 >= __pyx_temp) break;
+ }
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(__pyx_t_2, __pyx_t_11, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_11;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_2);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 436, __pyx_L15_error)
+ #endif
+ if (__pyx_t_11 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_6 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_2, __pyx_t_11));
+ #else
+ __pyx_t_6 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_11);
+ #endif
+ ++__pyx_t_11;
+ }
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 436, __pyx_L15_error)
+ } else {
+ __pyx_t_6 = __pyx_t_12(__pyx_t_2);
+ if (unlikely(!__pyx_t_6)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 436, __pyx_L15_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_XDECREF_SET(__pyx_8genexpr1__pyx_v_t, __pyx_t_6);
+ __pyx_t_6 = 0;
+ __pyx_t_6 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_8genexpr1__pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 436, __pyx_L15_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_6)) {
+ __Pyx_DECREF(__pyx_t_6);
+ __pyx_t_6 = PyObject_RichCompare(__pyx_8genexpr1__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 436, __pyx_L15_error)
+ }
+ __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 436, __pyx_L15_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_13) {
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_8genexpr1__pyx_v_t))) __PYX_ERR(0, 436, __pyx_L15_error)
+ }
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_t); __pyx_8genexpr1__pyx_v_t = 0;
+ goto __pyx_L20_exit_scope;
+ __pyx_L15_error:;
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_t); __pyx_8genexpr1__pyx_v_t = 0;
+ goto __pyx_L1_error;
+ __pyx_L20_exit_scope:;
+ } /* exit inner scope */
+ __pyx_v_xRoots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":437
+ * by2 = by * 2.0
+ * xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
+ * yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1] # <<<<<<<<<<<<<<
+ * roots = xRoots + yRoots
+ *
+*/
+ { /* enter inner scope */
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 437, __pyx_L23_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_solveQuadratic); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 437, __pyx_L23_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_6, __pyx_v_ay3, __pyx_v_by2, __pyx_v_cy};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 437, __pyx_L23_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ if (likely(PyList_CheckExact(__pyx_t_2)) || PyTuple_CheckExact(__pyx_t_2)) {
+ __pyx_t_5 = __pyx_t_2; __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_11 = 0;
+ __pyx_t_12 = NULL;
+ } else {
+ __pyx_t_11 = -1; __pyx_t_5 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 437, __pyx_L23_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_12 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 437, __pyx_L23_error)
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ for (;;) {
+ if (likely(!__pyx_t_12)) {
+ if (likely(PyList_CheckExact(__pyx_t_5))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_5);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 437, __pyx_L23_error)
+ #endif
+ if (__pyx_t_11 >= __pyx_temp) break;
+ }
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(__pyx_t_5, __pyx_t_11, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_11;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_5);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 437, __pyx_L23_error)
+ #endif
+ if (__pyx_t_11 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_2 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_5, __pyx_t_11));
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(__pyx_t_5, __pyx_t_11);
+ #endif
+ ++__pyx_t_11;
+ }
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 437, __pyx_L23_error)
+ } else {
+ __pyx_t_2 = __pyx_t_12(__pyx_t_5);
+ if (unlikely(!__pyx_t_2)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 437, __pyx_L23_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_8genexpr2__pyx_v_t, __pyx_t_2);
+ __pyx_t_2 = 0;
+ __pyx_t_2 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_8genexpr2__pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 437, __pyx_L23_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_2)) {
+ __Pyx_DECREF(__pyx_t_2);
+ __pyx_t_2 = PyObject_RichCompare(__pyx_8genexpr2__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 437, __pyx_L23_error)
+ }
+ __pyx_t_13 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_13 < 0))) __PYX_ERR(0, 437, __pyx_L23_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_13) {
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_8genexpr2__pyx_v_t))) __PYX_ERR(0, 437, __pyx_L23_error)
+ }
+ }
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_t); __pyx_8genexpr2__pyx_v_t = 0;
+ goto __pyx_L28_exit_scope;
+ __pyx_L23_error:;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_t); __pyx_8genexpr2__pyx_v_t = 0;
+ goto __pyx_L1_error;
+ __pyx_L28_exit_scope:;
+ } /* exit inner scope */
+ __pyx_v_yRoots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":438
+ * xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
+ * yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
+ * roots = xRoots + yRoots # <<<<<<<<<<<<<<
+ *
+ * points = [
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_xRoots, __pyx_v_yRoots); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 438, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_roots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":446
+ * )
+ * for t in roots
+ * ] + [pt1, pt4] # <<<<<<<<<<<<<<
+ * return calcBounds(points)
+ *
+*/
+ { /* enter inner scope */
+
+ /* "fontTools/misc/bezierTools.py":440
+ * roots = xRoots + yRoots
+ *
+ * points = [ # <<<<<<<<<<<<<<
+ * (
+ * ax * t * t * t + bx * t * t + cx * t + dx,
+*/
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 440, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":445
+ * ay * t * t * t + by * t * t + cy * t + dy,
+ * )
+ * for t in roots # <<<<<<<<<<<<<<
+ * ] + [pt1, pt4]
+ * return calcBounds(points)
+*/
+ __pyx_t_5 = __pyx_v_roots; __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_11 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_5);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 445, __pyx_L31_error)
+ #endif
+ if (__pyx_t_11 >= __pyx_temp) break;
+ }
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(__pyx_t_5, __pyx_t_11, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_11;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 445, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_8genexpr3__pyx_v_t, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":442
+ * points = [
+ * (
+ * ax * t * t * t + bx * t * t + cx * t + dx, # <<<<<<<<<<<<<<
+ * ay * t * t * t + by * t * t + cy * t + dy,
+ * )
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ax, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_6 = PyNumber_Multiply(__pyx_t_2, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_6, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Multiply(__pyx_v_bx, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_6, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Add(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_cx, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_6, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_2, __pyx_v_dx); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":443
+ * (
+ * ax * t * t * t + bx * t * t + cx * t + dx,
+ * ay * t * t * t + by * t * t + cy * t + dy, # <<<<<<<<<<<<<<
+ * )
+ * for t in roots
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ay, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_6 = PyNumber_Multiply(__pyx_t_2, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_6, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Multiply(__pyx_v_by, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = PyNumber_Multiply(__pyx_t_6, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Add(__pyx_t_2, __pyx_t_7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = PyNumber_Multiply(__pyx_v_cy, __pyx_8genexpr3__pyx_v_t); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_6, __pyx_t_7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = PyNumber_Add(__pyx_t_2, __pyx_v_dy); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 443, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":442
+ * points = [
+ * (
+ * ax * t * t * t + bx * t * t + cx * t + dx, # <<<<<<<<<<<<<<
+ * ay * t * t * t + by * t * t + cy * t + dy,
+ * )
+*/
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 442, __pyx_L31_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 442, __pyx_L31_error);
+ __Pyx_GIVEREF(__pyx_t_7);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_7) != (0)) __PYX_ERR(0, 442, __pyx_L31_error);
+ __pyx_t_3 = 0;
+ __pyx_t_7 = 0;
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_2))) __PYX_ERR(0, 440, __pyx_L31_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":445
+ * ay * t * t * t + by * t * t + cy * t + dy,
+ * )
+ * for t in roots # <<<<<<<<<<<<<<
+ * ] + [pt1, pt4]
+ * return calcBounds(points)
+*/
+ }
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_t); __pyx_8genexpr3__pyx_v_t = 0;
+ goto __pyx_L35_exit_scope;
+ __pyx_L31_error:;
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_t); __pyx_8genexpr3__pyx_v_t = 0;
+ goto __pyx_L1_error;
+ __pyx_L35_exit_scope:;
+ } /* exit inner scope */
+
+ /* "fontTools/misc/bezierTools.py":446
+ * )
+ * for t in roots
+ * ] + [pt1, pt4] # <<<<<<<<<<<<<<
+ * return calcBounds(points)
+ *
+*/
+ __pyx_t_5 = PyList_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 446, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_5, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 446, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt4);
+ __Pyx_GIVEREF(__pyx_v_pt4);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_5, 1, __pyx_v_pt4) != (0)) __PYX_ERR(0, 446, __pyx_L1_error);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_t_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 446, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_v_points = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":447
+ * for t in roots
+ * ] + [pt1, pt4]
+ * return calcBounds(points) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_calcBounds); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 447, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_v_points};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 447, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":412
+ *
+ *
+ * def calcCubicBounds(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicBounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_dx);
+ __Pyx_XDECREF(__pyx_v_dy);
+ __Pyx_XDECREF(__pyx_v_ax3);
+ __Pyx_XDECREF(__pyx_v_ay3);
+ __Pyx_XDECREF(__pyx_v_bx2);
+ __Pyx_XDECREF(__pyx_v_by2);
+ __Pyx_XDECREF(__pyx_v_xRoots);
+ __Pyx_XDECREF(__pyx_v_yRoots);
+ __Pyx_XDECREF(__pyx_v_roots);
+ __Pyx_XDECREF(__pyx_v_points);
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_t);
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_t);
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_t);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":450
+ *
+ *
+ * def splitLine(pt1, pt2, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a line at a given coordinate.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_25splitLine(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_24splitLine, "splitLine(pt1, pt2, where, isHorizontal)\n\nSplit a line at a given coordinate.\n\nArgs:\n pt1: Start point of line as 2D tuple.\n pt2: End point of line as 2D tuple.\n where: Position at which to split the line.\n isHorizontal: Direction of the ray splitting the line. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\nReturns:\n A list of two line segments (each line segment being two 2D tuples)\n if the line was successfully split, or a list containing the original\n line.\n\nExample::\n\n >>> printSegments(splitLine((0, 0), (100, 100), 50, True))\n ((0, 0), (50, 50))\n ((50, 50), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 100, True))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 0, True))\n ((0, 0), (0, 0))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 0, False))\n ((0, 0), (0, 0))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((100, 0), (0, 0), 50, False))\n ((100, 0), (50, 0))\n ((50, 0), (0, 0))\n >>> printSegments(splitLine((0, 100), (0, 0), 50, True))\n ((0, 100), (0, 50))\n ((0, 50), (0, 0))");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_25splitLine = {"splitLine", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_25splitLine, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_24splitLine};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_25splitLine(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_where = 0;
+ PyObject *__pyx_v_isHorizontal = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitLine (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_where,&__pyx_mstate_global->__pyx_n_u_isHorizontal,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 450, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 450, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 450, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 450, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 450, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "splitLine", 0) < (0)) __PYX_ERR(0, 450, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitLine", 1, 4, 4, i); __PYX_ERR(0, 450, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 450, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 450, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 450, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 450, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_where = values[2];
+ __pyx_v_isHorizontal = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitLine", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 450, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitLine", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_24splitLine(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_where, __pyx_v_isHorizontal);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_24splitLine(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal) {
+ PyObject *__pyx_v_pt1x = NULL;
+ PyObject *__pyx_v_pt1y = NULL;
+ PyObject *__pyx_v_pt2x = NULL;
+ PyObject *__pyx_v_pt2y = NULL;
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_t = NULL;
+ PyObject *__pyx_v_midPt = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ int __pyx_t_5;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitLine", 0);
+
+ /* "fontTools/misc/bezierTools.py":486
+ * ((0, 50), (0, 0))
+ * """
+ * pt1x, pt1y = pt1 # <<<<<<<<<<<<<<
+ * pt2x, pt2y = pt2
+ *
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt1))) || (PyList_CheckExact(__pyx_v_pt1))) {
+ PyObject* sequence = __pyx_v_pt1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 486, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 486, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 486, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_pt1x = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_pt1y = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":487
+ * """
+ * pt1x, pt1y = pt1
+ * pt2x, pt2y = pt2 # <<<<<<<<<<<<<<
+ *
+ * ax = pt2x - pt1x
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt2))) || (PyList_CheckExact(__pyx_v_pt2))) {
+ PyObject* sequence = __pyx_v_pt2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 487, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 487, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 487, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_pt2x = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_pt2y = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":489
+ * pt2x, pt2y = pt2
+ *
+ * ax = pt2x - pt1x # <<<<<<<<<<<<<<
+ * ay = pt2y - pt1y
+ *
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_pt2x, __pyx_v_pt1x); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 489, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ax = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":490
+ *
+ * ax = pt2x - pt1x
+ * ay = pt2y - pt1y # <<<<<<<<<<<<<<
+ *
+ * bx = pt1x
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_pt2y, __pyx_v_pt1y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 490, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_ay = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":492
+ * ay = pt2y - pt1y
+ *
+ * bx = pt1x # <<<<<<<<<<<<<<
+ * by = pt1y
+ *
+*/
+ __Pyx_INCREF(__pyx_v_pt1x);
+ __pyx_v_bx = __pyx_v_pt1x;
+
+ /* "fontTools/misc/bezierTools.py":493
+ *
+ * bx = pt1x
+ * by = pt1y # <<<<<<<<<<<<<<
+ *
+ * a = (ax, ay)[isHorizontal]
+*/
+ __Pyx_INCREF(__pyx_v_pt1y);
+ __pyx_v_by = __pyx_v_pt1y;
+
+ /* "fontTools/misc/bezierTools.py":495
+ * by = pt1y
+ *
+ * a = (ax, ay)[isHorizontal] # <<<<<<<<<<<<<<
+ *
+ * if a == 0:
+*/
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 495, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_ax);
+ __Pyx_GIVEREF(__pyx_v_ax);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_ax) != (0)) __PYX_ERR(0, 495, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_ay);
+ __Pyx_GIVEREF(__pyx_v_ay);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_ay) != (0)) __PYX_ERR(0, 495, __pyx_L1_error);
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_t_1, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 495, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_a = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":497
+ * a = (ax, ay)[isHorizontal]
+ *
+ * if a == 0: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2)]
+ * t = (where - (bx, by)[isHorizontal]) / a
+*/
+ __pyx_t_5 = (__Pyx_PyLong_BoolEqObjC(__pyx_v_a, __pyx_mstate_global->__pyx_int_0, 0, 0)); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 497, __pyx_L1_error)
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":498
+ *
+ * if a == 0:
+ * return [(pt1, pt2)] # <<<<<<<<<<<<<<
+ * t = (where - (bx, by)[isHorizontal]) / a
+ * if 0 <= t < 1:
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 498, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 498, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 498, __pyx_L1_error);
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 498, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 498, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":497
+ * a = (ax, ay)[isHorizontal]
+ *
+ * if a == 0: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2)]
+ * t = (where - (bx, by)[isHorizontal]) / a
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":499
+ * if a == 0:
+ * return [(pt1, pt2)]
+ * t = (where - (bx, by)[isHorizontal]) / a # <<<<<<<<<<<<<<
+ * if 0 <= t < 1:
+ * midPt = ax * t + bx, ay * t + by
+*/
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 499, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_bx);
+ __Pyx_GIVEREF(__pyx_v_bx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_bx) != (0)) __PYX_ERR(0, 499, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_by);
+ __Pyx_GIVEREF(__pyx_v_by);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_by) != (0)) __PYX_ERR(0, 499, __pyx_L1_error);
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_t_1, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 499, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_where, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 499, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_v_a); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 499, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_t = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":500
+ * return [(pt1, pt2)]
+ * t = (where - (bx, by)[isHorizontal]) / a
+ * if 0 <= t < 1: # <<<<<<<<<<<<<<
+ * midPt = ax * t + bx, ay * t + by
+ * return [(pt1, midPt), (midPt, pt2)]
+*/
+ __pyx_t_2 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 500, __pyx_L1_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_2)) {
+ __Pyx_DECREF(__pyx_t_2);
+ __pyx_t_2 = PyObject_RichCompare(__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 500, __pyx_L1_error)
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 500, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":501
+ * t = (where - (bx, by)[isHorizontal]) / a
+ * if 0 <= t < 1:
+ * midPt = ax * t + bx, ay * t + by # <<<<<<<<<<<<<<
+ * return [(pt1, midPt), (midPt, pt2)]
+ * else:
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ax, __pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 501, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 501, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ay, __pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 501, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Add(__pyx_t_2, __pyx_v_by); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 501, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 501, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 501, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 501, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_v_midPt = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":502
+ * if 0 <= t < 1:
+ * midPt = ax * t + bx, ay * t + by
+ * return [(pt1, midPt), (midPt, pt2)] # <<<<<<<<<<<<<<
+ * else:
+ * return [(pt1, pt2)]
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 502, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_midPt);
+ __Pyx_GIVEREF(__pyx_v_midPt);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_midPt) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 502, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_midPt);
+ __Pyx_GIVEREF(__pyx_v_midPt);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_midPt) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __pyx_t_1 = PyList_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 502, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 502, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":500
+ * return [(pt1, pt2)]
+ * t = (where - (bx, by)[isHorizontal]) / a
+ * if 0 <= t < 1: # <<<<<<<<<<<<<<
+ * midPt = ax * t + bx, ay * t + by
+ * return [(pt1, midPt), (midPt, pt2)]
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":504
+ * return [(pt1, midPt), (midPt, pt2)]
+ * else:
+ * return [(pt1, pt2)] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ /*else*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 504, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 504, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 504, __pyx_L1_error);
+ __pyx_t_3 = PyList_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 504, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 504, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":450
+ *
+ *
+ * def splitLine(pt1, pt2, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a line at a given coordinate.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitLine", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_pt1x);
+ __Pyx_XDECREF(__pyx_v_pt1y);
+ __Pyx_XDECREF(__pyx_v_pt2x);
+ __Pyx_XDECREF(__pyx_v_pt2y);
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_t);
+ __Pyx_XDECREF(__pyx_v_midPt);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":507
+ *
+ *
+ * def splitQuadratic(pt1, pt2, pt3, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at a given coordinate.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_27splitQuadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_26splitQuadratic, "splitQuadratic(pt1, pt2, pt3, where, isHorizontal)\n\nSplit a quadratic Bezier curve at a given coordinate.\n\nArgs:\n pt1,pt2,pt3: Control points of the Bezier as 2D tuples.\n where: Position at which to split the curve.\n isHorizontal: Direction of the ray splitting the curve. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\nReturns:\n A list of two curve segments (each curve segment being three 2D tuples)\n if the curve was successfully split, or a list containing the original\n curve.\n\nExample::\n\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))\n ((0, 0), (50, 100), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))\n ((0, 0), (12.5, 25), (25, 37.5))\n ((25, 37.5), (62.5, 75), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))\n ((0, 0), (7.32233, 14.6447), (14.6447, 25))\n ((14.6447, 25), (50, 75), (85.3553, 25))\n ((85.3553, 25), (92.6777, 14.6447), (100, -7.10543e-15))\n >>> # XXX I'm not at all sure if the following behavior is desirable:\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (50, 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_27splitQuadratic = {"splitQuadratic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_27splitQuadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_26splitQuadratic};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_27splitQuadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_where = 0;
+ PyObject *__pyx_v_isHorizontal = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitQuadratic (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_where,&__pyx_mstate_global->__pyx_n_u_isHorizontal,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 507, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 507, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 507, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 507, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 507, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 507, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "splitQuadratic", 0) < (0)) __PYX_ERR(0, 507, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 5; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitQuadratic", 1, 5, 5, i); __PYX_ERR(0, 507, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 5)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 507, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 507, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 507, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 507, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 507, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_where = values[3];
+ __pyx_v_isHorizontal = values[4];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitQuadratic", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 507, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitQuadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_26splitQuadratic(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_where, __pyx_v_isHorizontal);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_14splitQuadratic_2generator2(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":546
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3)]
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_14splitQuadratic_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 546, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_14splitQuadratic_2generator2, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[0]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_splitQuadratic_locals_genexpr, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitQuadratic.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_14splitQuadratic_2generator2(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __pyx_r = PyList_New(0); if (unlikely(!__pyx_r)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_r);
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 546, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 546, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 546, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 546, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 546, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 546, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_t);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_t, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_4 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_cur_scope->__pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 546, __pyx_L1_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_4)) {
+ __Pyx_DECREF(__pyx_t_4);
+ __pyx_t_4 = PyObject_RichCompare(__pyx_cur_scope->__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 546, __pyx_L1_error)
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (__pyx_t_5) {
+ if (unlikely(__Pyx_ListComp_Append(__pyx_r, (PyObject*)__pyx_cur_scope->__pyx_v_t))) __PYX_ERR(0, 546, __pyx_L1_error)
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_r); __pyx_r = 0;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":507
+ *
+ *
+ * def splitQuadratic(pt1, pt2, pt3, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at a given coordinate.
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_26splitQuadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal) {
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_b = NULL;
+ PyObject *__pyx_v_c = NULL;
+ PyObject *__pyx_v_solutions = NULL;
+ PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_14splitQuadratic_2generator2 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ PyObject *__pyx_t_8 = NULL;
+ PyObject *__pyx_t_9 = NULL;
+ int __pyx_t_10;
+ int __pyx_t_11;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitQuadratic", 0);
+
+ /* "fontTools/misc/bezierTools.py":542
+ * ((50, 50), (75, 50), (100, 0))
+ * """
+ * a, b, c = calcQuadraticParameters(pt1, pt2, pt3) # <<<<<<<<<<<<<<
+ * solutions = solveQuadratic(
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 542, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6);
+ index = 0; __pyx_t_3 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_3)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_2 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 2; __pyx_t_5 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_5)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_5);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 3) < (0)) __PYX_ERR(0, 542, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 542, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_b = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c = __pyx_t_5;
+ __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":543
+ * """
+ * a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
+ * solutions = solveQuadratic( # <<<<<<<<<<<<<<
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+ * )
+*/
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_solveQuadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 543, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+
+ /* "fontTools/misc/bezierTools.py":544
+ * a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
+ * solutions = solveQuadratic(
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where # <<<<<<<<<<<<<<
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+*/
+ __pyx_t_3 = __Pyx_PyObject_GetItem(__pyx_v_a, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 544, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyObject_GetItem(__pyx_v_b, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 544, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PyObject_GetItem(__pyx_v_c, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 544, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = PyNumber_Subtract(__pyx_t_8, __pyx_v_where); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 544, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_5, __pyx_t_3, __pyx_t_6, __pyx_t_9};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 543, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_solutions = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":546
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3)]
+*/
+ __pyx_t_1 = __pyx_pf_9fontTools_4misc_11bezierTools_14splitQuadratic_genexpr(NULL, __pyx_v_solutions); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_Generator_GetInlinedResult(__pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely((PyList_Sort(__pyx_t_2) < 0))) __PYX_ERR(0, 546, __pyx_L1_error)
+ __Pyx_DECREF_SET(__pyx_v_solutions, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":547
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2, pt3)]
+ * return _splitQuadraticAtT(a, b, c, *solutions)
+*/
+ __pyx_t_10 = __Pyx_PyObject_IsTrue(__pyx_v_solutions); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 547, __pyx_L1_error)
+ __pyx_t_11 = (!__pyx_t_10);
+ if (__pyx_t_11) {
+
+ /* "fontTools/misc/bezierTools.py":548
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions:
+ * return [(pt1, pt2, pt3)] # <<<<<<<<<<<<<<
+ * return _splitQuadraticAtT(a, b, c, *solutions)
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 548, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 548, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 548, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_v_pt3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_v_pt3) != (0)) __PYX_ERR(0, 548, __pyx_L1_error);
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 548, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 548, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":547
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2, pt3)]
+ * return _splitQuadraticAtT(a, b, c, *solutions)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":549
+ * if not solutions:
+ * return [(pt1, pt2, pt3)]
+ * return _splitQuadraticAtT(a, b, c, *solutions) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 549, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_New(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 549, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_a);
+ __Pyx_GIVEREF(__pyx_v_a);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_a) != (0)) __PYX_ERR(0, 549, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b);
+ __Pyx_GIVEREF(__pyx_v_b);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_b) != (0)) __PYX_ERR(0, 549, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c);
+ __Pyx_GIVEREF(__pyx_v_c);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_v_c) != (0)) __PYX_ERR(0, 549, __pyx_L1_error);
+ __pyx_t_9 = __Pyx_PySequence_Tuple(__pyx_v_solutions); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 549, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_2, __pyx_t_9); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 549, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_9 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_6, NULL); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 549, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_9;
+ __pyx_t_9 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":507
+ *
+ *
+ * def splitQuadratic(pt1, pt2, pt3, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at a given coordinate.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitQuadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_b);
+ __Pyx_XDECREF(__pyx_v_c);
+ __Pyx_XDECREF(__pyx_v_solutions);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_4misc_11bezierTools_14splitQuadratic_2generator2);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":552
+ *
+ *
+ * def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at a given coordinate.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_29splitCubic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_28splitCubic, "splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal)\n\nSplit a cubic Bezier curve at a given coordinate.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n where: Position at which to split the curve.\n isHorizontal: Direction of the ray splitting the curve. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\nReturns:\n A list of two curve segments (each curve segment being four 2D tuples)\n if the curve was successfully split, or a list containing the original\n curve.\n\nExample::\n\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))\n ((0, 0), (25, 100), (75, 100), (100, 0))\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (68.75, 75), (87.5, 50), (100, 0))\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))\n ((0, 0), (2.29379, 9.17517), (4.79804, 17.5085), (7.47414, 25))\n ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667), (92.5259, 25))\n ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_29splitCubic = {"splitCubic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_29splitCubic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_28splitCubic};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_29splitCubic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ PyObject *__pyx_v_where = 0;
+ PyObject *__pyx_v_isHorizontal = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[6] = {0,0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitCubic (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_where,&__pyx_mstate_global->__pyx_n_u_isHorizontal,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 552, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 6:
+ values[5] = __Pyx_ArgRef_FASTCALL(__pyx_args, 5);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[5])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 552, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "splitCubic", 0) < (0)) __PYX_ERR(0, 552, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 6; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitCubic", 1, 6, 6, i); __PYX_ERR(0, 552, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 6)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 552, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 552, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 552, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 552, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 552, __pyx_L3_error)
+ values[5] = __Pyx_ArgRef_FASTCALL(__pyx_args, 5);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[5])) __PYX_ERR(0, 552, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ __pyx_v_where = values[4];
+ __pyx_v_isHorizontal = values[5];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitCubic", 1, 6, 6, __pyx_nargs); __PYX_ERR(0, 552, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_28splitCubic(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_where, __pyx_v_isHorizontal);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_10splitCubic_2generator3(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":583
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3, pt4)]
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_10splitCubic_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 583, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_10splitCubic_2generator3, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[1]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_splitCubic_locals_genexpr, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubic.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_10splitCubic_2generator3(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __pyx_r = PyList_New(0); if (unlikely(!__pyx_r)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_r);
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 583, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 583, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 583, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 583, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 583, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 583, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_t);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_t, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_4 = PyObject_RichCompare(__pyx_mstate_global->__pyx_int_0, __pyx_cur_scope->__pyx_v_t, Py_LE); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 583, __pyx_L1_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_4)) {
+ __Pyx_DECREF(__pyx_t_4);
+ __pyx_t_4 = PyObject_RichCompare(__pyx_cur_scope->__pyx_v_t, __pyx_mstate_global->__pyx_int_1, Py_LT); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 583, __pyx_L1_error)
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (__pyx_t_5) {
+ if (unlikely(__Pyx_ListComp_Append(__pyx_r, (PyObject*)__pyx_cur_scope->__pyx_v_t))) __PYX_ERR(0, 583, __pyx_L1_error)
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_r); __pyx_r = 0;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":552
+ *
+ *
+ * def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at a given coordinate.
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_28splitCubic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_where, PyObject *__pyx_v_isHorizontal) {
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_b = NULL;
+ PyObject *__pyx_v_c = NULL;
+ PyObject *__pyx_v_d = NULL;
+ PyObject *__pyx_v_solutions = NULL;
+ PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_10splitCubic_2generator3 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ int __pyx_t_11;
+ int __pyx_t_12;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitCubic", 0);
+
+ /* "fontTools/misc/bezierTools.py":579
+ * ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))
+ * """
+ * a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ * solutions = solveCubic(
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcCubicParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (5-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 579, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_6);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_8(__pyx_t_7); if (unlikely(!item)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 4) < (0)) __PYX_ERR(0, 579, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 579, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_b = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c = __pyx_t_5;
+ __pyx_t_5 = 0;
+ __pyx_v_d = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":580
+ * """
+ * a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
+ * solutions = solveCubic( # <<<<<<<<<<<<<<
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+ * )
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_solveCubic); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 580, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+
+ /* "fontTools/misc/bezierTools.py":581
+ * a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
+ * solutions = solveCubic(
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where # <<<<<<<<<<<<<<
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+*/
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_v_a, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 581, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetItem(__pyx_v_b, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 581, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = __Pyx_PyObject_GetItem(__pyx_v_c, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 581, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_9 = __Pyx_PyObject_GetItem(__pyx_v_d, __pyx_v_isHorizontal); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 581, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_10 = PyNumber_Subtract(__pyx_t_9, __pyx_v_where); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 581, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_6, __pyx_t_2, __pyx_t_3, __pyx_t_7, __pyx_t_10};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_4, (5-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 580, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_solutions = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":583
+ * a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1) # <<<<<<<<<<<<<<
+ * if not solutions:
+ * return [(pt1, pt2, pt3, pt4)]
+*/
+ __pyx_t_1 = __pyx_pf_9fontTools_4misc_11bezierTools_10splitCubic_genexpr(NULL, __pyx_v_solutions); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_Generator_GetInlinedResult(__pyx_t_1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely((PyList_Sort(__pyx_t_5) < 0))) __PYX_ERR(0, 583, __pyx_L1_error)
+ __Pyx_DECREF_SET(__pyx_v_solutions, __pyx_t_5);
+ __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":584
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2, pt3, pt4)]
+ * return _splitCubicAtT(a, b, c, d, *solutions)
+*/
+ __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_v_solutions); if (unlikely((__pyx_t_11 < 0))) __PYX_ERR(0, 584, __pyx_L1_error)
+ __pyx_t_12 = (!__pyx_t_11);
+ if (__pyx_t_12) {
+
+ /* "fontTools/misc/bezierTools.py":585
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions:
+ * return [(pt1, pt2, pt3, pt4)] # <<<<<<<<<<<<<<
+ * return _splitCubicAtT(a, b, c, d, *solutions)
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 585, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 585, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 585, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_v_pt3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_v_pt3) != (0)) __PYX_ERR(0, 585, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt4);
+ __Pyx_GIVEREF(__pyx_v_pt4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_v_pt4) != (0)) __PYX_ERR(0, 585, __pyx_L1_error);
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 585, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_5) != (0)) __PYX_ERR(0, 585, __pyx_L1_error);
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":584
+ * )
+ * solutions = sorted(t for t in solutions if 0 <= t < 1)
+ * if not solutions: # <<<<<<<<<<<<<<
+ * return [(pt1, pt2, pt3, pt4)]
+ * return _splitCubicAtT(a, b, c, d, *solutions)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":586
+ * if not solutions:
+ * return [(pt1, pt2, pt3, pt4)]
+ * return _splitCubicAtT(a, b, c, d, *solutions) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_splitCubicAtT); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 586, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 586, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_a);
+ __Pyx_GIVEREF(__pyx_v_a);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_a) != (0)) __PYX_ERR(0, 586, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b);
+ __Pyx_GIVEREF(__pyx_v_b);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_b) != (0)) __PYX_ERR(0, 586, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c);
+ __Pyx_GIVEREF(__pyx_v_c);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_v_c) != (0)) __PYX_ERR(0, 586, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_d);
+ __Pyx_GIVEREF(__pyx_v_d);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_v_d) != (0)) __PYX_ERR(0, 586, __pyx_L1_error);
+ __pyx_t_10 = __Pyx_PySequence_Tuple(__pyx_v_solutions); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 586, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_7 = PyNumber_Add(__pyx_t_5, __pyx_t_10); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 586, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_10 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_7, NULL); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 586, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_r = __pyx_t_10;
+ __pyx_t_10 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":552
+ *
+ *
+ * def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at a given coordinate.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_b);
+ __Pyx_XDECREF(__pyx_v_c);
+ __Pyx_XDECREF(__pyx_v_d);
+ __Pyx_XDECREF(__pyx_v_solutions);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_4misc_11bezierTools_10splitCubic_2generator3);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":589
+ *
+ *
+ * def splitQuadraticAtT(pt1, pt2, pt3, *ts): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at one or more values of t.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_31splitQuadraticAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_30splitQuadraticAtT, "splitQuadraticAtT(pt1, pt2, pt3, *ts)\n\nSplit a quadratic Bezier curve at one or more values of t.\n\nArgs:\n pt1,pt2,pt3: Control points of the Bezier as 2D tuples.\n *ts: Positions at which to split the curve.\n\nReturns:\n A list of curve segments (each curve segment being three 2D tuples).\n\nExamples::\n\n >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))\n >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (62.5, 50), (75, 37.5))\n ((75, 37.5), (87.5, 25), (100, 0))");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_31splitQuadraticAtT = {"splitQuadraticAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_31splitQuadraticAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_30splitQuadraticAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_31splitQuadraticAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitQuadraticAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 3, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 589, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 589, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 589, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 589, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 3) ? kwd_pos_args : 3;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "splitQuadraticAtT", 0) < (0)) __PYX_ERR(0, 589, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitQuadraticAtT", 0, 3, 3, i); __PYX_ERR(0, 589, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 589, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 589, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 589, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitQuadraticAtT", 0, 3, 3, __pyx_nargs); __PYX_ERR(0, 589, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts); __pyx_v_ts = 0;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitQuadraticAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_30splitQuadraticAtT(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_30splitQuadraticAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_ts) {
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_b = NULL;
+ PyObject *__pyx_v_c = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitQuadraticAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":609
+ * ((75, 37.5), (87.5, 25), (100, 0))
+ * """
+ * a, b, c = calcQuadraticParameters(pt1, pt2, pt3) # <<<<<<<<<<<<<<
+ * return _splitQuadraticAtT(a, b, c, *ts)
+ *
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 609, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6);
+ index = 0; __pyx_t_3 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_3)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_2 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 2; __pyx_t_5 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_5)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_5);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 3) < (0)) __PYX_ERR(0, 609, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 609, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_b = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c = __pyx_t_5;
+ __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":610
+ * """
+ * a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
+ * return _splitQuadraticAtT(a, b, c, *ts) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 610, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = PyTuple_New(3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 610, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_a);
+ __Pyx_GIVEREF(__pyx_v_a);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_a) != (0)) __PYX_ERR(0, 610, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b);
+ __Pyx_GIVEREF(__pyx_v_b);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_b) != (0)) __PYX_ERR(0, 610, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c);
+ __Pyx_GIVEREF(__pyx_v_c);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_v_c) != (0)) __PYX_ERR(0, 610, __pyx_L1_error);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_5, __pyx_v_ts); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 610, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_2, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 610, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":589
+ *
+ *
+ * def splitQuadraticAtT(pt1, pt2, pt3, *ts): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at one or more values of t.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitQuadraticAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_b);
+ __Pyx_XDECREF(__pyx_v_c);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":613
+ *
+ *
+ * def splitCubicAtT(pt1, pt2, pt3, pt4, *ts): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at one or more values of t.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_33splitCubicAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_32splitCubicAtT, "splitCubicAtT(pt1, pt2, pt3, pt4, *ts)\n\nSplit a cubic Bezier curve at one or more values of t.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n *ts: Positions at which to split the curve.\n\nReturns:\n A list of curve segments (each curve segment being four 2D tuples).\n\nExamples::\n\n >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (68.75, 75), (87.5, 50), (100, 0))\n >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (59.375, 75), (68.75, 68.75), (77.3438, 56.25))\n ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_33splitCubicAtT = {"splitCubicAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_33splitCubicAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_32splitCubicAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_33splitCubicAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitCubicAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 4, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 613, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 613, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 613, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 613, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 613, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 4) ? kwd_pos_args : 4;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "splitCubicAtT", 0) < (0)) __PYX_ERR(0, 613, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitCubicAtT", 0, 4, 4, i); __PYX_ERR(0, 613, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 613, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 613, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 613, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 613, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitCubicAtT", 0, 4, 4, __pyx_nargs); __PYX_ERR(0, 613, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts); __pyx_v_ts = 0;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_32splitCubicAtT(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_32splitCubicAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_ts) {
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_b = NULL;
+ PyObject *__pyx_v_c = NULL;
+ PyObject *__pyx_v_d = NULL;
+ PyObject *__pyx_v_split = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitCubicAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":633
+ * ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))
+ * """
+ * a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ * split = _splitCubicAtT(a, b, c, d, *ts)
+ *
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcCubicParameters); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (5-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 633, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_6);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_3,&__pyx_t_2,&__pyx_t_5,&__pyx_t_6};
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_8(__pyx_t_7); if (unlikely(!item)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 4) < (0)) __PYX_ERR(0, 633, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 633, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_b = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c = __pyx_t_5;
+ __pyx_t_5 = 0;
+ __pyx_v_d = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":634
+ * """
+ * a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
+ * split = _splitCubicAtT(a, b, c, d, *ts) # <<<<<<<<<<<<<<
+ *
+ * # the split impl can introduce floating point errors; we know the first
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_splitCubicAtT); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 634, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 634, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx_v_a);
+ __Pyx_GIVEREF(__pyx_v_a);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_v_a) != (0)) __PYX_ERR(0, 634, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b);
+ __Pyx_GIVEREF(__pyx_v_b);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_v_b) != (0)) __PYX_ERR(0, 634, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c);
+ __Pyx_GIVEREF(__pyx_v_c);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_v_c) != (0)) __PYX_ERR(0, 634, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_d);
+ __Pyx_GIVEREF(__pyx_v_d);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_v_d) != (0)) __PYX_ERR(0, 634, __pyx_L1_error);
+ __pyx_t_5 = PyNumber_Add(__pyx_t_6, __pyx_v_ts); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 634, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_5, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 634, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_v_split = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":639
+ * # segment should always start at pt1 and the last segment should end at pt4,
+ * # so we set those values directly before returning.
+ * split[0] = (pt1, *split[0][1:]) # <<<<<<<<<<<<<<
+ * split[-1] = (*split[-1][:-1], pt4)
+ * return split
+*/
+ __pyx_t_5 = PyList_New(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_5, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 639, __pyx_L1_error);
+ __pyx_t_6 = __pyx_t_5;
+ __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_GetItemInt(__pyx_v_split, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_1 = __Pyx_PyObject_GetSlice(__pyx_t_5, 1, 0, NULL, NULL, &__pyx_mstate_global->__pyx_slice[0], 1, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (__Pyx_PyList_Extend(__pyx_t_6, __pyx_t_1) < (0)) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ {
+ PyObject *__pyx_temp = PyList_AsTuple(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_6);
+ __pyx_t_6 = __pyx_temp; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ if (unlikely((__Pyx_SetItemInt(__pyx_v_split, 0, __pyx_t_6, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference) < 0))) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":640
+ * # so we set those values directly before returning.
+ * split[0] = (pt1, *split[0][1:])
+ * split[-1] = (*split[-1][:-1], pt4) # <<<<<<<<<<<<<<
+ * return split
+ *
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_split, -1L, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_PyObject_GetSlice(__pyx_t_1, 0, -1L, NULL, NULL, &__pyx_mstate_global->__pyx_slice[1], 0, 1, 1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_6 = __Pyx_PySequence_ListKeepNew(__pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (__Pyx_ListComp_Append(__pyx_t_6, __pyx_v_pt4) < (0)) __PYX_ERR(0, 640, __pyx_L1_error)
+ {
+ PyObject *__pyx_temp = PyList_AsTuple(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_6);
+ __pyx_t_6 = __pyx_temp; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ if (unlikely((__Pyx_SetItemInt(__pyx_v_split, -1L, __pyx_t_6, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference) < 0))) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":641
+ * split[0] = (pt1, *split[0][1:])
+ * split[-1] = (*split[-1][:-1], pt4)
+ * return split # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_split);
+ __pyx_r = __pyx_v_split;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":613
+ *
+ *
+ * def splitCubicAtT(pt1, pt2, pt3, pt4, *ts): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at one or more values of t.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_b);
+ __Pyx_XDECREF(__pyx_v_c);
+ __Pyx_XDECREF(__pyx_v_d);
+ __Pyx_XDECREF(__pyx_v_split);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_36generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":644
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * pt1=cython.complex,
+ * pt2=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_35splitCubicAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_34splitCubicAtTC, "splitCubicAtTC(double complex pt1, double complex pt2, double complex pt3, double complex pt4, *ts)\n\nSplit a cubic Bezier curve at one or more values of t.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers..\n *ts: Positions at which to split the curve.\n\nYields:\n Curve segments (each curve segment being four complex numbers).");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_35splitCubicAtTC = {"splitCubicAtTC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_35splitCubicAtTC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_34splitCubicAtTC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_35splitCubicAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitCubicAtTC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 4, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 644, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 644, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 644, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 644, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 644, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 4) ? kwd_pos_args : 4;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "splitCubicAtTC", 0) < (0)) __PYX_ERR(0, 644, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitCubicAtTC", 0, 4, 4, i); __PYX_ERR(0, 644, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 644, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 644, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 644, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 644, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 654, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 654, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 654, __pyx_L3_error)
+ __pyx_v_pt4 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 654, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitCubicAtTC", 0, 4, 4, __pyx_nargs); __PYX_ERR(0, 644, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_CLEAR(__pyx_v_ts);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_34splitCubicAtTC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_34splitCubicAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, PyObject *__pyx_v_ts) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitCubicAtTC", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 644, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_v_pt1 = __pyx_v_pt1;
+ __pyx_cur_scope->__pyx_v_pt2 = __pyx_v_pt2;
+ __pyx_cur_scope->__pyx_v_pt3 = __pyx_v_pt3;
+ __pyx_cur_scope->__pyx_v_pt4 = __pyx_v_pt4;
+ __pyx_cur_scope->__pyx_v_ts = __pyx_v_ts;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_ts);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_ts);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_36generator, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[2]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 644, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_36generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ __pyx_t_double_complex __pyx_t_8;
+ __pyx_t_double_complex __pyx_t_9;
+ __pyx_t_double_complex __pyx_t_10;
+ __pyx_t_double_complex __pyx_t_11;
+ __Pyx_PySendResult __pyx_t_12;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitCubicAtTC", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ case 1: goto __pyx_L6_resume_from_yield_from;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(__pyx_sent_value != Py_None)) {
+ if (unlikely(__pyx_sent_value)) PyErr_SetString(PyExc_TypeError, "can't send non-None value to a just-started generator");
+ __PYX_ERR(0, 644, __pyx_L1_error)
+ }
+
+ /* "fontTools/misc/bezierTools.py":664
+ * Curve segments (each curve segment being four complex numbers).
+ * """
+ * a, b, c, d = calcCubicParametersC(pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ * yield from _splitCubicAtTC(a, b, c, d, *ts)
+ *
+*/
+ __pyx_t_1 = __pyx_f_9fontTools_4misc_11bezierTools_calcCubicParametersC(__pyx_cur_scope->__pyx_v_pt1, __pyx_cur_scope->__pyx_v_pt2, __pyx_cur_scope->__pyx_v_pt3, __pyx_cur_scope->__pyx_v_pt4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 664, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_4 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_5);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5};
+ __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_7(__pyx_t_6); if (unlikely(!item)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 4) < (0)) __PYX_ERR(0, 664, __pyx_L1_error)
+ __pyx_t_7 = NULL;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L5_unpacking_done;
+ __pyx_L4_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 664, __pyx_L1_error)
+ __pyx_L5_unpacking_done:;
+ }
+ __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_11 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 664, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_cur_scope->__pyx_v_a = __pyx_t_8;
+ __pyx_cur_scope->__pyx_v_b = __pyx_t_9;
+ __pyx_cur_scope->__pyx_v_c = __pyx_t_10;
+ __pyx_cur_scope->__pyx_v_d = __pyx_t_11;
+
+ /* "fontTools/misc/bezierTools.py":665
+ * """
+ * a, b, c, d = calcCubicParametersC(pt1, pt2, pt3, pt4)
+ * yield from _splitCubicAtTC(a, b, c, d, *ts) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_cur_scope->__pyx_v_a); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_cur_scope->__pyx_v_b); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_cur_scope->__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_cur_scope->__pyx_v_d); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_5) != (0)) __PYX_ERR(0, 665, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_4) != (0)) __PYX_ERR(0, 665, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 665, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_2) != (0)) __PYX_ERR(0, 665, __pyx_L1_error);
+ __pyx_t_5 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_6, __pyx_cur_scope->__pyx_v_ts); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_2, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 665, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_12 = __Pyx_Generator_Yield_From(__pyx_generator, __pyx_t_6, &__pyx_r);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (likely(__pyx_t_12 == PYGEN_NEXT)) {
+ __Pyx_GOTREF(__pyx_r);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ /* return from generator, yielding value */
+ __pyx_generator->resume_label = 1;
+ return __pyx_r;
+ __pyx_L6_resume_from_yield_from:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 665, __pyx_L1_error)
+ } else if (likely(__pyx_t_12 == PYGEN_RETURN)) {
+ __Pyx_GOTREF(__pyx_r);
+ __Pyx_DECREF(__pyx_r); __pyx_r = 0;
+ } else {
+ __Pyx_XGOTREF(__pyx_r);
+ __PYX_ERR(0, 665, __pyx_L1_error)
+ }
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* "fontTools/misc/bezierTools.py":644
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * pt1=cython.complex,
+ * pt2=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":668
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_38splitCubicIntoTwoAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_37splitCubicIntoTwoAtTC, "splitCubicIntoTwoAtTC(double complex pt1, double complex pt2, double complex pt3, double complex pt4, double t)\n\nSplit a cubic Bezier curve at t.\n\nArgs:\n pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.\n t: Position at which to split the curve.\n\nReturns:\n A tuple of two curve segments (each curve segment being four complex numbers).");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_38splitCubicIntoTwoAtTC = {"splitCubicIntoTwoAtTC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_38splitCubicIntoTwoAtTC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_37splitCubicIntoTwoAtTC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_38splitCubicIntoTwoAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ double __pyx_v_t;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("splitCubicIntoTwoAtTC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 668, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 668, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 668, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 668, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 668, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 668, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "splitCubicIntoTwoAtTC", 0) < (0)) __PYX_ERR(0, 668, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 5; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("splitCubicIntoTwoAtTC", 1, 5, 5, i); __PYX_ERR(0, 668, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 5)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 668, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 668, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 668, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 668, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 668, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 682, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 682, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 682, __pyx_L3_error)
+ __pyx_v_pt4 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 682, __pyx_L3_error)
+ __pyx_v_t = __Pyx_PyFloat_AsDouble(values[4]); if (unlikely((__pyx_v_t == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 682, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("splitCubicIntoTwoAtTC", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 668, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicIntoTwoAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_37splitCubicIntoTwoAtTC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_37splitCubicIntoTwoAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_t) {
+ double __pyx_v_t2;
+ double __pyx_v__1_t;
+ double __pyx_v__1_t_2;
+ double __pyx_v__2_t_1_t;
+ __pyx_t_double_complex __pyx_v_pointAtT;
+ __pyx_t_double_complex __pyx_v_off1;
+ __pyx_t_double_complex __pyx_v_off2;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("splitCubicIntoTwoAtTC", 0);
+
+ /* "fontTools/misc/bezierTools.py":692
+ * A tuple of two curve segments (each curve segment being four complex numbers).
+ * """
+ * t2 = t * t # <<<<<<<<<<<<<<
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t
+*/
+ __pyx_v_t2 = (__pyx_v_t * __pyx_v_t);
+
+ /* "fontTools/misc/bezierTools.py":693
+ * """
+ * t2 = t * t
+ * _1_t = 1 - t # <<<<<<<<<<<<<<
+ * _1_t_2 = _1_t * _1_t
+ * _2_t_1_t = 2 * t * _1_t
+*/
+ __pyx_v__1_t = (1.0 - __pyx_v_t);
+
+ /* "fontTools/misc/bezierTools.py":694
+ * t2 = t * t
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t # <<<<<<<<<<<<<<
+ * _2_t_1_t = 2 * t * _1_t
+ * pointAtT = (
+*/
+ __pyx_v__1_t_2 = (__pyx_v__1_t * __pyx_v__1_t);
+
+ /* "fontTools/misc/bezierTools.py":695
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t
+ * _2_t_1_t = 2 * t * _1_t # <<<<<<<<<<<<<<
+ * pointAtT = (
+ * _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+*/
+ __pyx_v__2_t_1_t = ((2.0 * __pyx_v_t) * __pyx_v__1_t);
+
+ /* "fontTools/misc/bezierTools.py":697
+ * _2_t_1_t = 2 * t * _1_t
+ * pointAtT = (
+ * _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4 # <<<<<<<<<<<<<<
+ * )
+ * off1 = _1_t_2 * pt1 + _2_t_1_t * pt2 + t2 * pt3
+*/
+ __pyx_v_pointAtT = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t_2 * __pyx_v__1_t), 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t_2 * __pyx_v_t), 0), __pyx_v_pt2), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t * __pyx_v_t2), 0), __pyx_v_pt3)))), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v_t2 * __pyx_v_t), 0), __pyx_v_pt4));
+
+ /* "fontTools/misc/bezierTools.py":699
+ * _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+ * )
+ * off1 = _1_t_2 * pt1 + _2_t_1_t * pt2 + t2 * pt3 # <<<<<<<<<<<<<<
+ * off2 = _1_t_2 * pt2 + _2_t_1_t * pt3 + t2 * pt4
+ *
+*/
+ __pyx_v_off1 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v__1_t_2, 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v__2_t_1_t, 0), __pyx_v_pt2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v_t2, 0), __pyx_v_pt3));
+
+ /* "fontTools/misc/bezierTools.py":700
+ * )
+ * off1 = _1_t_2 * pt1 + _2_t_1_t * pt2 + t2 * pt3
+ * off2 = _1_t_2 * pt2 + _2_t_1_t * pt3 + t2 * pt4 # <<<<<<<<<<<<<<
+ *
+ * pt2 = pt1 + (pt2 - pt1) * t
+*/
+ __pyx_v_off2 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v__1_t_2, 0), __pyx_v_pt2), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v__2_t_1_t, 0), __pyx_v_pt3)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(__pyx_v_t2, 0), __pyx_v_pt4));
+
+ /* "fontTools/misc/bezierTools.py":702
+ * off2 = _1_t_2 * pt2 + _2_t_1_t * pt3 + t2 * pt4
+ *
+ * pt2 = pt1 + (pt2 - pt1) * t # <<<<<<<<<<<<<<
+ * pt3 = pt4 + (pt3 - pt4) * _1_t
+ *
+*/
+ __pyx_v_pt2 = __Pyx_c_sum_double(__pyx_v_pt1, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_pt2, __pyx_v_pt1), __pyx_t_double_complex_from_parts(__pyx_v_t, 0)));
+
+ /* "fontTools/misc/bezierTools.py":703
+ *
+ * pt2 = pt1 + (pt2 - pt1) * t
+ * pt3 = pt4 + (pt3 - pt4) * _1_t # <<<<<<<<<<<<<<
+ *
+ * return ((pt1, pt2, off1, pointAtT), (pointAtT, off2, pt3, pt4))
+*/
+ __pyx_v_pt3 = __Pyx_c_sum_double(__pyx_v_pt4, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt4), __pyx_t_double_complex_from_parts(__pyx_v__1_t, 0)));
+
+ /* "fontTools/misc/bezierTools.py":705
+ * pt3 = pt4 + (pt3 - pt4) * _1_t
+ *
+ * return ((pt1, pt2, off1, pointAtT), (pointAtT, off2, pt3, pt4)) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_pt1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_pt2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_off1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_pointAtT); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_t_4) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_pointAtT); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_off2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_pt3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_pt4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_4) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_2) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_1) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __pyx_t_4 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 705, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_5) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_6);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_6) != (0)) __PYX_ERR(0, 705, __pyx_L1_error);
+ __pyx_t_5 = 0;
+ __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":668
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.splitCubicIntoTwoAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":708
+ *
+ *
+ * def _splitQuadraticAtT(a, b, c, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * segments = []
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_40_splitQuadraticAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_39_splitQuadraticAtT, "_splitQuadraticAtT(a, b, c, *ts)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_40_splitQuadraticAtT = {"_splitQuadraticAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_40_splitQuadraticAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_39_splitQuadraticAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_40_splitQuadraticAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_splitQuadraticAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 3, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 708, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 708, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 708, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 708, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 3) ? kwd_pos_args : 3;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "_splitQuadraticAtT", 0) < (0)) __PYX_ERR(0, 708, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_splitQuadraticAtT", 0, 3, 3, i); __PYX_ERR(0, 708, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 708, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 708, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 708, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_splitQuadraticAtT", 0, 3, 3, __pyx_nargs); __PYX_ERR(0, 708, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts); __pyx_v_ts = 0;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitQuadraticAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_39_splitQuadraticAtT(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_39_splitQuadraticAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_ts) {
+ PyObject *__pyx_v_segments = NULL;
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_i = NULL;
+ PyObject *__pyx_v_t1 = NULL;
+ PyObject *__pyx_v_t2 = NULL;
+ PyObject *__pyx_v_delta = NULL;
+ PyObject *__pyx_v_delta_2 = NULL;
+ PyObject *__pyx_v_a1x = NULL;
+ PyObject *__pyx_v_a1y = NULL;
+ PyObject *__pyx_v_b1x = NULL;
+ PyObject *__pyx_v_b1y = NULL;
+ PyObject *__pyx_v_t1_2 = NULL;
+ PyObject *__pyx_v_c1x = NULL;
+ PyObject *__pyx_v_c1y = NULL;
+ PyObject *__pyx_v_pt1 = NULL;
+ PyObject *__pyx_v_pt2 = NULL;
+ PyObject *__pyx_v_pt3 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *(*__pyx_t_5)(PyObject *);
+ Py_ssize_t __pyx_t_6;
+ size_t __pyx_t_7;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_splitQuadraticAtT", 0);
+ __Pyx_INCREF(__pyx_v_ts);
+
+ /* "fontTools/misc/bezierTools.py":709
+ *
+ * def _splitQuadraticAtT(a, b, c, *ts):
+ * ts = list(ts) # <<<<<<<<<<<<<<
+ * segments = []
+ * ts.insert(0, 0.0)
+*/
+ __pyx_t_1 = PySequence_List(__pyx_v_ts); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 709, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF_SET(__pyx_v_ts, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":710
+ * def _splitQuadraticAtT(a, b, c, *ts):
+ * ts = list(ts)
+ * segments = [] # <<<<<<<<<<<<<<
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+*/
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 710, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_segments = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":711
+ * ts = list(ts)
+ * segments = []
+ * ts.insert(0, 0.0) # <<<<<<<<<<<<<<
+ * ts.append(1.0)
+ * ax, ay = a
+*/
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_ts, __pyx_mstate_global->__pyx_n_u_insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 711, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_mstate_global->__pyx_tuple[0], NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 711, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":712
+ * segments = []
+ * ts.insert(0, 0.0)
+ * ts.append(1.0) # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+ __pyx_t_3 = __Pyx_PyObject_Append(__pyx_v_ts, __pyx_mstate_global->__pyx_float_1_0); if (unlikely(__pyx_t_3 == ((int)-1))) __PYX_ERR(0, 712, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":713
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+ * ax, ay = a # <<<<<<<<<<<<<<
+ * bx, by = b
+ * cx, cy = c
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_a))) || (PyList_CheckExact(__pyx_v_a))) {
+ PyObject* sequence = __pyx_v_a;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 713, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_a); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 713, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 713, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_ay = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":714
+ * ts.append(1.0)
+ * ax, ay = a
+ * bx, by = b # <<<<<<<<<<<<<<
+ * cx, cy = c
+ * for i in range(len(ts) - 1):
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_b))) || (PyList_CheckExact(__pyx_v_b))) {
+ PyObject* sequence = __pyx_v_b;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 714, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_b); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 714, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 714, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_by = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":715
+ * ax, ay = a
+ * bx, by = b
+ * cx, cy = c # <<<<<<<<<<<<<<
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_c))) || (PyList_CheckExact(__pyx_v_c))) {
+ PyObject* sequence = __pyx_v_c;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 715, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_c); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 715, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 715, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_cy = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":716
+ * bx, by = b
+ * cx, cy = c
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ __pyx_t_2 = NULL;
+ __pyx_t_6 = PyObject_Length(__pyx_v_ts); if (unlikely(__pyx_t_6 == ((Py_ssize_t)-1))) __PYX_ERR(0, 716, __pyx_L1_error)
+ __pyx_t_4 = PyLong_FromSsize_t((__pyx_t_6 - 1)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 716, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_t_4};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)(&PyRange_Type), __pyx_callargs+__pyx_t_7, (2-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 716, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_4 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 716, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 716, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ for (;;) {
+ {
+ __pyx_t_1 = __pyx_t_8(__pyx_t_4);
+ if (unlikely(!__pyx_t_1)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 716, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_i, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":717
+ * cx, cy = c
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i] # <<<<<<<<<<<<<<
+ * t2 = ts[i + 1]
+ * delta = t2 - t1
+*/
+ __pyx_t_1 = __Pyx_PyObject_GetItem(__pyx_v_ts, __pyx_v_i); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 717, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_t1, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":718
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+ * t2 = ts[i + 1] # <<<<<<<<<<<<<<
+ * delta = t2 - t1
+ * # calc new a, b and c
+*/
+ __pyx_t_1 = __Pyx_PyLong_AddObjC(__pyx_v_i, __pyx_mstate_global->__pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 718, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_v_ts, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 718, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_t2, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":719
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+ * delta = t2 - t1 # <<<<<<<<<<<<<<
+ * # calc new a, b and c
+ * delta_2 = delta * delta
+*/
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_t2, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 719, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_delta, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":721
+ * delta = t2 - t1
+ * # calc new a, b and c
+ * delta_2 = delta * delta # <<<<<<<<<<<<<<
+ * a1x = ax * delta_2
+ * a1y = ay * delta_2
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_delta, __pyx_v_delta); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 721, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_delta_2, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":722
+ * # calc new a, b and c
+ * delta_2 = delta * delta
+ * a1x = ax * delta_2 # <<<<<<<<<<<<<<
+ * a1y = ay * delta_2
+ * b1x = (2 * ax * t1 + bx) * delta
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ax, __pyx_v_delta_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 722, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_a1x, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":723
+ * delta_2 = delta * delta
+ * a1x = ax * delta_2
+ * a1y = ay * delta_2 # <<<<<<<<<<<<<<
+ * b1x = (2 * ax * t1 + bx) * delta
+ * b1y = (2 * ay * t1 + by) * delta
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ay, __pyx_v_delta_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 723, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_a1y, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":724
+ * a1x = ax * delta_2
+ * a1y = ay * delta_2
+ * b1x = (2 * ax * t1 + bx) * delta # <<<<<<<<<<<<<<
+ * b1y = (2 * ay * t1 + by) * delta
+ * t1_2 = t1 * t1
+*/
+ __pyx_t_2 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_v_ax, 2, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 724, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 724, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_bx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 724, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_delta); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 724, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_b1x, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":725
+ * a1y = ay * delta_2
+ * b1x = (2 * ax * t1 + bx) * delta
+ * b1y = (2 * ay * t1 + by) * delta # <<<<<<<<<<<<<<
+ * t1_2 = t1 * t1
+ * c1x = ax * t1_2 + bx * t1 + cx
+*/
+ __pyx_t_1 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_v_ay, 2, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 725, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 725, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_by); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 725, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_delta); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 725, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_b1y, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":726
+ * b1x = (2 * ax * t1 + bx) * delta
+ * b1y = (2 * ay * t1 + by) * delta
+ * t1_2 = t1 * t1 # <<<<<<<<<<<<<<
+ * c1x = ax * t1_2 + bx * t1 + cx
+ * c1y = ay * t1_2 + by * t1 + cy
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_t1, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 726, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_t1_2, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":727
+ * b1y = (2 * ay * t1 + by) * delta
+ * t1_2 = t1 * t1
+ * c1x = ax * t1_2 + bx * t1 + cx # <<<<<<<<<<<<<<
+ * c1y = ay * t1_2 + by * t1 + cy
+ *
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ax, __pyx_v_t1_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 727, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_bx, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 727, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_9 = PyNumber_Add(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 727, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_9, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 727, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_c1x, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":728
+ * t1_2 = t1 * t1
+ * c1x = ax * t1_2 + bx * t1 + cx
+ * c1y = ay * t1_2 + by * t1 + cy # <<<<<<<<<<<<<<
+ *
+ * pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ay, __pyx_v_t1_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 728, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_9 = PyNumber_Multiply(__pyx_v_by, __pyx_v_t1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 728, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_t_9); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 728, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_9 = PyNumber_Add(__pyx_t_2, __pyx_v_cy); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 728, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_c1y, __pyx_t_9);
+ __pyx_t_9 = 0;
+
+ /* "fontTools/misc/bezierTools.py":730
+ * c1y = ay * t1_2 + by * t1 + cy
+ *
+ * pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y)) # <<<<<<<<<<<<<<
+ * segments.append((pt1, pt2, pt3))
+ * return segments
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_calcQuadraticPoints); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_10 = PyTuple_New(2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_INCREF(__pyx_v_a1x);
+ __Pyx_GIVEREF(__pyx_v_a1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 0, __pyx_v_a1x) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_a1y);
+ __Pyx_GIVEREF(__pyx_v_a1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 1, __pyx_v_a1y) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __pyx_t_11 = PyTuple_New(2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_INCREF(__pyx_v_b1x);
+ __Pyx_GIVEREF(__pyx_v_b1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 0, __pyx_v_b1x) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b1y);
+ __Pyx_GIVEREF(__pyx_v_b1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 1, __pyx_v_b1y) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __pyx_t_12 = PyTuple_New(2); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __Pyx_INCREF(__pyx_v_c1x);
+ __Pyx_GIVEREF(__pyx_v_c1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_v_c1x) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c1y);
+ __Pyx_GIVEREF(__pyx_v_c1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 1, __pyx_v_c1y) != (0)) __PYX_ERR(0, 730, __pyx_L1_error);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_t_10, __pyx_t_11, __pyx_t_12};
+ __pyx_t_9 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_9))) || (PyList_CheckExact(__pyx_t_9))) {
+ PyObject* sequence = __pyx_t_9;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 730, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_12 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_12);
+ __pyx_t_11 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_11);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_12 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_12);
+ __pyx_t_11 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_11);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_12 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_11 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ #endif
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_9); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ index = 0; __pyx_t_1 = __pyx_t_5(__pyx_t_10); if (unlikely(!__pyx_t_1)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_12 = __pyx_t_5(__pyx_t_10); if (unlikely(!__pyx_t_12)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_12);
+ index = 2; __pyx_t_11 = __pyx_t_5(__pyx_t_10); if (unlikely(!__pyx_t_11)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_11);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_10), 3) < (0)) __PYX_ERR(0, 730, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L12_unpacking_done;
+ __pyx_L11_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 730, __pyx_L1_error)
+ __pyx_L12_unpacking_done:;
+ }
+ __Pyx_XDECREF_SET(__pyx_v_pt1, __pyx_t_1);
+ __pyx_t_1 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt2, __pyx_t_12);
+ __pyx_t_12 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt3, __pyx_t_11);
+ __pyx_t_11 = 0;
+
+ /* "fontTools/misc/bezierTools.py":731
+ *
+ * pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
+ * segments.append((pt1, pt2, pt3)) # <<<<<<<<<<<<<<
+ * return segments
+ *
+*/
+ __pyx_t_9 = PyTuple_New(3); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 731, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 731, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 731, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_v_pt3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 2, __pyx_v_pt3) != (0)) __PYX_ERR(0, 731, __pyx_L1_error);
+ __pyx_t_3 = __Pyx_PyList_Append(__pyx_v_segments, __pyx_t_9); if (unlikely(__pyx_t_3 == ((int)-1))) __PYX_ERR(0, 731, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+
+ /* "fontTools/misc/bezierTools.py":716
+ * bx, by = b
+ * cx, cy = c
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":732
+ * pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
+ * segments.append((pt1, pt2, pt3))
+ * return segments # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_segments);
+ __pyx_r = __pyx_v_segments;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":708
+ *
+ *
+ * def _splitQuadraticAtT(a, b, c, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * segments = []
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitQuadraticAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ts);
+ __Pyx_XDECREF(__pyx_v_segments);
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_i);
+ __Pyx_XDECREF(__pyx_v_t1);
+ __Pyx_XDECREF(__pyx_v_t2);
+ __Pyx_XDECREF(__pyx_v_delta);
+ __Pyx_XDECREF(__pyx_v_delta_2);
+ __Pyx_XDECREF(__pyx_v_a1x);
+ __Pyx_XDECREF(__pyx_v_a1y);
+ __Pyx_XDECREF(__pyx_v_b1x);
+ __Pyx_XDECREF(__pyx_v_b1y);
+ __Pyx_XDECREF(__pyx_v_t1_2);
+ __Pyx_XDECREF(__pyx_v_c1x);
+ __Pyx_XDECREF(__pyx_v_c1y);
+ __Pyx_XDECREF(__pyx_v_pt1);
+ __Pyx_XDECREF(__pyx_v_pt2);
+ __Pyx_XDECREF(__pyx_v_pt3);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":735
+ *
+ *
+ * def _splitCubicAtT(a, b, c, d, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * ts.insert(0, 0.0)
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_42_splitCubicAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_41_splitCubicAtT, "_splitCubicAtT(a, b, c, d, *ts)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_42_splitCubicAtT = {"_splitCubicAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_42_splitCubicAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_41_splitCubicAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_42_splitCubicAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_d = 0;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_splitCubicAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 4, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_d,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 735, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 735, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 735, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 735, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 735, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 4) ? kwd_pos_args : 4;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "_splitCubicAtT", 0) < (0)) __PYX_ERR(0, 735, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_splitCubicAtT", 0, 4, 4, i); __PYX_ERR(0, 735, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 735, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 735, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 735, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 735, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ __pyx_v_d = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_splitCubicAtT", 0, 4, 4, __pyx_nargs); __PYX_ERR(0, 735, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts); __pyx_v_ts = 0;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitCubicAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_41_splitCubicAtT(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_d, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_41_splitCubicAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d, PyObject *__pyx_v_ts) {
+ PyObject *__pyx_v_segments = NULL;
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_dx = NULL;
+ PyObject *__pyx_v_dy = NULL;
+ PyObject *__pyx_v_i = NULL;
+ PyObject *__pyx_v_t1 = NULL;
+ PyObject *__pyx_v_t2 = NULL;
+ PyObject *__pyx_v_delta = NULL;
+ PyObject *__pyx_v_delta_2 = NULL;
+ PyObject *__pyx_v_delta_3 = NULL;
+ PyObject *__pyx_v_t1_2 = NULL;
+ PyObject *__pyx_v_t1_3 = NULL;
+ PyObject *__pyx_v_a1x = NULL;
+ PyObject *__pyx_v_a1y = NULL;
+ PyObject *__pyx_v_b1x = NULL;
+ PyObject *__pyx_v_b1y = NULL;
+ PyObject *__pyx_v_c1x = NULL;
+ PyObject *__pyx_v_c1y = NULL;
+ PyObject *__pyx_v_d1x = NULL;
+ PyObject *__pyx_v_d1y = NULL;
+ PyObject *__pyx_v_pt1 = NULL;
+ PyObject *__pyx_v_pt2 = NULL;
+ PyObject *__pyx_v_pt3 = NULL;
+ PyObject *__pyx_v_pt4 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *(*__pyx_t_5)(PyObject *);
+ Py_ssize_t __pyx_t_6;
+ size_t __pyx_t_7;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ PyObject *__pyx_t_13 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_splitCubicAtT", 0);
+ __Pyx_INCREF(__pyx_v_ts);
+
+ /* "fontTools/misc/bezierTools.py":736
+ *
+ * def _splitCubicAtT(a, b, c, d, *ts):
+ * ts = list(ts) # <<<<<<<<<<<<<<
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+*/
+ __pyx_t_1 = PySequence_List(__pyx_v_ts); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 736, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF_SET(__pyx_v_ts, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":737
+ * def _splitCubicAtT(a, b, c, d, *ts):
+ * ts = list(ts)
+ * ts.insert(0, 0.0) # <<<<<<<<<<<<<<
+ * ts.append(1.0)
+ * segments = []
+*/
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_ts, __pyx_mstate_global->__pyx_n_u_insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 737, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_mstate_global->__pyx_tuple[0], NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 737, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":738
+ * ts = list(ts)
+ * ts.insert(0, 0.0)
+ * ts.append(1.0) # <<<<<<<<<<<<<<
+ * segments = []
+ * ax, ay = a
+*/
+ __pyx_t_3 = __Pyx_PyObject_Append(__pyx_v_ts, __pyx_mstate_global->__pyx_float_1_0); if (unlikely(__pyx_t_3 == ((int)-1))) __PYX_ERR(0, 738, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":739
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+ * segments = [] # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+ __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 739, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_v_segments = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":740
+ * ts.append(1.0)
+ * segments = []
+ * ax, ay = a # <<<<<<<<<<<<<<
+ * bx, by = b
+ * cx, cy = c
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_a))) || (PyList_CheckExact(__pyx_v_a))) {
+ PyObject* sequence = __pyx_v_a;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 740, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_a); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 740, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 740, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_ay = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":741
+ * segments = []
+ * ax, ay = a
+ * bx, by = b # <<<<<<<<<<<<<<
+ * cx, cy = c
+ * dx, dy = d
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_b))) || (PyList_CheckExact(__pyx_v_b))) {
+ PyObject* sequence = __pyx_v_b;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 741, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_b); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 741, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 741, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_by = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":742
+ * ax, ay = a
+ * bx, by = b
+ * cx, cy = c # <<<<<<<<<<<<<<
+ * dx, dy = d
+ * for i in range(len(ts) - 1):
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_c))) || (PyList_CheckExact(__pyx_v_c))) {
+ PyObject* sequence = __pyx_v_c;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 742, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_c); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 742, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 742, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_cy = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":743
+ * bx, by = b
+ * cx, cy = c
+ * dx, dy = d # <<<<<<<<<<<<<<
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_d))) || (PyList_CheckExact(__pyx_v_d))) {
+ PyObject* sequence = __pyx_v_d;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 743, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_d); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_1 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_5(__pyx_t_4); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_4), 2) < (0)) __PYX_ERR(0, 743, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 743, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_dx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_dy = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":744
+ * cx, cy = c
+ * dx, dy = d
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ __pyx_t_1 = NULL;
+ __pyx_t_6 = PyObject_Length(__pyx_v_ts); if (unlikely(__pyx_t_6 == ((Py_ssize_t)-1))) __PYX_ERR(0, 744, __pyx_L1_error)
+ __pyx_t_4 = PyLong_FromSsize_t((__pyx_t_6 - 1)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 744, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_7 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_t_4};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)(&PyRange_Type), __pyx_callargs+__pyx_t_7, (2-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 744, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_4 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 744, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 744, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ for (;;) {
+ {
+ __pyx_t_2 = __pyx_t_8(__pyx_t_4);
+ if (unlikely(!__pyx_t_2)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 744, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_i, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":745
+ * dx, dy = d
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i] # <<<<<<<<<<<<<<
+ * t2 = ts[i + 1]
+ * delta = t2 - t1
+*/
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_v_ts, __pyx_v_i); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 745, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_t1, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":746
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+ * t2 = ts[i + 1] # <<<<<<<<<<<<<<
+ * delta = t2 - t1
+ *
+*/
+ __pyx_t_2 = __Pyx_PyLong_AddObjC(__pyx_v_i, __pyx_mstate_global->__pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 746, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyObject_GetItem(__pyx_v_ts, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 746, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_t2, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":747
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+ * delta = t2 - t1 # <<<<<<<<<<<<<<
+ *
+ * delta_2 = delta * delta
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_t2, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 747, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_delta, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":749
+ * delta = t2 - t1
+ *
+ * delta_2 = delta * delta # <<<<<<<<<<<<<<
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_delta, __pyx_v_delta); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 749, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_delta_2, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":750
+ *
+ * delta_2 = delta * delta
+ * delta_3 = delta * delta_2 # <<<<<<<<<<<<<<
+ * t1_2 = t1 * t1
+ * t1_3 = t1 * t1_2
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_delta, __pyx_v_delta_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 750, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_delta_3, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":751
+ * delta_2 = delta * delta
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1 # <<<<<<<<<<<<<<
+ * t1_3 = t1 * t1_2
+ *
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_t1, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 751, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_t1_2, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":752
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1
+ * t1_3 = t1 * t1_2 # <<<<<<<<<<<<<<
+ *
+ * # calc new a, b, c and d
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_t1, __pyx_v_t1_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 752, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_t1_3, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":755
+ *
+ * # calc new a, b, c and d
+ * a1x = ax * delta_3 # <<<<<<<<<<<<<<
+ * a1y = ay * delta_3
+ * b1x = (3 * ax * t1 + bx) * delta_2
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ax, __pyx_v_delta_3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 755, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_a1x, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":756
+ * # calc new a, b, c and d
+ * a1x = ax * delta_3
+ * a1y = ay * delta_3 # <<<<<<<<<<<<<<
+ * b1x = (3 * ax * t1 + bx) * delta_2
+ * b1y = (3 * ay * t1 + by) * delta_2
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ay, __pyx_v_delta_3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 756, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_a1y, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":757
+ * a1x = ax * delta_3
+ * a1y = ay * delta_3
+ * b1x = (3 * ax * t1 + bx) * delta_2 # <<<<<<<<<<<<<<
+ * b1y = (3 * ay * t1 + by) * delta_2
+ * c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
+*/
+ __pyx_t_1 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_v_ax, 3, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 757, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 757, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 757, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_delta_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 757, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_b1x, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":758
+ * a1y = ay * delta_3
+ * b1x = (3 * ax * t1 + bx) * delta_2
+ * b1y = (3 * ay * t1 + by) * delta_2 # <<<<<<<<<<<<<<
+ * c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
+ * c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
+*/
+ __pyx_t_2 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_v_ay, 3, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 758, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 758, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_by); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 758, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_delta_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 758, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_b1y, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":759
+ * b1x = (3 * ax * t1 + bx) * delta_2
+ * b1y = (3 * ay * t1 + by) * delta_2
+ * c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta # <<<<<<<<<<<<<<
+ * c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+*/
+ __pyx_t_1 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_v_bx, 2, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_v_ax, 3, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_9 = PyNumber_Multiply(__pyx_t_2, __pyx_v_t1_2); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_t_9); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_9 = PyNumber_Multiply(__pyx_t_2, __pyx_v_delta); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 759, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_c1x, __pyx_t_9);
+ __pyx_t_9 = 0;
+
+ /* "fontTools/misc/bezierTools.py":760
+ * b1y = (3 * ay * t1 + by) * delta_2
+ * c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
+ * c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta # <<<<<<<<<<<<<<
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+*/
+ __pyx_t_9 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_v_by, 2, 0, 0); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_9, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_9 = PyNumber_Add(__pyx_t_2, __pyx_v_cy); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_v_ay, 3, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_t1_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_9, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_delta); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 760, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_c1y, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":761
+ * c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
+ * c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx # <<<<<<<<<<<<<<
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+ * pt1, pt2, pt3, pt4 = calcCubicPoints(
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_ax, __pyx_v_t1_3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_bx, __pyx_v_t1_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_9 = PyNumber_Add(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_cx, __pyx_v_t1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_9, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_dx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 761, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_d1x, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":762
+ * c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy # <<<<<<<<<<<<<<
+ * pt1, pt2, pt3, pt4 = calcCubicPoints(
+ * (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_ay, __pyx_v_t1_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_by, __pyx_v_t1_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_9 = PyNumber_Add(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_cy, __pyx_v_t1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_9, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_dy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 762, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_d1y, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":763
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+ * pt1, pt2, pt3, pt4 = calcCubicPoints( # <<<<<<<<<<<<<<
+ * (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
+ * )
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_9, __pyx_mstate_global->__pyx_n_u_calcCubicPoints); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+
+ /* "fontTools/misc/bezierTools.py":764
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+ * pt1, pt2, pt3, pt4 = calcCubicPoints(
+ * (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y) # <<<<<<<<<<<<<<
+ * )
+ * segments.append((pt1, pt2, pt3, pt4))
+*/
+ __pyx_t_10 = PyTuple_New(2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 764, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_INCREF(__pyx_v_a1x);
+ __Pyx_GIVEREF(__pyx_v_a1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 0, __pyx_v_a1x) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_a1y);
+ __Pyx_GIVEREF(__pyx_v_a1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 1, __pyx_v_a1y) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __pyx_t_11 = PyTuple_New(2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 764, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_INCREF(__pyx_v_b1x);
+ __Pyx_GIVEREF(__pyx_v_b1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 0, __pyx_v_b1x) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_b1y);
+ __Pyx_GIVEREF(__pyx_v_b1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 1, __pyx_v_b1y) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __pyx_t_12 = PyTuple_New(2); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 764, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __Pyx_INCREF(__pyx_v_c1x);
+ __Pyx_GIVEREF(__pyx_v_c1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_v_c1x) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_c1y);
+ __Pyx_GIVEREF(__pyx_v_c1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 1, __pyx_v_c1y) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __pyx_t_13 = PyTuple_New(2); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 764, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ __Pyx_INCREF(__pyx_v_d1x);
+ __Pyx_GIVEREF(__pyx_v_d1x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_13, 0, __pyx_v_d1x) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_d1y);
+ __Pyx_GIVEREF(__pyx_v_d1y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_13, 1, __pyx_v_d1y) != (0)) __PYX_ERR(0, 764, __pyx_L1_error);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_9))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_9);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_9);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_9, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_2, __pyx_t_10, __pyx_t_11, __pyx_t_12, __pyx_t_13};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_9, __pyx_callargs+__pyx_t_7, (5-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 763, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_9);
+ __pyx_t_13 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_13);
+ __pyx_t_12 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_12);
+ __pyx_t_11 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_11);
+ } else {
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ __pyx_t_13 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_13);
+ __pyx_t_12 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_12);
+ __pyx_t_11 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_11);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_9,&__pyx_t_13,&__pyx_t_12,&__pyx_t_11};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_9,&__pyx_t_13,&__pyx_t_12,&__pyx_t_11};
+ __pyx_t_10 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_10);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_5(__pyx_t_10); if (unlikely(!item)) goto __pyx_L13_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_5(__pyx_t_10), 4) < (0)) __PYX_ERR(0, 763, __pyx_L1_error)
+ __pyx_t_5 = NULL;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ goto __pyx_L14_unpacking_done;
+ __pyx_L13_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_5 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 763, __pyx_L1_error)
+ __pyx_L14_unpacking_done:;
+ }
+
+ /* "fontTools/misc/bezierTools.py":763
+ * d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+ * d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+ * pt1, pt2, pt3, pt4 = calcCubicPoints( # <<<<<<<<<<<<<<
+ * (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
+ * )
+*/
+ __Pyx_XDECREF_SET(__pyx_v_pt1, __pyx_t_9);
+ __pyx_t_9 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt2, __pyx_t_13);
+ __pyx_t_13 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt3, __pyx_t_12);
+ __pyx_t_12 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt4, __pyx_t_11);
+ __pyx_t_11 = 0;
+
+ /* "fontTools/misc/bezierTools.py":766
+ * (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
+ * )
+ * segments.append((pt1, pt2, pt3, pt4)) # <<<<<<<<<<<<<<
+ * return segments
+ *
+*/
+ __pyx_t_1 = PyTuple_New(4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 766, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_pt1) != (0)) __PYX_ERR(0, 766, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_pt2) != (0)) __PYX_ERR(0, 766, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_v_pt3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_v_pt3) != (0)) __PYX_ERR(0, 766, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_pt4);
+ __Pyx_GIVEREF(__pyx_v_pt4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 3, __pyx_v_pt4) != (0)) __PYX_ERR(0, 766, __pyx_L1_error);
+ __pyx_t_3 = __Pyx_PyList_Append(__pyx_v_segments, __pyx_t_1); if (unlikely(__pyx_t_3 == ((int)-1))) __PYX_ERR(0, 766, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":744
+ * cx, cy = c
+ * dx, dy = d
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":767
+ * )
+ * segments.append((pt1, pt2, pt3, pt4))
+ * return segments # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_segments);
+ __pyx_r = __pyx_v_segments;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":735
+ *
+ *
+ * def _splitCubicAtT(a, b, c, d, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * ts.insert(0, 0.0)
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_XDECREF(__pyx_t_13);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitCubicAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ts);
+ __Pyx_XDECREF(__pyx_v_segments);
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_dx);
+ __Pyx_XDECREF(__pyx_v_dy);
+ __Pyx_XDECREF(__pyx_v_i);
+ __Pyx_XDECREF(__pyx_v_t1);
+ __Pyx_XDECREF(__pyx_v_t2);
+ __Pyx_XDECREF(__pyx_v_delta);
+ __Pyx_XDECREF(__pyx_v_delta_2);
+ __Pyx_XDECREF(__pyx_v_delta_3);
+ __Pyx_XDECREF(__pyx_v_t1_2);
+ __Pyx_XDECREF(__pyx_v_t1_3);
+ __Pyx_XDECREF(__pyx_v_a1x);
+ __Pyx_XDECREF(__pyx_v_a1y);
+ __Pyx_XDECREF(__pyx_v_b1x);
+ __Pyx_XDECREF(__pyx_v_b1y);
+ __Pyx_XDECREF(__pyx_v_c1x);
+ __Pyx_XDECREF(__pyx_v_c1y);
+ __Pyx_XDECREF(__pyx_v_d1x);
+ __Pyx_XDECREF(__pyx_v_d1y);
+ __Pyx_XDECREF(__pyx_v_pt1);
+ __Pyx_XDECREF(__pyx_v_pt2);
+ __Pyx_XDECREF(__pyx_v_pt3);
+ __Pyx_XDECREF(__pyx_v_pt4);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_45generator1(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":770
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * a=cython.complex,
+ * b=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_44_splitCubicAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_43_splitCubicAtTC, "_splitCubicAtTC(double complex a, double complex b, double complex c, double complex d, *ts)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_44_splitCubicAtTC = {"_splitCubicAtTC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_44_splitCubicAtTC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_43_splitCubicAtTC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_44_splitCubicAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_a;
+ __pyx_t_double_complex __pyx_v_b;
+ __pyx_t_double_complex __pyx_v_c;
+ __pyx_t_double_complex __pyx_v_d;
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_splitCubicAtTC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ __pyx_v_ts = __Pyx_ArgsSlice_FASTCALL(__pyx_args, 4, __pyx_nargs);
+ if (unlikely(!__pyx_v_ts)) {
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __Pyx_GOTREF(__pyx_v_ts);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_d,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 770, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ default:
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 770, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 770, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 770, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 770, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ const Py_ssize_t used_pos_args = (kwd_pos_args < 4) ? kwd_pos_args : 4;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, used_pos_args, __pyx_kwds_len, "_splitCubicAtTC", 0) < (0)) __PYX_ERR(0, 770, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_splitCubicAtTC", 0, 4, 4, i); __PYX_ERR(0, 770, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs < 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 770, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 770, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 770, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 770, __pyx_L3_error)
+ }
+ __pyx_v_a = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 785, __pyx_L3_error)
+ __pyx_v_b = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 785, __pyx_L3_error)
+ __pyx_v_c = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 785, __pyx_L3_error)
+ __pyx_v_d = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 785, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_splitCubicAtTC", 0, 4, 4, __pyx_nargs); __PYX_ERR(0, 770, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_CLEAR(__pyx_v_ts);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_43_splitCubicAtTC(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_d, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_DECREF(__pyx_v_ts);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_43_splitCubicAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d, PyObject *__pyx_v_ts) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_splitCubicAtTC", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 770, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_v_a = __pyx_v_a;
+ __pyx_cur_scope->__pyx_v_b = __pyx_v_b;
+ __pyx_cur_scope->__pyx_v_c = __pyx_v_c;
+ __pyx_cur_scope->__pyx_v_d = __pyx_v_d;
+ __pyx_cur_scope->__pyx_v_ts = __pyx_v_ts;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_ts);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_ts);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_45generator1, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[3]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC_2, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC_2, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 770, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_45generator1(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_t_3;
+ Py_ssize_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ double __pyx_t_8;
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ PyObject *(*__pyx_t_13)(PyObject *);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_splitCubicAtTC", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ case 1: goto __pyx_L8_resume_from_yield;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(__pyx_sent_value != Py_None)) {
+ if (unlikely(__pyx_sent_value)) PyErr_SetString(PyExc_TypeError, "can't send non-None value to a just-started generator");
+ __PYX_ERR(0, 770, __pyx_L1_error)
+ }
+
+ /* "fontTools/misc/bezierTools.py":786
+ * )
+ * def _splitCubicAtTC(a, b, c, d, *ts):
+ * ts = list(ts) # <<<<<<<<<<<<<<
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+*/
+ __pyx_t_1 = PySequence_List(__pyx_cur_scope->__pyx_v_ts); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 786, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GOTREF(__pyx_cur_scope->__pyx_v_ts);
+ __Pyx_DECREF_SET(__pyx_cur_scope->__pyx_v_ts, __pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":787
+ * def _splitCubicAtTC(a, b, c, d, *ts):
+ * ts = list(ts)
+ * ts.insert(0, 0.0) # <<<<<<<<<<<<<<
+ * ts.append(1.0)
+ * for i in range(len(ts) - 1):
+*/
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_cur_scope->__pyx_v_ts, __pyx_mstate_global->__pyx_n_u_insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 787, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_mstate_global->__pyx_tuple[0], NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 787, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":788
+ * ts = list(ts)
+ * ts.insert(0, 0.0)
+ * ts.append(1.0) # <<<<<<<<<<<<<<
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+*/
+ __pyx_t_3 = __Pyx_PyObject_Append(__pyx_cur_scope->__pyx_v_ts, __pyx_mstate_global->__pyx_float_1_0); if (unlikely(__pyx_t_3 == ((int)-1))) __PYX_ERR(0, 788, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":789
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ __pyx_t_1 = NULL;
+ __pyx_t_4 = PyObject_Length(__pyx_cur_scope->__pyx_v_ts); if (unlikely(__pyx_t_4 == ((Py_ssize_t)-1))) __PYX_ERR(0, 789, __pyx_L1_error)
+ __pyx_t_5 = PyLong_FromSsize_t((__pyx_t_4 - 1)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 789, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_t_5};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)(&PyRange_Type), __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 789, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_5 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 789, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 789, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ for (;;) {
+ {
+ __pyx_t_2 = __pyx_t_7(__pyx_t_5);
+ if (unlikely(!__pyx_t_2)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 789, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_i);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_i, __pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":790
+ * ts.append(1.0)
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i] # <<<<<<<<<<<<<<
+ * t2 = ts[i + 1]
+ * delta = t2 - t1
+*/
+ __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_cur_scope->__pyx_v_ts, __pyx_cur_scope->__pyx_v_i); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 790, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_8 = __Pyx_PyFloat_AsDouble(__pyx_t_2); if (unlikely((__pyx_t_8 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 790, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_cur_scope->__pyx_v_t1 = __pyx_t_8;
+
+ /* "fontTools/misc/bezierTools.py":791
+ * for i in range(len(ts) - 1):
+ * t1 = ts[i]
+ * t2 = ts[i + 1] # <<<<<<<<<<<<<<
+ * delta = t2 - t1
+ *
+*/
+ __pyx_t_2 = __Pyx_PyLong_AddObjC(__pyx_cur_scope->__pyx_v_i, __pyx_mstate_global->__pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 791, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyObject_GetItem(__pyx_cur_scope->__pyx_v_ts, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 791, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_8 = __Pyx_PyFloat_AsDouble(__pyx_t_1); if (unlikely((__pyx_t_8 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 791, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_cur_scope->__pyx_v_t2 = __pyx_t_8;
+
+ /* "fontTools/misc/bezierTools.py":792
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+ * delta = t2 - t1 # <<<<<<<<<<<<<<
+ *
+ * delta_2 = delta * delta
+*/
+ __pyx_cur_scope->__pyx_v_delta = (__pyx_cur_scope->__pyx_v_t2 - __pyx_cur_scope->__pyx_v_t1);
+
+ /* "fontTools/misc/bezierTools.py":794
+ * delta = t2 - t1
+ *
+ * delta_2 = delta * delta # <<<<<<<<<<<<<<
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1
+*/
+ __pyx_cur_scope->__pyx_v_delta_2 = (__pyx_cur_scope->__pyx_v_delta * __pyx_cur_scope->__pyx_v_delta);
+
+ /* "fontTools/misc/bezierTools.py":795
+ *
+ * delta_2 = delta * delta
+ * delta_3 = delta * delta_2 # <<<<<<<<<<<<<<
+ * t1_2 = t1 * t1
+ * t1_3 = t1 * t1_2
+*/
+ __pyx_cur_scope->__pyx_v_delta_3 = (__pyx_cur_scope->__pyx_v_delta * __pyx_cur_scope->__pyx_v_delta_2);
+
+ /* "fontTools/misc/bezierTools.py":796
+ * delta_2 = delta * delta
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1 # <<<<<<<<<<<<<<
+ * t1_3 = t1 * t1_2
+ *
+*/
+ __pyx_cur_scope->__pyx_v_t1_2 = (__pyx_cur_scope->__pyx_v_t1 * __pyx_cur_scope->__pyx_v_t1);
+
+ /* "fontTools/misc/bezierTools.py":797
+ * delta_3 = delta * delta_2
+ * t1_2 = t1 * t1
+ * t1_3 = t1 * t1_2 # <<<<<<<<<<<<<<
+ *
+ * # calc new a, b, c and d
+*/
+ __pyx_cur_scope->__pyx_v_t1_3 = (__pyx_cur_scope->__pyx_v_t1 * __pyx_cur_scope->__pyx_v_t1_2);
+
+ /* "fontTools/misc/bezierTools.py":800
+ *
+ * # calc new a, b, c and d
+ * a1 = a * delta_3 # <<<<<<<<<<<<<<
+ * b1 = (3 * a * t1 + b) * delta_2
+ * c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
+*/
+ __pyx_cur_scope->__pyx_v_a1 = __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_3, 0));
+
+ /* "fontTools/misc/bezierTools.py":801
+ * # calc new a, b, c and d
+ * a1 = a * delta_3
+ * b1 = (3 * a * t1 + b) * delta_2 # <<<<<<<<<<<<<<
+ * c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
+ * d1 = a * t1_3 + b * t1_2 + c * t1 + d
+*/
+ __pyx_cur_scope->__pyx_v_b1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_2, 0));
+
+ /* "fontTools/misc/bezierTools.py":802
+ * a1 = a * delta_3
+ * b1 = (3 * a * t1 + b) * delta_2
+ * c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta # <<<<<<<<<<<<<<
+ * d1 = a * t1_3 + b * t1_2 + c * t1 + d
+ * pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1)
+*/
+ __pyx_cur_scope->__pyx_v_c1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_c), __Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta, 0));
+
+ /* "fontTools/misc/bezierTools.py":803
+ * b1 = (3 * a * t1 + b) * delta_2
+ * c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
+ * d1 = a * t1_3 + b * t1_2 + c * t1 + d # <<<<<<<<<<<<<<
+ * pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1)
+ * yield (pt1, pt2, pt3, pt4)
+*/
+ __pyx_cur_scope->__pyx_v_d1 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_3, 0)), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_b, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_c, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0))), __pyx_cur_scope->__pyx_v_d);
+
+ /* "fontTools/misc/bezierTools.py":804
+ * c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
+ * d1 = a * t1_3 + b * t1_2 + c * t1 + d
+ * pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1) # <<<<<<<<<<<<<<
+ * yield (pt1, pt2, pt3, pt4)
+ *
+*/
+ __pyx_t_1 = __pyx_f_9fontTools_4misc_11bezierTools_calcCubicPointsC(__pyx_cur_scope->__pyx_v_a1, __pyx_cur_scope->__pyx_v_b1, __pyx_cur_scope->__pyx_v_c1, __pyx_cur_scope->__pyx_v_d1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 804, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_9 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_9);
+ __pyx_t_10 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_10);
+ __pyx_t_11 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_11);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_9);
+ __pyx_t_10 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_10);
+ __pyx_t_11 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_11);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_9,&__pyx_t_10,&__pyx_t_11};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_9,&__pyx_t_10,&__pyx_t_11};
+ __pyx_t_12 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_13 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_12);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_13(__pyx_t_12); if (unlikely(!item)) goto __pyx_L6_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_13(__pyx_t_12), 4) < (0)) __PYX_ERR(0, 804, __pyx_L1_error)
+ __pyx_t_13 = NULL;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ goto __pyx_L7_unpacking_done;
+ __pyx_L6_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __pyx_t_13 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 804, __pyx_L1_error)
+ __pyx_L7_unpacking_done:;
+ }
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_pt1);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_pt1, __pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_2);
+ __pyx_t_2 = 0;
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_pt2);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_pt2, __pyx_t_9);
+ __Pyx_GIVEREF(__pyx_t_9);
+ __pyx_t_9 = 0;
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_pt3);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_pt3, __pyx_t_10);
+ __Pyx_GIVEREF(__pyx_t_10);
+ __pyx_t_10 = 0;
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_pt4);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_pt4, __pyx_t_11);
+ __Pyx_GIVEREF(__pyx_t_11);
+ __pyx_t_11 = 0;
+
+ /* "fontTools/misc/bezierTools.py":805
+ * d1 = a * t1_3 + b * t1_2 + c * t1 + d
+ * pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1)
+ * yield (pt1, pt2, pt3, pt4) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_1 = PyTuple_New(4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 805, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_pt1);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_pt1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_cur_scope->__pyx_v_pt1) != (0)) __PYX_ERR(0, 805, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_pt2);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_pt2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_cur_scope->__pyx_v_pt2) != (0)) __PYX_ERR(0, 805, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_pt3);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_pt3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_cur_scope->__pyx_v_pt3) != (0)) __PYX_ERR(0, 805, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_pt4);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_pt4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 3, __pyx_cur_scope->__pyx_v_pt4) != (0)) __PYX_ERR(0, 805, __pyx_L1_error);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __Pyx_XGIVEREF(__pyx_t_5);
+ __pyx_cur_scope->__pyx_t_0 = __pyx_t_5;
+ __pyx_cur_scope->__pyx_t_1 = __pyx_t_7;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ /* return from generator, yielding value */
+ __pyx_generator->resume_label = 1;
+ return __pyx_r;
+ __pyx_L8_resume_from_yield:;
+ __pyx_t_5 = __pyx_cur_scope->__pyx_t_0;
+ __pyx_cur_scope->__pyx_t_0 = 0;
+ __Pyx_XGOTREF(__pyx_t_5);
+ __pyx_t_7 = __pyx_cur_scope->__pyx_t_1;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 805, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":789
+ * ts.insert(0, 0.0)
+ * ts.append(1.0)
+ * for i in range(len(ts) - 1): # <<<<<<<<<<<<<<
+ * t1 = ts[i]
+ * t2 = ts[i + 1]
+*/
+ }
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* "fontTools/misc/bezierTools.py":770
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * a=cython.complex,
+ * b=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("_splitCubicAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":815
+ *
+ *
+ * def solveQuadratic(a, b, c, sqrt=sqrt): # <<<<<<<<<<<<<<
+ * """Solve a quadratic equation.
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_96__defaults__(CYTHON_UNUSED PyObject *__pyx_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("__defaults__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__Pyx_CyFunction_Defaults(struct __pyx_defaults, __pyx_self)->arg0);
+ __Pyx_GIVEREF(__Pyx_CyFunction_Defaults(struct __pyx_defaults, __pyx_self)->arg0);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __Pyx_CyFunction_Defaults(struct __pyx_defaults, __pyx_self)->arg0) != (0)) __PYX_ERR(0, 815, __pyx_L1_error);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 815, __pyx_L1_error);
+ __Pyx_INCREF(Py_None);
+ __Pyx_GIVEREF(Py_None);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, Py_None) != (0)) __PYX_ERR(0, 815, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.__defaults__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_47solveQuadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_46solveQuadratic, "solveQuadratic(a, b, c, sqrt=sqrt)\n\nSolve a quadratic equation.\n\nSolves *a*x*x + b*x + c = 0* where a, b and c are real.\n\nArgs:\n a: coefficient of *x\302\262*\n b: coefficient of *x*\n c: constant term\n\nReturns:\n A list of roots. Note that the returned list is neither guaranteed to\n be sorted nor to contain unique values!");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_47solveQuadratic = {"solveQuadratic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_47solveQuadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_46solveQuadratic};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_47solveQuadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_sqrt = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("solveQuadratic (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_sqrt,0};
+ struct __pyx_defaults *__pyx_dynamic_args = __Pyx_CyFunction_Defaults(struct __pyx_defaults, __pyx_self);
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 815, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 815, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 815, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 815, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 815, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "solveQuadratic", 0) < (0)) __PYX_ERR(0, 815, __pyx_L3_error)
+ if (!values[3]) values[3] = __Pyx_NewRef(__pyx_dynamic_args->arg0);
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("solveQuadratic", 0, 3, 4, i); __PYX_ERR(0, 815, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 815, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 815, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 815, __pyx_L3_error)
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 815, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ if (!values[3]) values[3] = __Pyx_NewRef(__pyx_dynamic_args->arg0);
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ __pyx_v_sqrt = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("solveQuadratic", 0, 3, 4, __pyx_nargs); __PYX_ERR(0, 815, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.solveQuadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_46solveQuadratic(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_sqrt);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_46solveQuadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_sqrt) {
+ PyObject *__pyx_v_roots = NULL;
+ PyObject *__pyx_v_DD = NULL;
+ PyObject *__pyx_v_rDD = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ size_t __pyx_t_5;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("solveQuadratic", 0);
+
+ /* "fontTools/misc/bezierTools.py":829
+ * be sorted nor to contain unique values!
+ * """
+ * if abs(a) < epsilon: # <<<<<<<<<<<<<<
+ * if abs(b) < epsilon:
+ * # We have a non-equation; therefore, we have no valid solution
+*/
+ __pyx_t_1 = __Pyx_PyNumber_Absolute(__pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 829, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 829, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_1, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 829, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 829, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":830
+ * """
+ * if abs(a) < epsilon:
+ * if abs(b) < epsilon: # <<<<<<<<<<<<<<
+ * # We have a non-equation; therefore, we have no valid solution
+ * roots = []
+*/
+ __pyx_t_3 = __Pyx_PyNumber_Absolute(__pyx_v_b); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 830, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 830, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_3, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 830, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 830, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":832
+ * if abs(b) < epsilon:
+ * # We have a non-equation; therefore, we have no valid solution
+ * roots = [] # <<<<<<<<<<<<<<
+ * else:
+ * # We have a linear equation with 1 root.
+*/
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 832, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_roots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":830
+ * """
+ * if abs(a) < epsilon:
+ * if abs(b) < epsilon: # <<<<<<<<<<<<<<
+ * # We have a non-equation; therefore, we have no valid solution
+ * roots = []
+*/
+ goto __pyx_L4;
+ }
+
+ /* "fontTools/misc/bezierTools.py":835
+ * else:
+ * # We have a linear equation with 1 root.
+ * roots = [-c / b] # <<<<<<<<<<<<<<
+ * else:
+ * # We have a true quadratic equation. Apply the quadratic formula to find two roots.
+*/
+ /*else*/ {
+ __pyx_t_1 = PyNumber_Negative(__pyx_v_c); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 835, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 835, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 835, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 835, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_v_roots = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+ }
+ __pyx_L4:;
+
+ /* "fontTools/misc/bezierTools.py":829
+ * be sorted nor to contain unique values!
+ * """
+ * if abs(a) < epsilon: # <<<<<<<<<<<<<<
+ * if abs(b) < epsilon:
+ * # We have a non-equation; therefore, we have no valid solution
+*/
+ goto __pyx_L3;
+ }
+
+ /* "fontTools/misc/bezierTools.py":838
+ * else:
+ * # We have a true quadratic equation. Apply the quadratic formula to find two roots.
+ * DD = b * b - 4.0 * a * c # <<<<<<<<<<<<<<
+ * if DD >= 0.0:
+ * rDD = sqrt(DD)
+*/
+ /*else*/ {
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_b, __pyx_v_b); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 838, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_4_0, __pyx_v_a); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 838, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_2, __pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 838, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 838, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_DD = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":839
+ * # We have a true quadratic equation. Apply the quadratic formula to find two roots.
+ * DD = b * b - 4.0 * a * c
+ * if DD >= 0.0: # <<<<<<<<<<<<<<
+ * rDD = sqrt(DD)
+ * roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
+*/
+ __pyx_t_2 = PyObject_RichCompare(__pyx_v_DD, __pyx_mstate_global->__pyx_float_0_0, Py_GE); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 839, __pyx_L1_error)
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 839, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":840
+ * DD = b * b - 4.0 * a * c
+ * if DD >= 0.0:
+ * rDD = sqrt(DD) # <<<<<<<<<<<<<<
+ * roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
+ * else:
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_INCREF(__pyx_v_sqrt);
+ __pyx_t_1 = __pyx_v_sqrt;
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_v_DD};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 840, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_v_rDD = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":841
+ * if DD >= 0.0:
+ * rDD = sqrt(DD)
+ * roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a] # <<<<<<<<<<<<<<
+ * else:
+ * # complex roots, ignore
+*/
+ __pyx_t_2 = PyNumber_Negative(__pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_rDD); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_1, __pyx_mstate_global->__pyx_float_2_0, 2.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_t_2, __pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Negative(__pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_t_2, __pyx_v_rDD); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_3, __pyx_mstate_global->__pyx_float_2_0, 2.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_t_2, __pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyList_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 841, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 841, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 841, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_v_roots = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":839
+ * # We have a true quadratic equation. Apply the quadratic formula to find two roots.
+ * DD = b * b - 4.0 * a * c
+ * if DD >= 0.0: # <<<<<<<<<<<<<<
+ * rDD = sqrt(DD)
+ * roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
+*/
+ goto __pyx_L5;
+ }
+
+ /* "fontTools/misc/bezierTools.py":844
+ * else:
+ * # complex roots, ignore
+ * roots = [] # <<<<<<<<<<<<<<
+ * return roots
+ *
+*/
+ /*else*/ {
+ __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 844, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_v_roots = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+ }
+ __pyx_L5:;
+ }
+ __pyx_L3:;
+
+ /* "fontTools/misc/bezierTools.py":845
+ * # complex roots, ignore
+ * roots = []
+ * return roots # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_roots);
+ __pyx_r = __pyx_v_roots;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":815
+ *
+ *
+ * def solveQuadratic(a, b, c, sqrt=sqrt): # <<<<<<<<<<<<<<
+ * """Solve a quadratic equation.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.solveQuadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_roots);
+ __Pyx_XDECREF(__pyx_v_DD);
+ __Pyx_XDECREF(__pyx_v_rDD);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":848
+ *
+ *
+ * def solveCubic(a, b, c, d): # <<<<<<<<<<<<<<
+ * """Solve a cubic equation.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_49solveCubic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_48solveCubic, "solveCubic(a, b, c, d)\n\nSolve a cubic equation.\n\nSolves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.\n\nArgs:\n a: coefficient of *x\302\263*\n b: coefficient of *x\302\262*\n c: coefficient of *x*\n d: constant term\n\nReturns:\n A list of roots. Note that the returned list is neither guaranteed to\n be sorted nor to contain unique values!\n\nExamples::\n\n >>> solveCubic(1, 1, -6, 0)\n [-3.0, -0.0, 2.0]\n >>> solveCubic(-10.0, -9.0, 48.0, -29.0)\n [-2.9, 1.0, 1.0]\n >>> solveCubic(-9.875, -9.0, 47.625, -28.75)\n [-2.911392, 1.0, 1.0]\n >>> solveCubic(1.0, -4.5, 6.75, -3.375)\n [1.5, 1.5, 1.5]\n >>> solveCubic(-12.0, 18.0, -9.0, 1.50023651123)\n [0.5, 0.5, 0.5]\n >>> solveCubic(\n ... 9.0, 0.0, 0.0, -7.62939453125e-05\n ... ) == [-0.0, -0.0, -0.0]\n True");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_49solveCubic = {"solveCubic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_49solveCubic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_48solveCubic};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_49solveCubic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_d = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("solveCubic (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_d,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 848, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 848, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 848, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 848, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 848, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "solveCubic", 0) < (0)) __PYX_ERR(0, 848, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("solveCubic", 1, 4, 4, i); __PYX_ERR(0, 848, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 848, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 848, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 848, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 848, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ __pyx_v_d = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("solveCubic", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 848, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.solveCubic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_48solveCubic(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_d);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_48solveCubic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d) {
+ PyObject *__pyx_v_a1 = NULL;
+ PyObject *__pyx_v_a2 = NULL;
+ PyObject *__pyx_v_a3 = NULL;
+ PyObject *__pyx_v_Q = NULL;
+ PyObject *__pyx_v_R = NULL;
+ PyObject *__pyx_v_R2 = NULL;
+ PyObject *__pyx_v_Q3 = NULL;
+ PyObject *__pyx_v_R2_Q3 = NULL;
+ PyObject *__pyx_v_x = NULL;
+ PyObject *__pyx_v_theta = NULL;
+ PyObject *__pyx_v_rQ2 = NULL;
+ PyObject *__pyx_v_a1_3 = NULL;
+ PyObject *__pyx_v_x0 = NULL;
+ PyObject *__pyx_v_x1 = NULL;
+ PyObject *__pyx_v_x2 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ size_t __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_t_7;
+ double __pyx_t_8;
+ double __pyx_t_9;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("solveCubic", 0);
+ __Pyx_INCREF(__pyx_v_a);
+
+ /* "fontTools/misc/bezierTools.py":886
+ * # found at: http://www.strangecreations.com/library/snippets/Cubic.C
+ * #
+ * if abs(a) < epsilon: # <<<<<<<<<<<<<<
+ * # don't just test for zero; for very small values of 'a' solveCubic()
+ * # returns unreliable results, so we fall back to quad.
+*/
+ __pyx_t_1 = __Pyx_PyNumber_Absolute(__pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 886, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 886, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_1, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 886, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 886, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":889
+ * # don't just test for zero; for very small values of 'a' solveCubic()
+ * # returns unreliable results, so we fall back to quad.
+ * return solveQuadratic(b, c, d) # <<<<<<<<<<<<<<
+ * a = float(a)
+ * a1 = b / a
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_solveQuadratic); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 889, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_b, __pyx_v_c, __pyx_v_d};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_5, (4-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 889, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":886
+ * # found at: http://www.strangecreations.com/library/snippets/Cubic.C
+ * #
+ * if abs(a) < epsilon: # <<<<<<<<<<<<<<
+ * # don't just test for zero; for very small values of 'a' solveCubic()
+ * # returns unreliable results, so we fall back to quad.
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":890
+ * # returns unreliable results, so we fall back to quad.
+ * return solveQuadratic(b, c, d)
+ * a = float(a) # <<<<<<<<<<<<<<
+ * a1 = b / a
+ * a2 = c / a
+*/
+ __pyx_t_3 = __Pyx_PyNumber_Float(__pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 890, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF_SET(__pyx_v_a, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":891
+ * return solveQuadratic(b, c, d)
+ * a = float(a)
+ * a1 = b / a # <<<<<<<<<<<<<<
+ * a2 = c / a
+ * a3 = d / a
+*/
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_v_b, __pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 891, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_a1 = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":892
+ * a = float(a)
+ * a1 = b / a
+ * a2 = c / a # <<<<<<<<<<<<<<
+ * a3 = d / a
+ *
+*/
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_v_c, __pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 892, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_a2 = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":893
+ * a1 = b / a
+ * a2 = c / a
+ * a3 = d / a # <<<<<<<<<<<<<<
+ *
+ * Q = (a1 * a1 - 3.0 * a2) / 9.0
+*/
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_v_d, __pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 893, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_a3 = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":895
+ * a3 = d / a
+ *
+ * Q = (a1 * a1 - 3.0 * a2) / 9.0 # <<<<<<<<<<<<<<
+ * R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0
+ *
+*/
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_a1, __pyx_v_a1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 895, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_3_0, __pyx_v_a2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 895, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 895, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_2, __pyx_mstate_global->__pyx_float_9_0, 9.0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 895, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_Q = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":896
+ *
+ * Q = (a1 * a1 - 3.0 * a2) / 9.0
+ * R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0 # <<<<<<<<<<<<<<
+ *
+ * R2 = R * R
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_2_0, __pyx_v_a1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_v_a1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_a1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_9_0, __pyx_v_a1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_2, __pyx_v_a2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_27_0, __pyx_v_a3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_1, __pyx_mstate_global->__pyx_float_54_0, 54.0, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 896, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_R = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":898
+ * R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0
+ *
+ * R2 = R * R # <<<<<<<<<<<<<<
+ * Q3 = Q * Q * Q
+ * R2 = 0 if R2 < epsilon else R2
+*/
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_R, __pyx_v_R); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 898, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_R2 = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":899
+ *
+ * R2 = R * R
+ * Q3 = Q * Q * Q # <<<<<<<<<<<<<<
+ * R2 = 0 if R2 < epsilon else R2
+ * Q3 = 0 if abs(Q3) < epsilon else Q3
+*/
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_Q, __pyx_v_Q); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 899, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_v_Q); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 899, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_Q3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":900
+ * R2 = R * R
+ * Q3 = Q * Q * Q
+ * R2 = 0 if R2 < epsilon else R2 # <<<<<<<<<<<<<<
+ * Q3 = 0 if abs(Q3) < epsilon else Q3
+ *
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 900, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = PyObject_RichCompare(__pyx_v_R2, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 900, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 900, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_4) {
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_0);
+ __pyx_t_1 = __pyx_mstate_global->__pyx_int_0;
+ } else {
+ __Pyx_INCREF(__pyx_v_R2);
+ __pyx_t_1 = __pyx_v_R2;
+ }
+ __Pyx_DECREF_SET(__pyx_v_R2, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":901
+ * Q3 = Q * Q * Q
+ * R2 = 0 if R2 < epsilon else R2
+ * Q3 = 0 if abs(Q3) < epsilon else Q3 # <<<<<<<<<<<<<<
+ *
+ * R2_Q3 = R2 - Q3
+*/
+ __pyx_t_2 = __Pyx_PyNumber_Absolute(__pyx_v_Q3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 901, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 901, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = PyObject_RichCompare(__pyx_t_2, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 901, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 901, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_4) {
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_0);
+ __pyx_t_1 = __pyx_mstate_global->__pyx_int_0;
+ } else {
+ __Pyx_INCREF(__pyx_v_Q3);
+ __pyx_t_1 = __pyx_v_Q3;
+ }
+ __Pyx_DECREF_SET(__pyx_v_Q3, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":903
+ * Q3 = 0 if abs(Q3) < epsilon else Q3
+ *
+ * R2_Q3 = R2 - Q3 # <<<<<<<<<<<<<<
+ *
+ * if R2 == 0.0 and Q3 == 0.0:
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_R2, __pyx_v_Q3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 903, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_R2_Q3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":905
+ * R2_Q3 = R2 - Q3
+ *
+ * if R2 == 0.0 and Q3 == 0.0: # <<<<<<<<<<<<<<
+ * x = round(-a1 / 3.0, epsilonDigits)
+ * return [x, x, x]
+*/
+ __pyx_t_7 = (__Pyx_PyFloat_BoolEqObjC(__pyx_v_R2, __pyx_mstate_global->__pyx_float_0_0, 0.0, 0, 0)); if (unlikely((__pyx_t_7 < 0))) __PYX_ERR(0, 905, __pyx_L1_error)
+ if (__pyx_t_7) {
+ } else {
+ __pyx_t_4 = __pyx_t_7;
+ goto __pyx_L5_bool_binop_done;
+ }
+ __pyx_t_7 = (__Pyx_PyFloat_BoolEqObjC(__pyx_v_Q3, __pyx_mstate_global->__pyx_float_0_0, 0.0, 0, 0)); if (unlikely((__pyx_t_7 < 0))) __PYX_ERR(0, 905, __pyx_L1_error)
+ __pyx_t_4 = __pyx_t_7;
+ __pyx_L5_bool_binop_done:;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":906
+ *
+ * if R2 == 0.0 and Q3 == 0.0:
+ * x = round(-a1 / 3.0, epsilonDigits) # <<<<<<<<<<<<<<
+ * return [x, x, x]
+ * elif R2_Q3 <= epsilon * 0.5:
+*/
+ __pyx_t_6 = NULL;
+ __pyx_t_3 = PyNumber_Negative(__pyx_v_a1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 906, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_3, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 906, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 906, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_t_2, __pyx_t_3};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 906, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_x = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":907
+ * if R2 == 0.0 and Q3 == 0.0:
+ * x = round(-a1 / 3.0, epsilonDigits)
+ * return [x, x, x] # <<<<<<<<<<<<<<
+ * elif R2_Q3 <= epsilon * 0.5:
+ * # The epsilon * .5 above ensures that Q3 is not zero.
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 907, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 907, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 1, __pyx_v_x) != (0)) __PYX_ERR(0, 907, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 2, __pyx_v_x) != (0)) __PYX_ERR(0, 907, __pyx_L1_error);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":905
+ * R2_Q3 = R2 - Q3
+ *
+ * if R2 == 0.0 and Q3 == 0.0: # <<<<<<<<<<<<<<
+ * x = round(-a1 / 3.0, epsilonDigits)
+ * return [x, x, x]
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":908
+ * x = round(-a1 / 3.0, epsilonDigits)
+ * return [x, x, x]
+ * elif R2_Q3 <= epsilon * 0.5: # <<<<<<<<<<<<<<
+ * # The epsilon * .5 above ensures that Q3 is not zero.
+ * theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 908, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_0_5); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 908, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyObject_RichCompare(__pyx_v_R2_Q3, __pyx_t_3, Py_LE); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 908, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 908, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":910
+ * elif R2_Q3 <= epsilon * 0.5:
+ * # The epsilon * .5 above ensures that Q3 is not zero.
+ * theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0)) # <<<<<<<<<<<<<<
+ * rQ2 = -2.0 * sqrt(Q)
+ * a1_3 = a1 / 3.0
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_acos); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_8 = -1.0;
+ __pyx_t_9 = 1.0;
+ __pyx_t_10 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_sqrt); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_11))) {
+ __pyx_t_10 = PyMethod_GET_SELF(__pyx_t_11);
+ assert(__pyx_t_10);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11);
+ __Pyx_INCREF(__pyx_t_10);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_11, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_10, __pyx_v_Q3};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_11, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_11 = __Pyx_PyNumber_Divide(__pyx_v_R, __pyx_t_6); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_10 = PyFloat_FromDouble(__pyx_t_9); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_12 = PyObject_RichCompare(__pyx_t_10, __pyx_t_11, Py_LT); __Pyx_XGOTREF(__pyx_t_12); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_12); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ if (__pyx_t_4) {
+ __pyx_t_12 = PyFloat_FromDouble(__pyx_t_9); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_6 = __pyx_t_12;
+ __pyx_t_12 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_11);
+ __pyx_t_6 = __pyx_t_11;
+ }
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_11 = __pyx_t_6;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_12 = PyFloat_FromDouble(__pyx_t_8); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_10 = PyObject_RichCompare(__pyx_t_12, __pyx_t_11, Py_GT); __Pyx_XGOTREF(__pyx_t_10); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_10); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ if (__pyx_t_4) {
+ __pyx_t_10 = PyFloat_FromDouble(__pyx_t_8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_6 = __pyx_t_10;
+ __pyx_t_10 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_11);
+ __pyx_t_6 = __pyx_t_11;
+ }
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_t_6};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 910, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_theta = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":911
+ * # The epsilon * .5 above ensures that Q3 is not zero.
+ * theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+ * rQ2 = -2.0 * sqrt(Q) # <<<<<<<<<<<<<<
+ * a1_3 = a1 / 3.0
+ * x0 = rQ2 * cos(theta / 3.0) - a1_3
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_sqrt); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 911, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_Q};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 911, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_6 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_neg_2_0, __pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 911, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_rQ2 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":912
+ * theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+ * rQ2 = -2.0 * sqrt(Q)
+ * a1_3 = a1 / 3.0 # <<<<<<<<<<<<<<
+ * x0 = rQ2 * cos(theta / 3.0) - a1_3
+ * x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
+*/
+ __pyx_t_6 = __Pyx_PyFloat_TrueDivideObjC(__pyx_v_a1, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 912, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_v_a1_3 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":913
+ * rQ2 = -2.0 * sqrt(Q)
+ * a1_3 = a1 / 3.0
+ * x0 = rQ2 * cos(theta / 3.0) - a1_3 # <<<<<<<<<<<<<<
+ * x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
+ * x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_cos); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 913, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyFloat_TrueDivideObjC(__pyx_v_theta, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 913, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_t_3};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 913, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_rQ2, __pyx_t_6); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 913, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_2, __pyx_v_a1_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 913, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x0 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":914
+ * a1_3 = a1 / 3.0
+ * x0 = rQ2 * cos(theta / 3.0) - a1_3
+ * x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3 # <<<<<<<<<<<<<<
+ * x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3
+ * x0, x1, x2 = sorted([x0, x1, x2])
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_cos); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_pi); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_11 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_2_0, __pyx_t_1); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_v_theta, __pyx_t_11); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_t_11 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_t_11};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_rQ2, __pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_3, __pyx_v_a1_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 914, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_x1 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":915
+ * x0 = rQ2 * cos(theta / 3.0) - a1_3
+ * x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
+ * x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3 # <<<<<<<<<<<<<<
+ * x0, x1, x2 = sorted([x0, x1, x2])
+ * # Merge roots that are close-enough
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_cos); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_pi); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_4_0, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_v_theta, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_2, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_11))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_11);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_11, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_t_1};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_11, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_11 = PyNumber_Multiply(__pyx_v_rQ2, __pyx_t_6); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_11, __pyx_v_a1_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 915, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_v_x2 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":916
+ * x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
+ * x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3
+ * x0, x1, x2 = sorted([x0, x1, x2]) # <<<<<<<<<<<<<<
+ * # Merge roots that are close-enough
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon:
+*/
+ __pyx_t_6 = PyList_New(3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx_v_x0);
+ __Pyx_GIVEREF(__pyx_v_x0);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 0, __pyx_v_x0) != (0)) __PYX_ERR(0, 916, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x1);
+ __Pyx_GIVEREF(__pyx_v_x1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 1, __pyx_v_x1) != (0)) __PYX_ERR(0, 916, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x2);
+ __Pyx_GIVEREF(__pyx_v_x2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 2, __pyx_v_x2) != (0)) __PYX_ERR(0, 916, __pyx_L1_error);
+ if (unlikely((PyList_Sort(__pyx_t_6) < 0))) __PYX_ERR(0, 916, __pyx_L1_error)
+ if (1) {
+ PyObject* sequence = __pyx_t_6;
+ Py_ssize_t size = __Pyx_PyList_GET_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 916, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_11 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_11);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ #else
+ __pyx_t_11 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 916, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ #endif
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ __Pyx_DECREF_SET(__pyx_v_x0, __pyx_t_11);
+ __pyx_t_11 = 0;
+ __Pyx_DECREF_SET(__pyx_v_x1, __pyx_t_1);
+ __pyx_t_1 = 0;
+ __Pyx_DECREF_SET(__pyx_v_x2, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":918
+ * x0, x1, x2 = sorted([x0, x1, x2])
+ * # Merge roots that are close-enough
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ * elif x1 - x0 < epsilon:
+*/
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_x1, __pyx_v_x0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_6, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_7 < 0))) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_7) {
+ } else {
+ __pyx_t_4 = __pyx_t_7;
+ goto __pyx_L8_bool_binop_done;
+ }
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x2, __pyx_v_x1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = PyObject_RichCompare(__pyx_t_1, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_7 < 0))) __PYX_ERR(0, 918, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_4 = __pyx_t_7;
+ __pyx_L8_bool_binop_done:;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":919
+ * # Merge roots that are close-enough
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon:
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits) # <<<<<<<<<<<<<<
+ * elif x1 - x0 < epsilon:
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+*/
+ __pyx_t_3 = NULL;
+ __pyx_t_1 = PyNumber_Add(__pyx_v_x0, __pyx_v_x1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 919, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_11 = PyNumber_Add(__pyx_t_1, __pyx_v_x2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 919, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_11, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 919, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 919, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_t_1, __pyx_t_11};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 919, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_DECREF_SET(__pyx_v_x0, __pyx_t_6);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_DECREF_SET(__pyx_v_x1, __pyx_t_6);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_DECREF_SET(__pyx_v_x2, __pyx_t_6);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":918
+ * x0, x1, x2 = sorted([x0, x1, x2])
+ * # Merge roots that are close-enough
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ * elif x1 - x0 < epsilon:
+*/
+ goto __pyx_L7;
+ }
+
+ /* "fontTools/misc/bezierTools.py":920
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon:
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ * elif x1 - x0 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+*/
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_x1, __pyx_v_x0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 920, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 920, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_6, __pyx_t_11, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 920, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 920, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":921
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ * elif x1 - x0 < epsilon:
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits) # <<<<<<<<<<<<<<
+ * x2 = round(x2, epsilonDigits)
+ * elif x2 - x1 < epsilon:
+*/
+ __pyx_t_11 = NULL;
+ __pyx_t_6 = PyNumber_Add(__pyx_v_x0, __pyx_v_x1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 921, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_6, __pyx_mstate_global->__pyx_float_2_0, 2.0, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 921, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 921, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_11, __pyx_t_3, __pyx_t_6};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 921, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_DECREF_SET(__pyx_v_x0, __pyx_t_1);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_DECREF_SET(__pyx_v_x1, __pyx_t_1);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":922
+ * elif x1 - x0 < epsilon:
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ * x2 = round(x2, epsilonDigits) # <<<<<<<<<<<<<<
+ * elif x2 - x1 < epsilon:
+ * x0 = round(x0, epsilonDigits)
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 922, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_x2, __pyx_t_3};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 922, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x2, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":920
+ * if x1 - x0 < epsilon and x2 - x1 < epsilon:
+ * x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ * elif x1 - x0 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+*/
+ goto __pyx_L7;
+ }
+
+ /* "fontTools/misc/bezierTools.py":923
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+ * elif x2 - x1 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = round(x0, epsilonDigits)
+ * x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x2, __pyx_v_x1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 923, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 923, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = PyObject_RichCompare(__pyx_t_1, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 923, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 923, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":924
+ * x2 = round(x2, epsilonDigits)
+ * elif x2 - x1 < epsilon:
+ * x0 = round(x0, epsilonDigits) # <<<<<<<<<<<<<<
+ * x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
+ * else:
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 924, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_x0, __pyx_t_1};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 924, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x0, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":925
+ * elif x2 - x1 < epsilon:
+ * x0 = round(x0, epsilonDigits)
+ * x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits) # <<<<<<<<<<<<<<
+ * else:
+ * x0 = round(x0, epsilonDigits)
+*/
+ __pyx_t_1 = NULL;
+ __pyx_t_3 = PyNumber_Add(__pyx_v_x1, __pyx_v_x2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 925, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_11 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_3, __pyx_mstate_global->__pyx_float_2_0, 2.0, 0, 0); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 925, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 925, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_1, __pyx_t_11, __pyx_t_3};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 925, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_DECREF_SET(__pyx_v_x1, __pyx_t_6);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_DECREF_SET(__pyx_v_x2, __pyx_t_6);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":923
+ * x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+ * elif x2 - x1 < epsilon: # <<<<<<<<<<<<<<
+ * x0 = round(x0, epsilonDigits)
+ * x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
+*/
+ goto __pyx_L7;
+ }
+
+ /* "fontTools/misc/bezierTools.py":927
+ * x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
+ * else:
+ * x0 = round(x0, epsilonDigits) # <<<<<<<<<<<<<<
+ * x1 = round(x1, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+*/
+ /*else*/ {
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 927, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_x0, __pyx_t_11};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 927, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x0, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":928
+ * else:
+ * x0 = round(x0, epsilonDigits)
+ * x1 = round(x1, epsilonDigits) # <<<<<<<<<<<<<<
+ * x2 = round(x2, epsilonDigits)
+ * return [x0, x1, x2]
+*/
+ __pyx_t_11 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 928, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_11, __pyx_v_x1, __pyx_t_3};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 928, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x1, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":929
+ * x0 = round(x0, epsilonDigits)
+ * x1 = round(x1, epsilonDigits)
+ * x2 = round(x2, epsilonDigits) # <<<<<<<<<<<<<<
+ * return [x0, x1, x2]
+ * else:
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 929, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_x2, __pyx_t_11};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 929, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x2, __pyx_t_6);
+ __pyx_t_6 = 0;
+ }
+ __pyx_L7:;
+
+ /* "fontTools/misc/bezierTools.py":930
+ * x1 = round(x1, epsilonDigits)
+ * x2 = round(x2, epsilonDigits)
+ * return [x0, x1, x2] # <<<<<<<<<<<<<<
+ * else:
+ * x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_6 = PyList_New(3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 930, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx_v_x0);
+ __Pyx_GIVEREF(__pyx_v_x0);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 0, __pyx_v_x0) != (0)) __PYX_ERR(0, 930, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x1);
+ __Pyx_GIVEREF(__pyx_v_x1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 1, __pyx_v_x1) != (0)) __PYX_ERR(0, 930, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_x2);
+ __Pyx_GIVEREF(__pyx_v_x2);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 2, __pyx_v_x2) != (0)) __PYX_ERR(0, 930, __pyx_L1_error);
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":908
+ * x = round(-a1 / 3.0, epsilonDigits)
+ * return [x, x, x]
+ * elif R2_Q3 <= epsilon * 0.5: # <<<<<<<<<<<<<<
+ * # The epsilon * .5 above ensures that Q3 is not zero.
+ * theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":932
+ * return [x0, x1, x2]
+ * else:
+ * x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0) # <<<<<<<<<<<<<<
+ * x = x + Q / x
+ * if R >= 0.0:
+*/
+ /*else*/ {
+ __pyx_t_11 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_sqrt); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_11);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_11);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_11, __pyx_v_R2_Q3};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_3 = __Pyx_PyNumber_Absolute(__pyx_v_R); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_11 = PyNumber_Add(__pyx_t_6, __pyx_t_3); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyFloat_FromDouble((1.0 / 3.0)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyNumber_Power2(__pyx_t_11, __pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 932, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_x = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":933
+ * else:
+ * x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
+ * x = x + Q / x # <<<<<<<<<<<<<<
+ * if R >= 0.0:
+ * x = -x
+*/
+ __pyx_t_6 = __Pyx_PyNumber_Divide(__pyx_v_Q, __pyx_v_x); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 933, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = PyNumber_Add(__pyx_v_x, __pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 933, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF_SET(__pyx_v_x, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":934
+ * x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
+ * x = x + Q / x
+ * if R >= 0.0: # <<<<<<<<<<<<<<
+ * x = -x
+ * x = round(x - a1 / 3.0, epsilonDigits)
+*/
+ __pyx_t_3 = PyObject_RichCompare(__pyx_v_R, __pyx_mstate_global->__pyx_float_0_0, Py_GE); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 934, __pyx_L1_error)
+ __pyx_t_4 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_4 < 0))) __PYX_ERR(0, 934, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_4) {
+
+ /* "fontTools/misc/bezierTools.py":935
+ * x = x + Q / x
+ * if R >= 0.0:
+ * x = -x # <<<<<<<<<<<<<<
+ * x = round(x - a1 / 3.0, epsilonDigits)
+ * return [x]
+*/
+ __pyx_t_3 = PyNumber_Negative(__pyx_v_x); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 935, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF_SET(__pyx_v_x, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":934
+ * x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
+ * x = x + Q / x
+ * if R >= 0.0: # <<<<<<<<<<<<<<
+ * x = -x
+ * x = round(x - a1 / 3.0, epsilonDigits)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":936
+ * if R >= 0.0:
+ * x = -x
+ * x = round(x - a1 / 3.0, epsilonDigits) # <<<<<<<<<<<<<<
+ * return [x]
+ *
+*/
+ __pyx_t_6 = NULL;
+ __pyx_t_11 = __Pyx_PyFloat_TrueDivideObjC(__pyx_v_a1, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 936, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x, __pyx_t_11); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 936, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_epsilonDigits); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 936, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_t_1, __pyx_t_11};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_round, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 936, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __Pyx_DECREF_SET(__pyx_v_x, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":937
+ * x = -x
+ * x = round(x - a1 / 3.0, epsilonDigits)
+ * return [x] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 937, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 937, __pyx_L1_error);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":848
+ *
+ *
+ * def solveCubic(a, b, c, d): # <<<<<<<<<<<<<<
+ * """Solve a cubic equation.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.solveCubic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_a1);
+ __Pyx_XDECREF(__pyx_v_a2);
+ __Pyx_XDECREF(__pyx_v_a3);
+ __Pyx_XDECREF(__pyx_v_Q);
+ __Pyx_XDECREF(__pyx_v_R);
+ __Pyx_XDECREF(__pyx_v_R2);
+ __Pyx_XDECREF(__pyx_v_Q3);
+ __Pyx_XDECREF(__pyx_v_R2_Q3);
+ __Pyx_XDECREF(__pyx_v_x);
+ __Pyx_XDECREF(__pyx_v_theta);
+ __Pyx_XDECREF(__pyx_v_rQ2);
+ __Pyx_XDECREF(__pyx_v_a1_3);
+ __Pyx_XDECREF(__pyx_v_x0);
+ __Pyx_XDECREF(__pyx_v_x1);
+ __Pyx_XDECREF(__pyx_v_x2);
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":945
+ *
+ *
+ * def calcQuadraticParameters(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_51calcQuadraticParameters(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_50calcQuadraticParameters, "calcQuadraticParameters(pt1, pt2, pt3)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_51calcQuadraticParameters = {"calcQuadraticParameters", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_51calcQuadraticParameters, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_50calcQuadraticParameters};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_51calcQuadraticParameters(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcQuadraticParameters (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 945, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 945, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 945, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 945, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcQuadraticParameters", 0) < (0)) __PYX_ERR(0, 945, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcQuadraticParameters", 1, 3, 3, i); __PYX_ERR(0, 945, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 945, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 945, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 945, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcQuadraticParameters", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 945, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticParameters", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_50calcQuadraticParameters(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_50calcQuadraticParameters(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3) {
+ PyObject *__pyx_v_x2 = NULL;
+ PyObject *__pyx_v_y2 = NULL;
+ PyObject *__pyx_v_x3 = NULL;
+ PyObject *__pyx_v_y3 = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcQuadraticParameters", 0);
+
+ /* "fontTools/misc/bezierTools.py":946
+ *
+ * def calcQuadraticParameters(pt1, pt2, pt3):
+ * x2, y2 = pt2 # <<<<<<<<<<<<<<
+ * x3, y3 = pt3
+ * cx, cy = pt1
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt2))) || (PyList_CheckExact(__pyx_v_pt2))) {
+ PyObject* sequence = __pyx_v_pt2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 946, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 946, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 946, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_x2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_y2 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":947
+ * def calcQuadraticParameters(pt1, pt2, pt3):
+ * x2, y2 = pt2
+ * x3, y3 = pt3 # <<<<<<<<<<<<<<
+ * cx, cy = pt1
+ * bx = (x2 - cx) * 2.0
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt3))) || (PyList_CheckExact(__pyx_v_pt3))) {
+ PyObject* sequence = __pyx_v_pt3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 947, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 947, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 947, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_x3 = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_y3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":948
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+ * cx, cy = pt1 # <<<<<<<<<<<<<<
+ * bx = (x2 - cx) * 2.0
+ * by = (y2 - cy) * 2.0
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt1))) || (PyList_CheckExact(__pyx_v_pt1))) {
+ PyObject* sequence = __pyx_v_pt1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 948, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 948, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 948, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_cy = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":949
+ * x3, y3 = pt3
+ * cx, cy = pt1
+ * bx = (x2 - cx) * 2.0 # <<<<<<<<<<<<<<
+ * by = (y2 - cy) * 2.0
+ * ax = x3 - cx - bx
+*/
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_x2, __pyx_v_cx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 949, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 949, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_bx = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":950
+ * cx, cy = pt1
+ * bx = (x2 - cx) * 2.0
+ * by = (y2 - cy) * 2.0 # <<<<<<<<<<<<<<
+ * ax = x3 - cx - bx
+ * ay = y3 - cy - by
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_y2, __pyx_v_cy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 950, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_2_0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 950, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_by = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":951
+ * bx = (x2 - cx) * 2.0
+ * by = (y2 - cy) * 2.0
+ * ax = x3 - cx - bx # <<<<<<<<<<<<<<
+ * ay = y3 - cy - by
+ * return (ax, ay), (bx, by), (cx, cy)
+*/
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_x3, __pyx_v_cx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 951, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 951, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_ax = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":952
+ * by = (y2 - cy) * 2.0
+ * ax = x3 - cx - bx
+ * ay = y3 - cy - by # <<<<<<<<<<<<<<
+ * return (ax, ay), (bx, by), (cx, cy)
+ *
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_y3, __pyx_v_cy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 952, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_v_by); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 952, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_ay = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":953
+ * ax = x3 - cx - bx
+ * ay = y3 - cy - by
+ * return (ax, ay), (bx, by), (cx, cy) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 953, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_ax);
+ __Pyx_GIVEREF(__pyx_v_ax);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_ax) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_ay);
+ __Pyx_GIVEREF(__pyx_v_ay);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_ay) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 953, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_bx);
+ __Pyx_GIVEREF(__pyx_v_bx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_bx) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_by);
+ __Pyx_GIVEREF(__pyx_v_by);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_by) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 953, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_cx);
+ __Pyx_GIVEREF(__pyx_v_cx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_cx) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_cy);
+ __Pyx_GIVEREF(__pyx_v_cy);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_cy) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __pyx_t_5 = PyTuple_New(3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 953, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 953, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":945
+ *
+ *
+ * def calcQuadraticParameters(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticParameters", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_x2);
+ __Pyx_XDECREF(__pyx_v_y2);
+ __Pyx_XDECREF(__pyx_v_x3);
+ __Pyx_XDECREF(__pyx_v_y3);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":956
+ *
+ *
+ * def calcCubicParameters(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_53calcCubicParameters(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_52calcCubicParameters, "calcCubicParameters(pt1, pt2, pt3, pt4)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_53calcCubicParameters = {"calcCubicParameters", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_53calcCubicParameters, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_52calcCubicParameters};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_53calcCubicParameters(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcCubicParameters (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 956, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 956, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 956, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 956, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 956, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcCubicParameters", 0) < (0)) __PYX_ERR(0, 956, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcCubicParameters", 1, 4, 4, i); __PYX_ERR(0, 956, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 956, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 956, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 956, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 956, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcCubicParameters", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 956, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicParameters", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_52calcCubicParameters(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_52calcCubicParameters(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4) {
+ PyObject *__pyx_v_x2 = NULL;
+ PyObject *__pyx_v_y2 = NULL;
+ PyObject *__pyx_v_x3 = NULL;
+ PyObject *__pyx_v_y3 = NULL;
+ PyObject *__pyx_v_x4 = NULL;
+ PyObject *__pyx_v_y4 = NULL;
+ PyObject *__pyx_v_dx = NULL;
+ PyObject *__pyx_v_dy = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicParameters", 0);
+
+ /* "fontTools/misc/bezierTools.py":957
+ *
+ * def calcCubicParameters(pt1, pt2, pt3, pt4):
+ * x2, y2 = pt2 # <<<<<<<<<<<<<<
+ * x3, y3 = pt3
+ * x4, y4 = pt4
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt2))) || (PyList_CheckExact(__pyx_v_pt2))) {
+ PyObject* sequence = __pyx_v_pt2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 957, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 957, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 957, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_x2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_y2 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":958
+ * def calcCubicParameters(pt1, pt2, pt3, pt4):
+ * x2, y2 = pt2
+ * x3, y3 = pt3 # <<<<<<<<<<<<<<
+ * x4, y4 = pt4
+ * dx, dy = pt1
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt3))) || (PyList_CheckExact(__pyx_v_pt3))) {
+ PyObject* sequence = __pyx_v_pt3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 958, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 958, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 958, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_x3 = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_y3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":959
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+ * x4, y4 = pt4 # <<<<<<<<<<<<<<
+ * dx, dy = pt1
+ * cx = (x2 - dx) * 3.0
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt4))) || (PyList_CheckExact(__pyx_v_pt4))) {
+ PyObject* sequence = __pyx_v_pt4;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 959, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 959, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 959, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_x4 = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_y4 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":960
+ * x3, y3 = pt3
+ * x4, y4 = pt4
+ * dx, dy = pt1 # <<<<<<<<<<<<<<
+ * cx = (x2 - dx) * 3.0
+ * cy = (y2 - dy) * 3.0
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt1))) || (PyList_CheckExact(__pyx_v_pt1))) {
+ PyObject* sequence = __pyx_v_pt1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 960, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 960, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 960, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_dx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_dy = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":961
+ * x4, y4 = pt4
+ * dx, dy = pt1
+ * cx = (x2 - dx) * 3.0 # <<<<<<<<<<<<<<
+ * cy = (y2 - dy) * 3.0
+ * bx = (x3 - x2) * 3.0 - cx
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x2, __pyx_v_dx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 961, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 961, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_cx = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":962
+ * dx, dy = pt1
+ * cx = (x2 - dx) * 3.0
+ * cy = (y2 - dy) * 3.0 # <<<<<<<<<<<<<<
+ * bx = (x3 - x2) * 3.0 - cx
+ * by = (y3 - y2) * 3.0 - cy
+*/
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_y2, __pyx_v_dy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 962, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 962, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_cy = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":963
+ * cx = (x2 - dx) * 3.0
+ * cy = (y2 - dy) * 3.0
+ * bx = (x3 - x2) * 3.0 - cx # <<<<<<<<<<<<<<
+ * by = (y3 - y2) * 3.0 - cy
+ * ax = x4 - dx - cx - bx
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x3, __pyx_v_x2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 963, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 963, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 963, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_bx = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":964
+ * cy = (y2 - dy) * 3.0
+ * bx = (x3 - x2) * 3.0 - cx
+ * by = (y3 - y2) * 3.0 - cy # <<<<<<<<<<<<<<
+ * ax = x4 - dx - cx - bx
+ * ay = y4 - dy - cy - by
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_y3, __pyx_v_y2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 964, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 964, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_cy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 964, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_by = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":965
+ * bx = (x3 - x2) * 3.0 - cx
+ * by = (y3 - y2) * 3.0 - cy
+ * ax = x4 - dx - cx - bx # <<<<<<<<<<<<<<
+ * ay = y4 - dy - cy - by
+ * return (ax, ay), (bx, by), (cx, cy), (dx, dy)
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_x4, __pyx_v_dx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 965, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_v_cx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 965, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 965, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_ax = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":966
+ * by = (y3 - y2) * 3.0 - cy
+ * ax = x4 - dx - cx - bx
+ * ay = y4 - dy - cy - by # <<<<<<<<<<<<<<
+ * return (ax, ay), (bx, by), (cx, cy), (dx, dy)
+ *
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_y4, __pyx_v_dy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 966, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_1, __pyx_v_cy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 966, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_2, __pyx_v_by); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 966, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_ay = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":967
+ * ax = x4 - dx - cx - bx
+ * ay = y4 - dy - cy - by
+ * return (ax, ay), (bx, by), (cx, cy), (dx, dy) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 967, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_ax);
+ __Pyx_GIVEREF(__pyx_v_ax);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_ax) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_ay);
+ __Pyx_GIVEREF(__pyx_v_ay);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_ay) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 967, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_bx);
+ __Pyx_GIVEREF(__pyx_v_bx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_bx) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_by);
+ __Pyx_GIVEREF(__pyx_v_by);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_by) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 967, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_cx);
+ __Pyx_GIVEREF(__pyx_v_cx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_cx) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_cy);
+ __Pyx_GIVEREF(__pyx_v_cy);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_cy) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 967, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_dx);
+ __Pyx_GIVEREF(__pyx_v_dx);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_dx) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_dy);
+ __Pyx_GIVEREF(__pyx_v_dy);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_dy) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 967, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5) != (0)) __PYX_ERR(0, 967, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":956
+ *
+ *
+ * def calcCubicParameters(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicParameters", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_x2);
+ __Pyx_XDECREF(__pyx_v_y2);
+ __Pyx_XDECREF(__pyx_v_x3);
+ __Pyx_XDECREF(__pyx_v_y3);
+ __Pyx_XDECREF(__pyx_v_x4);
+ __Pyx_XDECREF(__pyx_v_y4);
+ __Pyx_XDECREF(__pyx_v_dx);
+ __Pyx_XDECREF(__pyx_v_dy);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":970
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.locals(
+*/
+
+static CYTHON_INLINE PyObject *__pyx_f_9fontTools_4misc_11bezierTools_calcCubicParametersC(__pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4) {
+ __pyx_t_double_complex __pyx_v_a;
+ __pyx_t_double_complex __pyx_v_b;
+ __pyx_t_double_complex __pyx_v_c;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicParametersC", 0);
+
+ /* "fontTools/misc/bezierTools.py":982
+ * )
+ * def calcCubicParametersC(pt1, pt2, pt3, pt4):
+ * c = (pt2 - pt1) * 3.0 # <<<<<<<<<<<<<<
+ * b = (pt3 - pt2) * 3.0 - c
+ * a = pt4 - pt1 - c - b
+*/
+ __pyx_v_c = __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_pt2, __pyx_v_pt1), __pyx_t_double_complex_from_parts(3.0, 0));
+
+ /* "fontTools/misc/bezierTools.py":983
+ * def calcCubicParametersC(pt1, pt2, pt3, pt4):
+ * c = (pt2 - pt1) * 3.0
+ * b = (pt3 - pt2) * 3.0 - c # <<<<<<<<<<<<<<
+ * a = pt4 - pt1 - c - b
+ * return (a, b, c, pt1)
+*/
+ __pyx_v_b = __Pyx_c_diff_double(__Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_pt3, __pyx_v_pt2), __pyx_t_double_complex_from_parts(3.0, 0)), __pyx_v_c);
+
+ /* "fontTools/misc/bezierTools.py":984
+ * c = (pt2 - pt1) * 3.0
+ * b = (pt3 - pt2) * 3.0 - c
+ * a = pt4 - pt1 - c - b # <<<<<<<<<<<<<<
+ * return (a, b, c, pt1)
+ *
+*/
+ __pyx_v_a = __Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__pyx_v_pt4, __pyx_v_pt1), __pyx_v_c), __pyx_v_b);
+
+ /* "fontTools/misc/bezierTools.py":985
+ * b = (pt3 - pt2) * 3.0 - c
+ * a = pt4 - pt1 - c - b
+ * return (a, b, c, pt1) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 985, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 985, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 985, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_pt1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 985, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 985, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 985, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 985, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 985, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_t_4) != (0)) __PYX_ERR(0, 985, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":970
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.locals(
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicParametersC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":988
+ *
+ *
+ * def calcQuadraticPoints(a, b, c): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_55calcQuadraticPoints(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_54calcQuadraticPoints, "calcQuadraticPoints(a, b, c)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_55calcQuadraticPoints = {"calcQuadraticPoints", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_55calcQuadraticPoints, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_54calcQuadraticPoints};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_55calcQuadraticPoints(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcQuadraticPoints (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 988, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 988, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 988, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 988, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcQuadraticPoints", 0) < (0)) __PYX_ERR(0, 988, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcQuadraticPoints", 1, 3, 3, i); __PYX_ERR(0, 988, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 988, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 988, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 988, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcQuadraticPoints", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 988, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticPoints", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_54calcQuadraticPoints(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_54calcQuadraticPoints(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c) {
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_x1 = NULL;
+ PyObject *__pyx_v_y1 = NULL;
+ PyObject *__pyx_v_x2 = NULL;
+ PyObject *__pyx_v_y2 = NULL;
+ PyObject *__pyx_v_x3 = NULL;
+ PyObject *__pyx_v_y3 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcQuadraticPoints", 0);
+
+ /* "fontTools/misc/bezierTools.py":989
+ *
+ * def calcQuadraticPoints(a, b, c):
+ * ax, ay = a # <<<<<<<<<<<<<<
+ * bx, by = b
+ * cx, cy = c
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_a))) || (PyList_CheckExact(__pyx_v_a))) {
+ PyObject* sequence = __pyx_v_a;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 989, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 989, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 989, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_ay = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":990
+ * def calcQuadraticPoints(a, b, c):
+ * ax, ay = a
+ * bx, by = b # <<<<<<<<<<<<<<
+ * cx, cy = c
+ * x1 = cx
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_b))) || (PyList_CheckExact(__pyx_v_b))) {
+ PyObject* sequence = __pyx_v_b;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 990, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_b); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 990, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 990, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_by = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":991
+ * ax, ay = a
+ * bx, by = b
+ * cx, cy = c # <<<<<<<<<<<<<<
+ * x1 = cx
+ * y1 = cy
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_c))) || (PyList_CheckExact(__pyx_v_c))) {
+ PyObject* sequence = __pyx_v_c;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 991, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 991, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 991, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_cy = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":992
+ * bx, by = b
+ * cx, cy = c
+ * x1 = cx # <<<<<<<<<<<<<<
+ * y1 = cy
+ * x2 = (bx * 0.5) + cx
+*/
+ __Pyx_INCREF(__pyx_v_cx);
+ __pyx_v_x1 = __pyx_v_cx;
+
+ /* "fontTools/misc/bezierTools.py":993
+ * cx, cy = c
+ * x1 = cx
+ * y1 = cy # <<<<<<<<<<<<<<
+ * x2 = (bx * 0.5) + cx
+ * y2 = (by * 0.5) + cy
+*/
+ __Pyx_INCREF(__pyx_v_cy);
+ __pyx_v_y1 = __pyx_v_cy;
+
+ /* "fontTools/misc/bezierTools.py":994
+ * x1 = cx
+ * y1 = cy
+ * x2 = (bx * 0.5) + cx # <<<<<<<<<<<<<<
+ * y2 = (by * 0.5) + cy
+ * x3 = ax + bx + cx
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v_bx, __pyx_mstate_global->__pyx_float_0_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 994, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 994, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":995
+ * y1 = cy
+ * x2 = (bx * 0.5) + cx
+ * y2 = (by * 0.5) + cy # <<<<<<<<<<<<<<
+ * x3 = ax + bx + cx
+ * y3 = ay + by + cy
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_by, __pyx_mstate_global->__pyx_float_0_5); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 995, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_cy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 995, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_y2 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":996
+ * x2 = (bx * 0.5) + cx
+ * y2 = (by * 0.5) + cy
+ * x3 = ax + bx + cx # <<<<<<<<<<<<<<
+ * y3 = ay + by + cy
+ * return (x1, y1), (x2, y2), (x3, y3)
+*/
+ __pyx_t_2 = PyNumber_Add(__pyx_v_ax, __pyx_v_bx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 996, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 996, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":997
+ * y2 = (by * 0.5) + cy
+ * x3 = ax + bx + cx
+ * y3 = ay + by + cy # <<<<<<<<<<<<<<
+ * return (x1, y1), (x2, y2), (x3, y3)
+ *
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_ay, __pyx_v_by); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 997, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_cy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 997, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_y3 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":998
+ * x3 = ax + bx + cx
+ * y3 = ay + by + cy
+ * return (x1, y1), (x2, y2), (x3, y3) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 998, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_x1);
+ __Pyx_GIVEREF(__pyx_v_x1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_x1) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y1);
+ __Pyx_GIVEREF(__pyx_v_y1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_y1) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 998, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_x2);
+ __Pyx_GIVEREF(__pyx_v_x2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_x2) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y2);
+ __Pyx_GIVEREF(__pyx_v_y2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_y2) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 998, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_x3);
+ __Pyx_GIVEREF(__pyx_v_x3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_x3) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y3);
+ __Pyx_GIVEREF(__pyx_v_y3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_y3) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __pyx_t_5 = PyTuple_New(3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 998, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 998, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":988
+ *
+ *
+ * def calcQuadraticPoints(a, b, c): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcQuadraticPoints", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_x1);
+ __Pyx_XDECREF(__pyx_v_y1);
+ __Pyx_XDECREF(__pyx_v_x2);
+ __Pyx_XDECREF(__pyx_v_y2);
+ __Pyx_XDECREF(__pyx_v_x3);
+ __Pyx_XDECREF(__pyx_v_y3);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1001
+ *
+ *
+ * def calcCubicPoints(a, b, c, d): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_57calcCubicPoints(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_56calcCubicPoints, "calcCubicPoints(a, b, c, d)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_57calcCubicPoints = {"calcCubicPoints", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_57calcCubicPoints, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_56calcCubicPoints};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_57calcCubicPoints(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_d = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("calcCubicPoints (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_d,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1001, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "calcCubicPoints", 0) < (0)) __PYX_ERR(0, 1001, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("calcCubicPoints", 1, 4, 4, i); __PYX_ERR(0, 1001, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1001, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_c = values[2];
+ __pyx_v_d = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("calcCubicPoints", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 1001, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicPoints", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_56calcCubicPoints(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_c, __pyx_v_d);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_56calcCubicPoints(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_c, PyObject *__pyx_v_d) {
+ PyObject *__pyx_v_ax = NULL;
+ PyObject *__pyx_v_ay = NULL;
+ PyObject *__pyx_v_bx = NULL;
+ PyObject *__pyx_v_by = NULL;
+ PyObject *__pyx_v_cx = NULL;
+ PyObject *__pyx_v_cy = NULL;
+ PyObject *__pyx_v_dx = NULL;
+ PyObject *__pyx_v_dy = NULL;
+ PyObject *__pyx_v_x1 = NULL;
+ PyObject *__pyx_v_y1 = NULL;
+ PyObject *__pyx_v_x2 = NULL;
+ PyObject *__pyx_v_y2 = NULL;
+ PyObject *__pyx_v_x3 = NULL;
+ PyObject *__pyx_v_y3 = NULL;
+ PyObject *__pyx_v_x4 = NULL;
+ PyObject *__pyx_v_y4 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicPoints", 0);
+
+ /* "fontTools/misc/bezierTools.py":1002
+ *
+ * def calcCubicPoints(a, b, c, d):
+ * ax, ay = a # <<<<<<<<<<<<<<
+ * bx, by = b
+ * cx, cy = c
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_a))) || (PyList_CheckExact(__pyx_v_a))) {
+ PyObject* sequence = __pyx_v_a;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1002, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1002, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1002, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_ax = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_ay = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1003
+ * def calcCubicPoints(a, b, c, d):
+ * ax, ay = a
+ * bx, by = b # <<<<<<<<<<<<<<
+ * cx, cy = c
+ * dx, dy = d
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_b))) || (PyList_CheckExact(__pyx_v_b))) {
+ PyObject* sequence = __pyx_v_b;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1003, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_b); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1003, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1003, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_bx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_by = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1004
+ * ax, ay = a
+ * bx, by = b
+ * cx, cy = c # <<<<<<<<<<<<<<
+ * dx, dy = d
+ * x1 = dx
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_c))) || (PyList_CheckExact(__pyx_v_c))) {
+ PyObject* sequence = __pyx_v_c;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1004, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1004, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1004, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_cx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_cy = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1005
+ * bx, by = b
+ * cx, cy = c
+ * dx, dy = d # <<<<<<<<<<<<<<
+ * x1 = dx
+ * y1 = dy
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_d))) || (PyList_CheckExact(__pyx_v_d))) {
+ PyObject* sequence = __pyx_v_d;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1005, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_d); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1005, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1005, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_dx = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_dy = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1006
+ * cx, cy = c
+ * dx, dy = d
+ * x1 = dx # <<<<<<<<<<<<<<
+ * y1 = dy
+ * x2 = (cx / 3.0) + dx
+*/
+ __Pyx_INCREF(__pyx_v_dx);
+ __pyx_v_x1 = __pyx_v_dx;
+
+ /* "fontTools/misc/bezierTools.py":1007
+ * dx, dy = d
+ * x1 = dx
+ * y1 = dy # <<<<<<<<<<<<<<
+ * x2 = (cx / 3.0) + dx
+ * y2 = (cy / 3.0) + dy
+*/
+ __Pyx_INCREF(__pyx_v_dy);
+ __pyx_v_y1 = __pyx_v_dy;
+
+ /* "fontTools/misc/bezierTools.py":1008
+ * x1 = dx
+ * y1 = dy
+ * x2 = (cx / 3.0) + dx # <<<<<<<<<<<<<<
+ * y2 = (cy / 3.0) + dy
+ * x3 = (bx + cx) / 3.0 + x2
+*/
+ __pyx_t_1 = __Pyx_PyFloat_TrueDivideObjC(__pyx_v_cx, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1008, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_dx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1008, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_x2 = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1009
+ * y1 = dy
+ * x2 = (cx / 3.0) + dx
+ * y2 = (cy / 3.0) + dy # <<<<<<<<<<<<<<
+ * x3 = (bx + cx) / 3.0 + x2
+ * y3 = (by + cy) / 3.0 + y2
+*/
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_v_cy, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1009, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_dy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1009, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_y2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1010
+ * x2 = (cx / 3.0) + dx
+ * y2 = (cy / 3.0) + dy
+ * x3 = (bx + cx) / 3.0 + x2 # <<<<<<<<<<<<<<
+ * y3 = (by + cy) / 3.0 + y2
+ * x4 = ax + dx + cx + bx
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_bx, __pyx_v_cx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1010, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1010, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_x2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1010, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1011
+ * y2 = (cy / 3.0) + dy
+ * x3 = (bx + cx) / 3.0 + x2
+ * y3 = (by + cy) / 3.0 + y2 # <<<<<<<<<<<<<<
+ * x4 = ax + dx + cx + bx
+ * y4 = ay + dy + cy + by
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_by, __pyx_v_cy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1011, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyFloat_TrueDivideObjC(__pyx_t_1, __pyx_mstate_global->__pyx_float_3_0, 3.0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1011, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_y2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1011, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_y3 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1012
+ * x3 = (bx + cx) / 3.0 + x2
+ * y3 = (by + cy) / 3.0 + y2
+ * x4 = ax + dx + cx + bx # <<<<<<<<<<<<<<
+ * y4 = ay + dy + cy + by
+ * return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_ax, __pyx_v_dx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1012, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_cx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1012, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_bx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1012, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x4 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1013
+ * y3 = (by + cy) / 3.0 + y2
+ * x4 = ax + dx + cx + bx
+ * y4 = ay + dy + cy + by # <<<<<<<<<<<<<<
+ * return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
+ *
+*/
+ __pyx_t_1 = PyNumber_Add(__pyx_v_ay, __pyx_v_dy); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1013, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Add(__pyx_t_1, __pyx_v_cy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1013, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_2, __pyx_v_by); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1013, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_y4 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1014
+ * x4 = ax + dx + cx + bx
+ * y4 = ay + dy + cy + by
+ * return (x1, y1), (x2, y2), (x3, y3), (x4, y4) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1014, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_x1);
+ __Pyx_GIVEREF(__pyx_v_x1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_x1) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y1);
+ __Pyx_GIVEREF(__pyx_v_y1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_y1) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1014, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_x2);
+ __Pyx_GIVEREF(__pyx_v_x2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_x2) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y2);
+ __Pyx_GIVEREF(__pyx_v_y2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_y2) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1014, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_x3);
+ __Pyx_GIVEREF(__pyx_v_x3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_x3) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y3);
+ __Pyx_GIVEREF(__pyx_v_y3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_y3) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1014, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_x4);
+ __Pyx_GIVEREF(__pyx_v_x4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_x4) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y4);
+ __Pyx_GIVEREF(__pyx_v_y4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_y4) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1014, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5) != (0)) __PYX_ERR(0, 1014, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1001
+ *
+ *
+ * def calcCubicPoints(a, b, c, d): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicPoints", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ax);
+ __Pyx_XDECREF(__pyx_v_ay);
+ __Pyx_XDECREF(__pyx_v_bx);
+ __Pyx_XDECREF(__pyx_v_by);
+ __Pyx_XDECREF(__pyx_v_cx);
+ __Pyx_XDECREF(__pyx_v_cy);
+ __Pyx_XDECREF(__pyx_v_dx);
+ __Pyx_XDECREF(__pyx_v_dy);
+ __Pyx_XDECREF(__pyx_v_x1);
+ __Pyx_XDECREF(__pyx_v_y1);
+ __Pyx_XDECREF(__pyx_v_x2);
+ __Pyx_XDECREF(__pyx_v_y2);
+ __Pyx_XDECREF(__pyx_v_x3);
+ __Pyx_XDECREF(__pyx_v_y3);
+ __Pyx_XDECREF(__pyx_v_x4);
+ __Pyx_XDECREF(__pyx_v_y4);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1017
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.locals(
+*/
+
+static CYTHON_INLINE PyObject *__pyx_f_9fontTools_4misc_11bezierTools_calcCubicPointsC(__pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d) {
+ __pyx_t_double_complex __pyx_v_p2;
+ __pyx_t_double_complex __pyx_v_p3;
+ __pyx_t_double_complex __pyx_v_p4;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("calcCubicPointsC", 0);
+
+ /* "fontTools/misc/bezierTools.py":1029
+ * )
+ * def calcCubicPointsC(a, b, c, d):
+ * p2 = c * (1 / 3) + d # <<<<<<<<<<<<<<
+ * p3 = (b + c) * (1 / 3) + p2
+ * p4 = a + b + c + d
+*/
+ __pyx_v_p2 = __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_v_c, __pyx_t_double_complex_from_parts((1.0 / 3.0), 0)), __pyx_v_d);
+
+ /* "fontTools/misc/bezierTools.py":1030
+ * def calcCubicPointsC(a, b, c, d):
+ * p2 = c * (1 / 3) + d
+ * p3 = (b + c) * (1 / 3) + p2 # <<<<<<<<<<<<<<
+ * p4 = a + b + c + d
+ * return (d, p2, p3, p4)
+*/
+ __pyx_v_p3 = __Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_b, __pyx_v_c), __pyx_t_double_complex_from_parts((1.0 / 3.0), 0)), __pyx_v_p2);
+
+ /* "fontTools/misc/bezierTools.py":1031
+ * p2 = c * (1 / 3) + d
+ * p3 = (b + c) * (1 / 3) + p2
+ * p4 = a + b + c + d # <<<<<<<<<<<<<<
+ * return (d, p2, p3, p4)
+ *
+*/
+ __pyx_v_p4 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_a, __pyx_v_b), __pyx_v_c), __pyx_v_d);
+
+ /* "fontTools/misc/bezierTools.py":1032
+ * p3 = (b + c) * (1 / 3) + p2
+ * p4 = a + b + c + d
+ * return (d, p2, p3, p4) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_d); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1032, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1032, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1032, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_p4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1032, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1032, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 1032, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 1032, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 1032, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_t_4) != (0)) __PYX_ERR(0, 1032, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1017
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.inline
+ * @cython.locals(
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.calcCubicPointsC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1040
+ *
+ *
+ * def linePointAtT(pt1, pt2, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a line.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_59linePointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_58linePointAtT, "linePointAtT(pt1, pt2, t)\n\nFinds the point at time `t` on a line.\n\nArgs:\n pt1, pt2: Coordinates of the line as 2D tuples.\n t: The time along the line.\n\nReturns:\n A 2D tuple with the coordinates of the point.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_59linePointAtT = {"linePointAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_59linePointAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_58linePointAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_59linePointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_t = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("linePointAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1040, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "linePointAtT", 0) < (0)) __PYX_ERR(0, 1040, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("linePointAtT", 1, 3, 3, i); __PYX_ERR(0, 1040, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1040, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_t = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("linePointAtT", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 1040, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.linePointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_58linePointAtT(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_58linePointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_t) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("linePointAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":1050
+ * A 2D tuple with the coordinates of the point.
+ * """
+ * return ((pt1[0] * (1 - t) + pt2[0] * t), (pt1[1] * (1 - t) + pt2[1] * t)) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_pt1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_2, __pyx_v_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_pt1, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_pt2, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_v_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_4, __pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1050, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1050, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 1050, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1040
+ *
+ *
+ * def linePointAtT(pt1, pt2, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a line.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.linePointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1053
+ *
+ *
+ * def quadraticPointAtT(pt1, pt2, pt3, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a quadratic curve.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_61quadraticPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_60quadraticPointAtT, "quadraticPointAtT(pt1, pt2, pt3, t)\n\nFinds the point at time `t` on a quadratic curve.\n\nArgs:\n pt1, pt2, pt3: Coordinates of the curve as 2D tuples.\n t: The time along the curve.\n\nReturns:\n A 2D tuple with the coordinates of the point.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_61quadraticPointAtT = {"quadraticPointAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_61quadraticPointAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_60quadraticPointAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_61quadraticPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_t = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("quadraticPointAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1053, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "quadraticPointAtT", 0) < (0)) __PYX_ERR(0, 1053, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("quadraticPointAtT", 1, 4, 4, i); __PYX_ERR(0, 1053, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1053, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_t = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("quadraticPointAtT", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 1053, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.quadraticPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_60quadraticPointAtT(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_60quadraticPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_t) {
+ PyObject *__pyx_v_x = NULL;
+ PyObject *__pyx_v_y = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("quadraticPointAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":1063
+ * A 2D tuple with the coordinates of the point.
+ * """
+ * x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0] # <<<<<<<<<<<<<<
+ * y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1]
+ * return (x, y)
+*/
+ __pyx_t_1 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_t_2, 2, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_3, __pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_pt2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyNumber_Multiply(__pyx_v_t, __pyx_v_t); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_pt3, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_4, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1063, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_x = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1064
+ * """
+ * x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0]
+ * y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1] # <<<<<<<<<<<<<<
+ * return (x, y)
+ *
+*/
+ __pyx_t_1 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt1, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_2, __pyx_t_2, 2, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_3, __pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_pt2, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyNumber_Multiply(__pyx_v_t, __pyx_v_t); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_pt3, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_4, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1064, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_y = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1065
+ * x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0]
+ * y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1]
+ * return (x, y) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1065, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 1065, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y);
+ __Pyx_GIVEREF(__pyx_v_y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_y) != (0)) __PYX_ERR(0, 1065, __pyx_L1_error);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1053
+ *
+ *
+ * def quadraticPointAtT(pt1, pt2, pt3, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a quadratic curve.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.quadraticPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_x);
+ __Pyx_XDECREF(__pyx_v_y);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1068
+ *
+ *
+ * def cubicPointAtT(pt1, pt2, pt3, pt4, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a cubic curve.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_63cubicPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_62cubicPointAtT, "cubicPointAtT(pt1, pt2, pt3, pt4, t)\n\nFinds the point at time `t` on a cubic curve.\n\nArgs:\n pt1, pt2, pt3, pt4: Coordinates of the curve as 2D tuples.\n t: The time along the curve.\n\nReturns:\n A 2D tuple with the coordinates of the point.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_63cubicPointAtT = {"cubicPointAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_63cubicPointAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_62cubicPointAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_63cubicPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_pt1 = 0;
+ PyObject *__pyx_v_pt2 = 0;
+ PyObject *__pyx_v_pt3 = 0;
+ PyObject *__pyx_v_pt4 = 0;
+ PyObject *__pyx_v_t = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("cubicPointAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1068, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "cubicPointAtT", 0) < (0)) __PYX_ERR(0, 1068, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 5; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("cubicPointAtT", 1, 5, 5, i); __PYX_ERR(0, 1068, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 5)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1068, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = values[0];
+ __pyx_v_pt2 = values[1];
+ __pyx_v_pt3 = values[2];
+ __pyx_v_pt4 = values[3];
+ __pyx_v_t = values[4];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("cubicPointAtT", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 1068, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.cubicPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_62cubicPointAtT(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_62cubicPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_pt1, PyObject *__pyx_v_pt2, PyObject *__pyx_v_pt3, PyObject *__pyx_v_pt4, PyObject *__pyx_v_t) {
+ PyObject *__pyx_v_t2 = NULL;
+ PyObject *__pyx_v__1_t = NULL;
+ PyObject *__pyx_v__1_t_2 = NULL;
+ PyObject *__pyx_v_x = NULL;
+ PyObject *__pyx_v_y = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("cubicPointAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":1078
+ * A 2D tuple with the coordinates of the point.
+ * """
+ * t2 = t * t # <<<<<<<<<<<<<<
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_t, __pyx_v_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1078, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_t2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1079
+ * """
+ * t2 = t * t
+ * _1_t = 1 - t # <<<<<<<<<<<<<<
+ * _1_t_2 = _1_t * _1_t
+ * x = (
+*/
+ __pyx_t_1 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_v_t, 1, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1079, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v__1_t = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1080
+ * t2 = t * t
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t # <<<<<<<<<<<<<<
+ * x = (
+ * _1_t_2 * _1_t * pt1[0]
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v__1_t, __pyx_v__1_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1080, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v__1_t_2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1082
+ * _1_t_2 = _1_t * _1_t
+ * x = (
+ * _1_t_2 * _1_t * pt1[0] # <<<<<<<<<<<<<<
+ * + 3 * (_1_t_2 * t * pt2[0] + _1_t * t2 * pt3[0])
+ * + t2 * t * pt4[0]
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v__1_t_2, __pyx_v__1_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1082, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1082, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1082, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1083
+ * x = (
+ * _1_t_2 * _1_t * pt1[0]
+ * + 3 * (_1_t_2 * t * pt2[0] + _1_t * t2 * pt3[0]) # <<<<<<<<<<<<<<
+ * + t2 * t * pt4[0]
+ * )
+*/
+ __pyx_t_2 = PyNumber_Multiply(__pyx_v__1_t_2, __pyx_v_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_pt2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v__1_t, __pyx_v_t2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt3, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_t_2, 3, 0, 0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_3, __pyx_t_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1083, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1084
+ * _1_t_2 * _1_t * pt1[0]
+ * + 3 * (_1_t_2 * t * pt2[0] + _1_t * t2 * pt3[0])
+ * + t2 * t * pt4[0] # <<<<<<<<<<<<<<
+ * )
+ * y = (
+*/
+ __pyx_t_5 = PyNumber_Multiply(__pyx_v_t2, __pyx_v_t); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1084, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_pt4, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1084, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyNumber_Multiply(__pyx_t_5, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1084, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_2, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1084, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_v_x = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1087
+ * )
+ * y = (
+ * _1_t_2 * _1_t * pt1[1] # <<<<<<<<<<<<<<
+ * + 3 * (_1_t_2 * t * pt2[1] + _1_t * t2 * pt3[1])
+ * + t2 * t * pt4[1]
+*/
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v__1_t_2, __pyx_v__1_t); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1087, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_pt1, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1087, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_2 = PyNumber_Multiply(__pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1087, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1088
+ * y = (
+ * _1_t_2 * _1_t * pt1[1]
+ * + 3 * (_1_t_2 * t * pt2[1] + _1_t * t2 * pt3[1]) # <<<<<<<<<<<<<<
+ * + t2 * t * pt4[1]
+ * )
+*/
+ __pyx_t_4 = PyNumber_Multiply(__pyx_v__1_t_2, __pyx_v_t); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_pt2, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = PyNumber_Multiply(__pyx_t_4, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v__1_t, __pyx_v_t2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_pt3, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyNumber_Add(__pyx_t_5, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyLong_MultiplyCObj(__pyx_mstate_global->__pyx_int_3, __pyx_t_4, 3, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyNumber_Add(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1088, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1089
+ * _1_t_2 * _1_t * pt1[1]
+ * + 3 * (_1_t_2 * t * pt2[1] + _1_t * t2 * pt3[1])
+ * + t2 * t * pt4[1] # <<<<<<<<<<<<<<
+ * )
+ * return (x, y)
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_t2, __pyx_v_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1089, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_pt4, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1089, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1089, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1089, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_v_y = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1091
+ * + t2 * t * pt4[1]
+ * )
+ * return (x, y) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1091, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 1091, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y);
+ __Pyx_GIVEREF(__pyx_v_y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_v_y) != (0)) __PYX_ERR(0, 1091, __pyx_L1_error);
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1068
+ *
+ *
+ * def cubicPointAtT(pt1, pt2, pt3, pt4, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a cubic curve.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.cubicPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_t2);
+ __Pyx_XDECREF(__pyx_v__1_t);
+ __Pyx_XDECREF(__pyx_v__1_t_2);
+ __Pyx_XDECREF(__pyx_v_x);
+ __Pyx_XDECREF(__pyx_v_y);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1094
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_65cubicPointAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_64cubicPointAtTC, "cubicPointAtTC(double complex pt1, double complex pt2, double complex pt3, double complex pt4, double t)\n\nFinds the point at time `t` on a cubic curve.\n\nArgs:\n pt1, pt2, pt3, pt4: Coordinates of the curve as complex numbers.\n t: The time along the curve.\n\nReturns:\n A complex number with the coordinates of the point.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_65cubicPointAtTC = {"cubicPointAtTC", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_65cubicPointAtTC, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_64cubicPointAtTC};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_65cubicPointAtTC(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_pt1;
+ __pyx_t_double_complex __pyx_v_pt2;
+ __pyx_t_double_complex __pyx_v_pt3;
+ __pyx_t_double_complex __pyx_v_pt4;
+ double __pyx_v_t;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("cubicPointAtTC (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pt1,&__pyx_mstate_global->__pyx_n_u_pt2,&__pyx_mstate_global->__pyx_n_u_pt3,&__pyx_mstate_global->__pyx_n_u_pt4,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1094, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "cubicPointAtTC", 0) < (0)) __PYX_ERR(0, 1094, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 5; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("cubicPointAtTC", 1, 5, 5, i); __PYX_ERR(0, 1094, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 5)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1094, __pyx_L3_error)
+ }
+ __pyx_v_pt1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1103, __pyx_L3_error)
+ __pyx_v_pt2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1103, __pyx_L3_error)
+ __pyx_v_pt3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1103, __pyx_L3_error)
+ __pyx_v_pt4 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1103, __pyx_L3_error)
+ __pyx_v_t = __Pyx_PyFloat_AsDouble(values[4]); if (unlikely((__pyx_v_t == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 1103, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("cubicPointAtTC", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 1094, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.cubicPointAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_64cubicPointAtTC(__pyx_self, __pyx_v_pt1, __pyx_v_pt2, __pyx_v_pt3, __pyx_v_pt4, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_64cubicPointAtTC(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_pt1, __pyx_t_double_complex __pyx_v_pt2, __pyx_t_double_complex __pyx_v_pt3, __pyx_t_double_complex __pyx_v_pt4, double __pyx_v_t) {
+ double __pyx_v_t2;
+ double __pyx_v__1_t;
+ double __pyx_v__1_t_2;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __pyx_t_double_complex __pyx_t_1;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("cubicPointAtTC", 0);
+
+ /* "fontTools/misc/bezierTools.py":1113
+ * A complex number with the coordinates of the point.
+ * """
+ * t2 = t * t # <<<<<<<<<<<<<<
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t
+*/
+ __pyx_v_t2 = (__pyx_v_t * __pyx_v_t);
+
+ /* "fontTools/misc/bezierTools.py":1114
+ * """
+ * t2 = t * t
+ * _1_t = 1 - t # <<<<<<<<<<<<<<
+ * _1_t_2 = _1_t * _1_t
+ * return _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+*/
+ __pyx_v__1_t = (1.0 - __pyx_v_t);
+
+ /* "fontTools/misc/bezierTools.py":1115
+ * t2 = t * t
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t # <<<<<<<<<<<<<<
+ * return _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+ *
+*/
+ __pyx_v__1_t_2 = (__pyx_v__1_t * __pyx_v__1_t);
+
+ /* "fontTools/misc/bezierTools.py":1116
+ * _1_t = 1 - t
+ * _1_t_2 = _1_t * _1_t
+ * return _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t_2 * __pyx_v__1_t), 0), __pyx_v_pt1), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t_2 * __pyx_v_t), 0), __pyx_v_pt2), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v__1_t * __pyx_v_t2), 0), __pyx_v_pt3)))), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts((__pyx_v_t2 * __pyx_v_t), 0), __pyx_v_pt4));
+ __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1116, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1094
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.cubicPointAtTC", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1119
+ *
+ *
+ * def segmentPointAtT(seg, t): # <<<<<<<<<<<<<<
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t)
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_67segmentPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_66segmentPointAtT, "segmentPointAtT(seg, t)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_67segmentPointAtT = {"segmentPointAtT", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_67segmentPointAtT, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_66segmentPointAtT};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_67segmentPointAtT(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_seg = 0;
+ PyObject *__pyx_v_t = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("segmentPointAtT (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_seg,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1119, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1119, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1119, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "segmentPointAtT", 0) < (0)) __PYX_ERR(0, 1119, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("segmentPointAtT", 1, 2, 2, i); __PYX_ERR(0, 1119, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1119, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1119, __pyx_L3_error)
+ }
+ __pyx_v_seg = values[0];
+ __pyx_v_t = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("segmentPointAtT", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1119, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.segmentPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_66segmentPointAtT(__pyx_self, __pyx_v_seg, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_66segmentPointAtT(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_seg, PyObject *__pyx_v_t) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ size_t __pyx_t_7;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("segmentPointAtT", 0);
+
+ /* "fontTools/misc/bezierTools.py":1120
+ *
+ * def segmentPointAtT(seg, t):
+ * if len(seg) == 2: # <<<<<<<<<<<<<<
+ * return linePointAtT(*seg, t)
+ * elif len(seg) == 3:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_seg); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1120, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 2);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1121
+ * def segmentPointAtT(seg, t):
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t) # <<<<<<<<<<<<<<
+ * elif len(seg) == 3:
+ * return quadraticPointAtT(*seg, t)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_linePointAtT); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1121, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_seg); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1121, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1121, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1121, __pyx_L1_error);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1121, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_6, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1121, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1120
+ *
+ * def segmentPointAtT(seg, t):
+ * if len(seg) == 2: # <<<<<<<<<<<<<<
+ * return linePointAtT(*seg, t)
+ * elif len(seg) == 3:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1122
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t)
+ * elif len(seg) == 3: # <<<<<<<<<<<<<<
+ * return quadraticPointAtT(*seg, t)
+ * elif len(seg) == 4:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_seg); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1122, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 3);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1123
+ * return linePointAtT(*seg, t)
+ * elif len(seg) == 3:
+ * return quadraticPointAtT(*seg, t) # <<<<<<<<<<<<<<
+ * elif len(seg) == 4:
+ * return cubicPointAtT(*seg, t)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_quadraticPointAtT); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PySequence_Tuple(__pyx_v_seg); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1123, __pyx_L1_error);
+ __pyx_t_4 = PyNumber_Add(__pyx_t_6, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1122
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t)
+ * elif len(seg) == 3: # <<<<<<<<<<<<<<
+ * return quadraticPointAtT(*seg, t)
+ * elif len(seg) == 4:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1124
+ * elif len(seg) == 3:
+ * return quadraticPointAtT(*seg, t)
+ * elif len(seg) == 4: # <<<<<<<<<<<<<<
+ * return cubicPointAtT(*seg, t)
+ * raise ValueError("Unknown curve degree")
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_seg); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1124, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 4);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1125
+ * return quadraticPointAtT(*seg, t)
+ * elif len(seg) == 4:
+ * return cubicPointAtT(*seg, t) # <<<<<<<<<<<<<<
+ * raise ValueError("Unknown curve degree")
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_cubicPointAtT); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_seg); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1125, __pyx_L1_error);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_6, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1124
+ * elif len(seg) == 3:
+ * return quadraticPointAtT(*seg, t)
+ * elif len(seg) == 4: # <<<<<<<<<<<<<<
+ * return cubicPointAtT(*seg, t)
+ * raise ValueError("Unknown curve degree")
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1126
+ * elif len(seg) == 4:
+ * return cubicPointAtT(*seg, t)
+ * raise ValueError("Unknown curve degree") # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_6 = NULL;
+ __pyx_t_7 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_6, __pyx_mstate_global->__pyx_kp_u_Unknown_curve_degree};
+ __pyx_t_5 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_7, (2-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ }
+ __Pyx_Raise(__pyx_t_5, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __PYX_ERR(0, 1126, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":1119
+ *
+ *
+ * def segmentPointAtT(seg, t): # <<<<<<<<<<<<<<
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t)
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.segmentPointAtT", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1134
+ *
+ *
+ * def _line_t_of_pt(s, e, pt): # <<<<<<<<<<<<<<
+ * sx, sy = s
+ * ex, ey = e
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_69_line_t_of_pt(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_68_line_t_of_pt, "_line_t_of_pt(s, e, pt)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_69_line_t_of_pt = {"_line_t_of_pt", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_69_line_t_of_pt, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_68_line_t_of_pt};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_69_line_t_of_pt(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_s = 0;
+ PyObject *__pyx_v_e = 0;
+ PyObject *__pyx_v_pt = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_line_t_of_pt (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_s,&__pyx_mstate_global->__pyx_n_u_e,&__pyx_mstate_global->__pyx_n_u_pt,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1134, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_line_t_of_pt", 0) < (0)) __PYX_ERR(0, 1134, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_line_t_of_pt", 1, 3, 3, i); __PYX_ERR(0, 1134, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1134, __pyx_L3_error)
+ }
+ __pyx_v_s = values[0];
+ __pyx_v_e = values[1];
+ __pyx_v_pt = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_line_t_of_pt", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 1134, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._line_t_of_pt", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_68_line_t_of_pt(__pyx_self, __pyx_v_s, __pyx_v_e, __pyx_v_pt);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_68_line_t_of_pt(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_s, PyObject *__pyx_v_e, PyObject *__pyx_v_pt) {
+ PyObject *__pyx_v_sx = NULL;
+ PyObject *__pyx_v_sy = NULL;
+ PyObject *__pyx_v_ex = NULL;
+ PyObject *__pyx_v_ey = NULL;
+ PyObject *__pyx_v_px = NULL;
+ PyObject *__pyx_v_py = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ int __pyx_t_5;
+ int __pyx_t_6;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_line_t_of_pt", 0);
+
+ /* "fontTools/misc/bezierTools.py":1135
+ *
+ * def _line_t_of_pt(s, e, pt):
+ * sx, sy = s # <<<<<<<<<<<<<<
+ * ex, ey = e
+ * px, py = pt
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_s))) || (PyList_CheckExact(__pyx_v_s))) {
+ PyObject* sequence = __pyx_v_s;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1135, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_s); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1135, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1135, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_sx = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_sy = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1136
+ * def _line_t_of_pt(s, e, pt):
+ * sx, sy = s
+ * ex, ey = e # <<<<<<<<<<<<<<
+ * px, py = pt
+ * if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon:
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_e))) || (PyList_CheckExact(__pyx_v_e))) {
+ PyObject* sequence = __pyx_v_e;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1136, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_e); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1136, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1136, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_ex = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_ey = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1137
+ * sx, sy = s
+ * ex, ey = e
+ * px, py = pt # <<<<<<<<<<<<<<
+ * if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon:
+ * # Line is a point!
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_pt))) || (PyList_CheckExact(__pyx_v_pt))) {
+ PyObject* sequence = __pyx_v_pt;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1137, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_pt); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1137, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1137, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_px = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_py = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1138
+ * ex, ey = e
+ * px, py = pt
+ * if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon: # <<<<<<<<<<<<<<
+ * # Line is a point!
+ * return -1
+*/
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_sx, __pyx_v_ex); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyNumber_Absolute(__pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_1, __pyx_t_2, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_6) {
+ } else {
+ __pyx_t_5 = __pyx_t_6;
+ goto __pyx_L10_bool_binop_done;
+ }
+ __pyx_t_3 = PyNumber_Subtract(__pyx_v_sy, __pyx_v_ey); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyNumber_Absolute(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_epsilon); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_2, __pyx_t_3, Py_LT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = __pyx_t_6;
+ __pyx_L10_bool_binop_done:;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1140
+ * if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon:
+ * # Line is a point!
+ * return -1 # <<<<<<<<<<<<<<
+ * # Use the largest
+ * if abs(sx - ex) > abs(sy - ey):
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_neg_1);
+ __pyx_r = __pyx_mstate_global->__pyx_int_neg_1;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1138
+ * ex, ey = e
+ * px, py = pt
+ * if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon: # <<<<<<<<<<<<<<
+ * # Line is a point!
+ * return -1
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1142
+ * return -1
+ * # Use the largest
+ * if abs(sx - ex) > abs(sy - ey): # <<<<<<<<<<<<<<
+ * return (px - sx) / (ex - sx)
+ * else:
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_sx, __pyx_v_ex); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_PyNumber_Absolute(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_sy, __pyx_v_ey); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyNumber_Absolute(__pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyObject_RichCompare(__pyx_t_3, __pyx_t_2, Py_GT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1142, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1143
+ * # Use the largest
+ * if abs(sx - ex) > abs(sy - ey):
+ * return (px - sx) / (ex - sx) # <<<<<<<<<<<<<<
+ * else:
+ * return (py - sy) / (ey - sy)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_px, __pyx_v_sx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1143, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_ex, __pyx_v_sx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1143, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1143, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1142
+ * return -1
+ * # Use the largest
+ * if abs(sx - ex) > abs(sy - ey): # <<<<<<<<<<<<<<
+ * return (px - sx) / (ex - sx)
+ * else:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1145
+ * return (px - sx) / (ex - sx)
+ * else:
+ * return (py - sy) / (ey - sy) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ /*else*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_v_py, __pyx_v_sy); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1145, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_v_ey, __pyx_v_sy); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1145, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1145, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1134
+ *
+ *
+ * def _line_t_of_pt(s, e, pt): # <<<<<<<<<<<<<<
+ * sx, sy = s
+ * ex, ey = e
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._line_t_of_pt", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_sx);
+ __Pyx_XDECREF(__pyx_v_sy);
+ __Pyx_XDECREF(__pyx_v_ex);
+ __Pyx_XDECREF(__pyx_v_ey);
+ __Pyx_XDECREF(__pyx_v_px);
+ __Pyx_XDECREF(__pyx_v_py);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1148
+ *
+ *
+ * def _both_points_are_on_same_side_of_origin(a, b, origin): # <<<<<<<<<<<<<<
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_71_both_points_are_on_same_side_of_origin(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_70_both_points_are_on_same_side_of_origin, "_both_points_are_on_same_side_of_origin(a, b, origin)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_71_both_points_are_on_same_side_of_origin = {"_both_points_are_on_same_side_of_origin", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_71_both_points_are_on_same_side_of_origin, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_70_both_points_are_on_same_side_of_origin};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_71_both_points_are_on_same_side_of_origin(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_a = 0;
+ PyObject *__pyx_v_b = 0;
+ PyObject *__pyx_v_origin = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_both_points_are_on_same_side_of_origin (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_a,&__pyx_mstate_global->__pyx_n_u_b,&__pyx_mstate_global->__pyx_n_u_origin,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1148, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_both_points_are_on_same_side_of_origin", 0) < (0)) __PYX_ERR(0, 1148, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_both_points_are_on_same_side_of_origin", 1, 3, 3, i); __PYX_ERR(0, 1148, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1148, __pyx_L3_error)
+ }
+ __pyx_v_a = values[0];
+ __pyx_v_b = values[1];
+ __pyx_v_origin = values[2];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_both_points_are_on_same_side_of_origin", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 1148, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._both_points_are_on_same_side_of_origin", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_70_both_points_are_on_same_side_of_origin(__pyx_self, __pyx_v_a, __pyx_v_b, __pyx_v_origin);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_70_both_points_are_on_same_side_of_origin(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b, PyObject *__pyx_v_origin) {
+ PyObject *__pyx_v_xDiff = NULL;
+ PyObject *__pyx_v_yDiff = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_t_6;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_both_points_are_on_same_side_of_origin", 0);
+
+ /* "fontTools/misc/bezierTools.py":1149
+ *
+ * def _both_points_are_on_same_side_of_origin(a, b, origin):
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0]) # <<<<<<<<<<<<<<
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+ * return not (xDiff <= 0.0 and yDiff <= 0.0)
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_origin, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_origin, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = PyNumber_Subtract(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_v_xDiff = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1150
+ * def _both_points_are_on_same_side_of_origin(a, b, origin):
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1]) # <<<<<<<<<<<<<<
+ * return not (xDiff <= 0.0 and yDiff <= 0.0)
+ *
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_origin, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_origin, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyNumber_Subtract(__pyx_t_4, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1150, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_yDiff = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1151
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+ * return not (xDiff <= 0.0 and yDiff <= 0.0) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyObject_RichCompare(__pyx_v_xDiff, __pyx_mstate_global->__pyx_float_0_0, Py_LE); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1151, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1151, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_6) {
+ } else {
+ __pyx_t_5 = __pyx_t_6;
+ goto __pyx_L3_bool_binop_done;
+ }
+ __pyx_t_1 = PyObject_RichCompare(__pyx_v_yDiff, __pyx_mstate_global->__pyx_float_0_0, Py_LE); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1151, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1151, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = __pyx_t_6;
+ __pyx_L3_bool_binop_done:;
+ __pyx_t_1 = __Pyx_PyBool_FromLong((!__pyx_t_5)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1151, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1148
+ *
+ *
+ * def _both_points_are_on_same_side_of_origin(a, b, origin): # <<<<<<<<<<<<<<
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._both_points_are_on_same_side_of_origin", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_xDiff);
+ __Pyx_XDECREF(__pyx_v_yDiff);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1154
+ *
+ *
+ * def lineLineIntersections(s1, e1, s2, e2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two line segments.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_73lineLineIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_72lineLineIntersections, "lineLineIntersections(s1, e1, s2, e2)\n\nFinds intersections between two line segments.\n\nArgs:\n s1, e1: Coordinates of the first line as 2D tuples.\n s2, e2: Coordinates of the second line as 2D tuples.\n\nReturns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\nExamples::\n\n >>> a = lineLineIntersections( (310,389), (453, 222), (289, 251), (447, 367))\n >>> len(a)\n 1\n >>> intersection = a[0]\n >>> intersection.pt\n (374.44882952482897, 313.73458370177315)\n >>> (intersection.t1, intersection.t2)\n (0.45069111555824465, 0.5408153767394238)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_73lineLineIntersections = {"lineLineIntersections", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_73lineLineIntersections, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_72lineLineIntersections};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_73lineLineIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_s1 = 0;
+ PyObject *__pyx_v_e1 = 0;
+ PyObject *__pyx_v_s2 = 0;
+ PyObject *__pyx_v_e2 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("lineLineIntersections (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_s1,&__pyx_mstate_global->__pyx_n_u_e1,&__pyx_mstate_global->__pyx_n_u_s2,&__pyx_mstate_global->__pyx_n_u_e2,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1154, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "lineLineIntersections", 0) < (0)) __PYX_ERR(0, 1154, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 4; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("lineLineIntersections", 1, 4, 4, i); __PYX_ERR(0, 1154, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 4)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1154, __pyx_L3_error)
+ }
+ __pyx_v_s1 = values[0];
+ __pyx_v_e1 = values[1];
+ __pyx_v_s2 = values[2];
+ __pyx_v_e2 = values[3];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("lineLineIntersections", 1, 4, 4, __pyx_nargs); __PYX_ERR(0, 1154, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.lineLineIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_72lineLineIntersections(__pyx_self, __pyx_v_s1, __pyx_v_e1, __pyx_v_s2, __pyx_v_e2);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_72lineLineIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_s1, PyObject *__pyx_v_e1, PyObject *__pyx_v_s2, PyObject *__pyx_v_e2) {
+ PyObject *__pyx_v_s1x = NULL;
+ PyObject *__pyx_v_s1y = NULL;
+ PyObject *__pyx_v_e1x = NULL;
+ PyObject *__pyx_v_e1y = NULL;
+ PyObject *__pyx_v_s2x = NULL;
+ PyObject *__pyx_v_s2y = NULL;
+ PyObject *__pyx_v_e2x = NULL;
+ PyObject *__pyx_v_e2y = NULL;
+ PyObject *__pyx_v_x = NULL;
+ PyObject *__pyx_v_slope34 = NULL;
+ PyObject *__pyx_v_y = NULL;
+ PyObject *__pyx_v_pt = NULL;
+ PyObject *__pyx_v_slope12 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *(*__pyx_t_4)(PyObject *);
+ int __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ size_t __pyx_t_7;
+ int __pyx_t_8;
+ int __pyx_t_9;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("lineLineIntersections", 0);
+
+ /* "fontTools/misc/bezierTools.py":1177
+ * (0.45069111555824465, 0.5408153767394238)
+ * """
+ * s1x, s1y = s1 # <<<<<<<<<<<<<<
+ * e1x, e1y = e1
+ * s2x, s2y = s2
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_s1))) || (PyList_CheckExact(__pyx_v_s1))) {
+ PyObject* sequence = __pyx_v_s1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1177, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_s1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1177, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L4_unpacking_done;
+ __pyx_L3_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1177, __pyx_L1_error)
+ __pyx_L4_unpacking_done:;
+ }
+ __pyx_v_s1x = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_s1y = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1178
+ * """
+ * s1x, s1y = s1
+ * e1x, e1y = e1 # <<<<<<<<<<<<<<
+ * s2x, s2y = s2
+ * e2x, e2y = e2
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_e1))) || (PyList_CheckExact(__pyx_v_e1))) {
+ PyObject* sequence = __pyx_v_e1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1178, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_e1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1178, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1178, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_e1x = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_e1y = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1179
+ * s1x, s1y = s1
+ * e1x, e1y = e1
+ * s2x, s2y = s2 # <<<<<<<<<<<<<<
+ * e2x, e2y = e2
+ * if (
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_s2))) || (PyList_CheckExact(__pyx_v_s2))) {
+ PyObject* sequence = __pyx_v_s2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1179, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_s2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 1; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L7_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1179, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L8_unpacking_done;
+ __pyx_L7_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1179, __pyx_L1_error)
+ __pyx_L8_unpacking_done:;
+ }
+ __pyx_v_s2x = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_s2y = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1180
+ * e1x, e1y = e1
+ * s2x, s2y = s2
+ * e2x, e2y = e2 # <<<<<<<<<<<<<<
+ * if (
+ * math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_e2))) || (PyList_CheckExact(__pyx_v_e2))) {
+ PyObject* sequence = __pyx_v_e2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1180, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_v_e2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_2 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_4(__pyx_t_3); if (unlikely(!__pyx_t_1)) goto __pyx_L9_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_4(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 1180, __pyx_L1_error)
+ __pyx_t_4 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L10_unpacking_done;
+ __pyx_L9_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_4 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1180, __pyx_L1_error)
+ __pyx_L10_unpacking_done:;
+ }
+ __pyx_v_e2x = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_e2y = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1182
+ * e2x, e2y = e2
+ * if (
+ * math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x) # <<<<<<<<<<<<<<
+ * ): # Parallel vertical
+ * return []
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_s2x, __pyx_v_e2x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_8) {
+ } else {
+ __pyx_t_5 = __pyx_t_8;
+ goto __pyx_L12_bool_binop_done;
+ }
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_s1x, __pyx_v_e1x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_8) {
+ } else {
+ __pyx_t_5 = __pyx_t_8;
+ goto __pyx_L12_bool_binop_done;
+ }
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_s1x, __pyx_v_s2x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1182, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_9 = (!__pyx_t_8);
+ __pyx_t_5 = __pyx_t_9;
+ __pyx_L12_bool_binop_done:;
+
+ /* "fontTools/misc/bezierTools.py":1181
+ * s2x, s2y = s2
+ * e2x, e2y = e2
+ * if ( # <<<<<<<<<<<<<<
+ * math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+ * ): # Parallel vertical
+*/
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1184
+ * math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+ * ): # Parallel vertical
+ * return [] # <<<<<<<<<<<<<<
+ * if (
+ * math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1184, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1181
+ * s2x, s2y = s2
+ * e2x, e2y = e2
+ * if ( # <<<<<<<<<<<<<<
+ * math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+ * ): # Parallel vertical
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1186
+ * return []
+ * if (
+ * math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y) # <<<<<<<<<<<<<<
+ * ): # Parallel horizontal
+ * return []
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_s2y, __pyx_v_e2y};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_9 < 0))) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_9) {
+ } else {
+ __pyx_t_5 = __pyx_t_9;
+ goto __pyx_L16_bool_binop_done;
+ }
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_s1y, __pyx_v_e1y};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_9 < 0))) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_9) {
+ } else {
+ __pyx_t_5 = __pyx_t_9;
+ goto __pyx_L16_bool_binop_done;
+ }
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_s1y, __pyx_v_s2y};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_9 < 0))) __PYX_ERR(0, 1186, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_8 = (!__pyx_t_9);
+ __pyx_t_5 = __pyx_t_8;
+ __pyx_L16_bool_binop_done:;
+
+ /* "fontTools/misc/bezierTools.py":1185
+ * ): # Parallel vertical
+ * return []
+ * if ( # <<<<<<<<<<<<<<
+ * math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+ * ): # Parallel horizontal
+*/
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1188
+ * math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+ * ): # Parallel horizontal
+ * return [] # <<<<<<<<<<<<<<
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
+ * return []
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1188, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1185
+ * ): # Parallel vertical
+ * return []
+ * if ( # <<<<<<<<<<<<<<
+ * math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+ * ): # Parallel horizontal
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1189
+ * ): # Parallel horizontal
+ * return []
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny # <<<<<<<<<<<<<<
+ * return []
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_s2x, __pyx_v_e2x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_8) {
+ } else {
+ __pyx_t_5 = __pyx_t_8;
+ goto __pyx_L20_bool_binop_done;
+ }
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_s2y, __pyx_v_e2y};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1189, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = __pyx_t_8;
+ __pyx_L20_bool_binop_done:;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1190
+ * return []
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
+ * return [] # <<<<<<<<<<<<<<
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+ * return []
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1190, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1189
+ * ): # Parallel horizontal
+ * return []
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny # <<<<<<<<<<<<<<
+ * return []
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1191
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
+ * return []
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny # <<<<<<<<<<<<<<
+ * return []
+ * if math.isclose(e1x, s1x):
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_v_s1x, __pyx_v_e1x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_8) {
+ } else {
+ __pyx_t_5 = __pyx_t_8;
+ goto __pyx_L23_bool_binop_done;
+ }
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_s1y, __pyx_v_e1y};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1191, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = __pyx_t_8;
+ __pyx_L23_bool_binop_done:;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1192
+ * return []
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+ * return [] # <<<<<<<<<<<<<<
+ * if math.isclose(e1x, s1x):
+ * x = s1x
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1192, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1191
+ * if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
+ * return []
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny # <<<<<<<<<<<<<<
+ * return []
+ * if math.isclose(e1x, s1x):
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1193
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+ * return []
+ * if math.isclose(e1x, s1x): # <<<<<<<<<<<<<<
+ * x = s1x
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1193, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1193, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_e1x, __pyx_v_s1x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1193, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1193, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1194
+ * return []
+ * if math.isclose(e1x, s1x):
+ * x = s1x # <<<<<<<<<<<<<<
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * y = slope34 * (x - s2x) + s2y
+*/
+ __Pyx_INCREF(__pyx_v_s1x);
+ __pyx_v_x = __pyx_v_s1x;
+
+ /* "fontTools/misc/bezierTools.py":1195
+ * if math.isclose(e1x, s1x):
+ * x = s1x
+ * slope34 = (e2y - s2y) / (e2x - s2x) # <<<<<<<<<<<<<<
+ * y = slope34 * (x - s2x) + s2y
+ * pt = (x, y)
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_e2y, __pyx_v_s2y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1195, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_v_e2x, __pyx_v_s2x); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1195, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1195, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_slope34 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1196
+ * x = s1x
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * y = slope34 * (x - s2x) + s2y # <<<<<<<<<<<<<<
+ * pt = (x, y)
+ * return [
+*/
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_x, __pyx_v_s2x); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1196, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = PyNumber_Multiply(__pyx_v_slope34, __pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1196, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Add(__pyx_t_3, __pyx_v_s2y); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1196, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_y = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1197
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * y = slope34 * (x - s2x) + s2y
+ * pt = (x, y) # <<<<<<<<<<<<<<
+ * return [
+ * Intersection(
+*/
+ __pyx_t_6 = PyTuple_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1197, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 1197, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y);
+ __Pyx_GIVEREF(__pyx_v_y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_v_y) != (0)) __PYX_ERR(0, 1197, __pyx_L1_error);
+ __pyx_v_pt = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1198
+ * y = slope34 * (x - s2x) + s2y
+ * pt = (x, y)
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __Pyx_XDECREF(__pyx_r);
+
+ /* "fontTools/misc/bezierTools.py":1199
+ * pt = (x, y)
+ * return [
+ * Intersection( # <<<<<<<<<<<<<<
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ * )
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":1200
+ * return [
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt) # <<<<<<<<<<<<<<
+ * )
+ * ]
+*/
+ __pyx_t_10 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1200, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_11))) {
+ __pyx_t_10 = PyMethod_GET_SELF(__pyx_t_11);
+ assert(__pyx_t_10);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11);
+ __Pyx_INCREF(__pyx_t_10);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_11, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_10, __pyx_v_s1, __pyx_v_e1, __pyx_v_pt};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_11, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1200, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_10 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_12, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1200, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_12))) {
+ __pyx_t_10 = PyMethod_GET_SELF(__pyx_t_12);
+ assert(__pyx_t_10);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_12);
+ __Pyx_INCREF(__pyx_t_10);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_12, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_10, __pyx_v_s2, __pyx_v_e2, __pyx_v_pt};
+ __pyx_t_11 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_12, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1200, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ }
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_3, NULL};
+ __pyx_t_12 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_v_pt, __pyx_t_12, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_2, __pyx_t_12, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_11, __pyx_t_12, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_7, (1-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_12);
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1199, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1198
+ * y = slope34 * (x - s2x) + s2y
+ * pt = (x, y)
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1198, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_6);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_6) != (0)) __PYX_ERR(0, 1198, __pyx_L1_error);
+ __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1193
+ * if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+ * return []
+ * if math.isclose(e1x, s1x): # <<<<<<<<<<<<<<
+ * x = s1x
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1203
+ * )
+ * ]
+ * if math.isclose(s2x, e2x): # <<<<<<<<<<<<<<
+ * x = s2x
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_12, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1203, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_11 = __Pyx_PyObject_GetAttrStr(__pyx_t_12, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1203, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_11))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_11);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_11, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_s2x, __pyx_v_e2x};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_11, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1203, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1203, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1204
+ * ]
+ * if math.isclose(s2x, e2x):
+ * x = s2x # <<<<<<<<<<<<<<
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * y = slope12 * (x - s1x) + s1y
+*/
+ __Pyx_INCREF(__pyx_v_s2x);
+ __pyx_v_x = __pyx_v_s2x;
+
+ /* "fontTools/misc/bezierTools.py":1205
+ * if math.isclose(s2x, e2x):
+ * x = s2x
+ * slope12 = (e1y - s1y) / (e1x - s1x) # <<<<<<<<<<<<<<
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_e1y, __pyx_v_s1y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_11 = PyNumber_Subtract(__pyx_v_e1x, __pyx_v_s1x); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_6 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_11); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_v_slope12 = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1206
+ * x = s2x
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * y = slope12 * (x - s1x) + s1y # <<<<<<<<<<<<<<
+ * pt = (x, y)
+ * return [
+*/
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_x, __pyx_v_s1x); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1206, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_11 = PyNumber_Multiply(__pyx_v_slope12, __pyx_t_6); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1206, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Add(__pyx_t_11, __pyx_v_s1y); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1206, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_v_y = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1207
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y) # <<<<<<<<<<<<<<
+ * return [
+ * Intersection(
+*/
+ __pyx_t_6 = PyTuple_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1207, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 1207, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y);
+ __Pyx_GIVEREF(__pyx_v_y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_v_y) != (0)) __PYX_ERR(0, 1207, __pyx_L1_error);
+ __pyx_v_pt = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1208
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __Pyx_XDECREF(__pyx_r);
+
+ /* "fontTools/misc/bezierTools.py":1209
+ * pt = (x, y)
+ * return [
+ * Intersection( # <<<<<<<<<<<<<<
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ * )
+*/
+ __pyx_t_11 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":1210
+ * return [
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt) # <<<<<<<<<<<<<<
+ * )
+ * ]
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1210, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_s1, __pyx_v_e1, __pyx_v_pt};
+ __pyx_t_12 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1210, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ }
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_10, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1210, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_10))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_10);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_10);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_10, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_2, __pyx_v_s2, __pyx_v_e2, __pyx_v_pt};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_10, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1210, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_11);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_11);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_11, NULL};
+ __pyx_t_10 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_v_pt, __pyx_t_10, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_12, __pyx_t_10, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_3, __pyx_t_10, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_7, (1-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1209, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1208
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1208, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_6);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_6) != (0)) __PYX_ERR(0, 1208, __pyx_L1_error);
+ __pyx_t_6 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1203
+ * )
+ * ]
+ * if math.isclose(s2x, e2x): # <<<<<<<<<<<<<<
+ * x = s2x
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1214
+ * ]
+ *
+ * slope12 = (e1y - s1y) / (e1x - s1x) # <<<<<<<<<<<<<<
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * if math.isclose(slope12, slope34):
+*/
+ __pyx_t_1 = PyNumber_Subtract(__pyx_v_e1y, __pyx_v_s1y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1214, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_e1x, __pyx_v_s1x); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1214, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_10 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_6); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1214, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_slope12 = __pyx_t_10;
+ __pyx_t_10 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1215
+ *
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * slope34 = (e2y - s2y) / (e2x - s2x) # <<<<<<<<<<<<<<
+ * if math.isclose(slope12, slope34):
+ * return []
+*/
+ __pyx_t_10 = PyNumber_Subtract(__pyx_v_e2y, __pyx_v_s2y); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1215, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_e2x, __pyx_v_s2x); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1215, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_t_10, __pyx_t_6); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1215, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_slope34 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1216
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * if math.isclose(slope12, slope34): # <<<<<<<<<<<<<<
+ * return []
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_10, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1216, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_10, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1216, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_6, __pyx_v_slope12, __pyx_v_slope34};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1216, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1216, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1217
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * if math.isclose(slope12, slope34):
+ * return [] # <<<<<<<<<<<<<<
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+ * y = slope12 * (x - s1x) + s1y
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1217, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1216
+ * slope12 = (e1y - s1y) / (e1x - s1x)
+ * slope34 = (e2y - s2y) / (e2x - s2x)
+ * if math.isclose(slope12, slope34): # <<<<<<<<<<<<<<
+ * return []
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1218
+ * if math.isclose(slope12, slope34):
+ * return []
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34) # <<<<<<<<<<<<<<
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+*/
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_slope12, __pyx_v_s1x); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_v_s1y); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Multiply(__pyx_v_slope34, __pyx_v_s2x); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = PyNumber_Add(__pyx_t_6, __pyx_v_s2y); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = PyNumber_Subtract(__pyx_v_slope12, __pyx_v_slope34); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_x = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1219
+ * return []
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+ * y = slope12 * (x - s1x) + s1y # <<<<<<<<<<<<<<
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin(
+*/
+ __pyx_t_3 = PyNumber_Subtract(__pyx_v_x, __pyx_v_s1x); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1219, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = PyNumber_Multiply(__pyx_v_slope12, __pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1219, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_Add(__pyx_t_6, __pyx_v_s1y); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1219, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_y = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1220
+ * x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y) # <<<<<<<<<<<<<<
+ * if _both_points_are_on_same_side_of_origin(
+ * pt, e1, s1
+*/
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1220, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_x);
+ __Pyx_GIVEREF(__pyx_v_x);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_x) != (0)) __PYX_ERR(0, 1220, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_y);
+ __Pyx_GIVEREF(__pyx_v_y);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_v_y) != (0)) __PYX_ERR(0, 1220, __pyx_L1_error);
+ __pyx_v_pt = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1221
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin( # <<<<<<<<<<<<<<
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_both_points_are_on_same_side_of); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1221, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":1222
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin(
+ * pt, e1, s1 # <<<<<<<<<<<<<<
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+ * return [
+*/
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_6, __pyx_v_pt, __pyx_v_e1, __pyx_v_s1};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1221, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1221
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin( # <<<<<<<<<<<<<<
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+*/
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1221, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_8) {
+ } else {
+ __pyx_t_5 = __pyx_t_8;
+ goto __pyx_L29_bool_binop_done;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1223
+ * if _both_points_are_on_same_side_of_origin(
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2): # <<<<<<<<<<<<<<
+ * return [
+ * Intersection(
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_both_points_are_on_same_side_of); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1223, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_1, __pyx_v_pt, __pyx_v_s2, __pyx_v_e2};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1223, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_8 < 0))) __PYX_ERR(0, 1223, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_5 = __pyx_t_8;
+ __pyx_L29_bool_binop_done:;
+
+ /* "fontTools/misc/bezierTools.py":1221
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin( # <<<<<<<<<<<<<<
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+*/
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1224
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __Pyx_XDECREF(__pyx_r);
+
+ /* "fontTools/misc/bezierTools.py":1225
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+ * return [
+ * Intersection( # <<<<<<<<<<<<<<
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ * )
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":1226
+ * return [
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt) # <<<<<<<<<<<<<<
+ * )
+ * ]
+*/
+ __pyx_t_12 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_11))) {
+ __pyx_t_12 = PyMethod_GET_SELF(__pyx_t_11);
+ assert(__pyx_t_12);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11);
+ __Pyx_INCREF(__pyx_t_12);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_11, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_12, __pyx_v_s1, __pyx_v_e1, __pyx_v_pt};
+ __pyx_t_10 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_11, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ }
+ __pyx_t_12 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_12 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_12);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_12);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_12, __pyx_v_s2, __pyx_v_e2, __pyx_v_pt};
+ __pyx_t_11 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ }
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_6, NULL};
+ __pyx_t_2 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_v_pt, __pyx_t_2, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_10, __pyx_t_2, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_11, __pyx_t_2, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ __pyx_t_3 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_7, (1-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1225, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1224
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(
+ * pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+*/
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1224, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 1224, __pyx_L1_error);
+ __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1221
+ * y = slope12 * (x - s1x) + s1y
+ * pt = (x, y)
+ * if _both_points_are_on_same_side_of_origin( # <<<<<<<<<<<<<<
+ * pt, e1, s1
+ * ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1229
+ * )
+ * ]
+ * return [] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1229, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1154
+ *
+ *
+ * def lineLineIntersections(s1, e1, s2, e2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two line segments.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.lineLineIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_s1x);
+ __Pyx_XDECREF(__pyx_v_s1y);
+ __Pyx_XDECREF(__pyx_v_e1x);
+ __Pyx_XDECREF(__pyx_v_e1y);
+ __Pyx_XDECREF(__pyx_v_s2x);
+ __Pyx_XDECREF(__pyx_v_s2y);
+ __Pyx_XDECREF(__pyx_v_e2x);
+ __Pyx_XDECREF(__pyx_v_e2y);
+ __Pyx_XDECREF(__pyx_v_x);
+ __Pyx_XDECREF(__pyx_v_slope34);
+ __Pyx_XDECREF(__pyx_v_y);
+ __Pyx_XDECREF(__pyx_v_pt);
+ __Pyx_XDECREF(__pyx_v_slope12);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1232
+ *
+ *
+ * def _alignment_transformation(segment): # <<<<<<<<<<<<<<
+ * # Returns a transformation which aligns a segment horizontally at the
+ * # origin. Apply this transformation to curves and root-find to find
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_75_alignment_transformation(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_74_alignment_transformation, "_alignment_transformation(segment)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_75_alignment_transformation = {"_alignment_transformation", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_75_alignment_transformation, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_74_alignment_transformation};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_75_alignment_transformation(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_segment = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_alignment_transformation (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_segment,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1232, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1232, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_alignment_transformation", 0) < (0)) __PYX_ERR(0, 1232, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_alignment_transformation", 1, 1, 1, i); __PYX_ERR(0, 1232, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1232, __pyx_L3_error)
+ }
+ __pyx_v_segment = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_alignment_transformation", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1232, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._alignment_transformation", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_74_alignment_transformation(__pyx_self, __pyx_v_segment);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_74_alignment_transformation(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segment) {
+ PyObject *__pyx_v_start = NULL;
+ PyObject *__pyx_v_end = NULL;
+ PyObject *__pyx_v_angle = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ size_t __pyx_t_8;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_alignment_transformation", 0);
+
+ /* "fontTools/misc/bezierTools.py":1236
+ * # origin. Apply this transformation to curves and root-find to find
+ * # intersections with the segment.
+ * start = segment[0] # <<<<<<<<<<<<<<
+ * end = segment[-1]
+ * angle = math.atan2(end[1] - start[1], end[0] - start[0])
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_segment, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1236, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_start = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1237
+ * # intersections with the segment.
+ * start = segment[0]
+ * end = segment[-1] # <<<<<<<<<<<<<<
+ * angle = math.atan2(end[1] - start[1], end[0] - start[0])
+ * return Identity.rotate(-angle).translate(-start[0], -start[1])
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_segment, -1L, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1237, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_end = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1238
+ * start = segment[0]
+ * end = segment[-1]
+ * angle = math.atan2(end[1] - start[1], end[0] - start[0]) # <<<<<<<<<<<<<<
+ * return Identity.rotate(-angle).translate(-start[0], -start[1])
+ *
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_atan2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_end, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = __Pyx_GetItemInt(__pyx_v_start, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_3, __pyx_t_5); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_GetItemInt(__pyx_v_end, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_start, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = PyNumber_Subtract(__pyx_t_5, __pyx_t_3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_8 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_4);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_4, __pyx__function);
+ __pyx_t_8 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_t_6, __pyx_t_7};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_4, __pyx_callargs+__pyx_t_8, (3-__pyx_t_8) | (__pyx_t_8*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1238, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_angle = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1239
+ * end = segment[-1]
+ * angle = math.atan2(end[1] - start[1], end[0] - start[0])
+ * return Identity.rotate(-angle).translate(-start[0], -start[1]) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_Identity); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_rotate); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Negative(__pyx_v_angle); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_8 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_6);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_6);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_8 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_6, __pyx_t_2};
+ __pyx_t_7 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_8, (2-__pyx_t_8) | (__pyx_t_8*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ }
+ __pyx_t_4 = __pyx_t_7;
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_start, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = PyNumber_Negative(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_start, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = PyNumber_Negative(__pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_8 = 0;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_4, __pyx_t_2, __pyx_t_6};
+ __pyx_t_1 = __Pyx_PyObject_FastCallMethod((PyObject*)__pyx_mstate_global->__pyx_n_u_translate, __pyx_callargs+__pyx_t_8, (3-__pyx_t_8) | (1*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1232
+ *
+ *
+ * def _alignment_transformation(segment): # <<<<<<<<<<<<<<
+ * # Returns a transformation which aligns a segment horizontally at the
+ * # origin. Apply this transformation to curves and root-find to find
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._alignment_transformation", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_start);
+ __Pyx_XDECREF(__pyx_v_end);
+ __Pyx_XDECREF(__pyx_v_angle);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1242
+ *
+ *
+ * def _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3:
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_77_curve_line_intersections_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_76_curve_line_intersections_t, "_curve_line_intersections_t(curve, line)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_77_curve_line_intersections_t = {"_curve_line_intersections_t", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_77_curve_line_intersections_t, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_76_curve_line_intersections_t};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_77_curve_line_intersections_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_curve = 0;
+ PyObject *__pyx_v_line = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_curve_line_intersections_t (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curve,&__pyx_mstate_global->__pyx_n_u_line,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1242, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_curve_line_intersections_t", 0) < (0)) __PYX_ERR(0, 1242, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_curve_line_intersections_t", 1, 2, 2, i); __PYX_ERR(0, 1242, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1242, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1242, __pyx_L3_error)
+ }
+ __pyx_v_curve = values[0];
+ __pyx_v_line = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_curve_line_intersections_t", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1242, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_line_intersections_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_76_curve_line_intersections_t(__pyx_self, __pyx_v_curve, __pyx_v_line);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_2generator4(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":1252
+ * else:
+ * raise ValueError("Unknown curve degree")
+ * return sorted(i for i in intersections if 0.0 <= i <= 1) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 1252, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_2generator4, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[4]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_curve_line_intersections_t_loca, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_line_intersections_t.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_2generator4(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __pyx_r = PyList_New(0); if (unlikely(!__pyx_r)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_r);
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 1252, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_i);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_i, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_4 = PyObject_RichCompare(__pyx_mstate_global->__pyx_float_0_0, __pyx_cur_scope->__pyx_v_i, Py_LE); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ if (__Pyx_PyObject_IsTrue(__pyx_t_4)) {
+ __Pyx_DECREF(__pyx_t_4);
+ __pyx_t_4 = PyObject_RichCompare(__pyx_cur_scope->__pyx_v_i, __pyx_mstate_global->__pyx_int_1, Py_LE); __Pyx_XGOTREF(__pyx_t_4); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (__pyx_t_5) {
+ if (unlikely(__Pyx_ListComp_Append(__pyx_r, (PyObject*)__pyx_cur_scope->__pyx_v_i))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_r); __pyx_r = 0;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1242
+ *
+ *
+ * def _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3:
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_76_curve_line_intersections_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, PyObject *__pyx_v_line) {
+ PyObject *__pyx_v_aligned_curve = NULL;
+ PyObject *__pyx_v_a = NULL;
+ PyObject *__pyx_v_b = NULL;
+ PyObject *__pyx_v_c = NULL;
+ PyObject *__pyx_v_intersections = NULL;
+ PyObject *__pyx_v_d = NULL;
+ PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_2generator4 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ Py_ssize_t __pyx_t_7;
+ int __pyx_t_8;
+ PyObject *(*__pyx_t_9)(PyObject *);
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_curve_line_intersections_t", 0);
+
+ /* "fontTools/misc/bezierTools.py":1243
+ *
+ * def _curve_line_intersections_t(curve, line):
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve) # <<<<<<<<<<<<<<
+ * if len(curve) == 3:
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+*/
+ __pyx_t_4 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_alignment_transformation); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1243, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_4);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_v_line};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1243, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_t_2 = __pyx_t_3;
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_6 = 0;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_curve};
+ __pyx_t_1 = __Pyx_PyObject_FastCallMethod((PyObject*)__pyx_mstate_global->__pyx_n_u_transformPoints, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (1*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1243, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_aligned_curve = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1244
+ * def _curve_line_intersections_t(curve, line):
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3: # <<<<<<<<<<<<<<
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+*/
+ __pyx_t_7 = PyObject_Length(__pyx_v_curve); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1244, __pyx_L1_error)
+ __pyx_t_8 = (__pyx_t_7 == 3);
+ if (__pyx_t_8) {
+
+ /* "fontTools/misc/bezierTools.py":1245
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3:
+ * a, b, c = calcQuadraticParameters(*aligned_curve) # <<<<<<<<<<<<<<
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+ * elif len(curve) == 4:
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_PySequence_Tuple(__pyx_v_aligned_curve); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_t_3, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_2))) || (PyList_CheckExact(__pyx_t_2))) {
+ PyObject* sequence = __pyx_t_2;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 3)) {
+ if (size > 3) __Pyx_RaiseTooManyValuesError(3);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1245, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_5);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ #endif
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_4 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_9 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_4);
+ index = 0; __pyx_t_3 = __pyx_t_9(__pyx_t_4); if (unlikely(!__pyx_t_3)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_1 = __pyx_t_9(__pyx_t_4); if (unlikely(!__pyx_t_1)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ index = 2; __pyx_t_5 = __pyx_t_9(__pyx_t_4); if (unlikely(!__pyx_t_5)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_5);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_9(__pyx_t_4), 3) < (0)) __PYX_ERR(0, 1245, __pyx_L1_error)
+ __pyx_t_9 = NULL;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ goto __pyx_L5_unpacking_done;
+ __pyx_L4_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_9 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1245, __pyx_L1_error)
+ __pyx_L5_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_b = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_c = __pyx_t_5;
+ __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1246
+ * if len(curve) == 3:
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+ * intersections = solveQuadratic(a[1], b[1], c[1]) # <<<<<<<<<<<<<<
+ * elif len(curve) == 4:
+ * a, b, c, d = calcCubicParameters(*aligned_curve)
+*/
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_solveQuadratic); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1246, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1246, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1246, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_v_c, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1246, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_5, __pyx_t_3, __pyx_t_4, __pyx_t_10};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_6, (4-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1246, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_v_intersections = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1244
+ * def _curve_line_intersections_t(curve, line):
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3: # <<<<<<<<<<<<<<
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+*/
+ goto __pyx_L3;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1247
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+ * elif len(curve) == 4: # <<<<<<<<<<<<<<
+ * a, b, c, d = calcCubicParameters(*aligned_curve)
+ * intersections = solveCubic(a[1], b[1], c[1], d[1])
+*/
+ __pyx_t_7 = PyObject_Length(__pyx_v_curve); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1247, __pyx_L1_error)
+ __pyx_t_8 = (__pyx_t_7 == 4);
+ if (likely(__pyx_t_8)) {
+
+ /* "fontTools/misc/bezierTools.py":1248
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+ * elif len(curve) == 4:
+ * a, b, c, d = calcCubicParameters(*aligned_curve) # <<<<<<<<<<<<<<
+ * intersections = solveCubic(a[1], b[1], c[1], d[1])
+ * else:
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_calcCubicParameters); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_Tuple(__pyx_v_aligned_curve); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_10 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_1, NULL); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if ((likely(PyTuple_CheckExact(__pyx_t_10))) || (PyList_CheckExact(__pyx_t_10))) {
+ PyObject* sequence = __pyx_t_10;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1248, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_4 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_3);
+ } else {
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(sequence, 2, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_4);
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 3, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ }
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_1,&__pyx_t_2,&__pyx_t_4,&__pyx_t_3};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ PyObject** temps[4] = {&__pyx_t_1,&__pyx_t_2,&__pyx_t_4,&__pyx_t_3};
+ __pyx_t_5 = PyObject_GetIter(__pyx_t_10); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_9 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5);
+ for (index=0; index < 4; index++) {
+ PyObject* item = __pyx_t_9(__pyx_t_5); if (unlikely(!item)) goto __pyx_L6_unpacking_failed;
+ __Pyx_GOTREF(item);
+ *(temps[index]) = item;
+ }
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_9(__pyx_t_5), 4) < (0)) __PYX_ERR(0, 1248, __pyx_L1_error)
+ __pyx_t_9 = NULL;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L7_unpacking_done;
+ __pyx_L6_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_9 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1248, __pyx_L1_error)
+ __pyx_L7_unpacking_done:;
+ }
+ __pyx_v_a = __pyx_t_1;
+ __pyx_t_1 = 0;
+ __pyx_v_b = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c = __pyx_t_4;
+ __pyx_t_4 = 0;
+ __pyx_v_d = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1249
+ * elif len(curve) == 4:
+ * a, b, c, d = calcCubicParameters(*aligned_curve)
+ * intersections = solveCubic(a[1], b[1], c[1], d[1]) # <<<<<<<<<<<<<<
+ * else:
+ * raise ValueError("Unknown curve degree")
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_solveCubic); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_GetItemInt(__pyx_v_c, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_d, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_4);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_4, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_3, __pyx_t_2, __pyx_t_1, __pyx_t_5, __pyx_t_11};
+ __pyx_t_10 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_4, __pyx_callargs+__pyx_t_6, (5-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1249, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ }
+ __pyx_v_intersections = __pyx_t_10;
+ __pyx_t_10 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1247
+ * a, b, c = calcQuadraticParameters(*aligned_curve)
+ * intersections = solveQuadratic(a[1], b[1], c[1])
+ * elif len(curve) == 4: # <<<<<<<<<<<<<<
+ * a, b, c, d = calcCubicParameters(*aligned_curve)
+ * intersections = solveCubic(a[1], b[1], c[1], d[1])
+*/
+ goto __pyx_L3;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1251
+ * intersections = solveCubic(a[1], b[1], c[1], d[1])
+ * else:
+ * raise ValueError("Unknown curve degree") # <<<<<<<<<<<<<<
+ * return sorted(i for i in intersections if 0.0 <= i <= 1)
+ *
+*/
+ /*else*/ {
+ __pyx_t_4 = NULL;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_mstate_global->__pyx_kp_u_Unknown_curve_degree};
+ __pyx_t_10 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1251, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ }
+ __Pyx_Raise(__pyx_t_10, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __PYX_ERR(0, 1251, __pyx_L1_error)
+ }
+ __pyx_L3:;
+
+ /* "fontTools/misc/bezierTools.py":1252
+ * else:
+ * raise ValueError("Unknown curve degree")
+ * return sorted(i for i in intersections if 0.0 <= i <= 1) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_10 = __pyx_pf_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_genexpr(NULL, __pyx_v_intersections); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_4 = __Pyx_Generator_GetInlinedResult(__pyx_t_10); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ if (unlikely((PyList_Sort(__pyx_t_4) < 0))) __PYX_ERR(0, 1252, __pyx_L1_error)
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1242
+ *
+ *
+ * def _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3:
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_line_intersections_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_aligned_curve);
+ __Pyx_XDECREF(__pyx_v_a);
+ __Pyx_XDECREF(__pyx_v_b);
+ __Pyx_XDECREF(__pyx_v_c);
+ __Pyx_XDECREF(__pyx_v_intersections);
+ __Pyx_XDECREF(__pyx_v_d);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_4misc_11bezierTools_27_curve_line_intersections_t_2generator4);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1255
+ *
+ *
+ * def curveLineIntersections(curve, line): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a line.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_79curveLineIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_78curveLineIntersections, "curveLineIntersections(curve, line)\n\nFinds intersections between a curve and a line.\n\nArgs:\n curve: List of coordinates of the curve segment as 2D tuples.\n line: List of coordinates of the line segment as 2D tuples.\n\nReturns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\nExamples::\n >>> curve = [ (100, 240), (30, 60), (210, 230), (160, 30) ]\n >>> line = [ (25, 260), (230, 20) ]\n >>> intersections = curveLineIntersections(curve, line)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (84.9000930760723, 189.87306176459828)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_79curveLineIntersections = {"curveLineIntersections", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_79curveLineIntersections, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_78curveLineIntersections};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_79curveLineIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_curve = 0;
+ PyObject *__pyx_v_line = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("curveLineIntersections (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curve,&__pyx_mstate_global->__pyx_n_u_line,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1255, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1255, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1255, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "curveLineIntersections", 0) < (0)) __PYX_ERR(0, 1255, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("curveLineIntersections", 1, 2, 2, i); __PYX_ERR(0, 1255, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1255, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1255, __pyx_L3_error)
+ }
+ __pyx_v_curve = values[0];
+ __pyx_v_line = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("curveLineIntersections", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1255, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.curveLineIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_78curveLineIntersections(__pyx_self, __pyx_v_curve, __pyx_v_line);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_78curveLineIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, PyObject *__pyx_v_line) {
+ PyObject *__pyx_v_pointFinder = NULL;
+ PyObject *__pyx_v_intersections = NULL;
+ PyObject *__pyx_v_t = NULL;
+ PyObject *__pyx_v_pt = NULL;
+ PyObject *__pyx_v_line_t = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ size_t __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ PyObject *__pyx_t_8 = NULL;
+ PyObject *__pyx_t_9 = NULL;
+ int __pyx_t_10;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("curveLineIntersections", 0);
+
+ /* "fontTools/misc/bezierTools.py":1276
+ * (84.9000930760723, 189.87306176459828)
+ * """
+ * if len(curve) == 3: # <<<<<<<<<<<<<<
+ * pointFinder = quadraticPointAtT
+ * elif len(curve) == 4:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_curve); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1276, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 3);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1277
+ * """
+ * if len(curve) == 3:
+ * pointFinder = quadraticPointAtT # <<<<<<<<<<<<<<
+ * elif len(curve) == 4:
+ * pointFinder = cubicPointAtT
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_quadraticPointAtT); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1277, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_pointFinder = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1276
+ * (84.9000930760723, 189.87306176459828)
+ * """
+ * if len(curve) == 3: # <<<<<<<<<<<<<<
+ * pointFinder = quadraticPointAtT
+ * elif len(curve) == 4:
+*/
+ goto __pyx_L3;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1278
+ * if len(curve) == 3:
+ * pointFinder = quadraticPointAtT
+ * elif len(curve) == 4: # <<<<<<<<<<<<<<
+ * pointFinder = cubicPointAtT
+ * else:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_curve); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1278, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 4);
+ if (likely(__pyx_t_2)) {
+
+ /* "fontTools/misc/bezierTools.py":1279
+ * pointFinder = quadraticPointAtT
+ * elif len(curve) == 4:
+ * pointFinder = cubicPointAtT # <<<<<<<<<<<<<<
+ * else:
+ * raise ValueError("Unknown curve degree")
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_cubicPointAtT); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1279, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_pointFinder = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1278
+ * if len(curve) == 3:
+ * pointFinder = quadraticPointAtT
+ * elif len(curve) == 4: # <<<<<<<<<<<<<<
+ * pointFinder = cubicPointAtT
+ * else:
+*/
+ goto __pyx_L3;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1281
+ * pointFinder = cubicPointAtT
+ * else:
+ * raise ValueError("Unknown curve degree") # <<<<<<<<<<<<<<
+ * intersections = []
+ * for t in _curve_line_intersections_t(curve, line):
+*/
+ /*else*/ {
+ __pyx_t_4 = NULL;
+ __pyx_t_5 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_mstate_global->__pyx_kp_u_Unknown_curve_degree};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_5, (2-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1281, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __Pyx_Raise(__pyx_t_3, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __PYX_ERR(0, 1281, __pyx_L1_error)
+ }
+ __pyx_L3:;
+
+ /* "fontTools/misc/bezierTools.py":1282
+ * else:
+ * raise ValueError("Unknown curve degree")
+ * intersections = [] # <<<<<<<<<<<<<<
+ * for t in _curve_line_intersections_t(curve, line):
+ * pt = pointFinder(*curve, t)
+*/
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1282, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_intersections = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1283
+ * raise ValueError("Unknown curve degree")
+ * intersections = []
+ * for t in _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * pt = pointFinder(*curve, t)
+ * # Back-project the point onto the line, to avoid problems with
+*/
+ __pyx_t_4 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_curve_line_intersections_t); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1283, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_4);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_4, __pyx_v_curve, __pyx_v_line};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_5, (3-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1283, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ if (likely(PyList_CheckExact(__pyx_t_3)) || PyTuple_CheckExact(__pyx_t_3)) {
+ __pyx_t_6 = __pyx_t_3; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_1 = 0;
+ __pyx_t_7 = NULL;
+ } else {
+ __pyx_t_1 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1283, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1283, __pyx_L1_error)
+ }
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ for (;;) {
+ if (likely(!__pyx_t_7)) {
+ if (likely(PyList_CheckExact(__pyx_t_6))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1283, __pyx_L1_error)
+ #endif
+ if (__pyx_t_1 >= __pyx_temp) break;
+ }
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_1, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_1;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1283, __pyx_L1_error)
+ #endif
+ if (__pyx_t_1 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_3 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_1));
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_1);
+ #endif
+ ++__pyx_t_1;
+ }
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1283, __pyx_L1_error)
+ } else {
+ __pyx_t_3 = __pyx_t_7(__pyx_t_6);
+ if (unlikely(!__pyx_t_3)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1283, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_XDECREF_SET(__pyx_v_t, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1284
+ * intersections = []
+ * for t in _curve_line_intersections_t(curve, line):
+ * pt = pointFinder(*curve, t) # <<<<<<<<<<<<<<
+ * # Back-project the point onto the line, to avoid problems with
+ * # numerical accuracy in the case of vertical and horizontal lines
+*/
+ __pyx_t_3 = __Pyx_PySequence_Tuple(__pyx_v_curve); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1284, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1284, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1284, __pyx_L1_error);
+ __pyx_t_8 = PyNumber_Add(__pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1284, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PyObject_Call(__pyx_v_pointFinder, __pyx_t_8, NULL); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1284, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_pt, __pyx_t_4);
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1287
+ * # Back-project the point onto the line, to avoid problems with
+ * # numerical accuracy in the case of vertical and horizontal lines
+ * line_t = _line_t_of_pt(*line, pt) # <<<<<<<<<<<<<<
+ * pt = linePointAtT(*line, line_t)
+ * intersections.append(Intersection(pt=pt, t1=t, t2=line_t))
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_line_t_of_pt); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = __Pyx_PySequence_Tuple(__pyx_v_line); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_pt);
+ __Pyx_GIVEREF(__pyx_v_pt);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_pt) != (0)) __PYX_ERR(0, 1287, __pyx_L1_error);
+ __pyx_t_9 = PyNumber_Add(__pyx_t_8, __pyx_t_3); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 1287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_9, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_line_t, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1288
+ * # numerical accuracy in the case of vertical and horizontal lines
+ * line_t = _line_t_of_pt(*line, pt)
+ * pt = linePointAtT(*line, line_t) # <<<<<<<<<<<<<<
+ * intersections.append(Intersection(pt=pt, t1=t, t2=line_t))
+ * return intersections
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_linePointAtT); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1288, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_9 = __Pyx_PySequence_Tuple(__pyx_v_line); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 1288, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1288, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_v_line_t);
+ __Pyx_GIVEREF(__pyx_v_line_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_line_t) != (0)) __PYX_ERR(0, 1288, __pyx_L1_error);
+ __pyx_t_8 = PyNumber_Add(__pyx_t_9, __pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1288, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_8, NULL); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1288, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF_SET(__pyx_v_pt, __pyx_t_4);
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1289
+ * line_t = _line_t_of_pt(*line, pt)
+ * pt = linePointAtT(*line, line_t)
+ * intersections.append(Intersection(pt=pt, t1=t, t2=line_t)) # <<<<<<<<<<<<<<
+ * return intersections
+ *
+*/
+ __pyx_t_8 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_8 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_8);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_8);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_5 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_8, NULL};
+ __pyx_t_9 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_v_pt, __pyx_t_9, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_v_t, __pyx_t_9, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_v_line_t, __pyx_t_9, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ __pyx_t_4 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_5, (1-__pyx_t_5) | (__pyx_t_5*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1289, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ __pyx_t_10 = __Pyx_PyList_Append(__pyx_v_intersections, __pyx_t_4); if (unlikely(__pyx_t_10 == ((int)-1))) __PYX_ERR(0, 1289, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1283
+ * raise ValueError("Unknown curve degree")
+ * intersections = []
+ * for t in _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * pt = pointFinder(*curve, t)
+ * # Back-project the point onto the line, to avoid problems with
+*/
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1290
+ * pt = linePointAtT(*line, line_t)
+ * intersections.append(Intersection(pt=pt, t1=t, t2=line_t))
+ * return intersections # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_intersections);
+ __pyx_r = __pyx_v_intersections;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1255
+ *
+ *
+ * def curveLineIntersections(curve, line): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a line.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.curveLineIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_pointFinder);
+ __Pyx_XDECREF(__pyx_v_intersections);
+ __Pyx_XDECREF(__pyx_v_t);
+ __Pyx_XDECREF(__pyx_v_pt);
+ __Pyx_XDECREF(__pyx_v_line_t);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1293
+ *
+ *
+ * def _curve_bounds(c): # <<<<<<<<<<<<<<
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c)
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_81_curve_bounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_80_curve_bounds, "_curve_bounds(c)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_81_curve_bounds = {"_curve_bounds", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_81_curve_bounds, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_80_curve_bounds};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_81_curve_bounds(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_c = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_curve_bounds (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_c,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1293, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1293, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_curve_bounds", 0) < (0)) __PYX_ERR(0, 1293, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_curve_bounds", 1, 1, 1, i); __PYX_ERR(0, 1293, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1293, __pyx_L3_error)
+ }
+ __pyx_v_c = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_curve_bounds", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1293, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_bounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_80_curve_bounds(__pyx_self, __pyx_v_c);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_80_curve_bounds(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_c) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_curve_bounds", 0);
+
+ /* "fontTools/misc/bezierTools.py":1294
+ *
+ * def _curve_bounds(c):
+ * if len(c) == 3: # <<<<<<<<<<<<<<
+ * return calcQuadraticBounds(*c)
+ * elif len(c) == 4:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_c); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1294, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 3);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1295
+ * def _curve_bounds(c):
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c) # <<<<<<<<<<<<<<
+ * elif len(c) == 4:
+ * return calcCubicBounds(*c)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_calcQuadraticBounds); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1295, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_c); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1295, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_4, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1295, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1294
+ *
+ * def _curve_bounds(c):
+ * if len(c) == 3: # <<<<<<<<<<<<<<
+ * return calcQuadraticBounds(*c)
+ * elif len(c) == 4:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1296
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c)
+ * elif len(c) == 4: # <<<<<<<<<<<<<<
+ * return calcCubicBounds(*c)
+ * raise ValueError("Unknown curve degree")
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_c); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1296, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 4);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1297
+ * return calcQuadraticBounds(*c)
+ * elif len(c) == 4:
+ * return calcCubicBounds(*c) # <<<<<<<<<<<<<<
+ * raise ValueError("Unknown curve degree")
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_calcCubicBounds); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1297, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __Pyx_PySequence_Tuple(__pyx_v_c); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1297, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_4, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1297, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1296
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c)
+ * elif len(c) == 4: # <<<<<<<<<<<<<<
+ * return calcCubicBounds(*c)
+ * raise ValueError("Unknown curve degree")
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1298
+ * elif len(c) == 4:
+ * return calcCubicBounds(*c)
+ * raise ValueError("Unknown curve degree") # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_4 = NULL;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_mstate_global->__pyx_kp_u_Unknown_curve_degree};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1298, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __Pyx_Raise(__pyx_t_3, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __PYX_ERR(0, 1298, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":1293
+ *
+ *
+ * def _curve_bounds(c): # <<<<<<<<<<<<<<
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c)
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_bounds", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1301
+ *
+ *
+ * def _split_segment_at_t(c, t): # <<<<<<<<<<<<<<
+ * if len(c) == 2:
+ * s, e = c
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_83_split_segment_at_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_82_split_segment_at_t, "_split_segment_at_t(c, t)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_83_split_segment_at_t = {"_split_segment_at_t", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_83_split_segment_at_t, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_82_split_segment_at_t};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_83_split_segment_at_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_c = 0;
+ PyObject *__pyx_v_t = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_split_segment_at_t (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_c,&__pyx_mstate_global->__pyx_n_u_t,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1301, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1301, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1301, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_split_segment_at_t", 0) < (0)) __PYX_ERR(0, 1301, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_split_segment_at_t", 1, 2, 2, i); __PYX_ERR(0, 1301, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1301, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1301, __pyx_L3_error)
+ }
+ __pyx_v_c = values[0];
+ __pyx_v_t = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_split_segment_at_t", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1301, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._split_segment_at_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_82_split_segment_at_t(__pyx_self, __pyx_v_c, __pyx_v_t);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_82_split_segment_at_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_c, PyObject *__pyx_v_t) {
+ PyObject *__pyx_v_s = NULL;
+ PyObject *__pyx_v_e = NULL;
+ PyObject *__pyx_v_midpoint = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *(*__pyx_t_6)(PyObject *);
+ size_t __pyx_t_7;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_split_segment_at_t", 0);
+
+ /* "fontTools/misc/bezierTools.py":1302
+ *
+ * def _split_segment_at_t(c, t):
+ * if len(c) == 2: # <<<<<<<<<<<<<<
+ * s, e = c
+ * midpoint = linePointAtT(s, e, t)
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_c); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1302, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 2);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1303
+ * def _split_segment_at_t(c, t):
+ * if len(c) == 2:
+ * s, e = c # <<<<<<<<<<<<<<
+ * midpoint = linePointAtT(s, e, t)
+ * return [(s, midpoint), (midpoint, e)]
+*/
+ if ((likely(PyTuple_CheckExact(__pyx_v_c))) || (PyList_CheckExact(__pyx_v_c))) {
+ PyObject* sequence = __pyx_v_c;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1303, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_4 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_4);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_4);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ #endif
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_5 = PyObject_GetIter(__pyx_v_c); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5);
+ index = 0; __pyx_t_3 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_3)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_4 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_4)) goto __pyx_L4_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_4);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_6(__pyx_t_5), 2) < (0)) __PYX_ERR(0, 1303, __pyx_L1_error)
+ __pyx_t_6 = NULL;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L5_unpacking_done;
+ __pyx_L4_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_6 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1303, __pyx_L1_error)
+ __pyx_L5_unpacking_done:;
+ }
+ __pyx_v_s = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v_e = __pyx_t_4;
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1304
+ * if len(c) == 2:
+ * s, e = c
+ * midpoint = linePointAtT(s, e, t) # <<<<<<<<<<<<<<
+ * return [(s, midpoint), (midpoint, e)]
+ * if len(c) == 3:
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_linePointAtT); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1304, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4] = {__pyx_t_3, __pyx_v_s, __pyx_v_e, __pyx_v_t};
+ __pyx_t_4 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_7, (4-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1304, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ __pyx_v_midpoint = __pyx_t_4;
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1305
+ * s, e = c
+ * midpoint = linePointAtT(s, e, t)
+ * return [(s, midpoint), (midpoint, e)] # <<<<<<<<<<<<<<
+ * if len(c) == 3:
+ * return splitQuadraticAtT(*c, t)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1305, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_v_s);
+ __Pyx_GIVEREF(__pyx_v_s);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_s) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_midpoint);
+ __Pyx_GIVEREF(__pyx_v_midpoint);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_v_midpoint) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __pyx_t_5 = PyTuple_New(2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1305, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx_v_midpoint);
+ __Pyx_GIVEREF(__pyx_v_midpoint);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_midpoint) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_e);
+ __Pyx_GIVEREF(__pyx_v_e);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_v_e) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __pyx_t_3 = PyList_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1305, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_t_4) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 1305, __pyx_L1_error);
+ __pyx_t_4 = 0;
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1302
+ *
+ * def _split_segment_at_t(c, t):
+ * if len(c) == 2: # <<<<<<<<<<<<<<
+ * s, e = c
+ * midpoint = linePointAtT(s, e, t)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1306
+ * midpoint = linePointAtT(s, e, t)
+ * return [(s, midpoint), (midpoint, e)]
+ * if len(c) == 3: # <<<<<<<<<<<<<<
+ * return splitQuadraticAtT(*c, t)
+ * elif len(c) == 4:
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_c); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1306, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 3);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1307
+ * return [(s, midpoint), (midpoint, e)]
+ * if len(c) == 3:
+ * return splitQuadraticAtT(*c, t) # <<<<<<<<<<<<<<
+ * elif len(c) == 4:
+ * return splitCubicAtT(*c, t)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1307, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_5 = __Pyx_PySequence_Tuple(__pyx_v_c); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1307, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1307, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1307, __pyx_L1_error);
+ __pyx_t_8 = PyNumber_Add(__pyx_t_5, __pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1307, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_8, NULL); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1307, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1306
+ * midpoint = linePointAtT(s, e, t)
+ * return [(s, midpoint), (midpoint, e)]
+ * if len(c) == 3: # <<<<<<<<<<<<<<
+ * return splitQuadraticAtT(*c, t)
+ * elif len(c) == 4:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1308
+ * if len(c) == 3:
+ * return splitQuadraticAtT(*c, t)
+ * elif len(c) == 4: # <<<<<<<<<<<<<<
+ * return splitCubicAtT(*c, t)
+ * raise ValueError("Unknown curve degree")
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_c); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1308, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 == 4);
+ if (__pyx_t_2) {
+
+ /* "fontTools/misc/bezierTools.py":1309
+ * return splitQuadraticAtT(*c, t)
+ * elif len(c) == 4:
+ * return splitCubicAtT(*c, t) # <<<<<<<<<<<<<<
+ * raise ValueError("Unknown curve degree")
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_splitCubicAtT_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1309, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = __Pyx_PySequence_Tuple(__pyx_v_c); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1309, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_3 = PyTuple_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1309, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_v_t);
+ __Pyx_GIVEREF(__pyx_v_t);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_v_t) != (0)) __PYX_ERR(0, 1309, __pyx_L1_error);
+ __pyx_t_5 = PyNumber_Add(__pyx_t_8, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1309, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_5, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1309, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1308
+ * if len(c) == 3:
+ * return splitQuadraticAtT(*c, t)
+ * elif len(c) == 4: # <<<<<<<<<<<<<<
+ * return splitCubicAtT(*c, t)
+ * raise ValueError("Unknown curve degree")
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1310
+ * elif len(c) == 4:
+ * return splitCubicAtT(*c, t)
+ * raise ValueError("Unknown curve degree") # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_5 = NULL;
+ __pyx_t_7 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_mstate_global->__pyx_kp_u_Unknown_curve_degree};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_7, (2-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1310, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __Pyx_Raise(__pyx_t_3, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __PYX_ERR(0, 1310, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":1301
+ *
+ *
+ * def _split_segment_at_t(c, t): # <<<<<<<<<<<<<<
+ * if len(c) == 2:
+ * s, e = c
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._split_segment_at_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_s);
+ __Pyx_XDECREF(__pyx_v_e);
+ __Pyx_XDECREF(__pyx_v_midpoint);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_85_curve_curve_intersections_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_84_curve_curve_intersections_t, "_curve_curve_intersections_t(curve1, curve2, precision=1e-3, range1=None, range2=None)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_85_curve_curve_intersections_t = {"_curve_curve_intersections_t", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_85_curve_curve_intersections_t, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_84_curve_curve_intersections_t};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_85_curve_curve_intersections_t(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_curve1 = 0;
+ PyObject *__pyx_v_curve2 = 0;
+ PyObject *__pyx_v_precision = 0;
+ PyObject *__pyx_v_range1 = 0;
+ PyObject *__pyx_v_range2 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[5] = {0,0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_curve_curve_intersections_t (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curve1,&__pyx_mstate_global->__pyx_n_u_curve2,&__pyx_mstate_global->__pyx_n_u_precision,&__pyx_mstate_global->__pyx_n_u_range1,&__pyx_mstate_global->__pyx_n_u_range2,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1313, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_curve_curve_intersections_t", 0) < (0)) __PYX_ERR(0, 1313, __pyx_L3_error)
+ if (!values[2]) values[2] = __Pyx_NewRef(((PyObject *)((PyObject*)__pyx_mstate_global->__pyx_float_1eneg_3)));
+
+ /* "fontTools/misc/bezierTools.py":1314
+ *
+ * def _curve_curve_intersections_t(
+ * curve1, curve2, precision=1e-3, range1=None, range2=None # <<<<<<<<<<<<<<
+ * ):
+ * bounds1 = _curve_bounds(curve1)
+*/
+ if (!values[3]) values[3] = __Pyx_NewRef(((PyObject *)Py_None));
+ if (!values[4]) values[4] = __Pyx_NewRef(((PyObject *)Py_None));
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_curve_curve_intersections_t", 0, 2, 5, i); __PYX_ERR(0, 1313, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 5:
+ values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1313, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ if (!values[2]) values[2] = __Pyx_NewRef(((PyObject *)((PyObject*)__pyx_mstate_global->__pyx_float_1eneg_3)));
+ if (!values[3]) values[3] = __Pyx_NewRef(((PyObject *)Py_None));
+ if (!values[4]) values[4] = __Pyx_NewRef(((PyObject *)Py_None));
+ }
+ __pyx_v_curve1 = values[0];
+ __pyx_v_curve2 = values[1];
+ __pyx_v_precision = values[2];
+ __pyx_v_range1 = values[3];
+ __pyx_v_range2 = values[4];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_curve_curve_intersections_t", 0, 2, 5, __pyx_nargs); __PYX_ERR(0, 1313, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_84_curve_curve_intersections_t(__pyx_self, __pyx_v_curve1, __pyx_v_curve2, __pyx_v_precision, __pyx_v_range1, __pyx_v_range2);
+
+ /* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1329
+ * return []
+ *
+ * def midpoint(r): # <<<<<<<<<<<<<<
+ * return 0.5 * (r[0] + r[1])
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_1midpoint(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_1midpoint = {"midpoint", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_1midpoint, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_1midpoint(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_r = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("midpoint (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_r,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1329, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1329, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "midpoint", 0) < (0)) __PYX_ERR(0, 1329, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("midpoint", 1, 1, 1, i); __PYX_ERR(0, 1329, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1329, __pyx_L3_error)
+ }
+ __pyx_v_r = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("midpoint", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1329, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t.midpoint", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_self, __pyx_v_r);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_r) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("midpoint", 0);
+
+ /* "fontTools/misc/bezierTools.py":1330
+ *
+ * def midpoint(r):
+ * return 0.5 * (r[0] + r[1]) # <<<<<<<<<<<<<<
+ *
+ * # If they do overlap but they're tiny, approximate
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_r, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1330, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_r, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1330, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1330, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Multiply(__pyx_mstate_global->__pyx_float_0_5, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1330, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1329
+ * return []
+ *
+ * def midpoint(r): # <<<<<<<<<<<<<<
+ * return 0.5 * (r[0] + r[1])
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t.midpoint", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1366
+ * )
+ *
+ * unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision)) # <<<<<<<<<<<<<<
+ * seen = set()
+ * unique_values = []
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_2lambda3(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_2lambda3 = {"lambda3", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_2lambda3, __Pyx_METH_FASTCALL|METH_KEYWORDS, 0};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_2lambda3(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_ts = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("lambda3 (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_ts,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1366, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1366, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "lambda3", 0) < (0)) __PYX_ERR(0, 1366, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("lambda3", 1, 1, 1, i); __PYX_ERR(0, 1366, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1366, __pyx_L3_error)
+ }
+ __pyx_v_ts = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("lambda3", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1366, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t.lambda3", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_lambda_funcdef_lambda3(__pyx_self, __pyx_v_ts);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_lambda_funcdef_lambda3(PyObject *__pyx_self, PyObject *__pyx_v_ts) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *__pyx_cur_scope;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *__pyx_outer_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("lambda3", 0);
+ __pyx_outer_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *) __Pyx_CyFunction_GetClosure(__pyx_self);
+ __pyx_cur_scope = __pyx_outer_scope;
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_ts, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (unlikely(!__pyx_cur_scope->__pyx_v_precision)) { __Pyx_RaiseClosureNameError("precision"); __PYX_ERR(0, 1366, __pyx_L1_error) }
+ __pyx_t_2 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_cur_scope->__pyx_v_precision); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyNumber_Int(__pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_ts, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (unlikely(!__pyx_cur_scope->__pyx_v_precision)) { __Pyx_RaiseClosureNameError("precision"); __PYX_ERR(0, 1366, __pyx_L1_error) }
+ __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_t_2, __pyx_cur_scope->__pyx_v_precision); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyNumber_Int(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 1366, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 1366, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t.lambda3", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_84_curve_curve_intersections_t(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve1, PyObject *__pyx_v_curve2, PyObject *__pyx_v_precision, PyObject *__pyx_v_range1, PyObject *__pyx_v_range2) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *__pyx_cur_scope;
+ PyObject *__pyx_v_bounds1 = NULL;
+ PyObject *__pyx_v_bounds2 = NULL;
+ PyObject *__pyx_v_intersects = NULL;
+ CYTHON_UNUSED PyObject *__pyx_v__ = NULL;
+ PyObject *__pyx_v_midpoint = 0;
+ PyObject *__pyx_v_c11 = NULL;
+ PyObject *__pyx_v_c12 = NULL;
+ PyObject *__pyx_v_c11_range = NULL;
+ PyObject *__pyx_v_c12_range = NULL;
+ PyObject *__pyx_v_c21 = NULL;
+ PyObject *__pyx_v_c22 = NULL;
+ PyObject *__pyx_v_c21_range = NULL;
+ PyObject *__pyx_v_c22_range = NULL;
+ PyObject *__pyx_v_found = NULL;
+ PyObject *__pyx_v_unique_key = NULL;
+ PyObject *__pyx_v_seen = NULL;
+ PyObject *__pyx_v_unique_values = NULL;
+ PyObject *__pyx_v_ts = NULL;
+ PyObject *__pyx_v_key = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ int __pyx_t_5;
+ int __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ int __pyx_t_9;
+ Py_ssize_t __pyx_t_10;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_curve_curve_intersections_t", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 1313, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_v_precision = __pyx_v_precision;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_v_precision);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_precision);
+ __Pyx_INCREF(__pyx_v_range1);
+ __Pyx_INCREF(__pyx_v_range2);
+
+ /* "fontTools/misc/bezierTools.py":1316
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+ * bounds1 = _curve_bounds(curve1) # <<<<<<<<<<<<<<
+ * bounds2 = _curve_bounds(curve2)
+ *
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_curve_bounds); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_curve1};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1316, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_bounds1 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1317
+ * ):
+ * bounds1 = _curve_bounds(curve1)
+ * bounds2 = _curve_bounds(curve2) # <<<<<<<<<<<<<<
+ *
+ * if not range1:
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_curve_bounds); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1317, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_v_curve2};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1317, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_bounds2 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1319
+ * bounds2 = _curve_bounds(curve2)
+ *
+ * if not range1: # <<<<<<<<<<<<<<
+ * range1 = (0.0, 1.0)
+ * if not range2:
+*/
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_v_range1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1319, __pyx_L1_error)
+ __pyx_t_6 = (!__pyx_t_5);
+ if (__pyx_t_6) {
+
+ /* "fontTools/misc/bezierTools.py":1320
+ *
+ * if not range1:
+ * range1 = (0.0, 1.0) # <<<<<<<<<<<<<<
+ * if not range2:
+ * range2 = (0.0, 1.0)
+*/
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_tuple[1]);
+ __Pyx_DECREF_SET(__pyx_v_range1, __pyx_mstate_global->__pyx_tuple[1]);
+
+ /* "fontTools/misc/bezierTools.py":1319
+ * bounds2 = _curve_bounds(curve2)
+ *
+ * if not range1: # <<<<<<<<<<<<<<
+ * range1 = (0.0, 1.0)
+ * if not range2:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1321
+ * if not range1:
+ * range1 = (0.0, 1.0)
+ * if not range2: # <<<<<<<<<<<<<<
+ * range2 = (0.0, 1.0)
+ *
+*/
+ __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_v_range2); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1321, __pyx_L1_error)
+ __pyx_t_5 = (!__pyx_t_6);
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1322
+ * range1 = (0.0, 1.0)
+ * if not range2:
+ * range2 = (0.0, 1.0) # <<<<<<<<<<<<<<
+ *
+ * # If bounds don't intersect, go home
+*/
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_tuple[1]);
+ __Pyx_DECREF_SET(__pyx_v_range2, __pyx_mstate_global->__pyx_tuple[1]);
+
+ /* "fontTools/misc/bezierTools.py":1321
+ * if not range1:
+ * range1 = (0.0, 1.0)
+ * if not range2: # <<<<<<<<<<<<<<
+ * range2 = (0.0, 1.0)
+ *
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1325
+ *
+ * # If bounds don't intersect, go home
+ * intersects, _ = sectRect(bounds1, bounds2) # <<<<<<<<<<<<<<
+ * if not intersects:
+ * return []
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_sectRect); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_bounds1, __pyx_v_bounds2};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
+ PyObject* sequence = __pyx_t_1;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1325, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_2);
+ } else {
+ __pyx_t_3 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ }
+ #else
+ __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #endif
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ index = 0; __pyx_t_3 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_3)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_3);
+ index = 1; __pyx_t_2 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_2)) goto __pyx_L5_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 2) < (0)) __PYX_ERR(0, 1325, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L6_unpacking_done;
+ __pyx_L5_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1325, __pyx_L1_error)
+ __pyx_L6_unpacking_done:;
+ }
+ __pyx_v_intersects = __pyx_t_3;
+ __pyx_t_3 = 0;
+ __pyx_v__ = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1326
+ * # If bounds don't intersect, go home
+ * intersects, _ = sectRect(bounds1, bounds2)
+ * if not intersects: # <<<<<<<<<<<<<<
+ * return []
+ *
+*/
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_v_intersects); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1326, __pyx_L1_error)
+ __pyx_t_6 = (!__pyx_t_5);
+ if (__pyx_t_6) {
+
+ /* "fontTools/misc/bezierTools.py":1327
+ * intersects, _ = sectRect(bounds1, bounds2)
+ * if not intersects:
+ * return [] # <<<<<<<<<<<<<<
+ *
+ * def midpoint(r):
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1327, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1326
+ * # If bounds don't intersect, go home
+ * intersects, _ = sectRect(bounds1, bounds2)
+ * if not intersects: # <<<<<<<<<<<<<<
+ * return []
+ *
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1329
+ * return []
+ *
+ * def midpoint(r): # <<<<<<<<<<<<<<
+ * return 0.5 * (r[0] + r[1])
+ *
+*/
+ __pyx_t_1 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_1midpoint, 0, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t_loc, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[5])); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1329, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_midpoint = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1333
+ *
+ * # If they do overlap but they're tiny, approximate
+ * if rectArea(bounds1) < precision and rectArea(bounds2) < precision: # <<<<<<<<<<<<<<
+ * return [(midpoint(range1), midpoint(range2))]
+ *
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_rectArea); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_bounds1};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_1, __pyx_cur_scope->__pyx_v_precision, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (__pyx_t_5) {
+ } else {
+ __pyx_t_6 = __pyx_t_5;
+ goto __pyx_L9_bool_binop_done;
+ }
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_rectArea); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_v_bounds2};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_t_2 = PyObject_RichCompare(__pyx_t_3, __pyx_cur_scope->__pyx_v_precision, Py_LT); __Pyx_XGOTREF(__pyx_t_2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1333, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_6 = __pyx_t_5;
+ __pyx_L9_bool_binop_done:;
+ if (__pyx_t_6) {
+
+ /* "fontTools/misc/bezierTools.py":1334
+ * # If they do overlap but they're tiny, approximate
+ * if rectArea(bounds1) < precision and rectArea(bounds2) < precision:
+ * return [(midpoint(range1), midpoint(range2))] # <<<<<<<<<<<<<<
+ *
+ * c11, c12 = _split_segment_at_t(curve1, 0.5)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1334, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1334, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1334, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1334, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 1334, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_3 = PyList_New(1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1334, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_3, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 1334, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1333
+ *
+ * # If they do overlap but they're tiny, approximate
+ * if rectArea(bounds1) < precision and rectArea(bounds2) < precision: # <<<<<<<<<<<<<<
+ * return [(midpoint(range1), midpoint(range2))]
+ *
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1336
+ * return [(midpoint(range1), midpoint(range2))]
+ *
+ * c11, c12 = _split_segment_at_t(curve1, 0.5) # <<<<<<<<<<<<<<
+ * c11_range = (range1[0], midpoint(range1))
+ * c12_range = (midpoint(range1), range1[1])
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_split_segment_at_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_1, __pyx_v_curve1, __pyx_mstate_global->__pyx_float_0_5};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_3))) || (PyList_CheckExact(__pyx_t_3))) {
+ PyObject* sequence = __pyx_t_3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1336, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ index = 0; __pyx_t_2 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_2)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_1)) goto __pyx_L11_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 2) < (0)) __PYX_ERR(0, 1336, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L12_unpacking_done;
+ __pyx_L11_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1336, __pyx_L1_error)
+ __pyx_L12_unpacking_done:;
+ }
+ __pyx_v_c11 = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c12 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1337
+ *
+ * c11, c12 = _split_segment_at_t(curve1, 0.5)
+ * c11_range = (range1[0], midpoint(range1)) # <<<<<<<<<<<<<<
+ * c12_range = (midpoint(range1), range1[1])
+ *
+*/
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_range1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1337, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1337, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1337, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 1337, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1337, __pyx_L1_error);
+ __pyx_t_3 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_c11_range = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1338
+ * c11, c12 = _split_segment_at_t(curve1, 0.5)
+ * c11_range = (range1[0], midpoint(range1))
+ * c12_range = (midpoint(range1), range1[1]) # <<<<<<<<<<<<<<
+ *
+ * c21, c22 = _split_segment_at_t(curve2, 0.5)
+*/
+ __pyx_t_2 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1338, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_range1, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1338, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1338, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1338, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1338, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_c12_range = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1340
+ * c12_range = (midpoint(range1), range1[1])
+ *
+ * c21, c22 = _split_segment_at_t(curve2, 0.5) # <<<<<<<<<<<<<<
+ * c21_range = (range2[0], midpoint(range2))
+ * c22_range = (midpoint(range2), range2[1])
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_split_segment_at_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_1, __pyx_v_curve2, __pyx_mstate_global->__pyx_float_0_5};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ if ((likely(PyTuple_CheckExact(__pyx_t_3))) || (PyList_CheckExact(__pyx_t_3))) {
+ PyObject* sequence = __pyx_t_3;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 1340, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_1 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_1);
+ } else {
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_1);
+ }
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ #endif
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ index = 0; __pyx_t_2 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_2)) goto __pyx_L13_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_2);
+ index = 1; __pyx_t_1 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_1)) goto __pyx_L13_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 2) < (0)) __PYX_ERR(0, 1340, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L14_unpacking_done;
+ __pyx_L13_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 1340, __pyx_L1_error)
+ __pyx_L14_unpacking_done:;
+ }
+ __pyx_v_c21 = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __pyx_v_c22 = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1341
+ *
+ * c21, c22 = _split_segment_at_t(curve2, 0.5)
+ * c21_range = (range2[0], midpoint(range2)) # <<<<<<<<<<<<<<
+ * c22_range = (midpoint(range2), range2[1])
+ *
+*/
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_range2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1341, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1341, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1341, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 1341, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1341, __pyx_L1_error);
+ __pyx_t_3 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_c21_range = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1342
+ * c21, c22 = _split_segment_at_t(curve2, 0.5)
+ * c21_range = (range2[0], midpoint(range2))
+ * c22_range = (midpoint(range2), range2[1]) # <<<<<<<<<<<<<<
+ *
+ * found = []
+*/
+ __pyx_t_2 = __pyx_pf_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_midpoint(__pyx_v_midpoint, __pyx_v_range2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1342, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_range2, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1342, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1342, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1342, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1342, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_c22_range = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1344
+ * c22_range = (midpoint(range2), range2[1])
+ *
+ * found = [] # <<<<<<<<<<<<<<
+ * found.extend(
+ * _curve_curve_intersections_t(
+*/
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1344, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_found = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1346
+ * found = []
+ * found.extend(
+ * _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * c11, c21, precision, range1=c11_range, range2=c21_range
+ * )
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+
+ /* "fontTools/misc/bezierTools.py":1347
+ * found.extend(
+ * _curve_curve_intersections_t(
+ * c11, c21, precision, range1=c11_range, range2=c21_range # <<<<<<<<<<<<<<
+ * )
+ * )
+*/
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4 + ((CYTHON_VECTORCALL) ? 2 : 0)] = {__pyx_t_1, __pyx_v_c11, __pyx_v_c21, __pyx_cur_scope->__pyx_v_precision};
+ __pyx_t_7 = __Pyx_MakeVectorcallBuilderKwds(2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range1, __pyx_v_c11_range, __pyx_t_7, __pyx_callargs+4, 0) < (0)) __PYX_ERR(0, 1346, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range2, __pyx_v_c21_range, __pyx_t_7, __pyx_callargs+4, 1) < (0)) __PYX_ERR(0, 1346, __pyx_L1_error)
+ __pyx_t_3 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1345
+ *
+ * found = []
+ * found.extend( # <<<<<<<<<<<<<<
+ * _curve_curve_intersections_t(
+ * c11, c21, precision, range1=c11_range, range2=c21_range
+*/
+ __pyx_t_9 = __Pyx_PyList_Extend(__pyx_v_found, __pyx_t_3); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1345, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1351
+ * )
+ * found.extend(
+ * _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * c12, c21, precision, range1=c12_range, range2=c21_range
+ * )
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1351, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+
+ /* "fontTools/misc/bezierTools.py":1352
+ * found.extend(
+ * _curve_curve_intersections_t(
+ * c12, c21, precision, range1=c12_range, range2=c21_range # <<<<<<<<<<<<<<
+ * )
+ * )
+*/
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_7))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_7);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_7);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_7, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4 + ((CYTHON_VECTORCALL) ? 2 : 0)] = {__pyx_t_2, __pyx_v_c12, __pyx_v_c21, __pyx_cur_scope->__pyx_v_precision};
+ __pyx_t_1 = __Pyx_MakeVectorcallBuilderKwds(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1351, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range1, __pyx_v_c12_range, __pyx_t_1, __pyx_callargs+4, 0) < (0)) __PYX_ERR(0, 1351, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range2, __pyx_v_c21_range, __pyx_t_1, __pyx_callargs+4, 1) < (0)) __PYX_ERR(0, 1351, __pyx_L1_error)
+ __pyx_t_3 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_7, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1351, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1350
+ * )
+ * )
+ * found.extend( # <<<<<<<<<<<<<<
+ * _curve_curve_intersections_t(
+ * c12, c21, precision, range1=c12_range, range2=c21_range
+*/
+ __pyx_t_9 = __Pyx_PyList_Extend(__pyx_v_found, __pyx_t_3); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1350, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1356
+ * )
+ * found.extend(
+ * _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * c11, c22, precision, range1=c11_range, range2=c22_range
+ * )
+*/
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1356, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/misc/bezierTools.py":1357
+ * found.extend(
+ * _curve_curve_intersections_t(
+ * c11, c22, precision, range1=c11_range, range2=c22_range # <<<<<<<<<<<<<<
+ * )
+ * )
+*/
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_7);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4 + ((CYTHON_VECTORCALL) ? 2 : 0)] = {__pyx_t_7, __pyx_v_c11, __pyx_v_c22, __pyx_cur_scope->__pyx_v_precision};
+ __pyx_t_2 = __Pyx_MakeVectorcallBuilderKwds(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1356, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range1, __pyx_v_c11_range, __pyx_t_2, __pyx_callargs+4, 0) < (0)) __PYX_ERR(0, 1356, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range2, __pyx_v_c22_range, __pyx_t_2, __pyx_callargs+4, 1) < (0)) __PYX_ERR(0, 1356, __pyx_L1_error)
+ __pyx_t_3 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1356, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1355
+ * )
+ * )
+ * found.extend( # <<<<<<<<<<<<<<
+ * _curve_curve_intersections_t(
+ * c11, c22, precision, range1=c11_range, range2=c22_range
+*/
+ __pyx_t_9 = __Pyx_PyList_Extend(__pyx_v_found, __pyx_t_3); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1355, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1361
+ * )
+ * found.extend(
+ * _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * c12, c22, precision, range1=c12_range, range2=c22_range
+ * )
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1361, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+
+ /* "fontTools/misc/bezierTools.py":1362
+ * found.extend(
+ * _curve_curve_intersections_t(
+ * c12, c22, precision, range1=c12_range, range2=c22_range # <<<<<<<<<<<<<<
+ * )
+ * )
+*/
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[4 + ((CYTHON_VECTORCALL) ? 2 : 0)] = {__pyx_t_1, __pyx_v_c12, __pyx_v_c22, __pyx_cur_scope->__pyx_v_precision};
+ __pyx_t_7 = __Pyx_MakeVectorcallBuilderKwds(2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1361, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range1, __pyx_v_c12_range, __pyx_t_7, __pyx_callargs+4, 0) < (0)) __PYX_ERR(0, 1361, __pyx_L1_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_range2, __pyx_v_c22_range, __pyx_t_7, __pyx_callargs+4, 1) < (0)) __PYX_ERR(0, 1361, __pyx_L1_error)
+ __pyx_t_3 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (4-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1361, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+
+ /* "fontTools/misc/bezierTools.py":1360
+ * )
+ * )
+ * found.extend( # <<<<<<<<<<<<<<
+ * _curve_curve_intersections_t(
+ * c12, c22, precision, range1=c12_range, range2=c22_range
+*/
+ __pyx_t_9 = __Pyx_PyList_Extend(__pyx_v_found, __pyx_t_3); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1360, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1366
+ * )
+ *
+ * unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision)) # <<<<<<<<<<<<<<
+ * seen = set()
+ * unique_values = []
+*/
+ __pyx_t_3 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_28_curve_curve_intersections_t_2lambda3, 0, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t_loc_2, ((PyObject*)__pyx_cur_scope), __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[6])); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_unique_key = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1367
+ *
+ * unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision))
+ * seen = set() # <<<<<<<<<<<<<<
+ * unique_values = []
+ *
+*/
+ __pyx_t_3 = PySet_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1367, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_seen = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1368
+ * unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision))
+ * seen = set()
+ * unique_values = [] # <<<<<<<<<<<<<<
+ *
+ * for ts in found:
+*/
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1368, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_unique_values = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1370
+ * unique_values = []
+ *
+ * for ts in found: # <<<<<<<<<<<<<<
+ * key = unique_key(ts)
+ * if key in seen:
+*/
+ __pyx_t_3 = __pyx_v_found; __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_10 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_3);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1370, __pyx_L1_error)
+ #endif
+ if (__pyx_t_10 >= __pyx_temp) break;
+ }
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(__pyx_t_3, __pyx_t_10, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_10;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1370, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_ts, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1371
+ *
+ * for ts in found:
+ * key = unique_key(ts) # <<<<<<<<<<<<<<
+ * if key in seen:
+ * continue
+*/
+ __pyx_t_2 = __pyx_lambda_funcdef_lambda3(__pyx_v_unique_key, __pyx_v_ts); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1371, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_v_key, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1372
+ * for ts in found:
+ * key = unique_key(ts)
+ * if key in seen: # <<<<<<<<<<<<<<
+ * continue
+ * seen.add(key)
+*/
+ __pyx_t_6 = (__Pyx_PySet_ContainsTF(__pyx_v_key, __pyx_v_seen, Py_EQ)); if (unlikely((__pyx_t_6 < 0))) __PYX_ERR(0, 1372, __pyx_L1_error)
+ if (__pyx_t_6) {
+
+ /* "fontTools/misc/bezierTools.py":1373
+ * key = unique_key(ts)
+ * if key in seen:
+ * continue # <<<<<<<<<<<<<<
+ * seen.add(key)
+ * unique_values.append(ts)
+*/
+ goto __pyx_L15_continue;
+
+ /* "fontTools/misc/bezierTools.py":1372
+ * for ts in found:
+ * key = unique_key(ts)
+ * if key in seen: # <<<<<<<<<<<<<<
+ * continue
+ * seen.add(key)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1374
+ * if key in seen:
+ * continue
+ * seen.add(key) # <<<<<<<<<<<<<<
+ * unique_values.append(ts)
+ *
+*/
+ __pyx_t_9 = PySet_Add(__pyx_v_seen, __pyx_v_key); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1374, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":1375
+ * continue
+ * seen.add(key)
+ * unique_values.append(ts) # <<<<<<<<<<<<<<
+ *
+ * return unique_values
+*/
+ __pyx_t_9 = __Pyx_PyList_Append(__pyx_v_unique_values, __pyx_v_ts); if (unlikely(__pyx_t_9 == ((int)-1))) __PYX_ERR(0, 1375, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":1370
+ * unique_values = []
+ *
+ * for ts in found: # <<<<<<<<<<<<<<
+ * key = unique_key(ts)
+ * if key in seen:
+*/
+ __pyx_L15_continue:;
+ }
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1377
+ * unique_values.append(ts)
+ *
+ * return unique_values # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_unique_values);
+ __pyx_r = __pyx_v_unique_values;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._curve_curve_intersections_t", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_bounds1);
+ __Pyx_XDECREF(__pyx_v_bounds2);
+ __Pyx_XDECREF(__pyx_v_intersects);
+ __Pyx_XDECREF(__pyx_v__);
+ __Pyx_XDECREF(__pyx_v_midpoint);
+ __Pyx_XDECREF(__pyx_v_c11);
+ __Pyx_XDECREF(__pyx_v_c12);
+ __Pyx_XDECREF(__pyx_v_c11_range);
+ __Pyx_XDECREF(__pyx_v_c12_range);
+ __Pyx_XDECREF(__pyx_v_c21);
+ __Pyx_XDECREF(__pyx_v_c22);
+ __Pyx_XDECREF(__pyx_v_c21_range);
+ __Pyx_XDECREF(__pyx_v_c22_range);
+ __Pyx_XDECREF(__pyx_v_found);
+ __Pyx_XDECREF(__pyx_v_unique_key);
+ __Pyx_XDECREF(__pyx_v_seen);
+ __Pyx_XDECREF(__pyx_v_unique_values);
+ __Pyx_XDECREF(__pyx_v_ts);
+ __Pyx_XDECREF(__pyx_v_key);
+ __Pyx_XDECREF(__pyx_v_range1);
+ __Pyx_XDECREF(__pyx_v_range2);
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1380
+ *
+ *
+ * def _is_linelike(segment): # <<<<<<<<<<<<<<
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline)
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_87_is_linelike(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_86_is_linelike, "_is_linelike(segment)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_87_is_linelike = {"_is_linelike", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_87_is_linelike, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_86_is_linelike};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_87_is_linelike(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_segment = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_is_linelike (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_segment,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1380, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1380, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_is_linelike", 0) < (0)) __PYX_ERR(0, 1380, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_is_linelike", 1, 1, 1, i); __PYX_ERR(0, 1380, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1380, __pyx_L3_error)
+ }
+ __pyx_v_segment = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_is_linelike", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1380, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._is_linelike", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_86_is_linelike(__pyx_self, __pyx_v_segment);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_is_linelike_2generator5(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":1382
+ * def _is_linelike(segment):
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12_is_linelike_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 1382, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_12_is_linelike_2generator5, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[7]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_is_linelike_locals_genexpr, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._is_linelike.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_is_linelike_2generator5(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ size_t __pyx_t_8;
+ int __pyx_t_9;
+ int __pyx_t_10;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 1382, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1382, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1382, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1382, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_p);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_p, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_isclose); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = __Pyx_GetItemInt(__pyx_cur_scope->__pyx_v_p, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_SharedReference); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_7))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_7);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_7);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_7, __pyx__function);
+ __pyx_t_8 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_5, __pyx_t_6, __pyx_mstate_global->__pyx_float_0_0};
+ __pyx_t_4 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_7, __pyx_callargs+__pyx_t_8, (3-__pyx_t_8) | (__pyx_t_8*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely((__pyx_t_9 < 0))) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_10 = (!__pyx_t_9);
+ if (__pyx_t_10) {
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(Py_False);
+ __pyx_r = Py_False;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ /*else*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(Py_True);
+ __pyx_r = Py_True;
+ goto __pyx_L0;
+ }
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1380
+ *
+ *
+ * def _is_linelike(segment): # <<<<<<<<<<<<<<
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline)
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_86_is_linelike(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segment) {
+ PyObject *__pyx_v_maybeline = NULL;
+ PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_is_linelike_2generator5 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_is_linelike", 0);
+
+ /* "fontTools/misc/bezierTools.py":1381
+ *
+ * def _is_linelike(segment):
+ * maybeline = _alignment_transformation(segment).transformPoints(segment) # <<<<<<<<<<<<<<
+ * return all(math.isclose(p[1], 0.0) for p in maybeline)
+ *
+*/
+ __pyx_t_4 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_alignment_transformation); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1381, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_4);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_v_segment};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1381, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_t_2 = __pyx_t_3;
+ __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_6 = 0;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_segment};
+ __pyx_t_1 = __Pyx_PyObject_FastCallMethod((PyObject*)__pyx_mstate_global->__pyx_n_u_transformPoints, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (1*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1381, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_maybeline = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1382
+ * def _is_linelike(segment):
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __pyx_pf_9fontTools_4misc_11bezierTools_12_is_linelike_genexpr(NULL, __pyx_v_maybeline); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_Generator_GetInlinedResult(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1380
+ *
+ *
+ * def _is_linelike(segment): # <<<<<<<<<<<<<<
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline)
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._is_linelike", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_maybeline);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_4misc_11bezierTools_12_is_linelike_2generator5);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1385
+ *
+ *
+ * def curveCurveIntersections(curve1, curve2): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a curve.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_89curveCurveIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_88curveCurveIntersections, "curveCurveIntersections(curve1, curve2)\n\nFinds intersections between a curve and a curve.\n\nArgs:\n curve1: List of coordinates of the first curve segment as 2D tuples.\n curve2: List of coordinates of the second curve segment as 2D tuples.\n\nReturns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\nExamples::\n >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]\n >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]\n >>> intersections = curveCurveIntersections(curve1, curve2)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (81.7831487395506, 109.88904552375288)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_89curveCurveIntersections = {"curveCurveIntersections", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_89curveCurveIntersections, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_88curveCurveIntersections};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_89curveCurveIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_curve1 = 0;
+ PyObject *__pyx_v_curve2 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("curveCurveIntersections (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curve1,&__pyx_mstate_global->__pyx_n_u_curve2,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1385, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1385, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1385, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "curveCurveIntersections", 0) < (0)) __PYX_ERR(0, 1385, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("curveCurveIntersections", 1, 2, 2, i); __PYX_ERR(0, 1385, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1385, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1385, __pyx_L3_error)
+ }
+ __pyx_v_curve1 = values[0];
+ __pyx_v_curve2 = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("curveCurveIntersections", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1385, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.curveCurveIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_88curveCurveIntersections(__pyx_self, __pyx_v_curve1, __pyx_v_curve2);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_88curveCurveIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve1, PyObject *__pyx_v_curve2) {
+ PyObject *__pyx_v_line1 = NULL;
+ PyObject *__pyx_v_line2 = NULL;
+ PyObject *__pyx_v_hits = NULL;
+ PyObject *__pyx_v_intersection_ts = NULL;
+ PyObject *__pyx_8genexpr8__pyx_v_x = NULL;
+ PyObject *__pyx_8genexpr9__pyx_v_ts = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ size_t __pyx_t_4;
+ int __pyx_t_5;
+ Py_ssize_t __pyx_t_6;
+ PyObject *(*__pyx_t_7)(PyObject *);
+ PyObject *__pyx_t_8 = NULL;
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ PyObject *__pyx_t_13 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("curveCurveIntersections", 0);
+
+ /* "fontTools/misc/bezierTools.py":1406
+ * (81.7831487395506, 109.88904552375288)
+ * """
+ * if _is_linelike(curve1): # <<<<<<<<<<<<<<
+ * line1 = curve1[0], curve1[-1]
+ * if _is_linelike(curve2):
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_is_linelike); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1406, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_curve1};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1406, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1406, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1407
+ * """
+ * if _is_linelike(curve1):
+ * line1 = curve1[0], curve1[-1] # <<<<<<<<<<<<<<
+ * if _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1]
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curve1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1407, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_v_curve1, -1L, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1407, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1407, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 1407, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 1407, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_v_line1 = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1408
+ * if _is_linelike(curve1):
+ * line1 = curve1[0], curve1[-1]
+ * if _is_linelike(curve2): # <<<<<<<<<<<<<<
+ * line2 = curve2[0], curve2[-1]
+ * return lineLineIntersections(*line1, *line2)
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_is_linelike); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1408, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_v_curve2};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1408, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1408, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1409
+ * line1 = curve1[0], curve1[-1]
+ * if _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1] # <<<<<<<<<<<<<<
+ * return lineLineIntersections(*line1, *line2)
+ * else:
+*/
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_curve2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1409, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curve2, -1L, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1409, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1409, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1409, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1409, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_line2 = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1410
+ * if _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1]
+ * return lineLineIntersections(*line1, *line2) # <<<<<<<<<<<<<<
+ * else:
+ * hits = curveLineIntersections(curve2, line1)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_lineLineIntersections); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1410, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_1 = PyNumber_Add(__pyx_v_line1, __pyx_v_line2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1410, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_1, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1410, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1408
+ * if _is_linelike(curve1):
+ * line1 = curve1[0], curve1[-1]
+ * if _is_linelike(curve2): # <<<<<<<<<<<<<<
+ * line2 = curve2[0], curve2[-1]
+ * return lineLineIntersections(*line1, *line2)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1412
+ * return lineLineIntersections(*line1, *line2)
+ * else:
+ * hits = curveLineIntersections(curve2, line1) # <<<<<<<<<<<<<<
+ * # curve is passed first to this fn but is the second segment, so
+ * # we need to swap t1/t2 in the result
+*/
+ /*else*/ {
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_mstate_global->__pyx_n_u_curveLineIntersections); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1412, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_3))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_3);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_3);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_3, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_1, __pyx_v_curve2, __pyx_v_line1};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_3, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1412, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_v_hits = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1415
+ * # curve is passed first to this fn but is the second segment, so
+ * # we need to swap t1/t2 in the result
+ * return [Intersection(pt=x.pt, t1=x.t2, t2=x.t1) for x in hits] # <<<<<<<<<<<<<<
+ * elif _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1]
+*/
+ __Pyx_XDECREF(__pyx_r);
+ { /* enter inner scope */
+ __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (likely(PyList_CheckExact(__pyx_v_hits)) || PyTuple_CheckExact(__pyx_v_hits)) {
+ __pyx_t_3 = __pyx_v_hits; __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ } else {
+ __pyx_t_6 = -1; __pyx_t_3 = PyObject_GetIter(__pyx_v_hits); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_7)) {
+ if (likely(PyList_CheckExact(__pyx_t_3))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_3);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1415, __pyx_L7_error)
+ #endif
+ if (__pyx_t_6 >= __pyx_temp) break;
+ }
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(__pyx_t_3, __pyx_t_6, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_6;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_3);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1415, __pyx_L7_error)
+ #endif
+ if (__pyx_t_6 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_1 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_3, __pyx_t_6));
+ #else
+ __pyx_t_1 = __Pyx_PySequence_ITEM(__pyx_t_3, __pyx_t_6);
+ #endif
+ ++__pyx_t_6;
+ }
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ } else {
+ __pyx_t_1 = __pyx_t_7(__pyx_t_3);
+ if (unlikely(!__pyx_t_1)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1415, __pyx_L7_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_8genexpr8__pyx_v_x, __pyx_t_1);
+ __pyx_t_1 = 0;
+ __pyx_t_8 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_9, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr8__pyx_v_x, __pyx_mstate_global->__pyx_n_u_pt); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_11 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr8__pyx_v_x, __pyx_mstate_global->__pyx_n_u_t2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_12 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr8__pyx_v_x, __pyx_mstate_global->__pyx_n_u_t1); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_9))) {
+ __pyx_t_8 = PyMethod_GET_SELF(__pyx_t_9);
+ assert(__pyx_t_8);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_9);
+ __Pyx_INCREF(__pyx_t_8);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_9, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_8, NULL};
+ __pyx_t_13 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_t_10, __pyx_t_13, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_11, __pyx_t_13, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_12, __pyx_t_13, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __pyx_t_1 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_9, __pyx_callargs+__pyx_t_4, (1-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_13);
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_1))) __PYX_ERR(0, 1415, __pyx_L7_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr8__pyx_v_x); __pyx_8genexpr8__pyx_v_x = 0;
+ goto __pyx_L11_exit_scope;
+ __pyx_L7_error:;
+ __Pyx_XDECREF(__pyx_8genexpr8__pyx_v_x); __pyx_8genexpr8__pyx_v_x = 0;
+ goto __pyx_L1_error;
+ __pyx_L11_exit_scope:;
+ } /* exit inner scope */
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1406
+ * (81.7831487395506, 109.88904552375288)
+ * """
+ * if _is_linelike(curve1): # <<<<<<<<<<<<<<
+ * line1 = curve1[0], curve1[-1]
+ * if _is_linelike(curve2):
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1416
+ * # we need to swap t1/t2 in the result
+ * return [Intersection(pt=x.pt, t1=x.t2, t2=x.t1) for x in hits]
+ * elif _is_linelike(curve2): # <<<<<<<<<<<<<<
+ * line2 = curve2[0], curve2[-1]
+ * return curveLineIntersections(curve1, line2)
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_is_linelike); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1416, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_3, __pyx_v_curve2};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (2-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1416, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely((__pyx_t_5 < 0))) __PYX_ERR(0, 1416, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_5) {
+
+ /* "fontTools/misc/bezierTools.py":1417
+ * return [Intersection(pt=x.pt, t1=x.t2, t2=x.t1) for x in hits]
+ * elif _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1] # <<<<<<<<<<<<<<
+ * return curveLineIntersections(curve1, line2)
+ *
+*/
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_curve2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1417, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curve2, -1L, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1417, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1417, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_2);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2) != (0)) __PYX_ERR(0, 1417, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_1) != (0)) __PYX_ERR(0, 1417, __pyx_L1_error);
+ __pyx_t_2 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_line2 = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1418
+ * elif _is_linelike(curve2):
+ * line2 = curve2[0], curve2[-1]
+ * return curveLineIntersections(curve1, line2) # <<<<<<<<<<<<<<
+ *
+ * intersection_ts = _curve_curve_intersections_t(curve1, curve2)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_curveLineIntersections); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1418, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_2))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_2);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_2);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_2, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_1, __pyx_v_curve1, __pyx_v_line2};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_2, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1418, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1416
+ * # we need to swap t1/t2 in the result
+ * return [Intersection(pt=x.pt, t1=x.t2, t2=x.t1) for x in hits]
+ * elif _is_linelike(curve2): # <<<<<<<<<<<<<<
+ * line2 = curve2[0], curve2[-1]
+ * return curveLineIntersections(curve1, line2)
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1420
+ * return curveLineIntersections(curve1, line2)
+ *
+ * intersection_ts = _curve_curve_intersections_t(curve1, curve2) # <<<<<<<<<<<<<<
+ * return [
+ * Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+*/
+ __pyx_t_2 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1420, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_2);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_2, __pyx_v_curve1, __pyx_v_curve2};
+ __pyx_t_3 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1420, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ }
+ __pyx_v_intersection_ts = __pyx_t_3;
+ __pyx_t_3 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1421
+ *
+ * intersection_ts = _curve_curve_intersections_t(curve1, curve2)
+ * return [ # <<<<<<<<<<<<<<
+ * Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+ * for ts in intersection_ts
+*/
+ __Pyx_XDECREF(__pyx_r);
+ { /* enter inner scope */
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1421, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_3);
+
+ /* "fontTools/misc/bezierTools.py":1423
+ * return [
+ * Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+ * for ts in intersection_ts # <<<<<<<<<<<<<<
+ * ]
+ *
+*/
+ if (likely(PyList_CheckExact(__pyx_v_intersection_ts)) || PyTuple_CheckExact(__pyx_v_intersection_ts)) {
+ __pyx_t_1 = __pyx_v_intersection_ts; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ } else {
+ __pyx_t_6 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_v_intersection_ts); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1423, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1423, __pyx_L14_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_7)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1423, __pyx_L14_error)
+ #endif
+ if (__pyx_t_6 >= __pyx_temp) break;
+ }
+ __pyx_t_2 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_6, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_6;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1423, __pyx_L14_error)
+ #endif
+ if (__pyx_t_6 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_2 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_6));
+ #else
+ __pyx_t_2 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_6);
+ #endif
+ ++__pyx_t_6;
+ }
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1423, __pyx_L14_error)
+ } else {
+ __pyx_t_2 = __pyx_t_7(__pyx_t_1);
+ if (unlikely(!__pyx_t_2)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1423, __pyx_L14_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_XDECREF_SET(__pyx_8genexpr9__pyx_v_ts, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1422
+ * intersection_ts = _curve_curve_intersections_t(curve1, curve2)
+ * return [
+ * Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1]) # <<<<<<<<<<<<<<
+ * for ts in intersection_ts
+ * ]
+*/
+ __pyx_t_9 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_13, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ __pyx_t_11 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_10, __pyx_mstate_global->__pyx_n_u_segmentPointAtT); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_8 = __Pyx_GetItemInt(__pyx_8genexpr9__pyx_v_ts, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_SharedReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_10))) {
+ __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_10);
+ assert(__pyx_t_11);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_10);
+ __Pyx_INCREF(__pyx_t_11);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_10, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_11, __pyx_v_curve1, __pyx_t_8};
+ __pyx_t_12 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_10, __pyx_callargs+__pyx_t_4, (3-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ }
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_8genexpr9__pyx_v_ts, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_SharedReference); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_8 = __Pyx_GetItemInt(__pyx_8genexpr9__pyx_v_ts, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_SharedReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_4 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_13))) {
+ __pyx_t_9 = PyMethod_GET_SELF(__pyx_t_13);
+ assert(__pyx_t_9);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_13);
+ __Pyx_INCREF(__pyx_t_9);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_13, __pyx__function);
+ __pyx_t_4 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_9, NULL};
+ __pyx_t_11 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_t_12, __pyx_t_11, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_10, __pyx_t_11, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_8, __pyx_t_11, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __pyx_t_2 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_13, __pyx_callargs+__pyx_t_4, (1-__pyx_t_4) | (__pyx_t_4*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1422, __pyx_L14_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_3, (PyObject*)__pyx_t_2))) __PYX_ERR(0, 1421, __pyx_L14_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1423
+ * return [
+ * Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+ * for ts in intersection_ts # <<<<<<<<<<<<<<
+ * ]
+ *
+*/
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr9__pyx_v_ts); __pyx_8genexpr9__pyx_v_ts = 0;
+ goto __pyx_L18_exit_scope;
+ __pyx_L14_error:;
+ __Pyx_XDECREF(__pyx_8genexpr9__pyx_v_ts); __pyx_8genexpr9__pyx_v_ts = 0;
+ goto __pyx_L1_error;
+ __pyx_L18_exit_scope:;
+ } /* exit inner scope */
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1385
+ *
+ *
+ * def curveCurveIntersections(curve1, curve2): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a curve.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_XDECREF(__pyx_t_13);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.curveCurveIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_line1);
+ __Pyx_XDECREF(__pyx_v_line2);
+ __Pyx_XDECREF(__pyx_v_hits);
+ __Pyx_XDECREF(__pyx_v_intersection_ts);
+ __Pyx_XDECREF(__pyx_8genexpr8__pyx_v_x);
+ __Pyx_XDECREF(__pyx_8genexpr9__pyx_v_ts);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1427
+ *
+ *
+ * def segmentSegmentIntersections(seg1, seg2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two segments.
+ *
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_91segmentSegmentIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_90segmentSegmentIntersections, "segmentSegmentIntersections(seg1, seg2)\n\nFinds intersections between two segments.\n\nArgs:\n seg1: List of coordinates of the first segment as 2D tuples.\n seg2: List of coordinates of the second segment as 2D tuples.\n\nReturns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\nExamples::\n >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]\n >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]\n >>> intersections = segmentSegmentIntersections(curve1, curve2)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (81.7831487395506, 109.88904552375288)\n >>> curve3 = [ (100, 240), (30, 60), (210, 230), (160, 30) ]\n >>> line = [ (25, 260), (230, 20) ]\n >>> intersections = segmentSegmentIntersections(curve3, line)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (84.9000930760723, 189.87306176459828)");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_91segmentSegmentIntersections = {"segmentSegmentIntersections", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_91segmentSegmentIntersections, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_90segmentSegmentIntersections};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_91segmentSegmentIntersections(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_seg1 = 0;
+ PyObject *__pyx_v_seg2 = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[2] = {0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("segmentSegmentIntersections (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_seg1,&__pyx_mstate_global->__pyx_n_u_seg2,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1427, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1427, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1427, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "segmentSegmentIntersections", 0) < (0)) __PYX_ERR(0, 1427, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("segmentSegmentIntersections", 1, 2, 2, i); __PYX_ERR(0, 1427, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 2)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1427, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 1427, __pyx_L3_error)
+ }
+ __pyx_v_seg1 = values[0];
+ __pyx_v_seg2 = values[1];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("segmentSegmentIntersections", 1, 2, 2, __pyx_nargs); __PYX_ERR(0, 1427, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.segmentSegmentIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_90segmentSegmentIntersections(__pyx_self, __pyx_v_seg1, __pyx_v_seg2);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_90segmentSegmentIntersections(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_seg1, PyObject *__pyx_v_seg2) {
+ int __pyx_v_swapped;
+ PyObject *__pyx_v_intersections = NULL;
+ PyObject *__pyx_9genexpr10__pyx_v_i = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ Py_ssize_t __pyx_t_2;
+ int __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_t_10;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *(*__pyx_t_12)(PyObject *);
+ PyObject *__pyx_t_13 = NULL;
+ PyObject *__pyx_t_14 = NULL;
+ PyObject *__pyx_t_15 = NULL;
+ PyObject *__pyx_t_16 = NULL;
+ PyObject *__pyx_t_17 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("segmentSegmentIntersections", 0);
+ __Pyx_INCREF(__pyx_v_seg1);
+ __Pyx_INCREF(__pyx_v_seg2);
+
+ /* "fontTools/misc/bezierTools.py":1457
+ * """
+ * # Arrange by degree
+ * swapped = False # <<<<<<<<<<<<<<
+ * if len(seg2) > len(seg1):
+ * seg2, seg1 = seg1, seg2
+*/
+ __pyx_v_swapped = 0;
+
+ /* "fontTools/misc/bezierTools.py":1458
+ * # Arrange by degree
+ * swapped = False
+ * if len(seg2) > len(seg1): # <<<<<<<<<<<<<<
+ * seg2, seg1 = seg1, seg2
+ * swapped = True
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_seg2); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1458, __pyx_L1_error)
+ __pyx_t_2 = PyObject_Length(__pyx_v_seg1); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1458, __pyx_L1_error)
+ __pyx_t_3 = (__pyx_t_1 > __pyx_t_2);
+ if (__pyx_t_3) {
+
+ /* "fontTools/misc/bezierTools.py":1459
+ * swapped = False
+ * if len(seg2) > len(seg1):
+ * seg2, seg1 = seg1, seg2 # <<<<<<<<<<<<<<
+ * swapped = True
+ * if len(seg1) > 2:
+*/
+ __pyx_t_4 = __pyx_v_seg1;
+ __pyx_t_5 = __pyx_v_seg2;
+ __pyx_v_seg2 = __pyx_t_4;
+ __pyx_t_4 = 0;
+ __pyx_v_seg1 = __pyx_t_5;
+ __pyx_t_5 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1460
+ * if len(seg2) > len(seg1):
+ * seg2, seg1 = seg1, seg2
+ * swapped = True # <<<<<<<<<<<<<<
+ * if len(seg1) > 2:
+ * if len(seg2) > 2:
+*/
+ __pyx_v_swapped = 1;
+
+ /* "fontTools/misc/bezierTools.py":1458
+ * # Arrange by degree
+ * swapped = False
+ * if len(seg2) > len(seg1): # <<<<<<<<<<<<<<
+ * seg2, seg1 = seg1, seg2
+ * swapped = True
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1461
+ * seg2, seg1 = seg1, seg2
+ * swapped = True
+ * if len(seg1) > 2: # <<<<<<<<<<<<<<
+ * if len(seg2) > 2:
+ * intersections = curveCurveIntersections(seg1, seg2)
+*/
+ __pyx_t_2 = PyObject_Length(__pyx_v_seg1); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1461, __pyx_L1_error)
+ __pyx_t_3 = (__pyx_t_2 > 2);
+ if (__pyx_t_3) {
+
+ /* "fontTools/misc/bezierTools.py":1462
+ * swapped = True
+ * if len(seg1) > 2:
+ * if len(seg2) > 2: # <<<<<<<<<<<<<<
+ * intersections = curveCurveIntersections(seg1, seg2)
+ * else:
+*/
+ __pyx_t_2 = PyObject_Length(__pyx_v_seg2); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1462, __pyx_L1_error)
+ __pyx_t_3 = (__pyx_t_2 > 2);
+ if (__pyx_t_3) {
+
+ /* "fontTools/misc/bezierTools.py":1463
+ * if len(seg1) > 2:
+ * if len(seg2) > 2:
+ * intersections = curveCurveIntersections(seg1, seg2) # <<<<<<<<<<<<<<
+ * else:
+ * intersections = curveLineIntersections(seg1, seg2)
+*/
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_curveCurveIntersections); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1463, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_8))) {
+ __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_8);
+ assert(__pyx_t_7);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_8);
+ __Pyx_INCREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_8, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_7, __pyx_v_seg1, __pyx_v_seg2};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_8, __pyx_callargs+__pyx_t_9, (3-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1463, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_v_intersections = __pyx_t_6;
+ __pyx_t_6 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1462
+ * swapped = True
+ * if len(seg1) > 2:
+ * if len(seg2) > 2: # <<<<<<<<<<<<<<
+ * intersections = curveCurveIntersections(seg1, seg2)
+ * else:
+*/
+ goto __pyx_L5;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1465
+ * intersections = curveCurveIntersections(seg1, seg2)
+ * else:
+ * intersections = curveLineIntersections(seg1, seg2) # <<<<<<<<<<<<<<
+ * elif len(seg1) == 2 and len(seg2) == 2:
+ * intersections = lineLineIntersections(*seg1, *seg2)
+*/
+ /*else*/ {
+ __pyx_t_8 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_curveLineIntersections); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1465, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_7))) {
+ __pyx_t_8 = PyMethod_GET_SELF(__pyx_t_7);
+ assert(__pyx_t_8);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_7);
+ __Pyx_INCREF(__pyx_t_8);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_7, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_8, __pyx_v_seg1, __pyx_v_seg2};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_7, __pyx_callargs+__pyx_t_9, (3-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1465, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_v_intersections = __pyx_t_6;
+ __pyx_t_6 = 0;
+ }
+ __pyx_L5:;
+
+ /* "fontTools/misc/bezierTools.py":1461
+ * seg2, seg1 = seg1, seg2
+ * swapped = True
+ * if len(seg1) > 2: # <<<<<<<<<<<<<<
+ * if len(seg2) > 2:
+ * intersections = curveCurveIntersections(seg1, seg2)
+*/
+ goto __pyx_L4;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1466
+ * else:
+ * intersections = curveLineIntersections(seg1, seg2)
+ * elif len(seg1) == 2 and len(seg2) == 2: # <<<<<<<<<<<<<<
+ * intersections = lineLineIntersections(*seg1, *seg2)
+ * else:
+*/
+ __pyx_t_2 = PyObject_Length(__pyx_v_seg1); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1466, __pyx_L1_error)
+ __pyx_t_10 = (__pyx_t_2 == 2);
+ if (__pyx_t_10) {
+ } else {
+ __pyx_t_3 = __pyx_t_10;
+ goto __pyx_L6_bool_binop_done;
+ }
+ __pyx_t_2 = PyObject_Length(__pyx_v_seg2); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 1466, __pyx_L1_error)
+ __pyx_t_10 = (__pyx_t_2 == 2);
+ __pyx_t_3 = __pyx_t_10;
+ __pyx_L6_bool_binop_done:;
+ if (likely(__pyx_t_3)) {
+
+ /* "fontTools/misc/bezierTools.py":1467
+ * intersections = curveLineIntersections(seg1, seg2)
+ * elif len(seg1) == 2 and len(seg2) == 2:
+ * intersections = lineLineIntersections(*seg1, *seg2) # <<<<<<<<<<<<<<
+ * else:
+ * raise ValueError("Couldn't work out which intersection function to use")
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_lineLineIntersections); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1467, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = __Pyx_PySequence_Tuple(__pyx_v_seg1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 1467, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __Pyx_PySequence_Tuple(__pyx_v_seg2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1467, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_11 = PyNumber_Add(__pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1467, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PyObject_Call(__pyx_t_6, __pyx_t_11, NULL); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1467, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __pyx_v_intersections = __pyx_t_8;
+ __pyx_t_8 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1466
+ * else:
+ * intersections = curveLineIntersections(seg1, seg2)
+ * elif len(seg1) == 2 and len(seg2) == 2: # <<<<<<<<<<<<<<
+ * intersections = lineLineIntersections(*seg1, *seg2)
+ * else:
+*/
+ goto __pyx_L4;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1469
+ * intersections = lineLineIntersections(*seg1, *seg2)
+ * else:
+ * raise ValueError("Couldn't work out which intersection function to use") # <<<<<<<<<<<<<<
+ * if not swapped:
+ * return intersections
+*/
+ /*else*/ {
+ __pyx_t_11 = NULL;
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_11, __pyx_mstate_global->__pyx_kp_u_Couldn_t_work_out_which_intersec};
+ __pyx_t_8 = __Pyx_PyObject_FastCall((PyObject*)(((PyTypeObject*)PyExc_ValueError)), __pyx_callargs+__pyx_t_9, (2-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1469, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ }
+ __Pyx_Raise(__pyx_t_8, 0, 0, 0);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __PYX_ERR(0, 1469, __pyx_L1_error)
+ }
+ __pyx_L4:;
+
+ /* "fontTools/misc/bezierTools.py":1470
+ * else:
+ * raise ValueError("Couldn't work out which intersection function to use")
+ * if not swapped: # <<<<<<<<<<<<<<
+ * return intersections
+ * return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
+*/
+ __pyx_t_3 = (!__pyx_v_swapped);
+ if (__pyx_t_3) {
+
+ /* "fontTools/misc/bezierTools.py":1471
+ * raise ValueError("Couldn't work out which intersection function to use")
+ * if not swapped:
+ * return intersections # <<<<<<<<<<<<<<
+ * return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_intersections);
+ __pyx_r = __pyx_v_intersections;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1470
+ * else:
+ * raise ValueError("Couldn't work out which intersection function to use")
+ * if not swapped: # <<<<<<<<<<<<<<
+ * return intersections
+ * return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1472
+ * if not swapped:
+ * return intersections
+ * return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ { /* enter inner scope */
+ __pyx_t_8 = PyList_New(0); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (likely(PyList_CheckExact(__pyx_v_intersections)) || PyTuple_CheckExact(__pyx_v_intersections)) {
+ __pyx_t_11 = __pyx_v_intersections; __Pyx_INCREF(__pyx_t_11);
+ __pyx_t_2 = 0;
+ __pyx_t_12 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_11 = PyObject_GetIter(__pyx_v_intersections); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ __pyx_t_12 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_11); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_12)) {
+ if (likely(PyList_CheckExact(__pyx_t_11))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_11);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1472, __pyx_L11_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(__pyx_t_11, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_11);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1472, __pyx_L11_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_6 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_11, __pyx_t_2));
+ #else
+ __pyx_t_6 = __Pyx_PySequence_ITEM(__pyx_t_11, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ } else {
+ __pyx_t_6 = __pyx_t_12(__pyx_t_11);
+ if (unlikely(!__pyx_t_6)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1472, __pyx_L11_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_XDECREF_SET(__pyx_9genexpr10__pyx_v_i, __pyx_t_6);
+ __pyx_t_6 = 0;
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_13, __pyx_mstate_global->__pyx_n_u_Intersection); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ __pyx_t_14 = __Pyx_PyObject_GetAttrStr(__pyx_9genexpr10__pyx_v_i, __pyx_mstate_global->__pyx_n_u_pt); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_14);
+ __pyx_t_15 = __Pyx_PyObject_GetAttrStr(__pyx_9genexpr10__pyx_v_i, __pyx_mstate_global->__pyx_n_u_t2); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_15);
+ __pyx_t_16 = __Pyx_PyObject_GetAttrStr(__pyx_9genexpr10__pyx_v_i, __pyx_mstate_global->__pyx_n_u_t1); if (unlikely(!__pyx_t_16)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_16);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_13))) {
+ __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_13);
+ assert(__pyx_t_7);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_13);
+ __Pyx_INCREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_13, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2 + ((CYTHON_VECTORCALL) ? 3 : 0)] = {__pyx_t_7, NULL};
+ __pyx_t_17 = __Pyx_MakeVectorcallBuilderKwds(3); if (unlikely(!__pyx_t_17)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_17);
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_pt, __pyx_t_14, __pyx_t_17, __pyx_callargs+1, 0) < (0)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t1, __pyx_t_15, __pyx_t_17, __pyx_callargs+1, 1) < (0)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ if (__Pyx_VectorcallBuilder_AddArg(__pyx_mstate_global->__pyx_n_u_t2, __pyx_t_16, __pyx_t_17, __pyx_callargs+1, 2) < (0)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __pyx_t_6 = __Pyx_Object_Vectorcall_CallFromBuilder((PyObject*)__pyx_t_13, __pyx_callargs+__pyx_t_9, (1-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET), __pyx_t_17);
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0;
+ __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0;
+ __Pyx_DECREF(__pyx_t_16); __pyx_t_16 = 0;
+ __Pyx_DECREF(__pyx_t_17); __pyx_t_17 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_8, (PyObject*)__pyx_t_6))) __PYX_ERR(0, 1472, __pyx_L11_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_XDECREF(__pyx_9genexpr10__pyx_v_i); __pyx_9genexpr10__pyx_v_i = 0;
+ goto __pyx_L15_exit_scope;
+ __pyx_L11_error:;
+ __Pyx_XDECREF(__pyx_9genexpr10__pyx_v_i); __pyx_9genexpr10__pyx_v_i = 0;
+ goto __pyx_L1_error;
+ __pyx_L15_exit_scope:;
+ } /* exit inner scope */
+ __pyx_r = __pyx_t_8;
+ __pyx_t_8 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/misc/bezierTools.py":1427
+ *
+ *
+ * def segmentSegmentIntersections(seg1, seg2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two segments.
+ *
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_13);
+ __Pyx_XDECREF(__pyx_t_14);
+ __Pyx_XDECREF(__pyx_t_15);
+ __Pyx_XDECREF(__pyx_t_16);
+ __Pyx_XDECREF(__pyx_t_17);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.segmentSegmentIntersections", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_intersections);
+ __Pyx_XDECREF(__pyx_9genexpr10__pyx_v_i);
+ __Pyx_XDECREF(__pyx_v_seg1);
+ __Pyx_XDECREF(__pyx_v_seg2);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1475
+ *
+ *
+ * def _segmentrepr(obj): # <<<<<<<<<<<<<<
+ * """
+ * >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_93_segmentrepr(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_92_segmentrepr, "_segmentrepr(obj)\n\n>>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])\n'(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_93_segmentrepr = {"_segmentrepr", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_93_segmentrepr, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_92_segmentrepr};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_93_segmentrepr(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_obj = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("_segmentrepr (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_obj,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1475, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1475, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_segmentrepr", 0) < (0)) __PYX_ERR(0, 1475, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_segmentrepr", 1, 1, 1, i); __PYX_ERR(0, 1475, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1475, __pyx_L3_error)
+ }
+ __pyx_v_obj = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("_segmentrepr", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1475, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._segmentrepr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_92_segmentrepr(__pyx_self, __pyx_v_obj);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_segmentrepr_2generator6(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/misc/bezierTools.py":1485
+ * return "%g" % obj
+ * else:
+ * return "(%s)" % ", ".join(_segmentrepr(x) for x in it) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_12_segmentrepr_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 1485, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_4misc_11bezierTools_12_segmentrepr_2generator6, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[8]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_segmentrepr_locals_genexpr, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools); if (unlikely(!gen)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._segmentrepr.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_segmentrepr_2generator6(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ size_t __pyx_t_7;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __pyx_r = PyList_New(0); if (unlikely(!__pyx_r)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_r);
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 1485, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1485, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1485, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1485, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_x);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_x, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_segmentrepr); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_7 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_cur_scope->__pyx_v_x};
+ __pyx_t_4 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_7, (2-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ if (unlikely(__Pyx_ListComp_Append(__pyx_r, (PyObject*)__pyx_t_4))) __PYX_ERR(0, 1485, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_r); __pyx_r = 0;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1475
+ *
+ *
+ * def _segmentrepr(obj): # <<<<<<<<<<<<<<
+ * """
+ * >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+*/
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_92_segmentrepr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_obj) {
+ PyObject *__pyx_v_it = NULL;
+ PyObject *__pyx_gb_9fontTools_4misc_11bezierTools_12_segmentrepr_2generator6 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("_segmentrepr", 0);
+
+ /* "fontTools/misc/bezierTools.py":1480
+ * '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
+ * """
+ * try: # <<<<<<<<<<<<<<
+ * it = iter(obj)
+ * except TypeError:
+*/
+ {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ __Pyx_ExceptionSave(&__pyx_t_1, &__pyx_t_2, &__pyx_t_3);
+ __Pyx_XGOTREF(__pyx_t_1);
+ __Pyx_XGOTREF(__pyx_t_2);
+ __Pyx_XGOTREF(__pyx_t_3);
+ /*try:*/ {
+
+ /* "fontTools/misc/bezierTools.py":1481
+ * """
+ * try:
+ * it = iter(obj) # <<<<<<<<<<<<<<
+ * except TypeError:
+ * return "%g" % obj
+*/
+ __pyx_t_4 = PyObject_GetIter(__pyx_v_obj); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1481, __pyx_L3_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_v_it = __pyx_t_4;
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1480
+ * '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
+ * """
+ * try: # <<<<<<<<<<<<<<
+ * it = iter(obj)
+ * except TypeError:
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1485
+ * return "%g" % obj
+ * else:
+ * return "(%s)" % ", ".join(_segmentrepr(x) for x in it) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ /*else:*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __pyx_pf_9fontTools_4misc_11bezierTools_12_segmentrepr_genexpr(NULL, __pyx_v_it); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1485, __pyx_L5_except_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_Generator_GetInlinedResult(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1485, __pyx_L5_except_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = PyUnicode_Join(__pyx_mstate_global->__pyx_kp_u_, __pyx_t_5); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1485, __pyx_L5_except_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = PyUnicode_Format(__pyx_mstate_global->__pyx_kp_u_s_2, __pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1485, __pyx_L5_except_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L6_except_return;
+ }
+ __pyx_L3_error:;
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1482
+ * try:
+ * it = iter(obj)
+ * except TypeError: # <<<<<<<<<<<<<<
+ * return "%g" % obj
+ * else:
+*/
+ __pyx_t_6 = __Pyx_PyErr_ExceptionMatches(((PyObject *)(((PyTypeObject*)PyExc_TypeError))));
+ if (__pyx_t_6) {
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._segmentrepr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ if (__Pyx_GetException(&__pyx_t_5, &__pyx_t_4, &__pyx_t_7) < 0) __PYX_ERR(0, 1482, __pyx_L5_except_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ __Pyx_XGOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_t_7);
+
+ /* "fontTools/misc/bezierTools.py":1483
+ * it = iter(obj)
+ * except TypeError:
+ * return "%g" % obj # <<<<<<<<<<<<<<
+ * else:
+ * return "(%s)" % ", ".join(_segmentrepr(x) for x in it)
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_8 = __Pyx_PyUnicode_FormatSafe(__pyx_mstate_global->__pyx_kp_u_g, __pyx_v_obj); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1483, __pyx_L5_except_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_r = __pyx_t_8;
+ __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L6_except_return;
+ }
+ goto __pyx_L5_except_error;
+
+ /* "fontTools/misc/bezierTools.py":1480
+ * '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
+ * """
+ * try: # <<<<<<<<<<<<<<
+ * it = iter(obj)
+ * except TypeError:
+*/
+ __pyx_L5_except_error:;
+ __Pyx_XGIVEREF(__pyx_t_1);
+ __Pyx_XGIVEREF(__pyx_t_2);
+ __Pyx_XGIVEREF(__pyx_t_3);
+ __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3);
+ goto __pyx_L1_error;
+ __pyx_L6_except_return:;
+ __Pyx_XGIVEREF(__pyx_t_1);
+ __Pyx_XGIVEREF(__pyx_t_2);
+ __Pyx_XGIVEREF(__pyx_t_3);
+ __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3);
+ goto __pyx_L0;
+ }
+
+ /* "fontTools/misc/bezierTools.py":1475
+ *
+ *
+ * def _segmentrepr(obj): # <<<<<<<<<<<<<<
+ * """
+ * >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools._segmentrepr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_it);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_4misc_11bezierTools_12_segmentrepr_2generator6);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/misc/bezierTools.py":1488
+ *
+ *
+ * def printSegments(segments): # <<<<<<<<<<<<<<
+ * """Helper for the doctests, displaying each segment in a list of
+ * segments on a single line as a tuple.
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_95printSegments(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_4misc_11bezierTools_94printSegments, "printSegments(segments)\n\nHelper for the doctests, displaying each segment in a list of\nsegments on a single line as a tuple.");
+static PyMethodDef __pyx_mdef_9fontTools_4misc_11bezierTools_95printSegments = {"printSegments", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_4misc_11bezierTools_95printSegments, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_4misc_11bezierTools_94printSegments};
+static PyObject *__pyx_pw_9fontTools_4misc_11bezierTools_95printSegments(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_segments = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("printSegments (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_segments,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 1488, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1488, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "printSegments", 0) < (0)) __PYX_ERR(0, 1488, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("printSegments", 1, 1, 1, i); __PYX_ERR(0, 1488, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 1488, __pyx_L3_error)
+ }
+ __pyx_v_segments = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("printSegments", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 1488, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.printSegments", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_4misc_11bezierTools_94printSegments(__pyx_self, __pyx_v_segments);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_4misc_11bezierTools_94printSegments(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_segments) {
+ PyObject *__pyx_v_segment = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("printSegments", 0);
+
+ /* "fontTools/misc/bezierTools.py":1492
+ * segments on a single line as a tuple.
+ * """
+ * for segment in segments: # <<<<<<<<<<<<<<
+ * print(_segmentrepr(segment))
+ *
+*/
+ if (likely(PyList_CheckExact(__pyx_v_segments)) || PyTuple_CheckExact(__pyx_v_segments)) {
+ __pyx_t_1 = __pyx_v_segments; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_v_segments); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1492, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 1492, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1492, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 1492, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1492, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 1492, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XDECREF_SET(__pyx_v_segment, __pyx_t_4);
+ __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1493
+ * """
+ * for segment in segments:
+ * print(_segmentrepr(segment)) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_5 = NULL;
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_segmentrepr); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1493, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_8))) {
+ __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_8);
+ assert(__pyx_t_7);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_8);
+ __Pyx_INCREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_8, __pyx__function);
+ __pyx_t_9 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_7, __pyx_v_segment};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_8, __pyx_callargs+__pyx_t_9, (2-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1493, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_t_6};
+ __pyx_t_4 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_9, (2-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1493, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1492
+ * segments on a single line as a tuple.
+ * """
+ * for segment in segments: # <<<<<<<<<<<<<<
+ * print(_segmentrepr(segment))
+ *
+*/
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1488
+ *
+ *
+ * def printSegments(segments): # <<<<<<<<<<<<<<
+ * """Helper for the doctests, displaying each segment in a list of
+ * segments on a single line as a tuple.
+*/
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.misc.bezierTools.printSegments", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_segment);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+/* #### Code section: module_exttypes ### */
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_defaults(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_defaults(PyObject *o) {
+ struct __pyx_defaults *p = (struct __pyx_defaults *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_defaults) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->arg0);
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_defaults(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_defaults *p = (struct __pyx_defaults *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->arg0) {
+ e = (*v)(p->arg0, a); if (e) return e;
+ }
+ return 0;
+}
+
+static int __pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_defaults(PyObject *o) {
+ PyObject* tmp;
+ struct __pyx_defaults *p = (struct __pyx_defaults *)o;
+ tmp = ((PyObject*)p->arg0);
+ p->arg0 = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_defaults},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_defaults},
+ {Py_tp_clear, (void *)__pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_defaults},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_defaults},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults_spec = {
+ "fontTools.misc.bezierTools.__pyx_defaults",
+ sizeof(struct __pyx_defaults),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_defaults", /*tp_name*/
+ sizeof(struct __pyx_defaults), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_defaults, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_defaults, /*tp_traverse*/
+ __pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_defaults, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_defaults, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_t);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_t) {
+ e = (*v)(p->__pyx_v_t, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct__genexpr",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct__genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_t);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_t) {
+ e = (*v)(p->__pyx_v_t, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_1_genexpr",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_1_genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_v_ts);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_v_ts) {
+ e = (*v)(p->__pyx_v_ts, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_2_splitCubicAtTC",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_2_splitCubicAtTC", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_v_i);
+ Py_CLEAR(p->__pyx_v_pt1);
+ Py_CLEAR(p->__pyx_v_pt2);
+ Py_CLEAR(p->__pyx_v_pt3);
+ Py_CLEAR(p->__pyx_v_pt4);
+ Py_CLEAR(p->__pyx_v_ts);
+ Py_CLEAR(p->__pyx_t_0);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_v_i) {
+ e = (*v)(p->__pyx_v_i, a); if (e) return e;
+ }
+ if (p->__pyx_v_pt1) {
+ e = (*v)(p->__pyx_v_pt1, a); if (e) return e;
+ }
+ if (p->__pyx_v_pt2) {
+ e = (*v)(p->__pyx_v_pt2, a); if (e) return e;
+ }
+ if (p->__pyx_v_pt3) {
+ e = (*v)(p->__pyx_v_pt3, a); if (e) return e;
+ }
+ if (p->__pyx_v_pt4) {
+ e = (*v)(p->__pyx_v_pt4, a); if (e) return e;
+ }
+ if (p->__pyx_v_ts) {
+ e = (*v)(p->__pyx_v_ts, a); if (e) return e;
+ }
+ if (p->__pyx_t_0) {
+ e = (*v)(p->__pyx_t_0, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_3__splitCubicAtTC",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_3__splitCubicAtTC", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_i);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_i) {
+ e = (*v)(p->__pyx_v_i, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_4_genexpr",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_4_genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_v_precision);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_v_precision) {
+ e = (*v)(p->__pyx_v_precision, a); if (e) return e;
+ }
+ return 0;
+}
+
+static int __pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t(PyObject *o) {
+ PyObject* tmp;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t *)o;
+ tmp = ((PyObject*)p->__pyx_v_precision);
+ p->__pyx_v_precision = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t},
+ {Py_tp_clear, (void *)__pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_5__curve_curve_intersections_t",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_5__curve_curve_intersections_t", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, /*tp_traverse*/
+ __pyx_tp_clear_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_p);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_p) {
+ e = (*v)(p->__pyx_v_p, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_6_genexpr",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_6_genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_x);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr++] = ((struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *p = (struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_x) {
+ e = (*v)(p->__pyx_v_x, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr_spec = {
+ "fontTools.misc.bezierTools.__pyx_scope_struct_7_genexpr",
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.misc.bezierTools.""__pyx_scope_struct_7_genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyMethodDef __pyx_methods[] = {
+ {0, 0, 0, 0}
+};
+/* #### Code section: initfunc_declarations ### */
+static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate); /*proto*/
+/* #### Code section: init_module ### */
+
+static int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_global_init_code", 0);
+ /*--- Global init code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_variable_export_code", 0);
+ /*--- Variable export code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_function_export_code", 0);
+ /*--- Function export code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("__Pyx_modinit_type_init_code", 0);
+ /*--- Type init code ---*/
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults)) __PYX_ERR(0, 815, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults) < (0)) __PYX_ERR(0, 815, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_defaults;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults) < (0)) __PYX_ERR(0, 815, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr)) __PYX_ERR(0, 546, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr) < (0)) __PYX_ERR(0, 546, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr) < (0)) __PYX_ERR(0, 546, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct__genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr)) __PYX_ERR(0, 583, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr) < (0)) __PYX_ERR(0, 583, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr) < (0)) __PYX_ERR(0, 583, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_1_genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC)) __PYX_ERR(0, 644, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC) < (0)) __PYX_ERR(0, 644, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC) < (0)) __PYX_ERR(0, 644, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_2_splitCubicAtTC->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC)) __PYX_ERR(0, 770, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC) < (0)) __PYX_ERR(0, 770, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC) < (0)) __PYX_ERR(0, 770, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_3__splitCubicAtTC->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr) < (0)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr) < (0)) __PYX_ERR(0, 1252, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_4_genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t)) __PYX_ERR(0, 1313, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t) < (0)) __PYX_ERR(0, 1313, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t) < (0)) __PYX_ERR(0, 1313, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_5__curve_curve_intersections_t->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr) < (0)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr) < (0)) __PYX_ERR(0, 1382, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_6_genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr) < (0)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr = &__pyx_type_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr) < (0)) __PYX_ERR(0, 1485, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_scope_struct_7_genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ __Pyx_RefNannyFinishContext();
+ return 0;
+ __pyx_L1_error:;
+ __Pyx_RefNannyFinishContext();
+ return -1;
+}
+
+static int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_type_import_code", 0);
+ /*--- Type import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_variable_import_code", 0);
+ /*--- Variable import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_function_import_code", 0);
+ /*--- Function import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+#if CYTHON_PEP489_MULTI_PHASE_INIT
+static PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def); /*proto*/
+static int __pyx_pymod_exec_bezierTools(PyObject* module); /*proto*/
+static PyModuleDef_Slot __pyx_moduledef_slots[] = {
+ {Py_mod_create, (void*)__pyx_pymod_create},
+ {Py_mod_exec, (void*)__pyx_pymod_exec_bezierTools},
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ {Py_mod_gil, Py_MOD_GIL_USED},
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000 && CYTHON_USE_MODULE_STATE
+ {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
+ #endif
+ {0, NULL}
+};
+#endif
+
+#ifdef __cplusplus
+namespace {
+ struct PyModuleDef __pyx_moduledef =
+ #else
+ static struct PyModuleDef __pyx_moduledef =
+ #endif
+ {
+ PyModuleDef_HEAD_INIT,
+ "bezierTools",
+ __pyx_k_fontTools_misc_bezierTools_py_to, /* m_doc */
+ #if CYTHON_USE_MODULE_STATE
+ sizeof(__pyx_mstatetype), /* m_size */
+ #else
+ (CYTHON_PEP489_MULTI_PHASE_INIT) ? 0 : -1, /* m_size */
+ #endif
+ __pyx_methods /* m_methods */,
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __pyx_moduledef_slots, /* m_slots */
+ #else
+ NULL, /* m_reload */
+ #endif
+ #if CYTHON_USE_MODULE_STATE
+ __pyx_m_traverse, /* m_traverse */
+ __pyx_m_clear, /* m_clear */
+ NULL /* m_free */
+ #else
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+ #endif
+ };
+ #ifdef __cplusplus
+} /* anonymous namespace */
+#endif
+
+/* PyModInitFuncType */
+#ifndef CYTHON_NO_PYINIT_EXPORT
+ #define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC
+#else
+ #ifdef __cplusplus
+ #define __Pyx_PyMODINIT_FUNC extern "C" PyObject *
+ #else
+ #define __Pyx_PyMODINIT_FUNC PyObject *
+ #endif
+#endif
+
+__Pyx_PyMODINIT_FUNC PyInit_bezierTools(void) CYTHON_SMALL_CODE; /*proto*/
+__Pyx_PyMODINIT_FUNC PyInit_bezierTools(void)
+#if CYTHON_PEP489_MULTI_PHASE_INIT
+{
+ return PyModuleDef_Init(&__pyx_moduledef);
+}
+/* ModuleCreationPEP489 */
+#if CYTHON_COMPILING_IN_LIMITED_API && (__PYX_LIMITED_VERSION_HEX < 0x03090000\
+ || ((defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)) && __PYX_LIMITED_VERSION_HEX < 0x030A0000))
+static PY_INT64_T __Pyx_GetCurrentInterpreterId(void) {
+ {
+ PyObject *module = PyImport_ImportModule("_interpreters"); // 3.13+ I think
+ if (!module) {
+ PyErr_Clear(); // just try the 3.8-3.12 version
+ module = PyImport_ImportModule("_xxsubinterpreters");
+ if (!module) goto bad;
+ }
+ PyObject *current = PyObject_CallMethod(module, "get_current", NULL);
+ Py_DECREF(module);
+ if (!current) goto bad;
+ if (PyTuple_Check(current)) {
+ PyObject *new_current = PySequence_GetItem(current, 0);
+ Py_DECREF(current);
+ current = new_current;
+ if (!new_current) goto bad;
+ }
+ long long as_c_int = PyLong_AsLongLong(current);
+ Py_DECREF(current);
+ return as_c_int;
+ }
+ bad:
+ PySys_WriteStderr("__Pyx_GetCurrentInterpreterId failed. Try setting the C define CYTHON_PEP489_MULTI_PHASE_INIT=0\n");
+ return -1;
+}
+#endif
+#if !CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) {
+ static PY_INT64_T main_interpreter_id = -1;
+#if CYTHON_COMPILING_IN_GRAAL && defined(GRAALPY_VERSION_NUM) && GRAALPY_VERSION_NUM > 0x19000000
+ PY_INT64_T current_id = GraalPyInterpreterState_GetIDFromThreadState(PyThreadState_Get());
+#elif CYTHON_COMPILING_IN_GRAAL
+ PY_INT64_T current_id = PyInterpreterState_GetIDFromThreadState(PyThreadState_Get());
+#elif CYTHON_COMPILING_IN_LIMITED_API && (__PYX_LIMITED_VERSION_HEX < 0x03090000\
+ || ((defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)) && __PYX_LIMITED_VERSION_HEX < 0x030A0000))
+ PY_INT64_T current_id = __Pyx_GetCurrentInterpreterId();
+#elif CYTHON_COMPILING_IN_LIMITED_API
+ PY_INT64_T current_id = PyInterpreterState_GetID(PyInterpreterState_Get());
+#else
+ PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp);
+#endif
+ if (unlikely(current_id == -1)) {
+ return -1;
+ }
+ if (main_interpreter_id == -1) {
+ main_interpreter_id = current_id;
+ return 0;
+ } else if (unlikely(main_interpreter_id != current_id)) {
+ PyErr_SetString(
+ PyExc_ImportError,
+ "Interpreter change detected - this module can only be loaded into one interpreter per process.");
+ return -1;
+ }
+ return 0;
+}
+#endif
+static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none)
+{
+ PyObject *value = PyObject_GetAttrString(spec, from_name);
+ int result = 0;
+ if (likely(value)) {
+ if (allow_none || value != Py_None) {
+ result = PyDict_SetItemString(moddict, to_name, value);
+ }
+ Py_DECREF(value);
+ } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ } else {
+ result = -1;
+ }
+ return result;
+}
+static CYTHON_SMALL_CODE PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def) {
+ PyObject *module = NULL, *moddict, *modname;
+ CYTHON_UNUSED_VAR(def);
+ #if !CYTHON_USE_MODULE_STATE
+ if (__Pyx_check_single_interpreter())
+ return NULL;
+ #endif
+ if (__pyx_m)
+ return __Pyx_NewRef(__pyx_m);
+ modname = PyObject_GetAttrString(spec, "name");
+ if (unlikely(!modname)) goto bad;
+ module = PyModule_NewObject(modname);
+ Py_DECREF(modname);
+ if (unlikely(!module)) goto bad;
+ moddict = PyModule_GetDict(module);
+ if (unlikely(!moddict)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad;
+ return module;
+bad:
+ Py_XDECREF(module);
+ return NULL;
+}
+
+
+static CYTHON_SMALL_CODE int __pyx_pymod_exec_bezierTools(PyObject *__pyx_pyinit_module)
+#endif
+{
+ int stringtab_initialized = 0;
+ #if CYTHON_USE_MODULE_STATE
+ int pystate_addmodule_run = 0;
+ #endif
+ __pyx_mstatetype *__pyx_mstate = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ Py_ssize_t __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_t_10;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ PyObject *__pyx_t_13 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ if (__pyx_m) {
+ if (__pyx_m == __pyx_pyinit_module) return 0;
+ PyErr_SetString(PyExc_RuntimeError, "Module 'bezierTools' has already been imported. Re-initialisation is not supported.");
+ return -1;
+ }
+ #else
+ if (__pyx_m) return __Pyx_NewRef(__pyx_m);
+ #endif
+ /*--- Module creation code ---*/
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __pyx_t_1 = __pyx_pyinit_module;
+ Py_INCREF(__pyx_t_1);
+ #else
+ __pyx_t_1 = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #if CYTHON_USE_MODULE_STATE
+ {
+ int add_module_result = __Pyx_State_AddModule(__pyx_t_1, &__pyx_moduledef);
+ __pyx_t_1 = 0; /* transfer ownership from __pyx_t_1 to "bezierTools" pseudovariable */
+ if (unlikely((add_module_result < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ pystate_addmodule_run = 1;
+ }
+ #else
+ __pyx_m = __pyx_t_1;
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ PyUnstable_Module_SetGIL(__pyx_m, Py_MOD_GIL_USED);
+ #endif
+ __pyx_mstate = __pyx_mstate_global;
+ CYTHON_UNUSED_VAR(__pyx_t_1);
+ __pyx_mstate->__pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_mstate->__pyx_d)) __PYX_ERR(0, 1, __pyx_L1_error)
+ Py_INCREF(__pyx_mstate->__pyx_d);
+ __pyx_mstate->__pyx_b = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_mstate->__pyx_b)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_cython_runtime = __Pyx_PyImport_AddModuleRef("cython_runtime"); if (unlikely(!__pyx_mstate->__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_mstate->__pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ /* ImportRefnannyAPI */
+ #if CYTHON_REFNANNY
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny");
+ if (!__Pyx_RefNanny) {
+ PyErr_Clear();
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny");
+ if (!__Pyx_RefNanny)
+ Py_FatalError("failed to import 'refnanny' module");
+ }
+ #endif
+
+__Pyx_RefNannySetupContext("PyInit_bezierTools", 0);
+ __Pyx_init_runtime_version();
+ if (__Pyx_check_binary_version(__PYX_LIMITED_VERSION_HEX, __Pyx_get_runtime_version(), CYTHON_COMPILING_IN_LIMITED_API) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_mstate->__pyx_empty_tuple)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_bytes)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_unicode)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Library function declarations ---*/
+ /*--- Initialize various global constants etc. ---*/
+ if (__Pyx_InitConstants(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ stringtab_initialized = 1;
+ if (__Pyx_InitGlobals() < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (__pyx_module_is_main_fontTools__misc__bezierTools) {
+ if (PyObject_SetAttr(__pyx_m, __pyx_mstate_global->__pyx_n_u_name, __pyx_mstate_global->__pyx_n_u_main) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ {
+ PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (!PyDict_GetItemString(modules, "fontTools.misc.bezierTools")) {
+ if (unlikely((PyDict_SetItemString(modules, "fontTools.misc.bezierTools", __pyx_m) < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ /*--- Builtin init code ---*/
+ if (__Pyx_InitCachedBuiltins(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Constants init code ---*/
+ if (__Pyx_InitCachedConstants(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (__Pyx_CreateCodeObjects(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Global type/function init code ---*/
+ (void)__Pyx_modinit_global_init_code(__pyx_mstate);
+ (void)__Pyx_modinit_variable_export_code(__pyx_mstate);
+ (void)__Pyx_modinit_function_export_code(__pyx_mstate);
+ if (unlikely((__Pyx_modinit_type_init_code(__pyx_mstate) < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ (void)__Pyx_modinit_type_import_code(__pyx_mstate);
+ (void)__Pyx_modinit_variable_import_code(__pyx_mstate);
+ (void)__Pyx_modinit_function_import_code(__pyx_mstate);
+ /*--- Execution code ---*/
+
+ /* "fontTools/misc/bezierTools.py":5
+ * """
+ *
+ * from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea # <<<<<<<<<<<<<<
+ * from fontTools.misc.transform import Identity
+ * import math
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_calcBounds,__pyx_mstate_global->__pyx_n_u_sectRect,__pyx_mstate_global->__pyx_n_u_rectArea};
+ __pyx_t_1 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_fontTools_misc_arrayTools, __pyx_imported_names, 3, NULL, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
+ }
+ __pyx_t_2 = __pyx_t_1;
+ __Pyx_GOTREF(__pyx_t_2);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_calcBounds,__pyx_mstate_global->__pyx_n_u_sectRect,__pyx_mstate_global->__pyx_n_u_rectArea};
+ for (__pyx_t_3=0; __pyx_t_3 < 3; __pyx_t_3++) {
+ __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_2, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 5, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_3], __pyx_t_4) < (0)) __PYX_ERR(0, 5, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":6
+ *
+ * from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea
+ * from fontTools.misc.transform import Identity # <<<<<<<<<<<<<<
+ * import math
+ * from collections import namedtuple
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_Identity};
+ __pyx_t_1 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_fontTools_misc_transform, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
+ }
+ __pyx_t_2 = __pyx_t_1;
+ __Pyx_GOTREF(__pyx_t_2);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_Identity};
+ __pyx_t_3 = 0; {
+ __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_2, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_3], __pyx_t_4) < (0)) __PYX_ERR(0, 6, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":7
+ * from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea
+ * from fontTools.misc.transform import Identity
+ * import math # <<<<<<<<<<<<<<
+ * from collections import namedtuple
+ *
+*/
+ __pyx_t_1 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_math, 0, 0, NULL, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 7, __pyx_L1_error)
+ __pyx_t_2 = __pyx_t_1;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_math, __pyx_t_2) < (0)) __PYX_ERR(0, 7, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":8
+ * from fontTools.misc.transform import Identity
+ * import math
+ * from collections import namedtuple # <<<<<<<<<<<<<<
+ *
+ * try:
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_namedtuple};
+ __pyx_t_1 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_collections, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error)
+ }
+ __pyx_t_2 = __pyx_t_1;
+ __Pyx_GOTREF(__pyx_t_2);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_namedtuple};
+ __pyx_t_3 = 0; {
+ __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_2, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 8, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_3], __pyx_t_4) < (0)) __PYX_ERR(0, 8, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":10
+ * from collections import namedtuple
+ *
+ * try: # <<<<<<<<<<<<<<
+ * import cython
+ * except (AttributeError, ImportError):
+*/
+ {
+ (void)__pyx_t_1; (void)__pyx_t_5; (void)__pyx_t_6; /* mark used */
+ /*try:*/ {
+
+ /* "fontTools/misc/bezierTools.py":11
+ *
+ * try:
+ * import cython # <<<<<<<<<<<<<<
+ * except (AttributeError, ImportError):
+ * # if cython not installed, use mock module with no-op decorators and types
+*/
+ }
+ }
+
+ /* "fontTools/misc/bezierTools.py":15
+ * # if cython not installed, use mock module with no-op decorators and types
+ * from fontTools.misc import cython
+ * COMPILED = cython.compiled # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_COMPILED, Py_True) < (0)) __PYX_ERR(0, 15, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":18
+ *
+ *
+ * EPSILON = 1e-9 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_EPSILON, __pyx_mstate_global->__pyx_float_1eneg_9) < (0)) __PYX_ERR(0, 18, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":21
+ *
+ *
+ * Intersection = namedtuple("Intersection", ["pt", "t1", "t2"]) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_4 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_namedtuple); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 21, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __Pyx_PyList_Pack(3, __pyx_mstate_global->__pyx_n_u_pt, __pyx_mstate_global->__pyx_n_u_t1, __pyx_mstate_global->__pyx_n_u_t2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 21, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_4, __pyx_mstate_global->__pyx_n_u_Intersection, __pyx_t_8};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_7, __pyx_callargs+__pyx_t_9, (3-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 21, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_Intersection, __pyx_t_2) < (0)) __PYX_ERR(0, 21, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":24
+ *
+ *
+ * __all__ = [ # <<<<<<<<<<<<<<
+ * "approximateCubicArcLength",
+ * "approximateCubicArcLengthC",
+*/
+ __pyx_t_2 = __Pyx_PyList_Pack(28, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLength, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLengthC, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLength, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLengthC, __pyx_mstate_global->__pyx_n_u_calcCubicArcLength, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthC, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLength, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLengthC, __pyx_mstate_global->__pyx_n_u_calcCubicBounds, __pyx_mstate_global->__pyx_n_u_calcQuadraticBounds, __pyx_mstate_global->__pyx_n_u_splitLine, __pyx_mstate_global->__pyx_n_u_splitQuadratic, __pyx_mstate_global->__pyx_n_u_splitCubic, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT_2, __pyx_mstate_global->__pyx_n_u_splitCubicAtT_2, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC, __pyx_mstate_global->__pyx_n_u_splitCubicIntoTwoAtTC, __pyx_mstate_global->__pyx_n_u_solveQuadratic, __pyx_mstate_global->__pyx_n_u_solveCubic, __pyx_mstate_global->__pyx_n_u_quadraticPointAtT, __pyx_mstate_global->__pyx_n_u_cubicPointAtT, __pyx_mstate_global->__pyx_n_u_cubicPointAtTC, __pyx_mstate_global->__pyx_n_u_linePointAtT, __pyx_mstate_global->__pyx_n_u_segmentPointAtT, __pyx_mstate_global->__pyx_n_u_lineLineIntersections, __pyx_mstate_global->__pyx_n_u_curveLineIntersections, __pyx_mstate_global->__pyx_n_u_curveCurveIntersections, __pyx_mstate_global->__pyx_n_u_segmentSegmentIntersections); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 24, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_all, __pyx_t_2) < (0)) __PYX_ERR(0, 24, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":56
+ *
+ *
+ * def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a cubic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_1calcCubicArcLength, 0, __pyx_mstate_global->__pyx_n_u_calcCubicArcLength, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[9])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 56, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_2, __pyx_mstate_global->__pyx_tuple[2]);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicArcLength, __pyx_t_2) < (0)) __PYX_ERR(0, 56, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":75
+ *
+ *
+ * def _split_cubic_into_two(p0, p1, p2, p3): # <<<<<<<<<<<<<<
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_3_split_cubic_into_two, 0, __pyx_mstate_global->__pyx_n_u_split_cubic_into_two, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[10])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 75, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_split_cubic_into_two, __pyx_t_2) < (0)) __PYX_ERR(0, 75, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":84
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * p0=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_5_calcCubicArcLengthCRecurse, 0, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthCRecurse, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[11])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 84, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthCRecurse, __pyx_t_2) < (0)) __PYX_ERR(0, 84, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":115
+ * mult=cython.double,
+ * )
+ * def calcCubicArcLengthC(pt1, pt2, pt3, pt4, tolerance=0.005): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a cubic Bezier segment.
+ *
+*/
+ __pyx_t_2 = PyFloat_FromDouble(((double)0.005)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 115, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+
+ /* "fontTools/misc/bezierTools.py":104
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+ __pyx_t_7 = PyTuple_Pack(1, __pyx_t_2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 104, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_7calcCubicArcLengthC, 0, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[12])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 104, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_2, __pyx_t_7);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicArcLengthC, __pyx_t_2) < (0)) __PYX_ERR(0, 104, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":129
+ *
+ *
+ * epsilonDigits = 6 # <<<<<<<<<<<<<<
+ * epsilon = 1e-10
+ *
+*/
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_epsilonDigits, __pyx_mstate_global->__pyx_int_6) < (0)) __PYX_ERR(0, 129, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":130
+ *
+ * epsilonDigits = 6
+ * epsilon = 1e-10 # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_epsilon, __pyx_mstate_global->__pyx_float_1eneg_10) < (0)) __PYX_ERR(0, 130, __pyx_L1_error)
+
+ /* "fontTools/misc/bezierTools.py":151
+ *
+ *
+ * def calcQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_9calcQuadraticArcLength, 0, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLength, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[13])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 151, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLength, __pyx_t_2) < (0)) __PYX_ERR(0, 151, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":186
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_11calcQuadraticArcLengthC, 0, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLengthC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[14])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 186, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcQuadraticArcLengthC, __pyx_t_2) < (0)) __PYX_ERR(0, 186, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":237
+ *
+ *
+ * def approximateQuadraticArcLength(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a quadratic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_13approximateQuadraticArcLength, 0, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLength, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[15])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 237, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLength, __pyx_t_2) < (0)) __PYX_ERR(0, 237, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":254
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_15approximateQuadraticArcLengthC, 0, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLengthC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[16])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 254, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_approximateQuadraticArcLengthC, __pyx_t_2) < (0)) __PYX_ERR(0, 254, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":298
+ *
+ *
+ * def calcQuadraticBounds(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_17calcQuadraticBounds, 0, __pyx_mstate_global->__pyx_n_u_calcQuadraticBounds, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[17])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 298, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcQuadraticBounds, __pyx_t_2) < (0)) __PYX_ERR(0, 298, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":332
+ *
+ *
+ * def approximateCubicArcLength(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Approximates the arc length for a cubic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_19approximateCubicArcLength, 0, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLength, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[18])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 332, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLength, __pyx_t_2) < (0)) __PYX_ERR(0, 332, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":362
+ *
+ *
+ * @cython.returns(cython.double) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * pt1=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_21approximateCubicArcLengthC, 0, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLengthC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[19])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 362, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_approximateCubicArcLengthC, __pyx_t_2) < (0)) __PYX_ERR(0, 362, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":412
+ *
+ *
+ * def calcCubicBounds(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * """Calculates the bounding rectangle for a quadratic Bezier segment.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_23calcCubicBounds, 0, __pyx_mstate_global->__pyx_n_u_calcCubicBounds, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[20])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 412, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicBounds, __pyx_t_2) < (0)) __PYX_ERR(0, 412, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":450
+ *
+ *
+ * def splitLine(pt1, pt2, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a line at a given coordinate.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_25splitLine, 0, __pyx_mstate_global->__pyx_n_u_splitLine, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[21])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 450, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitLine, __pyx_t_2) < (0)) __PYX_ERR(0, 450, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":507
+ *
+ *
+ * def splitQuadratic(pt1, pt2, pt3, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at a given coordinate.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_27splitQuadratic, 0, __pyx_mstate_global->__pyx_n_u_splitQuadratic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[22])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 507, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitQuadratic, __pyx_t_2) < (0)) __PYX_ERR(0, 507, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":552
+ *
+ *
+ * def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at a given coordinate.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_29splitCubic, 0, __pyx_mstate_global->__pyx_n_u_splitCubic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[23])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 552, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubic, __pyx_t_2) < (0)) __PYX_ERR(0, 552, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":589
+ *
+ *
+ * def splitQuadraticAtT(pt1, pt2, pt3, *ts): # <<<<<<<<<<<<<<
+ * """Split a quadratic Bezier curve at one or more values of t.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_31splitQuadraticAtT, 0, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT_2, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[24])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 589, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT_2, __pyx_t_2) < (0)) __PYX_ERR(0, 589, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":613
+ *
+ *
+ * def splitCubicAtT(pt1, pt2, pt3, pt4, *ts): # <<<<<<<<<<<<<<
+ * """Split a cubic Bezier curve at one or more values of t.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_33splitCubicAtT, 0, __pyx_mstate_global->__pyx_n_u_splitCubicAtT_2, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[25])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 613, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubicAtT_2, __pyx_t_2) < (0)) __PYX_ERR(0, 613, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":644
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * pt1=cython.complex,
+ * pt2=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_35splitCubicAtTC, 0, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[2])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 644, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC, __pyx_t_2) < (0)) __PYX_ERR(0, 644, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":668
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_38splitCubicIntoTwoAtTC, 0, __pyx_mstate_global->__pyx_n_u_splitCubicIntoTwoAtTC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[26])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 668, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubicIntoTwoAtTC, __pyx_t_2) < (0)) __PYX_ERR(0, 668, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":708
+ *
+ *
+ * def _splitQuadraticAtT(a, b, c, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * segments = []
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_40_splitQuadraticAtT, 0, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[27])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 708, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitQuadraticAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 708, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":735
+ *
+ *
+ * def _splitCubicAtT(a, b, c, d, *ts): # <<<<<<<<<<<<<<
+ * ts = list(ts)
+ * ts.insert(0, 0.0)
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_42_splitCubicAtT, 0, __pyx_mstate_global->__pyx_n_u_splitCubicAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[28])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 735, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubicAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 735, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":770
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * a=cython.complex,
+ * b=cython.complex,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_44_splitCubicAtTC, 0, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC_2, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[3])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 770, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC_2, __pyx_t_2) < (0)) __PYX_ERR(0, 770, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":812
+ * #
+ *
+ * from math import sqrt, acos, cos, pi # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_sqrt,__pyx_mstate_global->__pyx_n_u_acos,__pyx_mstate_global->__pyx_n_u_cos,__pyx_mstate_global->__pyx_n_u_pi};
+ __pyx_t_6 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_math, __pyx_imported_names, 4, NULL, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 812, __pyx_L1_error)
+ }
+ __pyx_t_2 = __pyx_t_6;
+ __Pyx_GOTREF(__pyx_t_2);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_sqrt,__pyx_mstate_global->__pyx_n_u_acos,__pyx_mstate_global->__pyx_n_u_cos,__pyx_mstate_global->__pyx_n_u_pi};
+ for (__pyx_t_3=0; __pyx_t_3 < 4; __pyx_t_3++) {
+ __pyx_t_7 = __Pyx_ImportFrom(__pyx_t_2, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 812, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_3], __pyx_t_7) < (0)) __PYX_ERR(0, 812, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":815
+ *
+ *
+ * def solveQuadratic(a, b, c, sqrt=sqrt): # <<<<<<<<<<<<<<
+ * """Solve a quadratic equation.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_47solveQuadratic, 0, __pyx_mstate_global->__pyx_n_u_solveQuadratic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[29])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (!__Pyx_CyFunction_InitDefaults(__pyx_t_2, __pyx_mstate_global->__pyx_ptype_9fontTools_4misc_11bezierTools___pyx_defaults)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_sqrt); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_CyFunction_Defaults(struct __pyx_defaults, __pyx_t_2)->arg0 = __pyx_t_7;
+ __Pyx_GIVEREF(__pyx_t_7);
+ __pyx_t_7 = 0;
+ __Pyx_CyFunction_SetDefaultsGetter(__pyx_t_2, __pyx_pf_9fontTools_4misc_11bezierTools_96__defaults__);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_solveQuadratic, __pyx_t_2) < (0)) __PYX_ERR(0, 815, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":848
+ *
+ *
+ * def solveCubic(a, b, c, d): # <<<<<<<<<<<<<<
+ * """Solve a cubic equation.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_49solveCubic, 0, __pyx_mstate_global->__pyx_n_u_solveCubic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[30])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 848, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_solveCubic, __pyx_t_2) < (0)) __PYX_ERR(0, 848, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":945
+ *
+ *
+ * def calcQuadraticParameters(pt1, pt2, pt3): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_51calcQuadraticParameters, 0, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[31])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 945, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcQuadraticParameters, __pyx_t_2) < (0)) __PYX_ERR(0, 945, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":956
+ *
+ *
+ * def calcCubicParameters(pt1, pt2, pt3, pt4): # <<<<<<<<<<<<<<
+ * x2, y2 = pt2
+ * x3, y3 = pt3
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_53calcCubicParameters, 0, __pyx_mstate_global->__pyx_n_u_calcCubicParameters, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[32])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 956, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicParameters, __pyx_t_2) < (0)) __PYX_ERR(0, 956, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":988
+ *
+ *
+ * def calcQuadraticPoints(a, b, c): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_55calcQuadraticPoints, 0, __pyx_mstate_global->__pyx_n_u_calcQuadraticPoints, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[33])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 988, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcQuadraticPoints, __pyx_t_2) < (0)) __PYX_ERR(0, 988, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1001
+ *
+ *
+ * def calcCubicPoints(a, b, c, d): # <<<<<<<<<<<<<<
+ * ax, ay = a
+ * bx, by = b
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_57calcCubicPoints, 0, __pyx_mstate_global->__pyx_n_u_calcCubicPoints, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[34])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1001, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_calcCubicPoints, __pyx_t_2) < (0)) __PYX_ERR(0, 1001, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1040
+ *
+ *
+ * def linePointAtT(pt1, pt2, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a line.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_59linePointAtT, 0, __pyx_mstate_global->__pyx_n_u_linePointAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[35])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1040, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_linePointAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 1040, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1053
+ *
+ *
+ * def quadraticPointAtT(pt1, pt2, pt3, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a quadratic curve.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_61quadraticPointAtT, 0, __pyx_mstate_global->__pyx_n_u_quadraticPointAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[36])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1053, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_quadraticPointAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 1053, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1068
+ *
+ *
+ * def cubicPointAtT(pt1, pt2, pt3, pt4, t): # <<<<<<<<<<<<<<
+ * """Finds the point at time `t` on a cubic curve.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_63cubicPointAtT, 0, __pyx_mstate_global->__pyx_n_u_cubicPointAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[37])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1068, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_cubicPointAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 1068, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1094
+ *
+ *
+ * @cython.returns(cython.complex) # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * t=cython.double,
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_65cubicPointAtTC, 0, __pyx_mstate_global->__pyx_n_u_cubicPointAtTC, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[38])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1094, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_cubicPointAtTC, __pyx_t_2) < (0)) __PYX_ERR(0, 1094, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1119
+ *
+ *
+ * def segmentPointAtT(seg, t): # <<<<<<<<<<<<<<
+ * if len(seg) == 2:
+ * return linePointAtT(*seg, t)
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_67segmentPointAtT, 0, __pyx_mstate_global->__pyx_n_u_segmentPointAtT, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[39])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1119, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_segmentPointAtT, __pyx_t_2) < (0)) __PYX_ERR(0, 1119, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1134
+ *
+ *
+ * def _line_t_of_pt(s, e, pt): # <<<<<<<<<<<<<<
+ * sx, sy = s
+ * ex, ey = e
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_69_line_t_of_pt, 0, __pyx_mstate_global->__pyx_n_u_line_t_of_pt, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[40])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1134, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_line_t_of_pt, __pyx_t_2) < (0)) __PYX_ERR(0, 1134, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1148
+ *
+ *
+ * def _both_points_are_on_same_side_of_origin(a, b, origin): # <<<<<<<<<<<<<<
+ * xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ * yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_71_both_points_are_on_same_side_of_origin, 0, __pyx_mstate_global->__pyx_n_u_both_points_are_on_same_side_of, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[41])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1148, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_both_points_are_on_same_side_of, __pyx_t_2) < (0)) __PYX_ERR(0, 1148, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1154
+ *
+ *
+ * def lineLineIntersections(s1, e1, s2, e2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two line segments.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_73lineLineIntersections, 0, __pyx_mstate_global->__pyx_n_u_lineLineIntersections, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[42])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1154, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_lineLineIntersections, __pyx_t_2) < (0)) __PYX_ERR(0, 1154, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1232
+ *
+ *
+ * def _alignment_transformation(segment): # <<<<<<<<<<<<<<
+ * # Returns a transformation which aligns a segment horizontally at the
+ * # origin. Apply this transformation to curves and root-find to find
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_75_alignment_transformation, 0, __pyx_mstate_global->__pyx_n_u_alignment_transformation, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[43])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1232, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_alignment_transformation, __pyx_t_2) < (0)) __PYX_ERR(0, 1232, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1242
+ *
+ *
+ * def _curve_line_intersections_t(curve, line): # <<<<<<<<<<<<<<
+ * aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ * if len(curve) == 3:
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_77_curve_line_intersections_t, 0, __pyx_mstate_global->__pyx_n_u_curve_line_intersections_t, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[44])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1242, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curve_line_intersections_t, __pyx_t_2) < (0)) __PYX_ERR(0, 1242, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1255
+ *
+ *
+ * def curveLineIntersections(curve, line): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a line.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_79curveLineIntersections, 0, __pyx_mstate_global->__pyx_n_u_curveLineIntersections, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[45])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1255, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curveLineIntersections, __pyx_t_2) < (0)) __PYX_ERR(0, 1255, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1293
+ *
+ *
+ * def _curve_bounds(c): # <<<<<<<<<<<<<<
+ * if len(c) == 3:
+ * return calcQuadraticBounds(*c)
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_81_curve_bounds, 0, __pyx_mstate_global->__pyx_n_u_curve_bounds, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[46])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1293, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curve_bounds, __pyx_t_2) < (0)) __PYX_ERR(0, 1293, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1301
+ *
+ *
+ * def _split_segment_at_t(c, t): # <<<<<<<<<<<<<<
+ * if len(c) == 2:
+ * s, e = c
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_83_split_segment_at_t, 0, __pyx_mstate_global->__pyx_n_u_split_segment_at_t, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[47])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1301, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_split_segment_at_t, __pyx_t_2) < (0)) __PYX_ERR(0, 1301, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_85_curve_curve_intersections_t, 0, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[48])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1313, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_2, __pyx_mstate_global->__pyx_tuple[3]);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curve_curve_intersections_t, __pyx_t_2) < (0)) __PYX_ERR(0, 1313, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1380
+ *
+ *
+ * def _is_linelike(segment): # <<<<<<<<<<<<<<
+ * maybeline = _alignment_transformation(segment).transformPoints(segment)
+ * return all(math.isclose(p[1], 0.0) for p in maybeline)
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_87_is_linelike, 0, __pyx_mstate_global->__pyx_n_u_is_linelike, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[49])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1380, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_is_linelike, __pyx_t_2) < (0)) __PYX_ERR(0, 1380, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1385
+ *
+ *
+ * def curveCurveIntersections(curve1, curve2): # <<<<<<<<<<<<<<
+ * """Finds intersections between a curve and a curve.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_89curveCurveIntersections, 0, __pyx_mstate_global->__pyx_n_u_curveCurveIntersections, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[50])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1385, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curveCurveIntersections, __pyx_t_2) < (0)) __PYX_ERR(0, 1385, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1427
+ *
+ *
+ * def segmentSegmentIntersections(seg1, seg2): # <<<<<<<<<<<<<<
+ * """Finds intersections between two segments.
+ *
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_91segmentSegmentIntersections, 0, __pyx_mstate_global->__pyx_n_u_segmentSegmentIntersections, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[51])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1427, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_segmentSegmentIntersections, __pyx_t_2) < (0)) __PYX_ERR(0, 1427, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1475
+ *
+ *
+ * def _segmentrepr(obj): # <<<<<<<<<<<<<<
+ * """
+ * >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_93_segmentrepr, 0, __pyx_mstate_global->__pyx_n_u_segmentrepr, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[52])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1475, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_segmentrepr, __pyx_t_2) < (0)) __PYX_ERR(0, 1475, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1488
+ *
+ *
+ * def printSegments(segments): # <<<<<<<<<<<<<<
+ * """Helper for the doctests, displaying each segment in a list of
+ * segments on a single line as a tuple.
+*/
+ __pyx_t_2 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_4misc_11bezierTools_95printSegments, 0, __pyx_mstate_global->__pyx_n_u_printSegments, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[53])); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1488, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_2);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_printSegments, __pyx_t_2) < (0)) __PYX_ERR(0, 1488, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1496
+ *
+ *
+ * if __name__ == "__main__": # <<<<<<<<<<<<<<
+ * import sys
+ * import doctest
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_name); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1496, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_10 = (__Pyx_PyUnicode_Equals(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_main, Py_EQ)); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 1496, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ if (__pyx_t_10) {
+
+ /* "fontTools/misc/bezierTools.py":1497
+ *
+ * if __name__ == "__main__":
+ * import sys # <<<<<<<<<<<<<<
+ * import doctest
+ *
+*/
+ __pyx_t_6 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_sys, 0, 0, NULL, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1497, __pyx_L1_error)
+ __pyx_t_2 = __pyx_t_6;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_sys, __pyx_t_2) < (0)) __PYX_ERR(0, 1497, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1498
+ * if __name__ == "__main__":
+ * import sys
+ * import doctest # <<<<<<<<<<<<<<
+ *
+ * sys.exit(doctest.testmod().failed)
+*/
+ __pyx_t_6 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_doctest, 0, 0, NULL, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1498, __pyx_L1_error)
+ __pyx_t_2 = __pyx_t_6;
+ __Pyx_GOTREF(__pyx_t_2);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_doctest, __pyx_t_2) < (0)) __PYX_ERR(0, 1498, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1500
+ * import doctest
+ *
+ * sys.exit(doctest.testmod().failed) # <<<<<<<<<<<<<<
+*/
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_sys); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_exit); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_11 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_12, __pyx_mstate_global->__pyx_n_u_doctest); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __pyx_t_13 = __Pyx_PyObject_GetAttrStr(__pyx_t_12, __pyx_mstate_global->__pyx_n_u_testmod); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_11, NULL};
+ __pyx_t_8 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_13, __pyx_callargs+__pyx_t_9, (1-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ }
+ __pyx_t_13 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_failed); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_13);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_7, __pyx_t_13};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_4, __pyx_callargs+__pyx_t_9, (2-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1500, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/misc/bezierTools.py":1496
+ *
+ *
+ * if __name__ == "__main__": # <<<<<<<<<<<<<<
+ * import sys
+ * import doctest
+*/
+ }
+
+ /* "fontTools/misc/bezierTools.py":1
+ * # -*- coding: utf-8 -*- # <<<<<<<<<<<<<<
+ * """fontTools.misc.bezierTools.py -- tools for working with Bezier path segments.
+ * """
+*/
+ __pyx_t_2 = __Pyx_PyDict_NewPresized(15); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_calcQuadraticArcLength_line_151, __pyx_mstate_global->__pyx_kp_u_Calculates_the_arc_length_for_a) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_calcQuadraticBounds_line_298, __pyx_mstate_global->__pyx_kp_u_Calculates_the_bounding_rectangl) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_approximateCubicArcLength_line_3, __pyx_mstate_global->__pyx_kp_u_Approximates_the_arc_length_for) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_calcCubicBounds_line_412, __pyx_mstate_global->__pyx_kp_u_Calculates_the_bounding_rectangl_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_splitLine_line_450, __pyx_mstate_global->__pyx_kp_u_Split_a_line_at_a_given_coordina) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_splitQuadratic_line_507, __pyx_mstate_global->__pyx_kp_u_Split_a_quadratic_Bezier_curve_a) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_splitCubic_line_552, __pyx_mstate_global->__pyx_kp_u_Split_a_cubic_Bezier_curve_at_a) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_splitQuadraticAtT_line_589, __pyx_mstate_global->__pyx_kp_u_Split_a_quadratic_Bezier_curve_a_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_splitCubicAtT_line_613, __pyx_mstate_global->__pyx_kp_u_Split_a_cubic_Bezier_curve_at_on) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_solveCubic_line_848, __pyx_mstate_global->__pyx_kp_u_Solve_a_cubic_equation_Solves_a) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_lineLineIntersections_line_1154, __pyx_mstate_global->__pyx_kp_u_Finds_intersections_between_two) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_curveLineIntersections_line_1255, __pyx_mstate_global->__pyx_kp_u_Finds_intersections_between_a_cu) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_curveCurveIntersections_line_138, __pyx_mstate_global->__pyx_kp_u_Finds_intersections_between_a_cu_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_segmentSegmentIntersections_line, __pyx_mstate_global->__pyx_kp_u_Finds_intersections_between_two_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_2, __pyx_mstate_global->__pyx_kp_u_segmentrepr_line_1475, __pyx_mstate_global->__pyx_kp_u_segmentrepr_1_2_3_2_3_4_0_1_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_test, __pyx_t_2) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /*--- Wrapped vars code ---*/
+
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_XDECREF(__pyx_t_13);
+ if (__pyx_m) {
+ if (__pyx_mstate->__pyx_d && stringtab_initialized) {
+ __Pyx_AddTraceback("init fontTools.misc.bezierTools", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ #if !CYTHON_USE_MODULE_STATE
+ Py_CLEAR(__pyx_m);
+ #else
+ Py_DECREF(__pyx_m);
+ if (pystate_addmodule_run) {
+ PyObject *tp, *value, *tb;
+ PyErr_Fetch(&tp, &value, &tb);
+ PyState_RemoveModule(&__pyx_moduledef);
+ PyErr_Restore(tp, value, tb);
+ }
+ #endif
+ } else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError, "init fontTools.misc.bezierTools");
+ }
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ return (__pyx_m != NULL) ? 0 : -1;
+ #else
+ return __pyx_m;
+ #endif
+}
+/* #### Code section: pystring_table ### */
+/* #### Code section: cached_builtins ### */
+
+static int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate) {
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __pyx_builtin_round = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_round); if (!__pyx_builtin_round) __PYX_ERR(0, 906, __pyx_L1_error)
+ __pyx_builtin_print = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_print); if (!__pyx_builtin_print) __PYX_ERR(0, 1493, __pyx_L1_error)
+
+ /* Cached unbound methods */
+ __pyx_mstate->__pyx_umethod_PyDict_Type_items.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_items.method_name = &__pyx_mstate->__pyx_n_u_items;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_pop.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_pop.method_name = &__pyx_mstate->__pyx_n_u_pop;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_values.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_values.method_name = &__pyx_mstate->__pyx_n_u_values;
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: cached_constants ### */
+
+static int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0);
+
+ /* "fontTools/misc/bezierTools.py":639
+ * # segment should always start at pt1 and the last segment should end at pt4,
+ * # so we set those values directly before returning.
+ * split[0] = (pt1, *split[0][1:]) # <<<<<<<<<<<<<<
+ * split[-1] = (*split[-1][:-1], pt4)
+ * return split
+*/
+ __pyx_mstate_global->__pyx_slice[0] = PySlice_New(__pyx_mstate_global->__pyx_int_1, Py_None, Py_None); if (unlikely(!__pyx_mstate_global->__pyx_slice[0])) __PYX_ERR(0, 639, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_slice[0]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_slice[0]);
+
+ /* "fontTools/misc/bezierTools.py":640
+ * # so we set those values directly before returning.
+ * split[0] = (pt1, *split[0][1:])
+ * split[-1] = (*split[-1][:-1], pt4) # <<<<<<<<<<<<<<
+ * return split
+ *
+*/
+ __pyx_mstate_global->__pyx_slice[1] = PySlice_New(Py_None, __pyx_mstate_global->__pyx_int_neg_1, Py_None); if (unlikely(!__pyx_mstate_global->__pyx_slice[1])) __PYX_ERR(0, 640, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_slice[1]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_slice[1]);
+
+ /* "fontTools/misc/bezierTools.py":711
+ * ts = list(ts)
+ * segments = []
+ * ts.insert(0, 0.0) # <<<<<<<<<<<<<<
+ * ts.append(1.0)
+ * ax, ay = a
+*/
+ __pyx_mstate_global->__pyx_tuple[0] = PyTuple_Pack(2, __pyx_mstate_global->__pyx_int_0, __pyx_mstate_global->__pyx_float_0_0); if (unlikely(!__pyx_mstate_global->__pyx_tuple[0])) __PYX_ERR(0, 711, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_tuple[0]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_tuple[0]);
+
+ /* "fontTools/misc/bezierTools.py":1320
+ *
+ * if not range1:
+ * range1 = (0.0, 1.0) # <<<<<<<<<<<<<<
+ * if not range2:
+ * range2 = (0.0, 1.0)
+*/
+ __pyx_mstate_global->__pyx_tuple[1] = PyTuple_Pack(2, __pyx_mstate_global->__pyx_float_0_0, __pyx_mstate_global->__pyx_float_1_0); if (unlikely(!__pyx_mstate_global->__pyx_tuple[1])) __PYX_ERR(0, 1320, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_tuple[1]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_tuple[1]);
+
+ /* "fontTools/misc/bezierTools.py":56
+ *
+ *
+ * def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005): # <<<<<<<<<<<<<<
+ * """Calculates the arc length for a cubic Bezier segment.
+ *
+*/
+ __pyx_mstate_global->__pyx_tuple[2] = PyTuple_Pack(1, ((PyObject*)__pyx_mstate_global->__pyx_float_0_005)); if (unlikely(!__pyx_mstate_global->__pyx_tuple[2])) __PYX_ERR(0, 56, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_tuple[2]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_tuple[2]);
+
+ /* "fontTools/misc/bezierTools.py":1313
+ *
+ *
+ * def _curve_curve_intersections_t( # <<<<<<<<<<<<<<
+ * curve1, curve2, precision=1e-3, range1=None, range2=None
+ * ):
+*/
+ __pyx_mstate_global->__pyx_tuple[3] = PyTuple_Pack(3, ((PyObject*)__pyx_mstate_global->__pyx_float_1eneg_3), Py_None, Py_None); if (unlikely(!__pyx_mstate_global->__pyx_tuple[3])) __PYX_ERR(0, 1313, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_tuple[3]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_tuple[3]);
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_tuple;
+ for (Py_ssize_t i=0; i<4; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_slice;
+ for (Py_ssize_t i=0; i<2; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ __Pyx_RefNannyFinishContext();
+ return 0;
+ __pyx_L1_error:;
+ __Pyx_RefNannyFinishContext();
+ return -1;
+}
+/* #### Code section: init_constants ### */
+
+static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) {
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ {
+ const struct { const unsigned int length: 11; } index[] = {{2},{942},{1182},{542},{600},{52},{742},{779},{749},{1065},{33},{884},{1246},{768},{1293},{1557},{661},{20},{1},{1},{36},{26},{33},{30},{35},{34},{7},{6},{2},{2},{9},{33},{4},{39},{119},{24},{21},{24},{21},{20},{28},{25},{4},{6},{8},{8},{2},{7},{8},{12},{3},{20},{1},{2},{1},{2},{5},{1},{1},{2},{4},{3},{3},{2},{2},{4},{13},{25},{7},{5},{6},{25},{26},{29},{30},{4},{5},{18},{5},{2},{3},{3},{2},{3},{3},{1},{2},{3},{3},{39},{7},{7},{3},{2},{3},{2},{3},{1},{2},{3},{9},{3},{9},{3},{3},{3},{9},{3},{9},{10},{18},{19},{27},{15},{19},{15},{22},{23},{19},{23},{19},{17},{18},{5},{11},{3},{13},{14},{5},{6},{6},{23},{22},{13},{28},{46},{46},{27},{44},{2},{2},{1},{2},{2},{3},{3},{5},{7},{7},{6},{7},{2},{2},{1},{2},{3},{3},{2},{3},{3},{3},{7},{13},{2},{4},{2},{6},{25},{26},{24},{5},{8},{7},{4},{1},{6},{15},{13},{10},{12},{13},{12},{29},{7},{2},{5},{3},{8},{4},{5},{5},{21},{12},{6},{13},{8},{4},{9},{3},{5},{8},{10},{4},{1},{8},{10},{4},{3},{4},{4},{3},{8},{6},{1},{2},{2},{2},{2},{2},{8},{11},{6},{3},{9},{5},{13},{2},{3},{4},{4},{3},{4},{4},{3},{3},{2},{2},{17},{12},{1},{3},{3},{6},{6},{8},{5},{6},{5},{1},{2},{3},{3},{2},{3},{3},{5},{8},{4},{3},{4},{4},{7},{15},{27},{12},{29},{8},{4},{12},{10},{7},{7},{9},{10},{14},{5},{10},{14},{14},{15},{13},{21},{27},{9},{14},{18},{17},{31},{21},{19},{4},{5},{7},{2},{2},{3},{1},{2},{4},{4},{2},{8},{7},{5},{5},{9},{15},{9},{2},{3},{10},{13},{2},{2},{2},{2},{2},{5},{6},{5},{1},{2},{2},{2},{2},{2},{5},{6},{1},{2},{2},{2},{2},{5},{6},{205},{2},{204},{22},{42},{24},{87},{182},{141},{37},{127},{95},{40},{31},{106},{31},{61},{172},{23},{105},{94},{92},{151},{651},{404},{145},{155},{92},{102},{120},{386},{269},{87},{145},{244},{30},{252},{187},{43},{2},{2},{2},{179},{2},{568},{143},{104},{54},{80},{129},{38},{90}};
+ #if (CYTHON_COMPRESS_STRINGS) == 2 /* compression: bz2 (6697 bytes) */
+const char* const cstring = "BZh91AY&SYl\266\355\314\000\014\337\377\377\377\377\377\337\177\377\377\377\377\347\377\357\377\377\377\367\300@@@@@X@P@@@@\000@\000`\036\357wy}\267\276\357+\324<\356\327\336\032\273\271\336=\356\357{z\361f\301<\037z\311\\\362\341D\372\373\275\034\373\026o\226\357\273\236\236\236\200\367\261\362:)\356\364s\327N\215\034@M\203{\300( E\013\342\200 \037\tB \204\023\324\236M1\265\006\247\264\001\032jz\236\023\010S\324z\233ML#L\230A\210h\006\215\000\323M\000\006\202I\023\002i\2414\ni4\315I\345\033)\211\3523SF\236\246\200\315!\220\006\200h\000\320\000\014\232\000\r\002S@IE=#\322\206\020\306\232!\223LG\240\023?M)\244`@a\001\210\014\002i\202\006\231\006\023L\t4\222EOD\324\336\223D\364\323SOMM\030\232\033Q\210h\321\223cM\"6I\241\240\000i\241\240\311\221\351\032\000\000\010\244H\323F\232MS\304\365O\324\361'\222\203z\221\243\030\240\3204\315#@z@\000h\320\000d\003@\332\231\r\000$D!4\t\202b\t\204dL\3214\247\236\245\017\0014!\240\323@\003@\000\000\001\223@\000Q'P}Z\227U\331\231\320,Zu6\333\034f\334\304e\240\260f\276\343:v%\205U\023\374\333\257\333t\256E|\256w7w\311\213]^\261\276H7\377d\353\026\236\271\374\275\213m0\224\220J\222\232\n0\224:E\037\"!\245\304\230\240\247&\036\032\323\003u\207\004\201L\301$\224E\"\265\n\223\230\004\320\246\004\3479\314\377\241\354\263033@PP \261`\240\306AH\214\210\301`\n\002\222*\300TE\001`\260\006<\240\010J\210\252*\225B\265=\262\004Y!JR\222$\211X\227q/\374%e\343\022\024\305\006&!]\366HU4\306\010\301\001\245\2446\206\300\030\311\030\202\213\010\260\005\200\240\252\013\025d\016\021LGT\210\001m\"\006\254\314\303z]\311\370d\304\201\231\005\366\271\332\001\345C\236\000\374!y\352k\215\007r.\364\262\361\350\026l+Lt\346\222\347I\020C\314\204\005\316\263\236y\226\001|\252\211\370M\203\310\362qs\327\353\317|\013\330\345\301\237\014\217\"\213Fx\306r,8\003!GdPQf\326\340W\t\034\205\366\003\342\373\257AM\216l\245Q\321\003Cm\355\006\2771}\325\315\332\013\264\355@\306\220v\372\375t\027\247\317zW\336\001Pc\001\244\337\247\017HJ\273\301\203h\210\301w\207L4\304\355""\245\262\250\244Pp:\233O\026rtl\026c\001\246\226\177\005\365ld\265\220\330C\024\031\256\252W\332\301f\233\023HrH{\307%\026ET\245\031@\363\206H\232\211\257\223\252\244&\t\203(\262\240\273\221/}P\244a\202\243\035\264E\223M8\264\"\227RU\235J\257sh$[;\335\221\235A#$\202\251\031\010\262FH}\225\222\n\225=\004\325*\211\n\265Y\235\352\032\006\354\262\003<\267\022W\025\322\270]\\\214+T\252k\211\000E@\245\026\241\246x\342\225\212\225\014\252@\203\003\301\364\244\310]P\242\223\232\216UR1\221\300}\235s\274\222I\002\230\225\311\002\210\367G3B\036\207Q\316\211\365\020\212A\n\260B;\220\262 \0206\222\230\016\257\347(\213\277\211\322\"\312/=v\350\252\364`\016D\004\003\202\"#\030\000\376\335\361W\267\317\213n\022l\025\001U!\014ci\374\220\001\360\271`\376\347\211\0317\237\021\355\310fg\014\227\202\326\006\330\263[R>\375vC\020\005\013S\320\272\353!B^\003\035f \375\002\241\2543\2535\342r\337\230\265\001\321@\302\230\271\262\203\222C~\010\242\276\370BJ\231\331\016\235\210\032\032\021\206\315\336\207\356\3518\264-1\201\240\332\363\200\373L\221\262\331\rz\005T\356\270/\336\266\027\000\305\213\204u\246\235p\312\214\317&\302\240Xn\006\206\354c\244\205\016\037|\305f\022\013\2637\036'\341!\242\333`\252\235\305\273\027\203\263`\300\030%aM\350\272\030\271\246\026\256;\016\261\203\215vz\236\242#K\225\316>\007\206\201\021\004\006L&L\215\311\223\226@1\202\204}i0\034\304G\315pb@\276\254-i\006\204j\2707\0030\0237\262\250\177w\026b\014\207\005Cx\361R\005q\026\030\"b\321^\304\342\340T3\014\205@\222\001\203\"\002\0245\245H\3032+\233R4\\\265O\235F\251\2646[+j\207v\370\312/\217\266Z\333\332\345\021\311F\023i\016gx5\204\356m\315\272g\013\252\"\244yF\312f\307\205\226\020\357\364;\236\026\301\221\021{\313\252\244TE\013\026\303\334\010\367\306\"\026)b\202\001D^\224\357\224\241EOP\367\341\351\330\013\026W22X\227\357\262\347\266h\314\365\r\267\347\231\232\276\314\371\026@\276\263\270\376^\233\225.\310d2\036\276<\377\007\320u\005!m\006\210\230\371K\224\006\023H\335\032\206\260\331\024\346\246\344\222\3541I""\315\024\201\023\nX\262\213\330\321\201\r\"\310\002\301a\005\204\322\022\211 \254\030\304\202\300PP\240\326E\017\013\271\244&2\010VB\034s\262\367|n\214\265\234<\374|\275\351\264*\236.\344\222\370A\335YqgNL\003*^\003\343K\007\035\001\352E@`\222N\222d\223\010pBL\006\202c\031B%\341\267$\210/v\272\033@\332\002\262\002\222\025\r\314.P\352}*Bj\013\246U\010\240\310a\316\007\010,?\317$J\362\"\325|)N\224v\267\372>\247g\262{>\316\265\345\322L\331\234\267\362L\322\361x\263x_\036w:\250f\205Lp\200\230\003,D\0242\276\232\001&\204\023\021\rs\367liXEEUE$\010\004\005Y\326\365\220s\262\344[\t\235\005\324m\263D\314\351\225\032\336n03v\261*B\2501\202\210\263q\036\201\224X\203m1\225\273&\031+f\223c\177\2357A\336\0336\366YZ\0023\351\237\005\t\207\240\3403(f\032E\342\300X\006\337\263\312\261}\304\022H\331-\214O\357\363\372\217:\361\347\362\375\030\233\023le\344r\266\327\324\351\032\244\260hou\275\2647\016\321`\203\330\031i\340\232\016s\337\275\343\321\347\332\232L-9\3557^r\232\035\271\366M\317<\260\222\332\215\263F\250B\004\030>!\004\250\201\332qL\224\032\202\344\333e\202{N\326\330l\311\032q^\323\367\272o\260\364\363L\314\037=E\251l\363\004A_\335\3768\356\257\270u\3719N \330\337\227i\255\267\300\302wjD\334ZT\030\250s^r\006\225\223H\245Z(\250\266P\215\021j\033\2317\032/xSyL\tL\224\320\233\230\006H\320(^v\363A\200\260\027O\"T\301\217\277D\312\005C\221\301\232\024\272\3032\230\342\"\333%\020\250\002\220-\220\2536\222\220\246(\r\206\033\204\3406\300\251\220u.\250\260\205BRw\020\224M\362\2070\2570\031q\220f$\324\2559\334\3530\263\3102]\361>l\221RZX\227\t\002fb\363\007'Z\221|a\014^\363\030\302e\3078~es\211\213\261\221#\003+\003\007\237\364\377\307s\323\337\33018plNv\211\324\351J2\r\202.\\\306\330\347\304\220\304\237\004\220y\303\004\022\001\206@2\316\354\342\231\267Q\374\272\307S\032\205!ye3wC\345\tTg\0243$\362D\233\022\004`\214g$\323\320\211\n\266d\315\t\035\017\220\306&7<\033\303\310l\363\376\211\341\207c\234xJ\236\033\036\016\275_;?\257\327\313+cIS\267\031JR\212-t\310\274\345U\021\220\030""\013\210b\016\316\004\3428x\211\221\307\0334\301\201\206\"\207Q&Lj\241\320\237\007\222E\002vS\ru\014\350\310\217\263\316\346\034\304l\352S\241\316\243V\223V\234\272Y\202G)\n\260BFF\265L\302\024\2461\026vl\320\232\352\004d@\236p)\3330 K,N\334\013O\267\006\300\311\222f!@\007\247\254\206\224\206p\354\350^\246n\201J\302\017\305\361-]\265M\332\362\360H^\265{\216\226\273`\360\245Sj\256\243\243\215\371\323\024\261bw\221\363g\315\216\316d1\3059\316t\240\025\001\022\232\010TD\227\341\360\325\n\311^\303kl\355\2059\372\273\034\332.)\245;\021sB\241\034\332\263\211\245aI\245r\001\310\264{\204/\333\204\267aI\342H\236z\274\276\351\315\314\031ey]\244e\014\037\221\2269\352\320(\371\341^\\\345\245Z\225'K2\244\327\276\030eD\t@IBT\225\tiQ\r\372\272\271T\364\362\365\320:jy\013\350t\026\260uZ\353\257'\243\003\n\343\325\324S\014\320\303-ns8^s\3415$W\\d\306rD\225yy\202\317'\036\237\032\331\r\215\025\333\305u\371}W\007W\227g{J\372`\203\276\007r1Qs\222(\030\026\005\003\2659p\240\026\365K\325\341*6i\006\266k\020\2741\022\204\243\342\210C\016\363\337\364\346B\324#0\252/p\367\025\256\263\313\216""\322\034\035\357R\351\304\325\004\307\360\247h\032\266@\246_i\231n>\230\030\260\211\202\301\"\010\010\211M\025\242\354dd\373\371\247\245\2719;0\036\343\016\370c\005'\010\253d`\243C\206N\0318@\341\207\014\3419G\201\236\251\013\366}\017\204\335>St\360]\370j\203P\321h\032\032z\262\242\344%@\242[P\350\212\\e\305\211\315O\"\231tVF.=\240<)B\220\365\275\177Y\353\207\2004Z\262a\231\216\266*\377<\347\200\205\276IHMQvu\251\353\344uF\030\334\377\2019s\376\035W}\317J:\365#\223\222\017\267\212=\271\217g\263\315\364\323\360\263\030\245\210\214\255\270C\023\216SK5\2001wv\007*UG\232\236erB\272\034\014j\305K\2124M\244z\271\240\206&\245\0341Vf \207\243^\307(3\344Z\"&\306\316\224\340\0031\000\317A.+\201\2707nE\355\222H\263\205\341+ \260X\025Q\277\035\371\357\013\010\3110\272\215w;\273t\234\216\357t\321\006\200h\221\2747\233\240y\227\336J\354mBB\302\n\235V\256\005\202FC\014\355\006F\222\025\341\343v\033\026{Y\010\367\236\341\201\004L\204o\212N\331\331\021\223 \215\262l\341\330\220\035\211v\001\330\227`v\021\350\210\274\2614\007#\n\010\266\260h\346\350i\304ETj/,\346I\247\007\034U\326\031\272H\004\2073'\035WA\212\004TA_#y\264\360\340\034\022\340\267\371\321\347V\363sf\003x\314\343\001\373\335\340\266\021\270d\234\226\t\033%\t\260\251X\003\263\002\207i\023K\227\0016\314\220#Sg\005\025\030\340O=!6\306\233U\205\361\375\256\024\266\n\342e\336\312(\202`PG\243<\207\344tA5\364\366\363T,\316\273[\262\3250\306-\251JQ\231Q&\003\rRC\033lm\214\222)\265B(\333\036\207\246M\330Z\245d^ti\010\302\250\217F\220\315,\017\367\010\206\014i\241\"\315h\n\007\327\227\237H\262=\261y~\214\207\322\366\337\360\005\331r\353\362\251\004\260\006\222b\006\233\227\316,\320\324J\320\031\0255\307B\344*\231\225\037b2\032\264\354\025\2540bM\210\266\260\016\364\322\013\314\360\000g\330':,\013c\r\014X\302Et\215\244%\247\026\3409\214\t\242!U\261$\300\376J)\352$\250\317\026F\222\226\034D\267\205Z\205\\\032\307z.d\273\035@K\246\311\244t\272C\273x\232e\323vU\312\300\204\r6\330\274\023S\370\007T\235\234\252dj\342\252\200t\357>\031D.\230\010_7\2777\036\316d\205\210c\327\"\361/i\351\250\260UE\302\000\221\364\t9I*}\200\343\303\202\315\322v\203\030\332\006!;\342\360\302\022B\357Hi97\2303\276\344Oxx{\203n\204T\0220`\252\202\304\021\210\212\214D\030\250\242\212\253\023\301'6f}\036\367\222+J\377\221#J\361\033\261\210\237\270^\317\202\017@,@\237\214\037G\221v \037\320\214\r\215\002bb\030\232W\264\207\036\260\366q\3460\013\00786\245h\314\226^\276\344\032\332CH\031\265\013\336y\030\020\0145#\001\332\310\016r\001A\222A\246\227Xq]T\261\003\232\302\313w@_#\210\034D\302\305\210\027\3324\234w\357\314\207\313\321x]\225\205\024\007WJ\016\320\321G\314c\257\226\360\347\016d\263@7\376\006\036\325\205\020\004,\341\274\3474\260\033J\224\303=NdY\243,\010X-\205\016\224\223B\347d\370\314\346~:4\354>G\245\014\360c1+\211\n\346il\235\275-\210""\344d5\246\211 \030\232\252a\340H**\006\375\377\303$=\031A9\346\300\240\r\033\304\007\026\000A]\267\363x\007\2621\326\257\022\2619\310Zh\240\204\327L \317\214\"\002B\264\344\251\331\3351\003\266\334hq\203\232k\355\273\215\211y\014\230p\2155\320\301(!\031!\241!\016\036_o\201\2417\310\310M\362\025V@R\357SI\337Z\270\003P\260\215\311\324\316!\232\204\320\200\235\205\006F\003 `5hL\311'\220\240 0@F\n\272\226\2245%,\020\022\002#\025\036\343\226\210*Kj\370\\\200/.N\347\3039\363bHwG\225\340t!8\201Y#m\275Icx\275<\211u|m\003\324\031\201\200\301\243K3\264^\225\0175h\002)\2000=\320\177P]\351ZR%\246\363\237!C?&\031S\014\\v`\200\340\272\230-6\360\007EVg\006.qFZ\371\276>\201\342\2341\300<\324\232j\354\324\016I\036S\031\271\202\317\267\351\347\346\246\201\205H\371\373\204pI\202\032\354%\264\006\304rf\316L\034\270-\341\355\013\360\247K\204\033f\031\027\006wT:\260\233\002e\217\004{P[u~\330\250\2747\322\202\241H\r\372\276\270k\337\366sd\247\"2;\364o^\357\213\371>o\231\312E\002\005D\276\224|\365*\3304V\"\030V\013\013M\206\345\304[\036\253Z\363\275\361\320\003\267 3\241\n\021J\017\247\345\251&\216\233\312tt\353\n\205ru\240F\325\226\207X\330\204\362\347@ew\322\010\225,WZ\331d3/I\343\230(Xi1;\357\022\232\006\"\201\"\220\206\326)*%lx\332\306K\301JZ-\006\r T+Z\032h;\023\355\200\255\270 \033\246%\036\002\2060\3069\000\340\231^x\374C\024\273\303\234\344\322\220HD \201@kJ\200\340\250\230\025L u\252\030H\201\204\243\353H\267\262D\237\251\036\213\253\325\333Jv\365\323\021.\261,\206)b\226p\250P\210\303,\222'\317\323|\205\216\313\3423R\373\335DxQ\303\245*\275 U\322\355\254\\\014\220a\361\234u\364C\222\332\227\266{\256a\337\241mL\024V&\020o\020\344\350\326\257\305\036,\007\336\276.\256\026]\347q1\007\001\017\206\274\2478\341\270\301\326\204\005\301\316F\016p\350\315\212\321L+\321\263\344\264\260l\231\266\000\346i\005#\213\301Q7\016\373\353\315S\270A\334%\334.\344w\007j\347F\215\341\244\314\232r#\235\242\201\260\031\262T\002cbj\244lL*S\240\233\321k\232>zQ4Wq\013\225z\263 \354\362x\004\362\262\255""\007\256\201\010\r8\013\014\242P\206\003hS\302RG\202\023\006*\252Z\232\235\316\253\241\314\352\260r)\0057\205\376\352{\262\021RZA\"\335v@\331Y\025F\220-\301 \263b\342\356\360lH\310\326\014`\205>J\310\300F.\000\300\310mU\306\364\320\\\301\264(\030%l\263u\003\313\276\230\254XQ6\357E\373n\263r\204\002\200\311Q0\200\2602\333\321\034\236\331\350\325\3037\300\302N\334\3319\345\242\267\"\3026d\201\322H\034*GIt\241\320.\2014\007\033G\245\310H\025\210`\313\034C\200\020\010A\021\005\367e\030\215\304\014U[V\337\205\\\275\\\262\252\220n~~q%\001\205\362\001\002\005\2311+2\377\206\346\014\312\367nF\340\334\033\205\270\366\232>\037G\037G\243R:|\236\316\215m\t\014\006\201\264\213\322\264\355F\275z\326\277V\301\352\301p\r\321\355^\241VEF\352/[\2278\254Lq\357Y\306\014\3678G?\030\343\307~\203\330u\365\337\316}\002Y'\035q\326\374\257=\365\004\322\231\363\304p\242\350\335\320v\362\234\336E\2450\325\034\231;.Yx\374\353\3149mJ\214b\356,q\312\3340|\334\347\230d\347\t\035!\307\303E\265\331\035\264C\277\363\206\217\361\346\245G\301\332N\257W\255*Y\304\271ob\013P\275\321\274\363\312\030\n \300X!\003\357\376&A\210\261N\222T\205aS\224 W\342\014\261\345Bv\262\004\261\023\207\327\310R\204\366\372$\277\307\327\222\305GI\243\230\\K\006\025+\252X\232\205\225=\255\217d\020?e\242\010\300\362\3540Ly\300\223f\025\247\257\345\373\226\372\332)p\315g\300\260\203\021\230\260\321\242`v\251\210G\277\373;Q\233\202\206\323y\223\",D\351\035\021}\224\355\t\311\321\321\325_HCdLK\260\315\006\354\224\035]\260\034=\203v\227f\032N#{\341\005\257\221\346{9\r\226\313Aa\252\027k,d\304\"x\226%\016T\020\002\376\252\010\007\233\034)vo\345~\022;\270ND\177\240z\246\242\324}]\027%:Y\203\262\352\224O\017Gl\233\350\177,78\355\325\351>W\343O\037\322\361v5\344 \256\333`;\250`T\337\201E\nQ\355[\030n\325\024\306\360A\202*\221\021\3619\036\256\2751\370\347\006\331\232S\344X\210\231\177\\O6\354;M\333\267R\354X\271\361\275o\365\344\366\241\343\357'\222\207\313\r\203}20\351\3503\ne\2718<\242\351\220\3632\212\032\025\2233\321\301u^+t>#""\213\367\236^\370_N\334\2244K\210\345a\345\np\340\321T\205C\241\3071\341C\327\335m\337\310\341\206\200\344\004\0335A\276F\223{\314\\\014#\227C\301\337\021\273\275\027\"97\323\020\316(\204\327r\241\302<\302(\226d\2416J\241\314\032\330t\304z\233\26699 \375\220\320\241\334t\210\345\202a\023.\232Sxd\301\014\343\016\n\213=\000\364w\206\356\361\267o7\262\240\345p^\315\022L\306H\302\023bS\352\234\234\236\2653\356\336\233\242E\202\272\014\266\177\237B\356w\010m\324,\336'V\235?O\347z\335i\3258\216\313P\373[\212\030\203\306T\353\205\2315\037\357\340u\375N\244z\374g\024+\256>\273*\360\026h0\330a\257?[\304f\326)\270p\353t\372\217[2\034O\031\356\021:\365\362j\237\002\244w\304t\177\377\376\035\217\027\331\360\374o\032\236P\361\343\300\374\317\356\364\031\216Z\2146\326\331h\233\316.?\350\237U\017\366\207\375\354^\307`\316\347\262lr&\273#\225\366\251Y\360~\207d=\0272S\263Q.^\310\206\276\355\343\354\177/?\3204\214=KV\\\364!\227\321\030\211\211\277\213\214\343\274~7\235\347M\220v\271\272\263-\037l<\376\267L\361\347W\252qkI\220\320j\234L0x\373\216\242y\223\315\0310\366\204\350\245\017\367\340\252&\335\033\217\004\337\313\320\0274p^\n\267v\227\034\201PJ8t\0317\326\271\215q\212\356\363\323&Q\360\261\332Q\223\264\333\326?\234\001\227\023d\236\347\014\203\3312(*\017p\361\3300\371\023\200\357\3424\227'\201C\272\344\216bv\314^\261\274\354\216TU\036\212\212\236\000\301\214\366l\246.\3434\n*\246L\252\252\212\350]\213\312b\367r\343\034/n\023x\267\220\242J\336@\352\034\375\275\377~r\300\217\210\336\341)\331\017\314\357\267\373\233\371\003y\231 EF7\262\344\306\273\217\030\306\370\030\201\356\200\313\223\251|\237\177\250n\375\354\301\214\352\251\000\252]\276a0\273\n0\270wE\031U/w\327\006&V\302\247\207\033\n\220\0306;\367\261\376M\3108b\021W\246\306<\212\347;\241D\337/_\025\373\256\302\257\344\277J\370\233\2528R\261\376\267x\035|\323\351\206\0073\225y\367\305]\004\347w/^G\251x\267\350q\363\312+y.\333\267n\343\230\270\254\367y\316_^\332\266\340\345rV\257\236\326m\026\236\370\034\356\023\252\276""\225\255\240\360[\266\177\026x\226\337Kr\345\262\001Wkk\207\275qU\316}\233In\255ql\214\245\253#\337Z\266U\377\252\225r\265\213\025S\303^\266uB\220\250\246l\233>;\362H7e$/I\321\327\3175\222Mb\355\335s\203[_\\\354kj\026\213r&\251\347\263@\325\241\245\251\352\034\304\323\371vj\223Gu\2217\317\022*\321\230v\016AS\305\224\346wt\017L\212NN#\004Z|\\t(\311\253\320\227\307Ma,\003\364p\2256+\226\327\244-'\333\275\320QN^C\264c\262\233\302i\310\204\\:I\231\246\234M.\321Y)N\006\004S9\216\321\216'{L\202\305\rBT\256\262b!\016g\014i\245\2574E,(\215<\2172\232\356\320\236^\310\203\007\251\232\230\234\353\350\300\220{\274\321j]B5\334jI3cD\350\223\365pN\245\tp4\022K\355\253\235U\216\367\211\331\256\266h\"\322\314\225g\327\202\226\235;\313^\311\365\253e\005F\031\0253A\201owS\212\235\344\314FJr\3038\242K@\352\240\034\247P<\353\211j\313\3321=\333\352L\307\245\232\231-l#\t|\rB\204\313l\211\333\"/\206\207\227c\362\"\370\\\023\377\027rE8P\220l\266\355\314";
+ PyObject *data = __Pyx_DecompressString(cstring, 6697, 2);
+ if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
+ const char* const bytes = __Pyx_PyBytes_AsString(data);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
+ #endif
+ #elif (CYTHON_COMPRESS_STRINGS) != 0 /* compression: zlib (6885 bytes) */
+const char* const cstring = "x\332\355[KW\033I\22666\270\260\r\266\301\330\3453]\335'1\270\014nP)\362\241\007]\345i\036v\227\317qU\233WU\365q\327\210\224\224\200\272\204Rd\246@\352\3233\343\245\226Zj\251\245\226:3\033-uf\305RK\226\374\204\372\t\363\335\210L\275%\300\217\251\3563\215!32\0367n\334\270\367\273\367F\246\027\244\345t\3322\263\211\003\3351l\311\3317$\335\212II#\265\347\354K\273\246%\351R,\023M\304\244\025\343\257\t\303\222lc\357\300H9\276\2337%\374l\333\030\364\007=c\333\213\257\314\250\3568\246t\230\321\343\226\356d,C:N\200F\352+MJ\233\211\224\003\352\246\2447gk\231\310\307\211m\032\206\264\264\233I\305\226vbz2\266J\323.[\261W\274\313\216\313\214\2354\217\301F4\343H\007&\346\320c\261\214E\324,\303\316$=\276\226\255={\211\227\350'\355\260\205\264#\343O\301\237\272$\255\232)\3072\223\036_\346._\270\273B\335\226\3445\311\311\244\223\206\355\222\3330\260\236T\013\305\345\246\214\216\364d\306p\373=\317\352\007\030\266\264t\263\321\363\331\263g\255\213n_\324\334\234\177A\362\317/Hs\262\266 1?/\006\233E\334\250}\276A\215\205\375>\277\252(r8\020\n+r\210\005/;\221\346\0258m\255Y\244)[&\322T_(\244\311\376\240\032V\265\260_y\277yDI\324\315K3R2\2212~'\031Y=\346\270\033'\331\373f&\031\227\242\006\346\366\373\232\214\250a_\270\365\207}\000N\026\233\234\3042v\372\202\234(\001_X\016\004\003\001\231i\001% \277\037#\213Z/V\3327\300\037RC*\013hJ\320\257\211\235^\205ad\222\375m\325\265\276~\366\332e\027K\322\246\243[\216\260\204\376\206\340k\031#/I_\353\251x\322\270\314 eIz\236\212\237?\342C\230\033\201\307\272'\207\036\373\320v'\301\033\007i'\347I\252A\n\226v)\262\215\355\r5w\324L\222\246\353\226\2134\rz\241\313\022o\330*\n\241\336\304\245#\303\302`=\371\356\263\320\022\344\246\212\252\347\254\202\371\203\276\240\237\300\210\251r \354\367\207.\273\246\2768\007\355\367\313\341`\200iZ@\325\344\200\362n\322\352&,\373}0\337\020\223U%\034R\375\301\300\245\005\264\310\032\273\254\265\221\366\313>M\221\203J\210\005TU\013\311\332\245H\253\rpR\373\352\317\002jZ<\227df\034;\0217\032\363\004\002\276@\353O\360""\3358\0300\177\3224\323\211\324\236\024\325c?5\210\253\236\236u\240S\324\314\244\342\324\3332b\216\236\332\003d|\024\224\322\337\r\247\364w@*\375|\254\302\0323\326b\3021\016DG\254>\r\317\202\345\221(\372\310egg.\373M\"\265 \345\3705\373\215\236\245\262\236\235\337\331\271\004\332\255\020e\273]_\373\331\030\357\304\233I\221}\376\371\213\223m\361\351\335\241\303\034h-H\374\322h\375\250\332\361\241c\273_`\003y\310\320)\345\013\306\204\255\373\030\324z\355cO\352\027\014\004\373\356\2467A\332\202\234\347\036=\336\225\274\337G\322\343\356i\265>\250\357\226\335(\250IZ\321|\032\"-U\241\271\371\217\024P}\010}\203Z\220/\223\377\010\275\242p-\365\304\221\216M\353'\302D\351x?\021\333\227\300\227a\331\330\241\204\231\222(\257\340\005$!\031\333x\221\000Wm=l\004|\316\261a\244x\306c\035!\270\002\014\350\014?\254)L\326\214E\277\3266b^\372\n`\"6\247ym\316\266ee\304I\356f:\231p:\337\273\272\311\032\325\357\301\203\244Z|\352\007?\027\363hp\303]\222^\233v\202;@\335;Y\201\376\332\234\311\306)GsP\302\376\332\264\022\177%\365N.Ik\t\313\365\236\356\204\226\236\023c\033\207ib\274\364\022\355\220\300B\203\020\375\354\354p\036\340\243\023n<\223\206\341\301\212\370Q\354\237Z\204\360;)\261+\355\352I\033\2117\250\246.N&%\375\320-\314\001p@\001T[vjKs<\354hOY\243\006-\220\216\022\233\242m\252\\\242\365\204\350\030l\330\231X\314\260\355\335L2\351\nhA\342\247\241|\332\216\260\004\002\336\003\267\311\366\274z\3601#?\247s""\375\221=\307g\020j}\261\363\306\005\211\277\276}A\022n=\036\274\354i\345\007ae0'\260{\315;\321T\230\217h\001>\334CF\224Z\307\270Uh\014\2048\330\210\207P\260I\342\343,\201\232\311\342{\313\322\007\034\t\002\177\303>\026\324X\220\007}\276`8\344WA\t\274\371C\234\315\240O\r\252L%jmtZ\353]!\204B\360\025aF\357\207\202\336r\031ui\251\013Cp\262\026\356\"\327ZO\3354\237\354\227\333\030\t\323\213\300\200\334\306\2608\336\365\005\203\001%`,2\217\344`t3S\244\335\342\323\016\341\023\271\311}4\210{\352\330M\200\263\007#\334y\300\360\256\240pN\026\334O\325\226\235\255\013k\033\334\332\337\235\251\\\222\177\272\004?\344*\2640\205\025\035k\3427\316E\320\247\250J\010d\003\276N\353jo\"\031h\2760\247\245*\356\360\260\302\311\211\3466\261x\372/\016|.\343\317\273\336\202v\236\031u\274\375l{\215\331\277\357\305\274\274x\r\362\316N\236\017\377\307\362\361m'\202\2565\267\275\214\021\306L]\373;x>\340\375\375{\313K\250\213\273w:6\353xg+\354\212l\240\257\357\321:\277k\230\363\252z\277 \274\354\344\274\330w\366\0170\301 \362\235\257N?\360\274\375c\222\0176q\363\325\373\300X\250\331M\353\232Z\353\303\324y\013vA\271e\336^rn\366\322\272V\254uN\353\201a\327+\377\367Hw\376\231\352\3742\251\216\263o\031\306\337m\256\323\370\226e\320\3071\347&9\203\276\247y\277\211\317O\256\264\363\3419x\351H\354\202\334\321\374\347%\\\"\326\241\236J\260#\334lTR\240\305;\007\265\217\304e_\350G\320&\313\n\035\206\252\276\200\252\212\024E\024\273\022\236\326\372f\030)\002=E\343\257\233\332\007\264\326\213D*\020\014\006\333\347\362\213#\"\346\327T\245%\037\362\226=#\375\360\303\017\322\313'\007R\312t8\362%\223\260\024\344A\256\375\354\232\311\244y\314?\2763\350\325\006,\006&\0367\354\204\245Ga\003\037P\021\373'\246\027\323\303\256\322\245\324\365|\247\360\216Y\342?N\206\330\211\245\227O\021\233\037z\266\246Y\2756\274_n\370\3611\347\"\254\rH\373.\310`\240%;\014\366B\247`\013:\211l\266G\332\266\235\372)e\036\247\334\335\212\033{\330 \337\277\366\375\352_\232\343\221\277\242\310\363\035\237\302\271-*\023-\335\037\344\272""\035\230\306\346{|\204\351\266\312\341\320|\237\317.\274\361JH\233\357\375\201\222\327C\326\264\371x\302&\3640Rt}\274\267\027K\330\242\034\357\371\346\335\033\3124u~\356\261=?\340=\227\327U\225\203B\204\244\016\021w\000}R9\367\206-Hod\010\377G\334\351\217\036\336\000EUz\360\373\030\275\305\220\177\304\217 \360\204^{\314\321\000\332\036\372\243\2079\032\300\343Jw\300<~\236\360\001\255\2635\330\20165\017\365\335\332\220\032\232o;\231p\353\003Li\251w+5M\236oD\306\336vB\277\272\224\333\353\037\nw\264y\r\376\340|\204E\034\376'\343\037/\255\376\361\233\327/_=_[[{\376z\363\345\253?~\3732N\237\236:\271V\351BU\"\221\327\271,\376\326\0221'\362\255\221u6\214\335\365uecC\336\220#\353JD\327\231\316\"\212\316\262:\313\351\262\256\3501\323\326\223\211\275\224\021\217p\305\210\360'\022P\304\261\364\224\275kZ\007\374\375^\004-I\\\350\313V\250\270\221\212\367U\364\276\r\253--\335J>\260qU\267b\373\272\235H\341\222K\305\022\246/fZf\006a\264a\353\216\236\222\365\254\236\305E\321szN\326sJ4\312\242,\033e\271H\324t\366#\002\335#\272eD\260\026[?0\"\364\231|\304\334\215\210h\222\177\277k3q\223\243f6\212\177r4\027\315\311\261\030\2131\372\215@\"{F\214\311\370\365\312\331\030\313\305d\206_\267F\226\361\353\226a\253\302D\273\377\363Zw\315j\244G\335\206\201m\261\215\016\300h<\276\326-,\205t\240Y\305W\332\033Gz\327\256\366\300\224\266\252\366Y\232\325B\246\221XR\267\355\310\236\341\320\367\321\364\210M\211$R\244@1\203\376W@,i\322w\017\311\244\013\003P\272X\203U\030E\333\303*\327C\361\t\201\370\200\240\017\250\365\0062W\215\305>\272\017\342\332\366\202\034v5\240\315\367e\322\304R\355g\276\203D\234\253\316\305z\177\231\324\017\242q\375\231\333\317\025D\257i{55\351\354\031)#\233\266b\331X.\036\367\307Y\234e\343,\0277\222\216\316/\021Y\334\224\270a%\216\224\270\031s\014\333\211g\3439\303`\006\313\032,g\310\206\2345\344\034\014\325H\333\211$\342#q[\203\266;\266\2215\262\330\256\334\256\236\000\2567\336\367\373\350}\277O\267\220\247\362\347\216\206\226\017\001:Z\032`\261K\202\217D\350\333\352H\304]""\307>&L$R6\375g\244\226%GP\333*\201\306\203\335\232EG\022\330F\317\326\351\201D\227L\374\324V\356\022\035x\"\245Kp\235\264\1772r\336\326\210\021)\203\321E\356\351\320\250\322\323F\276ON\304\275\001-\322N$r\200\\\224\256\316\376\201\236\213rr\320\023\374\276v\032\372\02290\343\231\244\201{&\351\240w\212 \207_\343<\200\003\227\216\031\375\213\271\273\313\360'c{\010\207\326\020\025\nf\30496\335JWx\021\035\216\337>\264\034\233\336+\330\307\344\352\343v\326\316\341\237\3430\207Ed\374)\216\034\211\020\344\210+T\035\031\017\324a\3372\217\0353i@]bF\003\022\204\237\340\217\364_}P<6\3057.\021\030\246[\0229\333\221\377\210\035\311G\312\221\312\237E%?\275\312f\375Y\226\225\263JV\315\256%vw\263\033\244\204\271\034\313\3119%\247\346\250.\307\353\336\016\375\354\2772r3?\231\227\363\353\247\303\267p\333\310\037\236\016\217\347\267\013\217\n\313\247\303w\362Va\262 \0276\n\250\0359\035\245\347\373\205hq\270\270R\214\226\256\225\202\345G\345\225r\2642\\Y\251\030U\271\272Y\273Z{\\\263N\356\235\254\234D\353\2577\352\033\233g \033\004\265\265\342\325\342|I.m\225'\313\n\206\014\235\366k8\033\276\231\237\315\307\nS\2306\203\211\364\323\316\212\263\341\261\3742x|\\8*\376\271\374/\225@\365~U/2,\347\351\225\221\321\267\177+00?z\267p\275\240\027\234b\2404Yb\247\243\267\362_\026\016\213C\247cS\205\027\305\351\242Z<*m\224\016O\307\356\325\357\315\225\257\226\347*""\323gT\236/OP\371\347\353W\306\247@\347Ui\250t\257\264\\\332.\317\224\327\313FE\251\350\225Lu\2556R[?\275q\277\260^\320\273&\272S\277\363\270\264^\332/\203\317\007\365\007re\272\022\2522Z.\032\247\n\313\205\315\342\235\322ay\264r\025-Z\365\032\344\266^\215\327fj\033\265\303\223\241\323\321\261\374\213\3024\3262ze\344F\376V\001\203\306\363\353\371$xf\365+\277-\263\237?\27322V\277\365\033\010\206o\310\203\30216\343zi\257\274Q>\252|_]\251\356\326\226\337B\276w\353w\371j\352\362Jm\272\006\016~\305e\364\371\300-\027\233\362\2748\t\tYX\220\302\351Z\225\311\212Z\261 \347h\355ZM\256m\237<:Y=\261\352\353\233\365\315\355\372\366w ;5\220l\213\362\\+\262\"\266\347f\376Q~5\177\214\212\253\305G\305\325\342!\364I\206\234\037\225W1\335\275\312re\235:\315\200\020)\241\002\021\037\236}0:c\030s\330\312\365f\341\032Wt\273\205\310F\311\346\n\036\303\352\345\312F\305\256NW\025H\340j\355Qm\245\026=\271v\302N\260\266\033\357K\300cF\346\033\013\345\351\267\261::M\242S^\311\353y[H\226\226\266\016\353\230,\250\005\253x\257\270\\\334,]-\315\226\242\345ke\306e0q:\374\311\333L~%o\200\301\037\320Z\270Z\230\201\036_-\316\202\303\341\322J\tFz\2333\241\360\235\221\213[\334\030h\277n\344\3577\330\273_\214\227\036\225\276./\237\336\270\235\217c\001/\033\334\216\224\327\261\272\007W\026}\264)cy\025\022\230\022\310s\253~\353\363\222^:*o\203\237L\025\312\355\332\000\251\367\023\3106P\375\0242\375\374\344\023R\311\r4\007\010\007lHf\253>1\313\315\367q9S\201\264\356\026n\026\247\211\247\211\374\027\305\t\330\324\277b\231\254\374\307*\253.W\377\355D>Y\207\321\222\212\220\314\034\262X\241nFQ+\361\305?\303\250\0310}\255\244\226h\317~?\3049yT\232\346h\370\242\362\244:Y\r\324>=\271z2-\364k\034X\265Y\030*\334+\254B\002\223E\205C'+\255\226\254\362}\250\306De\246\262Y\275Z}T]\255Z\265{\265\345\332\246;\370\031)\347=\014\337\202\3704H{\232\224\r\025\247\243\277\202\210I\257\3537?\343\020\331\243t&\372""\362\301$\373\251\342F1#\266\352\335\210p\016\316\\\005 e'\005\230\001\325\303\267\313$'G\030\027\337\376\365\242^$\361\254\014\271k\020\263A+\3523\032,\301\251\312\365\257^{.\342F\237U\022\013P\230\372l\240\022\255\016WW\352\317\334!\200gn\005\202\017R\234\027Cb\313g\353\267\t}\215\262F.\346n\3416\255\246s\273\277\205\260\227\373m4\343\n\367k,!\006\350_+\017\t,\036\317\377\201\373\025\030\334\0230\312\310l\3278\362\333\240K\233:\202y\343\334O\220\275k\325\241\352\004iL\250\020$\354.\276(=-/s'\302*kh\204\317\252\222\210\276\006\347\267@sB\220\236 q\334\307\312\206N\275\002\325L\320\023o\233\342\332\204\342'o-!&\3019\337\327\007\200\205\331B\014k\375\002\376\224\235\272\235\326\n\334\346\005B\221*\220\325=\342\312\340\216N\024\217K\200\002\227\024\330z\345\nt\232\213y\010\022\277q7\377\037\256T \206\321\374\010pG\367\340o\275\273 \366u\245a\374\244\200\207\247|\005\033\034\275V9`\361-\346\326p\017Pi\000(\267`B\354\254\223\036\341\367\204\220\tQ\374\2524\344=L`\311\321\342xi]\340\272%\204\303\315\037\020\262I\236\202\334\352st\234\346\0005R\344\020{\010\201@eOo\334\001;s\300\355\3453\010i\030\240\264\001\3377\305\325(\000\370\034\275\315\241s\2134\210\306\t\344\005ln\271\016\345\250aZ\215\312h\361\272\013\215q\300\302s(\004;\247\221\324\351\026\3714X\t\346>\033m\032\332\017\250\272\207\3361\276\247c\023. ]-\315\270.f\r\301\304\343\312\341\351\370]\216\314\204\232n/\271\025`E%\305*\254\253\253\250\3555\352\254\263\275\253\310e9\214Pn\350l\224;3<,\027\276\307\326\306J\023\234\314\325\3624\255p\205\273\016.\275[\334g\"\036\273K\332\354\355\317\243\302\n\264w\222\243-\247\n\353;\235\232-}S\301\236\336\316\037p\237\357\025\316<\323\235D(\3304d\376D\0101\0016\276\206<\377D\230\327\260r\212\"\310\240G\337fA\377\006&[B\360\371\0208D\0265\001\307|\210\020\370k\370\225uz\024}\003\360E\323\227\353\213(\226T\370S2Nx)!\321\237J\237\226oU\202U\nA\001\235\037\276\021K\237$C\272M\306\213x\364\014\246|\035eh\371_\212C""\305\t\332\032\025:>}:6.\264\222\357\317}\354\347rq\235\360\237\307\001\334\376\373\334D\270\345\0204\367}\330\202\340Y\217\212&f\220\235\014\365\250\000\003\034y\343\305\317aC/\312\263e\375<\206<\374\364\346\313@\315\364\236\017\377w\254\365\027R\223,\353xh\247\277\334\227\366E\326\373\376\263|\322p\017\303 \271\017\034\237\001f\022H\251\024v\022\230\016\235u\364\212\361\370\211\034\201\353\217\266\340\002V\001\267\274{[\305\333!\341,\270\227\271\376v\217\207\271\023^Q\247\364\364p\360\266\323m\233c>\341\024\017v\306)1!\313\020\005\027\214\304\314\300\334\311\302\347<\002i\026\032p\357\026\310\021\336q%5z\333+\334q\223\213\326\340\367\"\225\236\037&\241\005\204\0378\267\203\230Ts\203\257\214\333(\222\352\301\215\343n\322\034A\000>>Yx\202x|\017\261\361\223\312\375\212N\342\377\204\207\350FI+\013]n\333\001.\355^\333\360\241E\177\373\275\244\353\356V\273\034l\310\260w\345\031\027\312\347\365\207\277-\313\310/\236 ,\333Cz;}\332\"\016\326\020\006e\342\"\363\022g\0106\200^D\362\017\312\207\225\213t\270\225\327`Y1\004C[\245\007\360\335\323Dv\252>\265H\221a]Y\255\261\332\262gYA\370\347\365S\036\007\304\353\223\363\334\371\217>,\336F.t\310\263\265\365\262^v\020NR\216{\203\316D\276GLu(F\304\nS\365\373s\302\361>,\336\344|,\303\321\301\363\300<\301\013ez\025\275rx\326\000~\370\247\t~\364\360{\036\265O\211\034\376\206W\030\315_\023\361\330h\376*\222n/q\245@\365\223\267G\334\231s\371O\363ho\2024\306A\310\274*\\3-h\211\207\033\334\277\210x\201\"3$\226-J\342\372\036l\222\233\337\337\345\212\262\322\310\357m7nu\0253\346%\211\035\217\"-\246\360\351wHE\034$\343o\260\344\373\310#n \307zq2s\262)R\t\276\267\205\317(-&\025\342{\365\031\022\262!\nByJ\006 \372y\366\312\310\365\267[\010\257\235B\000{\367\035\262\277\243\372\342\357kC\265\3735\343D\253\277^?s\321\365\360tPa\23435\\x^\374\r\366p\004\0327[\211a&\245\032\257M\237\333<\206\034\204l\205\372\235\2155O\347(\255lB\005E[3\356\241\r\304{\241ng\"\376:\275>\232\037BL\255\223T\226\350x\253\307\332\3533K\325\351\252\306\317\300z,\221G5""\024m)y\036\017\177\217Pp\235\007u\254\177=\026D\032\354\305\207\"\010\224a\244NI\341\234\212\203\306k\374\030\305\252z\361\242\027F\"\334\354\311\372M\260.\302n(\366\351\2551RP\356\376(\3239\346'\231\317K\277&']\230\246\353\317_^\031\271\223\327=}\rx\007F\004\035{\205\357\212<\272\234nmf\302\332\364\374\221\010\245?\253\177\246\300\"_T\247\317\250L\247\177\001\260\313\255\363;\216V\303\374x\350:p\031\354?\254?\\\304\036\034\023\3766\314\260\031\252\n_\374\n\325t\260\263\315\375\253\0014\327\313\031\344\220#\325\365\002e\220>\262\326\007\302V{\334FHX_C\332\333\305\307\220\247Z\312\302\374G*\333\325Yh\325lm\377D?\311\324\267\266\317\304\204\227\353M\254\206`\344\317\371\321\316,\235z\362\203\226\313\265\362\355D\365=\367\220(\206\004\210\016g9'\342\324\204\262\035\3277L\210\361c\017\021q\335\245!vq\021\3262\214,h\004\206;^[\257\305OfOt\n\203G?\366\034\210\246\357\362\270m\033>\305=\364\272\323Y\341\255\364\245\233w\322\326\364ll\212G;\343\367\271\241\022\376Rfq\343\266{\222*\302bY\304r\302\343L\234\216M\"\027'\203\301`\230YC\254\224\003\016\227\2279\334\334\027\2079U\335%\336\360\256^\244\030+\270I\310!m\327\227\3740U\361\216*\356\025^\225n\226\247;z\337\251\337\221x>\354:B:\215\345&\364\357\"\t%\371=\245ut\315s\207\3225\212\016\332\307\375'\022\364\241\036\2434\257\303\337\260\364m\250\313\r\361.\202{j\227\213\307P\253\266j\316\005\217#\032\364\304\253\212\026/\377\200\016\"Z|\370\203B+$L\025\304\221K}\234\316i?\025\301\334\033l\311\023l\"6e\262\360k~V\314\221\365\317\020z\240\324\310\331\376\014\237?S\336\252\334\203v\331P\023!\356\237\347z\035\230u\235|\315\365;I\035\313\177\313\317\275\334\256\004\2657\363\023\371\317\021\335\030\360\334+t\000(\216n\002\000\337\307\356\321g\274$\346\377_\376\243\024\271";
+ PyObject *data = __Pyx_DecompressString(cstring, 6885, 1);
+ if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
+ const char* const bytes = __Pyx_PyBytes_AsString(data);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
+ #endif
+ #else /* compression: none (22843 bytes) */
+const char* const bytes = ", Approximates the arc length for a cubic Bezier segment.\n\n Uses Gauss-Lobatto quadrature with n=5 points to approximate arc length.\n See :func:`calcCubicArcLength` for a slower but more accurate result.\n\n Args:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n\n Returns:\n Arc length value.\n\n Example::\n\n >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))\n 190.04332968932817\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 50), (100, 100))\n 154.8852074945903\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (150, 0)) # line; exact result should be 150.\n 149.99999999999991\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (-50, 0)) # cusp; exact result should be 150.\n 136.9267662156362\n >>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp\n 154.80848416537057\n Calculates the arc length for a quadratic Bezier segment.\n\n Args:\n pt1: Start point of the Bezier as 2D tuple.\n pt2: Handle point of the Bezier as 2D tuple.\n pt3: End point of the Bezier as 2D tuple.\n\n Returns:\n Arc length value.\n\n Example::\n\n >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment\n 0.0\n >>> calcQuadraticArcLength((0, 0), (50, 0), (80, 0)) # collinear points\n 80.0\n >>> calcQuadraticArcLength((0, 0), (0, 50), (0, 80)) # collinear points vertical\n 80.0\n >>> calcQuadraticArcLength((0, 0), (50, 20), (100, 40)) # collinear points\n 107.70329614269008\n >>> calcQuadraticArcLength((0, 0), (0, 100), (100, 0))\n 154.02976155645263\n >>> calcQuadraticArcLength((0, 0), (0, 50), (100, 0))\n 120.21581243984076\n >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))\n 102.53273816445825\n >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0)) # c""ollinear points, control point outside\n 66.66666666666667\n >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0)) # collinear points, looping back\n 40.0\n Calculates the bounding rectangle for a quadratic Bezier segment.\n\n Args:\n pt1: Start point of the Bezier as a 2D tuple.\n pt2: Handle point of the Bezier as a 2D tuple.\n pt3: End point of the Bezier as a 2D tuple.\n\n Returns:\n A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.\n\n Example::\n\n >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))\n (0, 0, 100, 50.0)\n >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))\n (0.0, 0.0, 100, 100)\n Calculates the bounding rectangle for a quadratic Bezier segment.\n\n Args:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n\n Returns:\n A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.\n\n Example::\n\n >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))\n (0, 0, 100, 75.0)\n >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))\n (0.0, 0.0, 100, 100)\n >>> print(\"%f %f %f %f\" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))\n 35.566243 0.000000 64.433757 75.000000\n Couldn't work out which intersection function to useFinds intersections between a curve and a line.\n\n Args:\n curve: List of coordinates of the curve segment as 2D tuples.\n line: List of coordinates of the line segment as 2D tuples.\n\n Returns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\n Examples::\n >>> curve = [ (100, 240), (30, 60), (210, 230), (160, 30) ]\n >>> line = [ (25, 260), (230, 20) ]\n >>> intersections"" = curveLineIntersections(curve, line)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (84.9000930760723, 189.87306176459828)\n Finds intersections between a curve and a curve.\n\n Args:\n curve1: List of coordinates of the first curve segment as 2D tuples.\n curve2: List of coordinates of the second curve segment as 2D tuples.\n\n Returns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\n Examples::\n >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]\n >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]\n >>> intersections = curveCurveIntersections(curve1, curve2)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (81.7831487395506, 109.88904552375288)\n Finds intersections between two line segments.\n\n Args:\n s1, e1: Coordinates of the first line as 2D tuples.\n s2, e2: Coordinates of the second line as 2D tuples.\n\n Returns:\n A list of ``Intersection`` objects, each object having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\n Examples::\n\n >>> a = lineLineIntersections( (310,389), (453, 222), (289, 251), (447, 367))\n >>> len(a)\n 1\n >>> intersection = a[0]\n >>> intersection.pt\n (374.44882952482897, 313.73458370177315)\n >>> (intersection.t1, intersection.t2)\n (0.45069111555824465, 0.5408153767394238)\n Finds intersections between two segments.\n\n Args:\n seg1: List of coordinates of the first segment as 2D tuples.\n seg2: List of coordinates of the second segment as 2D tuples.\n\n Returns:\n A list of ``Intersection`` objects, each ob""ject having ``pt``, ``t1``\n and ``t2`` attributes containing the intersection point, time on first\n segment and time on second segment respectively.\n\n Examples::\n >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]\n >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]\n >>> intersections = segmentSegmentIntersections(curve1, curve2)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (81.7831487395506, 109.88904552375288)\n >>> curve3 = [ (100, 240), (30, 60), (210, 230), (160, 30) ]\n >>> line = [ (25, 260), (230, 20) ]\n >>> intersections = segmentSegmentIntersections(curve3, line)\n >>> len(intersections)\n 3\n >>> intersections[0].pt\n (84.9000930760723, 189.87306176459828)\n\n Lib/fontTools/misc/bezierTools.pySolve a cubic equation.\n\n Solves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.\n\n Args:\n a: coefficient of *x\302\263*\n b: coefficient of *x\302\262*\n c: coefficient of *x*\n d: constant term\n\n Returns:\n A list of roots. Note that the returned list is neither guaranteed to\n be sorted nor to contain unique values!\n\n Examples::\n\n >>> solveCubic(1, 1, -6, 0)\n [-3.0, -0.0, 2.0]\n >>> solveCubic(-10.0, -9.0, 48.0, -29.0)\n [-2.9, 1.0, 1.0]\n >>> solveCubic(-9.875, -9.0, 47.625, -28.75)\n [-2.911392, 1.0, 1.0]\n >>> solveCubic(1.0, -4.5, 6.75, -3.375)\n [1.5, 1.5, 1.5]\n >>> solveCubic(-12.0, 18.0, -9.0, 1.50023651123)\n [0.5, 0.5, 0.5]\n >>> solveCubic(\n ... 9.0, 0.0, 0.0, -7.62939453125e-05\n ... ) == [-0.0, -0.0, -0.0]\n True\n Split a cubic Bezier curve at a given coordinate.\n\n Args:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n where: Position at which to split the curve.\n isHorizontal: Dire""ction of the ray splitting the curve. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\n Returns:\n A list of two curve segments (each curve segment being four 2D tuples)\n if the curve was successfully split, or a list containing the original\n curve.\n\n Example::\n\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))\n ((0, 0), (25, 100), (75, 100), (100, 0))\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (68.75, 75), (87.5, 50), (100, 0))\n >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))\n ((0, 0), (2.29379, 9.17517), (4.79804, 17.5085), (7.47414, 25))\n ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667), (92.5259, 25))\n ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))\n Split a cubic Bezier curve at one or more values of t.\n\n Args:\n pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.\n *ts: Positions at which to split the curve.\n\n Returns:\n A list of curve segments (each curve segment being four 2D tuples).\n\n Examples::\n\n >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (68.75, 75), (87.5, 50), (100, 0))\n >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))\n ((0, 0), (12.5, 50), (31.25, 75), (50, 75))\n ((50, 75), (59.375, 75), (68.75, 68.75), (77.3438, 56.25))\n ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))\n Split a line at a given coordinate.\n\n Args:\n pt1: Start point of line as 2D tuple.\n pt2: End point of line as 2D tuple.\n where:"" Position at which to split the line.\n isHorizontal: Direction of the ray splitting the line. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\n Returns:\n A list of two line segments (each line segment being two 2D tuples)\n if the line was successfully split, or a list containing the original\n line.\n\n Example::\n\n >>> printSegments(splitLine((0, 0), (100, 100), 50, True))\n ((0, 0), (50, 50))\n ((50, 50), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 100, True))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 0, True))\n ((0, 0), (0, 0))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((0, 0), (100, 100), 0, False))\n ((0, 0), (0, 0))\n ((0, 0), (100, 100))\n >>> printSegments(splitLine((100, 0), (0, 0), 50, False))\n ((100, 0), (50, 0))\n ((50, 0), (0, 0))\n >>> printSegments(splitLine((0, 100), (0, 0), 50, True))\n ((0, 100), (0, 50))\n ((0, 50), (0, 0))\n Split a quadratic Bezier curve at a given coordinate.\n\n Args:\n pt1,pt2,pt3: Control points of the Bezier as 2D tuples.\n where: Position at which to split the curve.\n isHorizontal: Direction of the ray splitting the curve. If true,\n ``where`` is interpreted as a Y coordinate; if false, then\n ``where`` is interpreted as an X coordinate.\n\n Returns:\n A list of two curve segments (each curve segment being three 2D tuples)\n if the curve was successfully split, or a list containing the original\n curve.\n\n Example::\n\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))\n ((0, 0), (50, 100), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))\n ((0, 0), (25"", 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))\n ((0, 0), (12.5, 25), (25, 37.5))\n ((25, 37.5), (62.5, 75), (100, 0))\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))\n ((0, 0), (7.32233, 14.6447), (14.6447, 25))\n ((14.6447, 25), (50, 75), (85.3553, 25))\n ((85.3553, 25), (92.6777, 14.6447), (100, -7.10543e-15))\n >>> # XXX I'm not at all sure if the following behavior is desirable:\n >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (50, 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))\n Split a quadratic Bezier curve at one or more values of t.\n\n Args:\n pt1,pt2,pt3: Control points of the Bezier as 2D tuples.\n *ts: Positions at which to split the curve.\n\n Returns:\n A list of curve segments (each curve segment being three 2D tuples).\n\n Examples::\n\n >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (75, 50), (100, 0))\n >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))\n ((0, 0), (25, 50), (50, 50))\n ((50, 50), (62.5, 50), (75, 37.5))\n ((75, 37.5), (87.5, 25), (100, 0))\n Unknown curve degree.?approximateCubicArcLength (line 332)calcCubicBounds (line 412)calcQuadraticArcLength (line 151)calcQuadraticBounds (line 298)curveCurveIntersections (line 1385)curveLineIntersections (line 1255)disableenable%ggcisenabledlineLineIntersections (line 1154)(%s)segmentSegmentIntersections (line 1427)\n >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])\n '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'\n _segmentrepr (line 1475)solveCubic (line 848)splitCubicAtT (line 613)splitCubic (line 552)splitLine (line 450)splitQuadr""aticAtT (line 589)splitQuadratic (line 507)_1_t_1_t_2_2_t_1_tCOMPILEDDDEPSILONIdentityIntersectionLen__Pyx_PyDict_NextRefQQ3RR2R2_Q3_aa1a1_3a1xa1ya2a3acosaligned_curve_alignment_transformation__all__angleappendapproximateCubicArcLengthapproximateCubicArcLengthCapproximateQuadraticArcLengthapproximateQuadraticArcLengthCarchasinhasyncio.coroutinesatan2axax2ax3ayay2ay3bb1b1xb1y_both_points_are_on_same_side_of_originbounds1bounds2boxbxbx2byby2cc1c11c11_rangec12c12_rangec1xc1yc21c21_rangec22c22_rangecalcBoundscalcCubicArcLengthcalcCubicArcLengthC_calcCubicArcLengthCRecursecalcCubicBoundscalcCubicParameterscalcCubicPointscalcQuadraticArcLengthcalcQuadraticArcLengthCcalcQuadraticBoundscalcQuadraticParameterscalcQuadraticPoints__class_getitem__cline_in_tracebackclosecollectionscoscubicPointAtTcubicPointAtTCcurvecurve1curve2curveCurveIntersectionscurveLineIntersections_curve_bounds_curve_curve_intersections_t_curve_curve_intersections_t..midpoint_curve_curve_intersections_t.._curve_line_intersections_t_curve_line_intersections_t..genexprcxcydd0d1d1xd1ydeltadelta_2delta_3deriv3doctestdxdyee1e1xe1ye2e2xe2yendepsilonepsilonDigitsexexiteyfailedfontTools.misc.arrayToolsfontTools.misc.bezierToolsfontTools.misc.transformfound__func__genexprhitsiinsertintersection_tsintersectionsintersectsisHorizontal_is_coroutine_is_linelike_is_linelike..genexpriscloseititemskeylineline1line2lineLineIntersectionslinePointAtTline_t_line_t_of_pt__main__mathmaybelinemidmidPtmidpoint__module__multn__name__namedtuplenextobjoff1off2oneorigDistoriginpp0p1p2p3pipointAtTpointFinderpointspopprecisionprintprintSegmentsptpt1pt1xpt1ypt2pt2xpt2ypt3pt4pxpyquadraticPointAtT__qualname__rrDDrQ2range1range2rectArearootsrotateroundss1s1xs1ys2s2xs2yscalesectRectseensegseg1seg2segmentsegmentPointAtTsegmentSegmentIntersections_segmentrepr_segmentrepr..genexprsegmentssend__set_name__setdefaultslope12slope34solutionssolveCubicsolveQuadraticsplitsplitCubic_splitCubicAt""TsplitCubicAtTC_splitCubicAtTCsplitCubicAtTsplitCubicIntoTwoAtTCsplitCubic..genexprsplitLinesplitQuadratic_splitQuadraticAtTsplitQuadraticAtTsplitQuadratic..genexpr_split_cubic_into_two_split_segment_at_tsqrtstartswappedsxsysystt1t1_2t1_3t2__test__testmodthetathrowtolerancetransformPointstranslatetstwounique_keyunique_valuesv0v1v2v3v4valuevalueswherexx0x1x2x3x4xDiffxRootsyy1y2y3y4yDiffyRoots\200\001\3600\000\005\n\210\022\2102\210Q\330\004\013\2102\210R\210q\330\004\r\210U\220\"\220A\330\004\017\210r\220\022\2202\220R\220q\330\004\005\330\010\017\210r\220\025\220b\230\004\230B\230b\240\003\2407\250\"\250B\250b\260\004\260B\260e\2702\270S\300\002\300%\300r\310\023\310B\310b\320PR\320RS\340\004\013\2107\220\"\220D\230\002\230)\2402\240T\250\022\2503\250b\260\001\330\004\013\2107\220\"\220D\230\002\230)\2402\240T\250\022\2503\250b\260\001\340\004\n\210$\210c\220\024\220R\220u\230B\230a\330\004\n\210$\210c\220\024\220R\220u\230B\230a\340\004\014\210A\210U\220%\220v\230\\\250\032\2606\270\025\270a\2301\200\001\360*\000\005\010\200|\2201\220A\330\010\020\220\006\220a\220t\2306\240\022\2401\330\010\013\210<\220q\230\001\330\014\024\220F\230!\2304\230v\240R\240q\330\014\023\320\023(\250\002\250(\260!\340\014\023\320\023)\250\021\250(\260!\360\006\000\r\024\2201\220L\240\001\240\023\240A\240U\250#\250Q\250e\2603\260a\260u\270D\300\005\300Q\330\t\025\220Q\220a\330\010\020\220\006\220a\220t\2306\240\022\2401\330\010\017\320\017%\240Q\240h\250a\340\004\026\320\0262\260!\2608\2701\330\004\013\2101\330\010\024\220A\220S\230\017\240q\250\010\260\002\260!\2605\270\003\2702\270Q\270d\300#\300R\300q\310\001\330\010\014\210F\220!\200\001\360\010\000\005\t\210\013\2201\330\010\r\210Q\210l\230!\2301\320\000+\2501\360\034\000\005\014\320\013\036\230a\330\010\017\210r\220\026\220w\230b\240\006\240g\250R\250v\260W\270B\270f\300A\200\001\330\004\020\320\020)\250\021\250(\3202B\300!\3001\330\004\033\2301\200\001\360&\000\005\n\210\022\2102\210Q\330\004\013\2102\210R\210q\330""\004\r\210U\220\"\220A\330\004\013\2107\220\"\220E\230\022\2304\230r\240\022\2403\240g\250R\250r\260\022\2604\260r\270\025\270b\300\003\3002\300U\310\"\310C\310r\320QS\320SU\320UV\200\001\360\024\000\005\n\210\022\2102\210Q\330\004\013\2102\210R\210q\330\004\r\210U\220\"\220A\330\004\005\330\010\017\210r\220\025\220b\230\003\2301\230A\330\010\n\210\"\210C\210w\220b\230\002\230\"\230C\230q\240\003\2402\240U\250\"\250C\250r\260\023\260A\260Q\330\010\n\210#\210R\210r\220\022\2203\220a\220q\340\004\005\330\010\017\210r\220\025\220b\230\003\2301\230A\330\010\n\210\"\210C\210w\220b\230\002\230\"\230C\230q\240\003\2402\240U\250\"\250C\250r\260\023\260A\260Q\330\010\n\210#\210R\210r\220\022\2203\220a\220q\340\004\014\210C\210q\200\001\360\024\000\005\n\210\022\2102\210S\220\003\2202\220R\220s\230\"\230C\230q\240\003\2402\240R\240s\250\"\250B\250c\260\022\2602\260R\260s\270!\2703\270b\300\002\300\"\300B\300b\310\003\3101\310A\330\004\t\210\022\2102\210S\220\003\2202\220R\220s\230\"\230C\230q\240\003\2402\240R\240s\250\"\250B\250c\260\022\2602\260R\260s\270!\2703\270b\300\002\300\"\300B\300b\310\003\3101\310A\330\004\014\210C\210q\200\001\3602\000\005\014\320\013%\240Q\330\010\017\210r\220\026\220w\230b\240\006\240g\250R\250v\260W\270B\270a\200\001\360\022\000\005\014\2103\210a\210s\220\"\220A\330\004\n\210#\210Q\210c\220\022\2204\220r\230\023\230A\230S\240\002\240$\240b\250\003\2501\250C\250r\260\021\330\004\007\200u\210B\210e\2202\220X\230S\240\001\330\010\020\220\005\220R\220u\230B\230a\340\010\r\210V\320\023(\250\001\250\024\250T\260\024\260Q\330\010\017\320\017*\250!\2507\260%\260r\3209T\320TU\330\014\023\2201\200\001\330\004\013\2103\210b\220\002\220#\220S\230\002\230$\230b\240\004\240B\240a\330\004\016\210c\220\022\2203\220b\230\003\2302\230T\240\022\2401\330\004\005\330\010\t\210\025\210c\220\022\2204\220r\230\025\230d\240\"\240H\250A\330\t\016\210d\220\"\220I\230S\240\002\240$\240b\250\005\250Q\200\001\360\026\000-.\360\024\000\005\014\2104\210r\220\024\220R\220q""\330\004\013\320\013&\240a\240v\250U\260%\260u\270A\200\001\360\034\000\005\014\320\013)\250\021\250'\260\022\2606\270\027\300\002\300&\310\007\310r\320QR\200\001\3606\000\005\010\200s\210#\210T\320\021$\240A\240U\250%\250u\260A\330\004\020\220\n\230!\330\010\t\210\021\210/\230\021\230!\230?\250!\2501\250O\2701\270A\270^\3102\310Q\340\004\026\220a\220q\330\004\007\200t\2101\330\010\017\210r\220\025\220e\2305\240\001\330\004\013\210>\230\021\230#\230S\240\003\2404\240q\200\001\360@\001\000\005\014\320\013\"\240!\2407\250\"\250F\260'\270\022\2706\300\027\310\002\310!\200\001\360\024\000\005\r\210A\210S\220\001\220\023\220C\220r\230\022\2303\230b\240\003\2401\240C\240r\250\025\250c\260\021\260#\260S\270\002\270\"\270C\270r\300\023\300A\300S\310\002\310!\200\001\360>\000\005\n\210\023\210A\210T\220\022\2205\230\002\230!\330\004\t\210\023\210A\330\010\033\2302\230Q\330\010\n\320\n\034\230B\230a\330\010\n\320\n\034\230B\230a\330\010\n\320\n\034\230B\230a\340\004\t\210\023\210A\210T\220\022\2204\220r\230\024\230R\230u\240B\240a\330\004\t\210\023\210A\330\010\033\2302\230Q\330\010\n\320\n\034\230B\230a\330\010\n\320\n\034\230B\230a\330\010\n\320\n\034\230B\230a\340\004\t\210\023\210A\210T\220\022\2205\230\002\230!\340\004\013\2103\210b\220\003\2202\220S\230\002\230#\230R\230q\200A\330\010\017\210t\2203\220a\220q\230\003\2302\230Q\230a\230q\200\001\360B\001\000\005\n\210\023\210A\330\010\033\2302\230T\240\022\320#5\260R\260t\2702\320=P\320PR\320RS\340\004\t\210\023\210A\210T\220\022\2205\230\002\230!\330\004\t\210\023\210A\330\010\034\230B\230d\240\"\320$6\260b\270\004\270B\320>P\320PR\320RS\360\006\000\005\014\2103\210b\220\003\2202\220Q\200\001\360F\001\000\005\010\200s\210$\320\016%\240Q\240e\2505\260\001\330\004\020\220\016\230a\330\010\t\210\021\210/\230\021\230!\230?\250!\2501\250N\270\"\270A\340\004\026\220a\220q\330\004\007\200t\2101\330\010\017\210r\220\025\220e\2301\330\004\013\320\013\035\230Q\230c\240\023\240D\250\001\200\001\360\010\000\005\r\210G\2201\220A\330""\004\n\210'\220\022\2201\330\004\014\210D\220\006\220a\220s\230!\2303\230b\240\005\240Q\240d\250#\250Q\250c\260\022\2605\270\001\270\021\330\004\013\2108\2207\230!\2301\230F\240*\250A\250Q\250e\2601\260D\270\001\270\025\270a\270q\200\001\360H\001\000\005\013\210'\220\021\330\004\n\210'\220\021\340\004\t\210\025\210b\220\001\330\004\t\210\025\210b\220\001\340\004\t\210\021\330\004\t\210\021\340\004\t\210\024\210S\220\001\220\021\340\004\007\200r\210\023\210A\330\010\017\210r\220\025\220a\330\004\t\210\026\210s\220$\220c\230\021\230/\250\022\2501\330\004\007\200r\210\023\210D\220\001\330\010\020\220\003\2202\220R\220r\230\024\230S\240\002\240\"\240B\240a\330\010\017\210r\220\025\220i\230w\240a\340\010\017\210r\220\025\220a\200\001\360L\001\000\005\010\200s\210!\2103\210b\220\001\360\006\000\t\020\210~\230Q\230c\240\023\240A\330\004\010\210\005\210Q\210a\330\004\t\210\022\2102\210Q\330\004\t\210\022\2102\210Q\330\004\t\210\022\2102\210Q\340\004\t\210\023\210B\210c\220\022\2204\220r\230\024\230R\230q\330\004\t\210\024\210R\210s\220\"\220C\220r\230\023\230B\230d\240\"\240C\240r\250\023\250B\250e\2602\260T\270\022\2701\340\004\t\210\022\2102\210Q\330\004\t\210\022\2102\210R\210r\220\021\330\004\t\210\025\210c\220\022\220=\240\001\330\004\t\210\025\210c\220\021\220$\220b\230\r\240Q\340\004\014\210C\210r\220\021\340\004\007\200s\210#\210T\220\024\220S\230\003\2301\330\010\014\210E\220\021\220!\2203\220b\230\005\230Q\330\010\017\210q\220\003\2203\220a\330\t\017\210s\220(\230\"\230A\340\010\020\220\004\220I\230R\230r\240\024\240Q\240e\2506\260\021\330\010\016\210e\2202\220T\230\021\230!\330\010\017\210s\220\"\220A\330\010\r\210T\220\022\2203\220a\220v\230R\230u\240B\240a\330\010\r\210T\220\022\2203\220b\230\006\230b\240\004\240B\240d\250\"\250E\260\022\2601\330\010\r\210T\220\022\2203\220b\230\006\230b\240\004\240B\240d\250\"\250E\260\022\2601\330\010\014\210D\220\013\2301\230A\230T\240\024\240Q\340\010\013\2103\210b\220\003\2202\220X\230T\240\023\240B\240c\250\022\2501\330""\014\021\220\025\220e\2305\240\002\240#\240R\240s\250\"\250D\260\002\260%\260q\330\r\020\220\002\220#\220R\220q\330\014\021\220\025\220e\2302\230S\240\002\240$\240b\250\005\250Q\330\014\021\220\025\220a\220t\2301\330\r\020\220\002\220#\220R\220q\330\014\021\220\025\220a\220t\2301\330\014\021\220\025\220e\2302\230S\240\002\240$\240b\250\005\250Q\340\014\021\220\025\220a\220t\2301\330\014\021\220\025\220a\220t\2301\330\014\021\220\025\220a\220t\2301\330\010\017\210q\220\004\220D\230\001\340\010\014\210C\210q\220\004\220A\220W\230B\230c\240\021\240$\240b\250\002\250!\330\010\014\210B\210b\220\002\220\"\220A\330\010\013\2102\210S\220\001\330\014\020\220\001\220\021\330\010\014\210E\220\021\220\"\220B\220c\230\022\2305\240\001\330\010\017\210q\220\001\200\001\330\024$\240M\260\021\340\004\016\210m\2301\230A\330\004\016\210m\2301\230A\340\004\007\200t\2101\330\010\022\220%\220q\330\004\007\200t\2101\330\010\022\220%\220q\360\006\000\005\021\220\004\220H\230A\230Y\240a\330\004\007\200t\2101\330\010\017\210q\340\004\005\360\010\000\005\010\200x\210q\220\t\230\022\230:\240T\250\030\260\021\260)\2702\270Q\330\010\017\210r\220\030\230\021\230)\2408\2501\250A\340\004\t\210\026\320\017\"\240!\2408\2501\330\004\021\220\026\220q\230\004\230H\240A\240Q\330\004\021\220\030\230\021\230)\2406\250\021\250!\340\004\t\210\026\320\017\"\240!\2408\2501\330\004\021\220\026\220q\230\004\230H\240A\240Q\330\004\021\220\030\230\021\230)\2406\250\021\250!\340\004\014\210A\330\004\t\210\027\220\001\330\010$\240A\330\014\021\220\025\220k\240\027\250\013\2607\270!\360\006\000\005\n\210\027\220\001\330\010$\240A\330\014\021\220\025\220k\240\027\250\013\2607\270!\360\006\000\005\n\210\027\220\001\330\010$\240A\330\014\021\220\025\220k\240\027\250\013\2607\270!\360\006\000\005\n\210\027\220\001\330\010$\240A\330\014\021\220\025\220k\240\027\250\013\2607\270!\360\010\000\005\022\220\021\330\004\016\210a\330\004\024\220A\340\004\010\210\006\210a\330\010\016\210j\230\001\230\021\330\010\013\2104\210s""\220!\330\014\r\330\010\014\210D\220\001\220\021\330\010\025\220W\230A\230Q\340\004\013\2101\200\001\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\n\210#\210R\210t\2202\220Q\330\004\n\210#\210R\210t\2202\220Q\330\004\n\210#\210R\210t\2202\220T\230\022\2301\330\004\n\210#\210R\210t\2202\220T\230\022\2301\330\004\t\210\023\210B\210c\220\022\2203\220b\230\001\330\004\t\210\023\210B\210c\220\022\2203\220b\230\001\330\004\013\2101\210D\220\006\220d\230&\240\004\240F\250$\250a\200\001\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\t\210\021\330\004\t\210\021\330\004\n\210#\210R\210u\220B\220a\330\004\n\210#\210R\210u\220B\220a\330\004\n\210#\210R\210t\2202\220T\230\022\2301\330\004\n\210#\210R\210t\2202\220T\230\022\2301\330\004\t\210\023\210B\210c\220\022\2203\220b\230\001\330\004\t\210\023\210B\210c\220\022\2203\220b\230\001\330\004\013\2101\210D\220\006\220d\230&\240\004\240F\250$\250a\200\001\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\n\210#\210R\210t\2202\220Q\330\004\n\210#\210R\210t\2202\220Q\330\004\t\210\023\210B\210c\220\022\2201\330\004\t\210\023\210B\210c\220\022\2201\330\004\013\2101\210D\220\006\220d\230&\240\004\240A\200\001\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\t\210\021\330\004\t\210\021\330\004\n\210#\210R\210u\220B\220a\330\004\n\210#\210R\210u\220B\220a\330\004\t\210\023\210B\210c\220\022\2201\330\004\t\210\023\210B\210c\220\022\2201\330\004\013\2101\210D\220\006\220d\230&\240\004\240A\200\001\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\007\200s\210!\2103\210b\220\004\220B\220h\230d\240#\240Q\240c\250\022\2504\250r\260\021\340\010\020\220\001\340\004\007\200s\210!\2103\210b\220\004\220B\220c\230\021\230#\230R\230q\330\010\020\220\003\2202\220T\230\023\230C\230r\240\021\340\010\020\220\003\2202\220T\230\023""\230C\230r\240\021\200\001\330\004\t\210\024\210Q\210a\330\004\006\200g\210Q\210c\220\021\330\004\006\200g\210Q\210a\330\004\017\210q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210U\220!\2203\220a\220t\2302\230Q\330\010\r\210R\210q\220\001\330\010\r\210R\210q\220\002\220\"\220A\330\010\020\220\003\2202\220Q\340\010\022\220&\230\002\230!\330\010\022\220&\230\002\230!\330\010\017\210s\220\"\220A\330\010\017\210s\220\"\220A\360\006\000\t\017\210c\220\022\2201\330\010\016\210c\220\022\2201\330\010\017\210r\220\022\2203\220b\230\003\2302\230T\240\022\2401\330\010\017\210r\220\022\2203\220b\230\003\2302\230T\240\022\2401\330\010\017\210r\220\022\2203\220b\230\003\2302\230S\240\002\240\"\240B\240c\250\022\2506\260\022\2601\330\010\017\210r\220\022\2203\220b\230\003\2302\230S\240\002\240\"\240B\240c\250\022\2506\260\022\2601\330\010\016\210c\220\022\2205\230\002\230#\230R\230u\240B\240c\250\022\2503\250b\260\001\330\010\016\210c\220\022\2205\230\002\230#\230R\230u\240B\240c\250\022\2503\250b\260\001\330\010\r\210U\220%\220v\230_\250A\330\r\022\220'\230\025\230g\240U\250'\260\025\260a\340\010\020\220\007\220r\230\025\230e\2405\250\001\330\004\013\2101\200\001\330\004\t\210\024\210Q\210a\330\004\017\210q\330\004\006\200g\210Q\210c\220\021\330\004\006\200g\210Q\210a\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210Q\330\004\010\210\005\210U\220!\2203\220a\220t\2302\230Q\330\010\r\210R\210q\220\001\330\010\r\210R\210q\220\002\220\"\220A\330\010\020\220\003\2202\220Q\340\010\022\220&\230\002\230!\330\010\016\210c\220\022\2201\330\010\016\210c\220\022\2201\330\010\017\210r\220\022\2203\220b\230\003\2302\230T\240\022\2401\330\010\017\210r\220\022\2203\220b\230\003\2302\230T\240\022\2401\330\010\017\210s\220\"\220A\330\010\016\210c\220\022\2205\230\002\230#\230R\230s\240\"\240A\330\010\016\210c\220\022\2205\230\002\230#\230R\230s\240\"\240A\340\010\r\210U\220&\320\030+\2502\250U""\260'\270\025\270g\300U\310!\330\010\020\220\007\220r\230\025\230e\2401\330\004\013\2101\200\001\330\004\r\210Q\210a\210s\220\"\220F\230!\2304\230s\240!\2401\240C\240r\250\026\250q\260\001\330\004\r\210Q\210a\210s\220\"\220F\230!\2304\230s\240!\2401\240C\240r\250\026\250q\260\001\330\004\013\2105\220\006\220c\230\024\230T\240\026\240s\250!\200\001\330\004\024\320\024-\250Q\250e\3203C\3001\300A\330\004\007\200s\210!\2107\220#\220Q\330\010\013\2103\210d\320\022)\250\022\2501\330\010\030\230\016\240a\240q\250\001\250\024\250Q\250a\250t\2601\260A\260Q\330\t\014\210A\210W\220C\220q\330\010\013\2103\210c\220\024\320\025(\250\002\250!\330\010\030\230\n\240!\2401\240A\240T\250\021\250!\2504\250q\260\001\260\024\260Q\260a\260q\340\010\016\210j\230\001\230\021\330\004\021\220\021\220!\200\001\360@\001\000\005\n\210\024\210R\210q\330\004\t\210\024\210R\210q\330\004\010\210\003\2102\210Q\330\004\010\210\002\210\"\210A\330\004\014\210C\210q\220\001\330\004\007\200v\210S\220\001\330\010\017\210s\220!\2204\220r\230\021\330\004\017\210t\2201\220C\220q\330\004\007\200s\210!\210:\220R\220q\330\010\013\2104\210q\220\004\220D\230\003\2301\330\014\023\2203\220a\220t\2302\230Q\330\010\013\2104\210s\220!\2205\230\003\2301\230A\330\010\020\220\002\220\"\220B\220b\230\002\230\"\230C\230s\240\"\240B\240a\330\004\t\210\024\210Q\210c\220\024\220R\220q\330\004\t\210\024\210Q\210c\220\024\220R\220q\330\004\n\210#\210Q\210b\220\003\220;\230a\230t\2402\240[\260\001\260\025\260b\270\t\300\023\300F\310#\310S\320PR\320RS\330\004\013\2101\220\034\230S\240\001\240\022\2401\240C\240r\250\034\260S\270\001\270\022\2701\270C\270r\300\021\200\001\360$\000\005\006\200T\210\026\210t\2206\230\024\230V\2404\240v\320-@\300\001\300\025\300e\3105\320PQ\340\004\n\210#\210R\210q\330\004\n\210#\210R\210q\330\004\n\210#\210R\210q\330\004\n\210#\210R\210q\330\004\r\210Q\210b\220\004\220E\230\036\240q\250\005\250U\260$\260c\270\022\2703\270d\300!\330\004\r\210Q\210b\220\004\220E\230\036\240q\250\005\250U\260$\260c\270""\022\2703\270d\300!\330\004\014\210G\2202\220Q\340\004\r\210Q\340\014\017\210r\220\022\2202\220R\220r\230\022\2302\230S\240\002\240\"\240B\240b\250\002\250#\250R\250r\260\022\2601\330\014\017\210r\220\022\2202\220R\220r\230\022\2302\230S\240\002\240\"\240B\240b\250\002\250#\250R\250r\260\022\2601\340\010\014\210E\220\021\330\006\010\210\001\210\025\210a\330\004\013\210:\220Q\220a\200\001\360$\000\005\006\200T\210\026\210t\2206\230\024\230V\320#:\270!\2705\300\005\300Q\330\004\n\210#\210R\210q\330\004\n\210#\210R\210q\330\004\014\210A\330\004\007\200t\2103\210a\330\010\r\210W\220A\220Q\220c\230\022\2301\330\004\007\200t\2103\210a\330\010\r\210W\220A\220Q\220c\230\022\2301\330\004\r\210Q\330\t\014\210B\210b\220\002\220\"\220B\220c\230\022\2302\230R\230t\2403\240b\250\002\250\"\250B\250b\260\003\2602\260R\260r\270\021\330\010\014\210E\220\021\330\010\013\2102\210S\220\004\220A\330\006\010\210\001\210\025\210a\330\004\013\210:\220Q\220a\200\001\360\n\000\005\006\330\010\r\210T\220\021\220!\330\013\014\330\010\017\210u\220B\220a\340\010\017\210w\220b\230\004\230E\240\035\250a\200\001\220!\250a\200\001\360<\000\005\017\210a\330\004\007\200s\210!\2106\220\022\2203\220a\220q\330\010\016\210g\220V\2301\330\010\022\220!\330\004\007\200s\210!\2106\220\022\2201\330\010\013\2103\210a\210v\220R\220q\330\014\034\320\0343\2601\260F\270!\340\014\034\320\0342\260!\2606\270\021\330\t\014\210A\210V\2203\220b\230\004\230C\230q\240\006\240c\250\021\330\010\030\320\030-\250R\250w\260a\340\010\016\210j\230\001\230\021\330\004\007\200t\2101\330\010\017\210q\330\004\013\2101\210L\230\001\230\023\230A\230U\240#\240Q\240e\2503\250a\250u\260D\270\005\270Q\220q\200\001\360.\000\005\n\210\026\210q\330\004\t\210\026\210q\330\004\t\210\026\210q\330\004\t\210\026\210q\330\004\005\330\010\014\210H\220A\220U\230%\230t\2404\240x\250q\260\005\260U\270$\270d\300$\300h\310a\310u\320TU\340\010\017\210q\330\004\005\330\010\014\210H\220A\220U\230%\230t\2404\240x\250q\260\005\260U\270$\270d\300$\300h\310a""\310u\320TU\340\010\017\210q\330\004\007\200t\2108\2201\220E\230\025\230d\240$\240h\250a\250u\260A\330\010\017\210q\330\004\007\200t\2108\2201\220E\230\025\230d\240$\240h\250a\250u\260A\330\010\017\210q\330\004\007\200t\2108\2201\220E\230\021\330\010\014\210A\330\010\023\2204\220r\230\025\230c\240\024\240R\240q\330\010\014\210H\220C\220r\230\022\2305\240\002\240!\330\010\016\210c\220\021\330\010\017\210q\330\014\030\230\001\330\020\023\2204\220s\230-\240q\250\004\250D\260\005\260S\270\r\300Q\300d\310$\310a\360\006\000\005\010\200t\2108\2201\220E\230\021\330\010\014\210A\330\010\023\2204\220r\230\025\230c\240\024\240R\240q\330\010\014\210H\220C\220r\230\022\2305\240\002\240!\330\010\016\210c\220\021\330\010\017\210q\330\014\030\230\001\330\020\023\2204\220s\230-\240q\250\004\250D\260\005\260S\270\r\300Q\300d\310$\310a\360\010\000\005\020\210t\2202\220U\230#\230T\240\022\2401\330\004\017\210t\2202\220U\230#\230T\240\022\2401\330\004\007\200t\2108\2201\220I\230Q\330\010\017\210q\330\004\t\210\030\220\022\2204\220r\230\024\230R\230x\240r\250\024\250R\250u\260C\260x\270r\300\021\330\004\010\210\010\220\003\2202\220R\220u\230B\230a\330\004\n\210#\210Q\330\004\007\320\007.\250a\330\010\014\210D\220\001\330\006\n\320\n1\260\021\260$\260d\270!\330\010\017\210q\330\014\030\230\001\330\020\023\2204\220s\230-\240q\250\004\250D\260\005\260S\270\r\300Q\300d\310$\310a\360\006\000\005\014\2101\320\000!\240\021\360\034\000\005\010\200s\210!\2103\210b\220\001\330\010\013\2103\210a\210s\220\"\220A\340\014\024\220A\360\006\000\r\025\220A\220Q\220b\230\002\230!\360\006\000\t\016\210R\210r\220\022\2202\220T\230\022\2302\230R\230q\330\010\013\2103\210c\220\021\330\014\022\220$\220a\220q\330\014\024\220B\220a\220r\230\022\2305\240\002\240$\240b\250\004\250A\250R\250r\260\025\260b\270\004\270B\270a\360\006\000\r\025\220A\330\004\013\2101\200\001\330\004\007\200s\210!\2103\210c\220\021\330\010\013\2104\210q\330\010\023\220<\230q\240\003\2403\240a\330\010\017\210r\220\023\220L\240\n\250!\330""\004\007\200s\210!\2103\210c\220\021\330\010\017\320\017 \240\002\240#\240Q\330\t\014\210A\210S\220\003\2201\330\010\017\210}\230B\230c\240\021\330\004\n\210*\220A\220Q\200\001\330\004\007\200s\210!\2103\210c\220\021\330\010\017\320\017\"\240\"\240A\330\t\014\210A\210S\220\003\2201\330\010\017\210\177\230b\240\001\330\004\n\210*\220A\220Q\200\001\330\004\007\200s\210!\2105\220\003\2201\330\010\017\210|\2302\230U\240!\330\t\014\210A\210U\220#\220Q\330\010\017\320\017 \240\002\240%\240q\330\t\014\210A\210U\220#\220Q\330\010\017\210}\230B\230e\2401\330\004\n\210*\220A\220Q\200\001\360*\000\005\010\200s\210!\2107\220#\220Q\330\010\026\220a\330\t\014\210A\210W\220C\220q\330\010\026\220a\340\010\016\210j\230\001\230\021\330\004\024\220A\330\004\010\210\005\320\r(\250\001\250\027\260\001\330\010\r\210[\230\002\230'\240\021\360\006\000\t\022\220\035\230b\240\006\240a\330\010\r\210\\\230\022\2306\240\021\330\010\025\220W\230A\230\\\250\021\250#\250T\260\023\260C\260s\270!\330\004\013\2101\200\001\360(\000\005\010\200s\210$\320\016%\240Q\240e\2505\260\001\330\004\013\320\013\035\230Q\230c\240\023\240D\250\001\200\001\360(\000\005\010\200s\210#\210T\320\021$\240A\240U\250%\250u\260A\330\004\014\210N\230!\2303\230c\240\023\240D\250\001\360\n\000\005\n\210\021\210&\220\006\220e\2301\230B\230a\230q\330\004\t\210\022\2106\220\021\220%\220r\230\022\2303\230d\240!\330\004\013\2101";
+ PyObject *data = NULL;
+ CYTHON_UNUSED_VAR(__Pyx_DecompressString);
+ #endif
+ PyObject **stringtab = __pyx_mstate->__pyx_string_tab;
+ Py_ssize_t pos = 0;
+ for (int i = 0; i < 335; i++) {
+ Py_ssize_t bytes_length = index[i].length;
+ PyObject *string = PyUnicode_DecodeUTF8(bytes + pos, bytes_length, NULL);
+ if (likely(string) && i >= 42) PyUnicode_InternInPlace(&string);
+ if (unlikely(!string)) {
+ Py_XDECREF(data);
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ stringtab[i] = string;
+ pos += bytes_length;
+ }
+ for (int i = 335; i < 387; i++) {
+ Py_ssize_t bytes_length = index[i].length;
+ PyObject *string = PyBytes_FromStringAndSize(bytes + pos, bytes_length);
+ stringtab[i] = string;
+ pos += bytes_length;
+ if (unlikely(!string)) {
+ Py_XDECREF(data);
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ Py_XDECREF(data);
+ for (Py_ssize_t i = 0; i < 387; i++) {
+ if (unlikely(PyObject_Hash(stringtab[i]) == -1)) {
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = stringtab + 335;
+ for (Py_ssize_t i=0; i<52; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ }
+ {
+ PyObject **numbertab = __pyx_mstate->__pyx_number_tab;
+ double const c_constants[] = {0.0,0.5,1.0,-2.0,2.0,3.0,4.0,9.0,1e-3,1e-9,27.0,54.0,0.005,0.125,1e-10};
+ for (int i = 0; i < 15; i++) {
+ numbertab[i] = PyFloat_FromDouble(c_constants[i]);
+ if (unlikely(!numbertab[i])) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ {
+ PyObject **numbertab = __pyx_mstate->__pyx_number_tab + 15;
+ int8_t const cint_constants_1[] = {0,-1,1,2,3,6};
+ for (int i = 0; i < 6; i++) {
+ numbertab[i] = PyLong_FromLong(cint_constants_1[i - 0]);
+ if (unlikely(!numbertab[i])) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_number_tab;
+ for (Py_ssize_t i=0; i<21; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: init_codeobjects ### */
+typedef struct {
+ unsigned int argcount : 3;
+ unsigned int num_posonly_args : 1;
+ unsigned int num_kwonly_args : 1;
+ unsigned int nlocals : 6;
+ unsigned int flags : 10;
+ unsigned int first_line : 11;
+} __Pyx_PyCode_New_function_description;
+/* NewCodeObj.proto */
+static PyObject* __Pyx_PyCode_New(
+ const __Pyx_PyCode_New_function_description descr,
+ PyObject * const *varnames,
+ PyObject *filename,
+ PyObject *funcname,
+ PyObject *line_table,
+ PyObject *tuple_dedup_map
+);
+
+
+static int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate) {
+ PyObject* tuple_dedup_map = PyDict_New();
+ if (unlikely(!tuple_dedup_map)) return -1;
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 546};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[0] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591_q, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[0])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 583};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[1] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591_q, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[1])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 9, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS|CO_GENERATOR), 644};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d};
+ __pyx_mstate_global->__pyx_codeobj_tab[2] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubicAtTC, __pyx_mstate->__pyx_kp_b_iso88591__4, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[2])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 21, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS|CO_GENERATOR), 770};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_t1, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_delta, __pyx_mstate->__pyx_n_u_delta_2, __pyx_mstate->__pyx_n_u_delta_3, __pyx_mstate->__pyx_n_u_a1, __pyx_mstate->__pyx_n_u_b1, __pyx_mstate->__pyx_n_u_c1, __pyx_mstate->__pyx_n_u_d1, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_t1_2, __pyx_mstate->__pyx_n_u_t1_3, __pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4};
+ __pyx_mstate_global->__pyx_codeobj_tab[3] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubicAtTC_2, __pyx_mstate->__pyx_kp_b_iso88591__4, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[3])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 1252};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_i};
+ __pyx_mstate_global->__pyx_codeobj_tab[4] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591__5, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[4])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1329};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_r};
+ __pyx_mstate_global->__pyx_codeobj_tab[5] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_midpoint, __pyx_mstate->__pyx_kp_b_iso88591_A_t3aq_2Qaq, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[5])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1366};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_ts};
+ __pyx_mstate_global->__pyx_codeobj_tab[6] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_lambda, __pyx_mstate->__pyx_kp_b_iso88591_S_1Cr_S_1Cr, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[6])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 1382};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_p};
+ __pyx_mstate_global->__pyx_codeobj_tab[7] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[7])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 1485};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_x};
+ __pyx_mstate_global->__pyx_codeobj_tab[8] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591_a_2, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[8])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 5, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 56};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_tolerance};
+ __pyx_mstate_global->__pyx_codeobj_tab[9] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicArcLength, __pyx_mstate->__pyx_kp_b_iso88591_1_a_r_wb_gRvWBfA, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[9])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 6, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 75};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p3, __pyx_mstate->__pyx_n_u_mid, __pyx_mstate->__pyx_n_u_deriv3};
+ __pyx_mstate_global->__pyx_codeobj_tab[10] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_split_cubic_into_two, __pyx_mstate->__pyx_kp_b_iso88591_3b_S_b_Ba_c_3b_2T_1_c_4r_d_HA_d, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[10])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 9, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 84};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_mult, __pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p3, __pyx_mstate->__pyx_n_u_arch, __pyx_mstate->__pyx_n_u_box, __pyx_mstate->__pyx_n_u_one, __pyx_mstate->__pyx_n_u_two};
+ __pyx_mstate_global->__pyx_codeobj_tab[11] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicArcLengthCRecurse, __pyx_mstate->__pyx_kp_b_iso88591_3as_A_Qc_4r_AS_b_1Cr_uBe2XS_RuB, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[11])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 6, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 104};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_tolerance, __pyx_mstate->__pyx_n_u_mult};
+ __pyx_mstate_global->__pyx_codeobj_tab[12] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicArcLengthC, __pyx_mstate->__pyx_kp_b_iso88591_4r_Rq_avU_uA, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[12])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 3, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 151};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3};
+ __pyx_mstate_global->__pyx_codeobj_tab[13] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcQuadraticArcLength, __pyx_mstate->__pyx_kp_b_iso88591_7_F_6, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[13])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 14, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 186};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_scale, __pyx_mstate->__pyx_n_u_origDist, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_x0, __pyx_mstate->__pyx_n_u_x1, __pyx_mstate->__pyx_n_u_Len, __pyx_mstate->__pyx_n_u_d0, __pyx_mstate->__pyx_n_u_d1, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_n};
+ __pyx_mstate_global->__pyx_codeobj_tab[14] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcQuadraticArcLengthC, __pyx_mstate->__pyx_kp_b_iso88591_Rq_Rq_2Q_A_Cq_vS_s_4r_t1Cq_s_Rq, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[14])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 3, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 237};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3};
+ __pyx_mstate_global->__pyx_codeobj_tab[15] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_approximateQuadraticArcLength, __pyx_mstate->__pyx_kp_b_iso88591_6_rQR, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[15])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 6, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 254};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_v0, __pyx_mstate->__pyx_n_u_v1, __pyx_mstate->__pyx_n_u_v2};
+ __pyx_mstate_global->__pyx_codeobj_tab[16] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_approximateQuadraticArcLengthC, __pyx_mstate->__pyx_kp_b_iso88591_B_A_2T_5Rt2_PPRRS_AT_5_A_Bd_6b, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[16])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 14, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 298};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_ax2, __pyx_mstate->__pyx_n_u_ay2, __pyx_mstate->__pyx_n_u_roots, __pyx_mstate->__pyx_n_u_points, __pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[17] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcQuadraticBounds, __pyx_mstate->__pyx_kp_b_iso88591_T_t6_V_5_Q_Rq_Rq_A_t3a_WAQc_1_t, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[17])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 4, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 332};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4};
+ __pyx_mstate_global->__pyx_codeobj_tab[18] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_approximateCubicArcLength, __pyx_mstate->__pyx_kp_b_iso88591_2_Q_r_wb_gRvWBa, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[18])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 9, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 362};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_v0, __pyx_mstate->__pyx_n_u_v1, __pyx_mstate->__pyx_n_u_v2, __pyx_mstate->__pyx_n_u_v3, __pyx_mstate->__pyx_n_u_v4};
+ __pyx_mstate_global->__pyx_codeobj_tab[19] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_approximateCubicArcLengthC, __pyx_mstate->__pyx_kp_b_iso88591_AT_5_A_2Q_Ba_Ba_Ba_AT_4r_RuBa_A, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[19])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 23, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 412};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_dx, __pyx_mstate->__pyx_n_u_dy, __pyx_mstate->__pyx_n_u_ax3, __pyx_mstate->__pyx_n_u_ay3, __pyx_mstate->__pyx_n_u_bx2, __pyx_mstate->__pyx_n_u_by2, __pyx_mstate->__pyx_n_u_xRoots, __pyx_mstate->__pyx_n_u_yRoots, __pyx_mstate->__pyx_n_u_roots, __pyx_mstate->__pyx_n_u_points, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[20] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicBounds, __pyx_mstate->__pyx_kp_b_iso88591_T_t6_V4v_e5PQ_Rq_Rq_Rq_Rq_Qb_E, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[20])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 15, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 450};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_where, __pyx_mstate->__pyx_n_u_isHorizontal, __pyx_mstate->__pyx_n_u_pt1x, __pyx_mstate->__pyx_n_u_pt1y, __pyx_mstate->__pyx_n_u_pt2x, __pyx_mstate->__pyx_n_u_pt2y, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_midPt};
+ __pyx_mstate_global->__pyx_codeobj_tab[21] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitLine, __pyx_mstate->__pyx_kp_b_iso88591_H_b_b_S_r_A_r_a_s_c_1_r_D_2Rr_S, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[21])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 11, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 507};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_where, __pyx_mstate->__pyx_n_u_isHorizontal, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_solutions, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[22] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitQuadratic, __pyx_mstate->__pyx_kp_b_iso88591_F_s_Qe5_a_1N_A_aq_t1_r_e1_Qc_D, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[22])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {6, 0, 0, 13, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 552};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_where, __pyx_mstate->__pyx_n_u_isHorizontal, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_solutions, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[23] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubic, __pyx_mstate->__pyx_kp_b_iso88591_6_s_T_AU_uA_1O1A_2Q_aq_t1_r_e5, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[23])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 7, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS), 589};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c};
+ __pyx_mstate_global->__pyx_codeobj_tab[24] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitQuadraticAtT_2, __pyx_mstate->__pyx_kp_b_iso88591_s_Qe5_Qc_D, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[24])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 10, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS), 613};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_split};
+ __pyx_mstate_global->__pyx_codeobj_tab[25] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubicAtT_2, __pyx_mstate->__pyx_kp_b_iso88591_s_T_AU_uA_N_3c_D_e1Baq_6_r_3d_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[25])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 12, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 668};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_1_t, __pyx_mstate->__pyx_n_u_1_t_2, __pyx_mstate->__pyx_n_u_2_t_1_t, __pyx_mstate->__pyx_n_u_pointAtT, __pyx_mstate->__pyx_n_u_off1, __pyx_mstate->__pyx_n_u_off2};
+ __pyx_mstate_global->__pyx_codeobj_tab[26] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubicIntoTwoAtTC, __pyx_mstate->__pyx_kp_b_iso88591_0_2Q_2Rq_U_A_r_2Rq_r_b_Bb_7_Bb, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[26])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 26, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS), 708};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_segments, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_t1, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_delta, __pyx_mstate->__pyx_n_u_delta_2, __pyx_mstate->__pyx_n_u_a1x, __pyx_mstate->__pyx_n_u_a1y, __pyx_mstate->__pyx_n_u_b1x, __pyx_mstate->__pyx_n_u_b1y, __pyx_mstate->__pyx_n_u_t1_2, __pyx_mstate->__pyx_n_u_c1x, __pyx_mstate->__pyx_n_u_c1y, __pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3};
+ __pyx_mstate_global->__pyx_codeobj_tab[27] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitQuadraticAtT, __pyx_mstate->__pyx_kp_b_iso88591_Qa_q_gQc_gQa_Q_Q_Q_U_3at2Q_Rq_R, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[27])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 34, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_VARARGS), 735};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_segments, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_dx, __pyx_mstate->__pyx_n_u_dy, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_t1, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_delta, __pyx_mstate->__pyx_n_u_delta_2, __pyx_mstate->__pyx_n_u_delta_3, __pyx_mstate->__pyx_n_u_t1_2, __pyx_mstate->__pyx_n_u_t1_3, __pyx_mstate->__pyx_n_u_a1x, __pyx_mstate->__pyx_n_u_a1y, __pyx_mstate->__pyx_n_u_b1x, __pyx_mstate->__pyx_n_u_b1y, __pyx_mstate->__pyx_n_u_c1x, __pyx_mstate->__pyx_n_u_c1y, __pyx_mstate->__pyx_n_u_d1x, __pyx_mstate->__pyx_n_u_d1y, __pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4};
+ __pyx_mstate_global->__pyx_codeobj_tab[28] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_splitCubicAtT, __pyx_mstate->__pyx_kp_b_iso88591_Qa_gQc_gQa_q_Q_Q_Q_Q_U_3at2Q_Rq, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[28])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 7, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 815};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_sqrt, __pyx_mstate->__pyx_n_u_roots, __pyx_mstate->__pyx_n_u_DD, __pyx_mstate->__pyx_n_u_rDD};
+ __pyx_mstate_global->__pyx_codeobj_tab[29] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_solveQuadratic, __pyx_mstate->__pyx_kp_b_iso88591_s_3b_3as_A_A_AQb_Rr_2T_2Rq_3c_a, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[29])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 19, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 848};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_a1, __pyx_mstate->__pyx_n_u_a2, __pyx_mstate->__pyx_n_u_a3, __pyx_mstate->__pyx_n_u_Q, __pyx_mstate->__pyx_n_u_R, __pyx_mstate->__pyx_n_u_R2, __pyx_mstate->__pyx_n_u_Q3, __pyx_mstate->__pyx_n_u_R2_Q3, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_theta, __pyx_mstate->__pyx_n_u_rQ2, __pyx_mstate->__pyx_n_u_a1_3, __pyx_mstate->__pyx_n_u_x0, __pyx_mstate->__pyx_n_u_x1, __pyx_mstate->__pyx_n_u_x2};
+ __pyx_mstate_global->__pyx_codeobj_tab[30] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_solveCubic, __pyx_mstate->__pyx_kp_b_iso88591_L_s_3b_Qc_A_Qa_2Q_2Q_2Q_Bc_4r_R, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[30])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 13, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 945};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_x2, __pyx_mstate->__pyx_n_u_y2, __pyx_mstate->__pyx_n_u_x3, __pyx_mstate->__pyx_n_u_y3, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay};
+ __pyx_mstate_global->__pyx_codeobj_tab[31] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcQuadraticParameters, __pyx_mstate->__pyx_kp_b_iso88591_Q_Q_Q_Rt2Q_Rt2Q_Bc_1_Bc_1_1D_d, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[31])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 18, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 956};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_x2, __pyx_mstate->__pyx_n_u_y2, __pyx_mstate->__pyx_n_u_x3, __pyx_mstate->__pyx_n_u_y3, __pyx_mstate->__pyx_n_u_x4, __pyx_mstate->__pyx_n_u_y4, __pyx_mstate->__pyx_n_u_dx, __pyx_mstate->__pyx_n_u_dy, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay};
+ __pyx_mstate_global->__pyx_codeobj_tab[32] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicParameters, __pyx_mstate->__pyx_kp_b_iso88591_Q_Q_Q_Q_Rt2Q_Rt2Q_Rt2T_1_Rt2T_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[32])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 15, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 988};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_x1, __pyx_mstate->__pyx_n_u_y1, __pyx_mstate->__pyx_n_u_x2, __pyx_mstate->__pyx_n_u_y2, __pyx_mstate->__pyx_n_u_x3, __pyx_mstate->__pyx_n_u_y3};
+ __pyx_mstate_global->__pyx_codeobj_tab[33] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcQuadraticPoints, __pyx_mstate->__pyx_kp_b_iso88591_Q_Q_Q_RuBa_RuBa_Bc_1_Bc_1_1D_d, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[33])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 20, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1001};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_ax, __pyx_mstate->__pyx_n_u_ay, __pyx_mstate->__pyx_n_u_bx, __pyx_mstate->__pyx_n_u_by, __pyx_mstate->__pyx_n_u_cx, __pyx_mstate->__pyx_n_u_cy, __pyx_mstate->__pyx_n_u_dx, __pyx_mstate->__pyx_n_u_dy, __pyx_mstate->__pyx_n_u_x1, __pyx_mstate->__pyx_n_u_y1, __pyx_mstate->__pyx_n_u_x2, __pyx_mstate->__pyx_n_u_y2, __pyx_mstate->__pyx_n_u_x3, __pyx_mstate->__pyx_n_u_y3, __pyx_mstate->__pyx_n_u_x4, __pyx_mstate->__pyx_n_u_y4};
+ __pyx_mstate_global->__pyx_codeobj_tab[34] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_calcCubicPoints, __pyx_mstate->__pyx_kp_b_iso88591_Q_Q_Q_Q_RuBa_RuBa_Rt2T_1_Rt2T_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[34])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 3, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1040};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[35] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_linePointAtT, __pyx_mstate->__pyx_kp_b_iso88591_AS_Cr_3b_1Cr_c_S_Cr_AS, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[35])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 6, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1053};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_y};
+ __pyx_mstate_global->__pyx_codeobj_tab[36] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_quadraticPointAtT, __pyx_mstate->__pyx_kp_b_iso88591_2S_2Rs_Cq_2Rs_Bc_2Rs_3b_Bb_1A_2, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[36])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 10, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1068};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_1_t, __pyx_mstate->__pyx_n_u_1_t_2, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_y};
+ __pyx_mstate_global->__pyx_codeobj_tab[37] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_cubicPointAtT, __pyx_mstate->__pyx_kp_b_iso88591_2Q_2Rq_U_A_r_b_1A_Cwb_Cq_2U_Cr, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[37])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 8, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1094};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_pt1, __pyx_mstate->__pyx_n_u_pt2, __pyx_mstate->__pyx_n_u_pt3, __pyx_mstate->__pyx_n_u_pt4, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_t2, __pyx_mstate->__pyx_n_u_1_t, __pyx_mstate->__pyx_n_u_1_t_2};
+ __pyx_mstate_global->__pyx_codeobj_tab[38] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_cubicPointAtTC, __pyx_mstate->__pyx_kp_b_iso88591_2Q_2Rq_U_A_7_E_4r_3gRr_4r_b_2U, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[38])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 2, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1119};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_seg, __pyx_mstate->__pyx_n_u_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[39] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_segmentPointAtT, __pyx_mstate->__pyx_kp_b_iso88591_s_5_1_2U_AU_Q_q_AU_Q_Be1_AQ, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[39])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 9, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1134};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_s, __pyx_mstate->__pyx_n_u_e, __pyx_mstate->__pyx_n_u_pt, __pyx_mstate->__pyx_n_u_sx, __pyx_mstate->__pyx_n_u_sy, __pyx_mstate->__pyx_n_u_ex, __pyx_mstate->__pyx_n_u_ey, __pyx_mstate->__pyx_n_u_px, __pyx_mstate->__pyx_n_u_py};
+ __pyx_mstate_global->__pyx_codeobj_tab[40] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_line_t_of_pt, __pyx_mstate->__pyx_kp_b_iso88591_Q_Q_Q_s_3b_Bhd_Qc_4r_s_3b_Bc_Rq, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[40])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 5, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1148};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_origin, __pyx_mstate->__pyx_n_u_xDiff, __pyx_mstate->__pyx_n_u_yDiff};
+ __pyx_mstate_global->__pyx_codeobj_tab[41] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_both_points_are_on_same_side_of, __pyx_mstate->__pyx_kp_b_iso88591_Qas_F_4s_1Cr_q_Qas_F_4s_1Cr_q_5, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[41])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 17, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1154};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_s1, __pyx_mstate->__pyx_n_u_e1, __pyx_mstate->__pyx_n_u_s2, __pyx_mstate->__pyx_n_u_e2, __pyx_mstate->__pyx_n_u_s1x, __pyx_mstate->__pyx_n_u_s1y, __pyx_mstate->__pyx_n_u_e1x, __pyx_mstate->__pyx_n_u_e1y, __pyx_mstate->__pyx_n_u_s2x, __pyx_mstate->__pyx_n_u_s2y, __pyx_mstate->__pyx_n_u_e2x, __pyx_mstate->__pyx_n_u_e2y, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_slope34, __pyx_mstate->__pyx_n_u_y, __pyx_mstate->__pyx_n_u_pt, __pyx_mstate->__pyx_n_u_slope12};
+ __pyx_mstate_global->__pyx_codeobj_tab[42] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_lineLineIntersections, __pyx_mstate->__pyx_kp_b_iso88591_q_q_q_q_HAU_t4xq_U_d_hauTU_q_HA, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[42])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 4, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1232};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_segment, __pyx_mstate->__pyx_n_u_start, __pyx_mstate->__pyx_n_u_end, __pyx_mstate->__pyx_n_u_angle};
+ __pyx_mstate_global->__pyx_codeobj_tab[43] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_alignment_transformation, __pyx_mstate->__pyx_kp_b_iso88591_G1A_1_D_as_3b_Qd_Qc_5_87_1F_AQe, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[43])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 10, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1242};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_line, __pyx_mstate->__pyx_n_u_aligned_curve, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_intersections, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[44] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_curve_line_intersections_t, __pyx_mstate->__pyx_kp_b_iso88591_Qe3C1A_s_7_Q_3d_1_aq_Qat1AQ_AWC, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[44])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 7, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1255};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_line, __pyx_mstate->__pyx_n_u_pointFinder, __pyx_mstate->__pyx_n_u_intersections, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_pt, __pyx_mstate->__pyx_n_u_line_t};
+ __pyx_mstate_global->__pyx_codeobj_tab[45] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_curveLineIntersections, __pyx_mstate->__pyx_kp_b_iso88591_s_7_Q_a_AWCq_a_j_A_b_a_6_WA_T_C, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[45])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 1, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1293};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_c};
+ __pyx_mstate_global->__pyx_codeobj_tab[46] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_curve_bounds, __pyx_mstate->__pyx_kp_b_iso88591_s_3c_A_AS_1_b_AQ, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[46])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 5, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1301};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_t, __pyx_mstate->__pyx_n_u_s, __pyx_mstate->__pyx_n_u_e, __pyx_mstate->__pyx_n_u_midpoint};
+ __pyx_mstate_global->__pyx_codeobj_tab[47] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_split_segment_at_t, __pyx_mstate->__pyx_kp_b_iso88591_s_3c_4q_q_3a_r_L_s_3c_Q_AS_1_Bc, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[47])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 25, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1313};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curve1, __pyx_mstate->__pyx_n_u_curve2, __pyx_mstate->__pyx_n_u_precision, __pyx_mstate->__pyx_n_u_range1, __pyx_mstate->__pyx_n_u_range2, __pyx_mstate->__pyx_n_u_bounds1, __pyx_mstate->__pyx_n_u_bounds2, __pyx_mstate->__pyx_n_u_intersects, __pyx_mstate->__pyx_n_u__6, __pyx_mstate->__pyx_n_u_midpoint, __pyx_mstate->__pyx_n_u_midpoint, __pyx_mstate->__pyx_n_u_c11, __pyx_mstate->__pyx_n_u_c12, __pyx_mstate->__pyx_n_u_c11_range, __pyx_mstate->__pyx_n_u_c12_range, __pyx_mstate->__pyx_n_u_c21, __pyx_mstate->__pyx_n_u_c22, __pyx_mstate->__pyx_n_u_c21_range, __pyx_mstate->__pyx_n_u_c22_range, __pyx_mstate->__pyx_n_u_found, __pyx_mstate->__pyx_n_u_unique_key, __pyx_mstate->__pyx_n_u_seen, __pyx_mstate->__pyx_n_u_unique_values, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_key};
+ __pyx_mstate_global->__pyx_codeobj_tab[48] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_curve_curve_intersections_t, __pyx_mstate->__pyx_kp_b_iso88591_M_m1A_m1A_t1_q_t1_q_HAYa_t1_q_x, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[48])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 4, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1380};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_segment, __pyx_mstate->__pyx_n_u_maybeline, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[49] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_is_linelike, __pyx_mstate->__pyx_kp_b_iso88591_2B_1_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[49])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 8, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1385};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curve1, __pyx_mstate->__pyx_n_u_curve2, __pyx_mstate->__pyx_n_u_line1, __pyx_mstate->__pyx_n_u_line2, __pyx_mstate->__pyx_n_u_hits, __pyx_mstate->__pyx_n_u_intersection_ts, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_ts};
+ __pyx_mstate_global->__pyx_codeobj_tab[50] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_curveCurveIntersections, __pyx_mstate->__pyx_kp_b_iso88591_1A_at6_1_q_F_4vRq_1L_AU_Qe3auD, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[50])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {2, 0, 0, 5, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1427};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_seg1, __pyx_mstate->__pyx_n_u_seg2, __pyx_mstate->__pyx_n_u_swapped, __pyx_mstate->__pyx_n_u_intersections, __pyx_mstate->__pyx_n_u_i};
+ __pyx_mstate_global->__pyx_codeobj_tab[51] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_segmentSegmentIntersections, __pyx_mstate->__pyx_kp_b_iso88591_a_s_6_3aq_gV1_s_6_1_3avRq_31F_2, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[51])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 4, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1475};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_obj, __pyx_mstate->__pyx_n_u_it, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[52] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_segmentrepr, __pyx_mstate->__pyx_kp_b_iso88591_T_uBa_wb_E_a, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[52])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 2, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 1488};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_segments, __pyx_mstate->__pyx_n_u_segment};
+ __pyx_mstate_global->__pyx_codeobj_tab[53] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_misc_bezierTools_p, __pyx_mstate->__pyx_n_u_printSegments, __pyx_mstate->__pyx_kp_b_iso88591_1_Ql_1, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[53])) goto bad;
+ }
+ Py_DECREF(tuple_dedup_map);
+ return 0;
+ bad:
+ Py_DECREF(tuple_dedup_map);
+ return -1;
+}
+/* #### Code section: init_globals ### */
+
+static int __Pyx_InitGlobals(void) {
+ /* PythonCompatibility.init */
+ if (likely(__Pyx_init_co_variables() == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CommonTypesMetaclass.init */
+ if (likely(__pyx_CommonTypesMetaclass_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* Generator.init */
+ if (likely(__pyx_Generator_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CachedMethodType.init */
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ {
+ PyObject *typesModule=NULL;
+ typesModule = PyImport_ImportModule("types");
+ if (typesModule) {
+ __pyx_mstate_global->__Pyx_CachedMethodType = PyObject_GetAttrString(typesModule, "MethodType");
+ Py_DECREF(typesModule);
+ }
+ } // error handling follows
+ #endif
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CythonFunctionShared.init */
+ if (likely(__pyx_CyFunction_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: cleanup_globals ### */
+/* #### Code section: cleanup_module ### */
+/* #### Code section: main_method ### */
+/* #### Code section: utility_code_pragmas ### */
+#ifdef _MSC_VER
+#pragma warning( push )
+/* Warning 4127: conditional expression is constant
+ * Cython uses constant conditional expressions to allow in inline functions to be optimized at
+ * compile-time, so this warning is not useful
+ */
+#pragma warning( disable : 4127 )
+#endif
+
+
+
+/* #### Code section: utility_code_def ### */
+
+/* --- Runtime support code --- */
+/* Refnanny */
+#if CYTHON_REFNANNY
+static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) {
+ PyObject *m = NULL, *p = NULL;
+ void *r = NULL;
+ m = PyImport_ImportModule(modname);
+ if (!m) goto end;
+ p = PyObject_GetAttrString(m, "RefNannyAPI");
+ if (!p) goto end;
+ r = PyLong_AsVoidPtr(p);
+end:
+ Py_XDECREF(p);
+ Py_XDECREF(m);
+ return (__Pyx_RefNannyAPIStruct *)r;
+}
+#endif
+
+/* PyErrExceptionMatches (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(tuple);
+ for (i=0; i= 0x030C00A6
+ PyObject *current_exception = tstate->current_exception;
+ if (unlikely(!current_exception)) return 0;
+ exc_type = (PyObject*) Py_TYPE(current_exception);
+ if (exc_type == err) return 1;
+#else
+ exc_type = tstate->curexc_type;
+ if (exc_type == err) return 1;
+ if (unlikely(!exc_type)) return 0;
+#endif
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_INCREF(exc_type);
+ #endif
+ if (unlikely(PyTuple_Check(err))) {
+ result = __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err);
+ } else {
+ result = __Pyx_PyErr_GivenExceptionMatches(exc_type, err);
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(exc_type);
+ #endif
+ return result;
+}
+#endif
+
+/* PyErrFetchRestore (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyObject *tmp_value;
+ assert(type == NULL || (value != NULL && type == (PyObject*) Py_TYPE(value)));
+ if (value) {
+ #if CYTHON_COMPILING_IN_CPYTHON
+ if (unlikely(((PyBaseExceptionObject*) value)->traceback != tb))
+ #endif
+ PyException_SetTraceback(value, tb);
+ }
+ tmp_value = tstate->current_exception;
+ tstate->current_exception = value;
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+#else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ tmp_type = tstate->curexc_type;
+ tmp_value = tstate->curexc_value;
+ tmp_tb = tstate->curexc_traceback;
+ tstate->curexc_type = type;
+ tstate->curexc_value = value;
+ tstate->curexc_traceback = tb;
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+#endif
+}
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyObject* exc_value;
+ exc_value = tstate->current_exception;
+ tstate->current_exception = 0;
+ *value = exc_value;
+ *type = NULL;
+ *tb = NULL;
+ if (exc_value) {
+ *type = (PyObject*) Py_TYPE(exc_value);
+ Py_INCREF(*type);
+ #if CYTHON_COMPILING_IN_CPYTHON
+ *tb = ((PyBaseExceptionObject*) exc_value)->traceback;
+ Py_XINCREF(*tb);
+ #else
+ *tb = PyException_GetTraceback(exc_value);
+ #endif
+ }
+#else
+ *type = tstate->curexc_type;
+ *value = tstate->curexc_value;
+ *tb = tstate->curexc_traceback;
+ tstate->curexc_type = 0;
+ tstate->curexc_value = 0;
+ tstate->curexc_traceback = 0;
+#endif
+}
+#endif
+
+/* PyObjectGetAttrStr (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) {
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro))
+ return tp->tp_getattro(obj, attr_name);
+ return PyObject_GetAttr(obj, attr_name);
+}
+#endif
+
+/* PyObjectGetAttrStrNoError (used by GetBuiltinName) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError)))
+ __Pyx_PyErr_Clear();
+}
+#endif
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) {
+ PyObject *result;
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ (void) PyObject_GetOptionalAttr(obj, attr_name, &result);
+ return result;
+#else
+#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) {
+ return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1);
+ }
+#endif
+ result = __Pyx_PyObject_GetAttrStr(obj, attr_name);
+ if (unlikely(!result)) {
+ __Pyx_PyObject_GetAttrStr_ClearAttributeError();
+ }
+ return result;
+#endif
+}
+
+/* GetBuiltinName */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name) {
+ PyObject* result = __Pyx_PyObject_GetAttrStrNoError(__pyx_mstate_global->__pyx_b, name);
+ if (unlikely(!result) && !PyErr_Occurred()) {
+ PyErr_Format(PyExc_NameError,
+ "name '%U' is not defined", name);
+ }
+ return result;
+}
+
+/* TupleAndListFromArray (used by fastcall) */
+#if !CYTHON_COMPILING_IN_CPYTHON && CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject *
+__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ Py_ssize_t i;
+ if (n <= 0) {
+ return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple);
+ }
+ res = PyTuple_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ for (i = 0; i < n; i++) {
+ if (unlikely(__Pyx_PyTuple_SET_ITEM(res, i, src[i]) < (0))) {
+ Py_DECREF(res);
+ return NULL;
+ }
+ Py_INCREF(src[i]);
+ }
+ return res;
+}
+#elif CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) {
+ PyObject *v;
+ Py_ssize_t i;
+ for (i = 0; i < length; i++) {
+ v = dest[i] = src[i];
+ Py_INCREF(v);
+ }
+}
+static CYTHON_INLINE PyObject *
+__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple);
+ }
+ res = PyTuple_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n);
+ return res;
+}
+static CYTHON_INLINE PyObject *
+__Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ return PyList_New(0);
+ }
+ res = PyList_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n);
+ return res;
+}
+#endif
+
+/* BytesEquals (used by UnicodeEquals) */
+static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL ||\
+ !(CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS)
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+ if (s1 == s2) {
+ return (equals == Py_EQ);
+ } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) {
+ const char *ps1, *ps2;
+ Py_ssize_t length = PyBytes_GET_SIZE(s1);
+ if (length != PyBytes_GET_SIZE(s2))
+ return (equals == Py_NE);
+ ps1 = PyBytes_AS_STRING(s1);
+ ps2 = PyBytes_AS_STRING(s2);
+ if (ps1[0] != ps2[0]) {
+ return (equals == Py_NE);
+ } else if (length == 1) {
+ return (equals == Py_EQ);
+ } else {
+ int result;
+#if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000)
+ Py_hash_t hash1, hash2;
+ hash1 = ((PyBytesObject*)s1)->ob_shash;
+ hash2 = ((PyBytesObject*)s2)->ob_shash;
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ return (equals == Py_NE);
+ }
+#endif
+ result = memcmp(ps1, ps2, (size_t)length);
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) {
+ return (equals == Py_NE);
+ } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) {
+ return (equals == Py_NE);
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+#endif
+}
+
+/* UnicodeEquals (used by fastcall) */
+static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+ int s1_is_unicode, s2_is_unicode;
+ if (s1 == s2) {
+ goto return_eq;
+ }
+ s1_is_unicode = PyUnicode_CheckExact(s1);
+ s2_is_unicode = PyUnicode_CheckExact(s2);
+ if (s1_is_unicode & s2_is_unicode) {
+ Py_ssize_t length, length2;
+ int kind;
+ void *data1, *data2;
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0))
+ return -1;
+ #endif
+ length = __Pyx_PyUnicode_GET_LENGTH(s1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(length < 0)) return -1;
+ #endif
+ length2 = __Pyx_PyUnicode_GET_LENGTH(s2);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(length2 < 0)) return -1;
+ #endif
+ if (length != length2) {
+ goto return_ne;
+ }
+#if CYTHON_USE_UNICODE_INTERNALS
+ {
+ Py_hash_t hash1, hash2;
+ hash1 = ((PyASCIIObject*)s1)->hash;
+ hash2 = ((PyASCIIObject*)s2)->hash;
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ goto return_ne;
+ }
+ }
+#endif
+ kind = __Pyx_PyUnicode_KIND(s1);
+ if (kind != __Pyx_PyUnicode_KIND(s2)) {
+ goto return_ne;
+ }
+ data1 = __Pyx_PyUnicode_DATA(s1);
+ data2 = __Pyx_PyUnicode_DATA(s2);
+ if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) {
+ goto return_ne;
+ } else if (length == 1) {
+ goto return_eq;
+ } else {
+ int result = memcmp(data1, data2, (size_t)(length * kind));
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & s2_is_unicode) {
+ goto return_ne;
+ } else if ((s2 == Py_None) & s1_is_unicode) {
+ goto return_ne;
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+return_eq:
+ return (equals == Py_EQ);
+return_ne:
+ return (equals == Py_NE);
+#endif
+}
+
+/* fastcall */
+#if CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s)
+{
+ Py_ssize_t i, n = __Pyx_PyTuple_GET_SIZE(kwnames);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(n == -1)) return NULL;
+ #endif
+ for (i = 0; i < n; i++)
+ {
+ PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!namei)) return NULL;
+ #endif
+ if (s == namei) return kwvalues[i];
+ }
+ for (i = 0; i < n; i++)
+ {
+ PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!namei)) return NULL;
+ #endif
+ int eq = __Pyx_PyUnicode_Equals(s, namei, Py_EQ);
+ if (unlikely(eq != 0)) {
+ if (unlikely(eq < 0)) return NULL;
+ return kwvalues[i];
+ }
+ }
+ return NULL;
+}
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) {
+ Py_ssize_t i, nkwargs;
+ PyObject *dict;
+#if !CYTHON_ASSUME_SAFE_SIZE
+ nkwargs = PyTuple_Size(kwnames);
+ if (unlikely(nkwargs < 0)) return NULL;
+#else
+ nkwargs = PyTuple_GET_SIZE(kwnames);
+#endif
+ dict = PyDict_New();
+ if (unlikely(!dict))
+ return NULL;
+ for (i=0; itp_call;
+ if (unlikely(!call))
+ return PyObject_Call(func, arg, kw);
+ if (unlikely(Py_EnterRecursiveCall(" while calling a Python object")))
+ return NULL;
+ result = (*call)(func, arg, kw);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* PyObjectCallMethO (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) {
+ PyObject *self, *result;
+ PyCFunction cfunc;
+ cfunc = __Pyx_CyOrPyCFunction_GET_FUNCTION(func);
+ self = __Pyx_CyOrPyCFunction_GET_SELF(func);
+ if (unlikely(Py_EnterRecursiveCall(" while calling a Python object")))
+ return NULL;
+ result = cfunc(self, arg);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* PyObjectFastCall (used by PyObjectCallOneArg) */
+#if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API
+static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs) {
+ PyObject *argstuple;
+ PyObject *result = 0;
+ size_t i;
+ argstuple = PyTuple_New((Py_ssize_t)nargs);
+ if (unlikely(!argstuple)) return NULL;
+ for (i = 0; i < nargs; i++) {
+ Py_INCREF(args[i]);
+ if (__Pyx_PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]) != (0)) goto bad;
+ }
+ result = __Pyx_PyObject_Call(func, argstuple, kwargs);
+ bad:
+ Py_DECREF(argstuple);
+ return result;
+}
+#endif
+#if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API
+ #if PY_VERSION_HEX < 0x03090000
+ #define __Pyx_PyVectorcall_Function(callable) _PyVectorcall_Function(callable)
+ #elif CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE vectorcallfunc __Pyx_PyVectorcall_Function(PyObject *callable) {
+ PyTypeObject *tp = Py_TYPE(callable);
+ #if defined(__Pyx_CyFunction_USED)
+ if (__Pyx_CyFunction_CheckExact(callable)) {
+ return __Pyx_CyFunction_func_vectorcall(callable);
+ }
+ #endif
+ if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
+ return NULL;
+ }
+ assert(PyCallable_Check(callable));
+ Py_ssize_t offset = tp->tp_vectorcall_offset;
+ assert(offset > 0);
+ vectorcallfunc ptr;
+ memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
+ return ptr;
+}
+ #else
+ #define __Pyx_PyVectorcall_Function(callable) PyVectorcall_Function(callable)
+ #endif
+#endif
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject *const *args, size_t _nargs, PyObject *kwargs) {
+ Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs);
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (nargs == 0 && kwargs == NULL) {
+ if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS))
+ return __Pyx_PyObject_CallMethO(func, NULL);
+ }
+ else if (nargs == 1 && kwargs == NULL) {
+ if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O))
+ return __Pyx_PyObject_CallMethO(func, args[0]);
+ }
+#endif
+ if (kwargs == NULL) {
+ #if CYTHON_VECTORCALL
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ return PyObject_Vectorcall(func, args, _nargs, NULL);
+ #else
+ vectorcallfunc f = __Pyx_PyVectorcall_Function(func);
+ if (f) {
+ return f(func, args, _nargs, NULL);
+ }
+ #endif
+ #endif
+ }
+ if (nargs == 0) {
+ return __Pyx_PyObject_Call(func, __pyx_mstate_global->__pyx_empty_tuple, kwargs);
+ }
+ #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API
+ return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs);
+ #else
+ return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs);
+ #endif
+}
+
+/* PyObjectCallOneArg (used by CallUnboundCMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
+ PyObject *args[2] = {NULL, arg};
+ return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* UnpackUnboundCMethod (used by CallUnboundCMethod0) */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030C0000
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *args, PyObject *kwargs) {
+ PyObject *result;
+ PyObject *selfless_args = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
+ if (unlikely(!selfless_args)) return NULL;
+ result = PyObject_Call(method, selfless_args, kwargs);
+ Py_DECREF(selfless_args);
+ return result;
+}
+#elif CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03090000
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) {
+ return _PyObject_Vectorcall
+ (method, args ? args+1 : NULL, nargs ? nargs-1 : 0, kwnames);
+}
+#else
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) {
+ return
+#if PY_VERSION_HEX < 0x03090000
+ _PyObject_Vectorcall
+#else
+ PyObject_Vectorcall
+#endif
+ (method, args ? args+1 : NULL, nargs ? (size_t) nargs-1 : 0, kwnames);
+}
+#endif
+static PyMethodDef __Pyx_UnboundCMethod_Def = {
+ "CythonUnboundCMethod",
+ __PYX_REINTERPRET_FUNCION(PyCFunction, __Pyx_SelflessCall),
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030C0000
+ METH_VARARGS | METH_KEYWORDS,
+#else
+ METH_FASTCALL | METH_KEYWORDS,
+#endif
+ NULL
+};
+static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) {
+ PyObject *method, *result=NULL;
+ method = __Pyx_PyObject_GetAttrStr(target->type, *target->method_name);
+ if (unlikely(!method))
+ return -1;
+ result = method;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (likely(__Pyx_TypeCheck(method, &PyMethodDescr_Type)))
+ {
+ PyMethodDescrObject *descr = (PyMethodDescrObject*) method;
+ target->func = descr->d_method->ml_meth;
+ target->flag = descr->d_method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_STACKLESS);
+ } else
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+#else
+ if (PyCFunction_Check(method))
+#endif
+ {
+ PyObject *self;
+ int self_found;
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ self = PyObject_GetAttrString(method, "__self__");
+ if (!self) {
+ PyErr_Clear();
+ }
+#else
+ self = PyCFunction_GET_SELF(method);
+#endif
+ self_found = (self && self != Py_None);
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ Py_XDECREF(self);
+#endif
+ if (self_found) {
+ PyObject *unbound_method = PyCFunction_New(&__Pyx_UnboundCMethod_Def, method);
+ if (unlikely(!unbound_method)) return -1;
+ Py_DECREF(method);
+ result = unbound_method;
+ }
+ }
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ if (unlikely(target->method)) {
+ Py_DECREF(result);
+ } else
+#endif
+ target->method = result;
+ return 0;
+}
+
+/* CallUnboundCMethod0 */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self) {
+ int was_initialized = __Pyx_CachedCFunction_GetAndSetInitializing(cfunc);
+ if (likely(was_initialized == 2 && cfunc->func)) {
+ if (likely(cfunc->flag == METH_NOARGS))
+ return __Pyx_CallCFunction(cfunc, self, NULL);
+ if (likely(cfunc->flag == METH_FASTCALL))
+ return __Pyx_CallCFunctionFast(cfunc, self, NULL, 0);
+ if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS))
+ return __Pyx_CallCFunctionFastWithKeywords(cfunc, self, NULL, 0, NULL);
+ if (likely(cfunc->flag == (METH_VARARGS | METH_KEYWORDS)))
+ return __Pyx_CallCFunctionWithKeywords(cfunc, self, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (cfunc->flag == METH_VARARGS)
+ return __Pyx_CallCFunction(cfunc, self, __pyx_mstate_global->__pyx_empty_tuple);
+ return __Pyx__CallUnboundCMethod0(cfunc, self);
+ }
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ else if (unlikely(was_initialized == 1)) {
+ __Pyx_CachedCFunction tmp_cfunc = {
+#ifndef __cplusplus
+ 0
+#endif
+ };
+ tmp_cfunc.type = cfunc->type;
+ tmp_cfunc.method_name = cfunc->method_name;
+ return __Pyx__CallUnboundCMethod0(&tmp_cfunc, self);
+ }
+#endif
+ PyObject *result = __Pyx__CallUnboundCMethod0(cfunc, self);
+ __Pyx_CachedCFunction_SetFinishedInitializing(cfunc);
+ return result;
+}
+#endif
+static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self) {
+ PyObject *result;
+ if (unlikely(!cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+ result = __Pyx_PyObject_CallOneArg(cfunc->method, self);
+ return result;
+}
+
+/* py_dict_items (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) {
+ return __Pyx_CallUnboundCMethod0(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_items, d);
+}
+
+/* py_dict_values (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) {
+ return __Pyx_CallUnboundCMethod0(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_values, d);
+}
+
+/* OwnedDictNext (used by ParseKeywordsImpl) */
+#if CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, PyObject **ppos, PyObject **pkey, PyObject **pvalue) {
+ PyObject *next = NULL;
+ if (!*ppos) {
+ if (pvalue) {
+ PyObject *dictview = pkey ? __Pyx_PyDict_Items(p) : __Pyx_PyDict_Values(p);
+ if (unlikely(!dictview)) goto bad;
+ *ppos = PyObject_GetIter(dictview);
+ Py_DECREF(dictview);
+ } else {
+ *ppos = PyObject_GetIter(p);
+ }
+ if (unlikely(!*ppos)) goto bad;
+ }
+ next = PyIter_Next(*ppos);
+ if (!next) {
+ if (PyErr_Occurred()) goto bad;
+ return 0;
+ }
+ if (pkey && pvalue) {
+ *pkey = __Pyx_PySequence_ITEM(next, 0);
+ if (unlikely(*pkey)) goto bad;
+ *pvalue = __Pyx_PySequence_ITEM(next, 1);
+ if (unlikely(*pvalue)) goto bad;
+ Py_DECREF(next);
+ } else if (pkey) {
+ *pkey = next;
+ } else {
+ assert(pvalue);
+ *pvalue = next;
+ }
+ return 1;
+ bad:
+ Py_XDECREF(next);
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d0000
+ PyErr_FormatUnraisable("Exception ignored in __Pyx_PyDict_NextRef");
+#else
+ PyErr_WriteUnraisable(__pyx_mstate_global->__pyx_n_u_Pyx_PyDict_NextRef);
+#endif
+ if (pkey) *pkey = NULL;
+ if (pvalue) *pvalue = NULL;
+ return 0;
+}
+#else // !CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) {
+ int result = PyDict_Next(p, ppos, pkey, pvalue);
+ if (likely(result == 1)) {
+ if (pkey) Py_INCREF(*pkey);
+ if (pvalue) Py_INCREF(*pvalue);
+ }
+ return result;
+}
+#endif
+
+/* RaiseDoubleKeywords (used by ParseKeywordsImpl) */
+static void __Pyx_RaiseDoubleKeywordsError(
+ const char* func_name,
+ PyObject* kw_name)
+{
+ PyErr_Format(PyExc_TypeError,
+ "%s() got multiple values for keyword argument '%U'", func_name, kw_name);
+}
+
+/* CallUnboundCMethod2 */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2) {
+ int was_initialized = __Pyx_CachedCFunction_GetAndSetInitializing(cfunc);
+ if (likely(was_initialized == 2 && cfunc->func)) {
+ PyObject *args[2] = {arg1, arg2};
+ if (cfunc->flag == METH_FASTCALL) {
+ return __Pyx_CallCFunctionFast(cfunc, self, args, 2);
+ }
+ if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS))
+ return __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, 2, NULL);
+ }
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ else if (unlikely(was_initialized == 1)) {
+ __Pyx_CachedCFunction tmp_cfunc = {
+#ifndef __cplusplus
+ 0
+#endif
+ };
+ tmp_cfunc.type = cfunc->type;
+ tmp_cfunc.method_name = cfunc->method_name;
+ return __Pyx__CallUnboundCMethod2(&tmp_cfunc, self, arg1, arg2);
+ }
+#endif
+ PyObject *result = __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2);
+ __Pyx_CachedCFunction_SetFinishedInitializing(cfunc);
+ return result;
+}
+#endif
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2){
+ if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (cfunc->func && (cfunc->flag & METH_VARARGS)) {
+ PyObject *result = NULL;
+ PyObject *args = PyTuple_New(2);
+ if (unlikely(!args)) return NULL;
+ Py_INCREF(arg1);
+ PyTuple_SET_ITEM(args, 0, arg1);
+ Py_INCREF(arg2);
+ PyTuple_SET_ITEM(args, 1, arg2);
+ if (cfunc->flag & METH_KEYWORDS)
+ result = __Pyx_CallCFunctionWithKeywords(cfunc, self, args, NULL);
+ else
+ result = __Pyx_CallCFunction(cfunc, self, args);
+ Py_DECREF(args);
+ return result;
+ }
+#endif
+ {
+ PyObject *args[4] = {NULL, self, arg1, arg2};
+ return __Pyx_PyObject_FastCall(cfunc->method, args+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+ }
+}
+
+/* ParseKeywordsImpl (used by ParseKeywords) */
+static int __Pyx_ValidateDuplicatePosArgs(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ const char* function_name)
+{
+ PyObject ** const *name = argnames;
+ while (name != first_kw_arg) {
+ PyObject *key = **name;
+ int found = PyDict_Contains(kwds, key);
+ if (unlikely(found)) {
+ if (found == 1) __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+ }
+ name++;
+ }
+ return 0;
+bad:
+ return -1;
+}
+#if CYTHON_USE_UNICODE_INTERNALS
+static CYTHON_INLINE int __Pyx_UnicodeKeywordsEqual(PyObject *s1, PyObject *s2) {
+ int kind;
+ Py_ssize_t len = PyUnicode_GET_LENGTH(s1);
+ if (len != PyUnicode_GET_LENGTH(s2)) return 0;
+ kind = PyUnicode_KIND(s1);
+ if (kind != PyUnicode_KIND(s2)) return 0;
+ const void *data1 = PyUnicode_DATA(s1);
+ const void *data2 = PyUnicode_DATA(s2);
+ return (memcmp(data1, data2, (size_t) len * (size_t) kind) == 0);
+}
+#endif
+static int __Pyx_MatchKeywordArg_str(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ PyObject ** const *name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ Py_hash_t key_hash = ((PyASCIIObject*)key)->hash;
+ if (unlikely(key_hash == -1)) {
+ key_hash = PyObject_Hash(key);
+ if (unlikely(key_hash == -1))
+ goto bad;
+ }
+ #endif
+ name = first_kw_arg;
+ while (*name) {
+ PyObject *name_str = **name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ if (key_hash == ((PyASCIIObject*)name_str)->hash && __Pyx_UnicodeKeywordsEqual(name_str, key)) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ #else
+ #if CYTHON_ASSUME_SAFE_SIZE
+ if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key))
+ #endif
+ {
+ int cmp = PyUnicode_Compare(name_str, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ }
+ #endif
+ name++;
+ }
+ name = argnames;
+ while (name != first_kw_arg) {
+ PyObject *name_str = **name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ if (unlikely(key_hash == ((PyASCIIObject*)name_str)->hash)) {
+ if (__Pyx_UnicodeKeywordsEqual(name_str, key))
+ goto arg_passed_twice;
+ }
+ #else
+ #if CYTHON_ASSUME_SAFE_SIZE
+ if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key))
+ #endif
+ {
+ if (unlikely(name_str == key)) goto arg_passed_twice;
+ int cmp = PyUnicode_Compare(name_str, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) goto arg_passed_twice;
+ }
+ #endif
+ name++;
+ }
+ return 0;
+arg_passed_twice:
+ __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+bad:
+ return -1;
+}
+static int __Pyx_MatchKeywordArg_nostr(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ PyObject ** const *name;
+ if (unlikely(!PyUnicode_Check(key))) goto invalid_keyword_type;
+ name = first_kw_arg;
+ while (*name) {
+ int cmp = PyObject_RichCompareBool(**name, key, Py_EQ);
+ if (cmp == 1) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ if (unlikely(cmp == -1)) goto bad;
+ name++;
+ }
+ name = argnames;
+ while (name != first_kw_arg) {
+ int cmp = PyObject_RichCompareBool(**name, key, Py_EQ);
+ if (unlikely(cmp != 0)) {
+ if (cmp == 1) goto arg_passed_twice;
+ else goto bad;
+ }
+ name++;
+ }
+ return 0;
+arg_passed_twice:
+ __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+invalid_keyword_type:
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() keywords must be strings", function_name);
+ goto bad;
+bad:
+ return -1;
+}
+static CYTHON_INLINE int __Pyx_MatchKeywordArg(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ return likely(PyUnicode_CheckExact(key)) ?
+ __Pyx_MatchKeywordArg_str(key, argnames, first_kw_arg, index_found, function_name) :
+ __Pyx_MatchKeywordArg_nostr(key, argnames, first_kw_arg, index_found, function_name);
+}
+static void __Pyx_RejectUnknownKeyword(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ const char *function_name)
+{
+ #if CYTHON_AVOID_BORROWED_REFS
+ PyObject *pos = NULL;
+ #else
+ Py_ssize_t pos = 0;
+ #endif
+ PyObject *key = NULL;
+ __Pyx_BEGIN_CRITICAL_SECTION(kwds);
+ while (
+ #if CYTHON_AVOID_BORROWED_REFS
+ __Pyx_PyDict_NextRef(kwds, &pos, &key, NULL)
+ #else
+ PyDict_Next(kwds, &pos, &key, NULL)
+ #endif
+ ) {
+ PyObject** const *name = first_kw_arg;
+ while (*name && (**name != key)) name++;
+ if (!*name) {
+ size_t index_found = 0;
+ int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name);
+ if (cmp != 1) {
+ if (cmp == 0) {
+ PyErr_Format(PyExc_TypeError,
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ #endif
+ break;
+ }
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ #endif
+ }
+ __Pyx_END_CRITICAL_SECTION();
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(pos);
+ #endif
+ assert(PyErr_Occurred());
+}
+static int __Pyx_ParseKeywordDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ PyObject** const *name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ Py_ssize_t extracted = 0;
+#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments)
+ if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1;
+#endif
+ name = first_kw_arg;
+ while (*name && num_kwargs > extracted) {
+ PyObject * key = **name;
+ PyObject *value;
+ int found = 0;
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ found = PyDict_GetItemRef(kwds, key, &value);
+ #else
+ value = PyDict_GetItemWithError(kwds, key);
+ if (value) {
+ Py_INCREF(value);
+ found = 1;
+ } else {
+ if (unlikely(PyErr_Occurred())) goto bad;
+ }
+ #endif
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ extracted++;
+ }
+ name++;
+ }
+ if (num_kwargs > extracted) {
+ if (ignore_unknown_kwargs) {
+ if (unlikely(__Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name) == -1))
+ goto bad;
+ } else {
+ __Pyx_RejectUnknownKeyword(kwds, argnames, first_kw_arg, function_name);
+ goto bad;
+ }
+ }
+ return 0;
+bad:
+ return -1;
+}
+static int __Pyx_ParseKeywordDictToDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ const char* function_name)
+{
+ PyObject** const *name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ Py_ssize_t len;
+#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments)
+ if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1;
+#endif
+ if (PyDict_Update(kwds2, kwds) < 0) goto bad;
+ name = first_kw_arg;
+ while (*name) {
+ PyObject *key = **name;
+ PyObject *value;
+#if !CYTHON_COMPILING_IN_LIMITED_API && (PY_VERSION_HEX >= 0x030d00A2 || defined(PyDict_Pop))
+ int found = PyDict_Pop(kwds2, key, &value);
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ }
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ int found = PyDict_GetItemRef(kwds2, key, &value);
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ if (unlikely(PyDict_DelItem(kwds2, key) < 0)) goto bad;
+ }
+#else
+ #if CYTHON_COMPILING_IN_CPYTHON
+ value = _PyDict_Pop(kwds2, key, kwds2);
+ #else
+ value = __Pyx_CallUnboundCMethod2(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_pop, kwds2, key, kwds2);
+ #endif
+ if (value == kwds2) {
+ Py_DECREF(value);
+ } else {
+ if (unlikely(!value)) goto bad;
+ values[name-argnames] = value;
+ }
+#endif
+ name++;
+ }
+ len = PyDict_Size(kwds2);
+ if (len > 0) {
+ return __Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name);
+ } else if (unlikely(len == -1)) {
+ goto bad;
+ }
+ return 0;
+bad:
+ return -1;
+}
+static int __Pyx_ParseKeywordsTuple(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ PyObject *key = NULL;
+ PyObject** const * name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ for (Py_ssize_t pos = 0; pos < num_kwargs; pos++) {
+#if CYTHON_AVOID_BORROWED_REFS
+ key = __Pyx_PySequence_ITEM(kwds, pos);
+#else
+ key = __Pyx_PyTuple_GET_ITEM(kwds, pos);
+#endif
+#if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!key)) goto bad;
+#endif
+ name = first_kw_arg;
+ while (*name && (**name != key)) name++;
+ if (*name) {
+ PyObject *value = kwvalues[pos];
+ values[name-argnames] = __Pyx_NewRef(value);
+ } else {
+ size_t index_found = 0;
+ int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name);
+ if (cmp == 1) {
+ PyObject *value = kwvalues[pos];
+ values[index_found] = __Pyx_NewRef(value);
+ } else {
+ if (unlikely(cmp == -1)) goto bad;
+ if (kwds2) {
+ PyObject *value = kwvalues[pos];
+ if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad;
+ } else if (!ignore_unknown_kwargs) {
+ goto invalid_keyword;
+ }
+ }
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ key = NULL;
+ #endif
+ }
+ return 0;
+invalid_keyword:
+ PyErr_Format(PyExc_TypeError,
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ goto bad;
+bad:
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(key);
+ #endif
+ return -1;
+}
+
+/* ParseKeywords */
+static int __Pyx_ParseKeywords(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds)))
+ return __Pyx_ParseKeywordsTuple(kwds, kwvalues, argnames, kwds2, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs);
+ else if (kwds2)
+ return __Pyx_ParseKeywordDictToDict(kwds, argnames, kwds2, values, num_pos_args, function_name);
+ else
+ return __Pyx_ParseKeywordDict(kwds, argnames, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs);
+}
+
+/* RaiseArgTupleInvalid */
+static void __Pyx_RaiseArgtupleInvalid(
+ const char* func_name,
+ int exact,
+ Py_ssize_t num_min,
+ Py_ssize_t num_max,
+ Py_ssize_t num_found)
+{
+ Py_ssize_t num_expected;
+ const char *more_or_less;
+ if (num_found < num_min) {
+ num_expected = num_min;
+ more_or_less = "at least";
+ } else {
+ num_expected = num_max;
+ more_or_less = "at most";
+ }
+ if (exact) {
+ more_or_less = "exactly";
+ }
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes %.8s %" CYTHON_FORMAT_SSIZE_T "d positional argument%.1s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ func_name, more_or_less, num_expected,
+ (num_expected == 1) ? "" : "s", num_found);
+}
+
+/* PyDictVersioning (used by GetModuleGlobalName) */
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0;
+}
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) {
+ PyObject **dictptr = NULL;
+ Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset;
+ if (offset) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj);
+#else
+ dictptr = _PyObject_GetDictPtr(obj);
+#endif
+ }
+ return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0;
+}
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict)))
+ return 0;
+ return obj_dict_version == __Pyx_get_object_dict_version(obj);
+}
+#endif
+
+/* GetModuleGlobalName */
+#if CYTHON_USE_DICT_VERSIONS
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value)
+#else
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name)
+#endif
+{
+ PyObject *result;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(!__pyx_m)) {
+ if (!PyErr_Occurred())
+ PyErr_SetNone(PyExc_NameError);
+ return NULL;
+ }
+ result = PyObject_GetAttr(__pyx_m, name);
+ if (likely(result)) {
+ return result;
+ }
+ PyErr_Clear();
+#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ if (unlikely(__Pyx_PyDict_GetItemRef(__pyx_mstate_global->__pyx_d, name, &result) == -1)) PyErr_Clear();
+ __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return result;
+ }
+#else
+ result = _PyDict_GetItem_KnownHash(__pyx_mstate_global->__pyx_d, name, ((PyASCIIObject *) name)->hash);
+ __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return __Pyx_NewRef(result);
+ }
+ PyErr_Clear();
+#endif
+ return __Pyx_GetBuiltinName(name);
+}
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_MultiplyCObj(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceMultiply : PyNumber_Multiply)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_MultiplyCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ long b;
+ const PY_LONG_LONG lla = intval;
+ PY_LONG_LONG llb;
+ if (unlikely(__Pyx_PyLong_IsZero(op2))) {
+ return __Pyx_NewRef(op2);
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op2);
+ const digit* digits = __Pyx_PyLong_Digits(op2);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op2);
+ if (likely(size == 1)) {
+ b = (long) digits[0];
+ if (!is_positive) b *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT+30) {
+ b = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT+30) {
+ llb = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT+30) {
+ b = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT+30) {
+ llb = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT+30) {
+ b = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT+30) {
+ llb = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_multiply(op1, op2);
+ }
+ calculate_long:
+ CYTHON_UNUSED_VAR(a);
+ CYTHON_UNUSED_VAR(b);
+ llb = b;
+ goto calculate_long_long;
+ calculate_long_long:
+ {
+ PY_LONG_LONG llx;
+ llx = lla * llb;
+ return PyLong_FromLongLong(llx);
+ }
+
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_MultiplyCObj(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ double b = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) * (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_MultiplyCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op2))) {
+ return __Pyx_Unpacked___Pyx_PyLong_MultiplyCObj(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op2)) {
+ return __Pyx_Float___Pyx_PyLong_MultiplyCObj(op2, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_MultiplyCObj(op1, op2, inplace);
+}
+#endif
+
+/* RaiseTooManyValuesToUnpack */
+static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) {
+ PyErr_Format(PyExc_ValueError,
+ "too many values to unpack (expected %" CYTHON_FORMAT_SSIZE_T "d)", expected);
+}
+
+/* RaiseNeedMoreValuesToUnpack */
+static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
+ PyErr_Format(PyExc_ValueError,
+ "need more than %" CYTHON_FORMAT_SSIZE_T "d value%.1s to unpack",
+ index, (index == 1) ? "" : "s");
+}
+
+/* IterFinish */
+static CYTHON_INLINE int __Pyx_IterFinish(void) {
+ PyObject* exc_type;
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ exc_type = __Pyx_PyErr_CurrentExceptionType();
+ if (unlikely(exc_type)) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))
+ return -1;
+ __Pyx_PyErr_Clear();
+ return 0;
+ }
+ return 0;
+}
+
+/* UnpackItemEndCheck */
+static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) {
+ if (unlikely(retval)) {
+ Py_DECREF(retval);
+ __Pyx_RaiseTooManyValuesError(expected);
+ return -1;
+ }
+ return __Pyx_IterFinish();
+}
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_TrueDivideObjC(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceTrueDivide : PyNumber_TrueDivide)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_TrueDivideObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ long a;
+ if (unlikely(__Pyx_PyLong_IsZero(op1))) {
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op1);
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+ if (likely(size == 1)) {
+ a = (long) digits[0];
+ if (!is_positive) a *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT && 1 * PyLong_SHIFT < 53) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT && 2 * PyLong_SHIFT < 53) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT && 3 * PyLong_SHIFT < 53) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_true_divide(op1, op2);
+ }
+ calculate_long:
+ if ((8 * sizeof(long) <= 53 || likely(labs(a) <= ((PY_LONG_LONG)1 << 53)))
+ || __Pyx_PyLong_DigitCount(op1) <= 52 / PyLong_SHIFT) {
+ return PyFloat_FromDouble((double)a / (double)b);
+ }
+ return PyLong_Type.tp_as_number->nb_true_divide(op1, op2);
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_TrueDivideObjC(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ double a = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) / (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_TrueDivideObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op1))) {
+ return __Pyx_Unpacked___Pyx_PyLong_TrueDivideObjC(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ return __Pyx_Float___Pyx_PyLong_TrueDivideObjC(op1, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_TrueDivideObjC(op1, op2, inplace);
+}
+#endif
+
+/* PyLongCompare */
+static CYTHON_INLINE int __Pyx_PyLong_BoolNeObjC(PyObject *op1, PyObject *op2, long intval, long inplace) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(inplace);
+ if (op1 == op2) {
+ return 0;
+ }
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op1))) {
+ int unequal;
+ unsigned long uintval;
+ Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ if (intval == 0) {
+ return (__Pyx_PyLong_IsZero(op1) != 1);
+ } else if (intval < 0) {
+ if (__Pyx_PyLong_IsNonNeg(op1))
+ return 1;
+ intval = -intval;
+ } else {
+ if (__Pyx_PyLong_IsNeg(op1))
+ return 1;
+ }
+ uintval = (unsigned long) intval;
+#if PyLong_SHIFT * 4 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 4)) {
+ unequal = (size != 5) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[4] != ((uintval >> (4 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 3 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 3)) {
+ unequal = (size != 4) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 2 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 2)) {
+ unequal = (size != 3) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 1 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 1)) {
+ unequal = (size != 2) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+ unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK));
+ return (unequal != 0);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ const long b = intval;
+ double a = __Pyx_PyFloat_AS_DOUBLE(op1);
+ return ((double)a != (double)b);
+ }
+ return __Pyx_PyObject_IsTrueAndDecref(
+ PyObject_RichCompare(op1, op2, Py_NE));
+}
+
+/* GetItemInt */
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
+ PyObject *r;
+ if (unlikely(!j)) return NULL;
+ r = PyObject_GetItem(o, j);
+ Py_DECREF(j);
+ return r;
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_SIZE
+ Py_ssize_t wrapped_i = i;
+ if (wraparound & unlikely(i < 0)) {
+ wrapped_i += PyList_GET_SIZE(o);
+ }
+ if ((CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS)) {
+ return __Pyx_PyList_GetItemRefFast(o, wrapped_i, unsafe_shared);
+ } else
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyList_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyList_GET_ITEM(o, wrapped_i));
+ }
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+#else
+ (void)wraparound;
+ (void)boundscheck;
+ return PySequence_GetItem(o, i);
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ Py_ssize_t wrapped_i = i;
+ if (wraparound & unlikely(i < 0)) {
+ wrapped_i += PyTuple_GET_SIZE(o);
+ }
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyTuple_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyTuple_GET_ITEM(o, wrapped_i));
+ }
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+#else
+ (void)wraparound;
+ (void)boundscheck;
+ return PySequence_GetItem(o, i);
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ if (is_list || PyList_CheckExact(o)) {
+ Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o);
+ if ((CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS)) {
+ return __Pyx_PyList_GetItemRefFast(o, n, unsafe_shared);
+ } else if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) {
+ return __Pyx_NewRef(PyList_GET_ITEM(o, n));
+ }
+ } else
+ #if !CYTHON_AVOID_BORROWED_REFS
+ if (PyTuple_CheckExact(o)) {
+ Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o);
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyTuple_GET_ITEM(o, n));
+ }
+ } else
+ #endif
+#endif
+#if CYTHON_USE_TYPE_SLOTS && !CYTHON_COMPILING_IN_PYPY
+ {
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if (!is_list && mm && mm->mp_subscript) {
+ PyObject *r, *key = PyLong_FromSsize_t(i);
+ if (unlikely(!key)) return NULL;
+ r = mm->mp_subscript(o, key);
+ Py_DECREF(key);
+ return r;
+ }
+ if (is_list || likely(sm && sm->sq_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
+ if (likely(l >= 0)) {
+ i += l;
+ } else {
+ if (!PyErr_ExceptionMatches(PyExc_OverflowError))
+ return NULL;
+ PyErr_Clear();
+ }
+ }
+ return sm->sq_item(o, i);
+ }
+ }
+#else
+ if (is_list || !PyMapping_Check(o)) {
+ return PySequence_GetItem(o, i);
+ }
+#endif
+ (void)wraparound;
+ (void)boundscheck;
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+}
+
+/* ObjectGetItem */
+#if CYTHON_USE_TYPE_SLOTS
+static PyObject *__Pyx_PyObject_GetIndex(PyObject *obj, PyObject *index) {
+ PyObject *runerr = NULL;
+ Py_ssize_t key_value;
+ key_value = __Pyx_PyIndex_AsSsize_t(index);
+ if (likely(key_value != -1 || !(runerr = PyErr_Occurred()))) {
+ return __Pyx_GetItemInt_Fast(obj, key_value, 0, 1, 1, 1);
+ }
+ if (PyErr_GivenExceptionMatches(runerr, PyExc_OverflowError)) {
+ __Pyx_TypeName index_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(index));
+ PyErr_Clear();
+ PyErr_Format(PyExc_IndexError,
+ "cannot fit '" __Pyx_FMT_TYPENAME "' into an index-sized integer", index_type_name);
+ __Pyx_DECREF_TypeName(index_type_name);
+ }
+ return NULL;
+}
+static PyObject *__Pyx_PyObject_GetItem_Slow(PyObject *obj, PyObject *key) {
+ __Pyx_TypeName obj_type_name;
+ if (likely(PyType_Check(obj))) {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(obj, __pyx_mstate_global->__pyx_n_u_class_getitem);
+ if (!meth) {
+ PyErr_Clear();
+ } else {
+ PyObject *result = __Pyx_PyObject_CallOneArg(meth, key);
+ Py_DECREF(meth);
+ return result;
+ }
+ }
+ obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is not subscriptable", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return NULL;
+}
+static PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key) {
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyMappingMethods *mm = tp->tp_as_mapping;
+ PySequenceMethods *sm = tp->tp_as_sequence;
+ if (likely(mm && mm->mp_subscript)) {
+ return mm->mp_subscript(obj, key);
+ }
+ if (likely(sm && sm->sq_item)) {
+ return __Pyx_PyObject_GetIndex(obj, key);
+ }
+ return __Pyx_PyObject_GetItem_Slow(obj, key);
+}
+#endif
+
+/* PyLongCompare */
+static CYTHON_INLINE int __Pyx_PyLong_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(inplace);
+ if (op1 == op2) {
+ return 1;
+ }
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op1))) {
+ int unequal;
+ unsigned long uintval;
+ Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ if (intval == 0) {
+ return (__Pyx_PyLong_IsZero(op1) == 1);
+ } else if (intval < 0) {
+ if (__Pyx_PyLong_IsNonNeg(op1))
+ return 0;
+ intval = -intval;
+ } else {
+ if (__Pyx_PyLong_IsNeg(op1))
+ return 0;
+ }
+ uintval = (unsigned long) intval;
+#if PyLong_SHIFT * 4 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 4)) {
+ unequal = (size != 5) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[4] != ((uintval >> (4 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 3 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 3)) {
+ unequal = (size != 4) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 2 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 2)) {
+ unequal = (size != 3) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+#if PyLong_SHIFT * 1 < SIZEOF_LONG*8
+ if (uintval >> (PyLong_SHIFT * 1)) {
+ unequal = (size != 2) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
+ | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK));
+ } else
+#endif
+ unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK));
+ return (unequal == 0);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ const long b = intval;
+ double a = __Pyx_PyFloat_AS_DOUBLE(op1);
+ return ((double)a == (double)b);
+ }
+ return __Pyx_PyObject_IsTrueAndDecref(
+ PyObject_RichCompare(op1, op2, Py_EQ));
+}
+
+/* RaiseUnboundLocalError */
+static void __Pyx_RaiseUnboundLocalError(const char *varname) {
+ PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
+}
+
+/* GetException (used by pep479) */
+#if CYTHON_FAST_THREAD_STATE
+static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb)
+#else
+static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb)
+#endif
+{
+ PyObject *local_type = NULL, *local_value, *local_tb = NULL;
+#if CYTHON_FAST_THREAD_STATE
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if PY_VERSION_HEX >= 0x030C0000
+ local_value = tstate->current_exception;
+ tstate->current_exception = 0;
+ #else
+ local_type = tstate->curexc_type;
+ local_value = tstate->curexc_value;
+ local_tb = tstate->curexc_traceback;
+ tstate->curexc_type = 0;
+ tstate->curexc_value = 0;
+ tstate->curexc_traceback = 0;
+ #endif
+#elif __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ local_value = PyErr_GetRaisedException();
+#else
+ PyErr_Fetch(&local_type, &local_value, &local_tb);
+#endif
+#if __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ if (likely(local_value)) {
+ local_type = (PyObject*) Py_TYPE(local_value);
+ Py_INCREF(local_type);
+ local_tb = PyException_GetTraceback(local_value);
+ }
+#else
+ PyErr_NormalizeException(&local_type, &local_value, &local_tb);
+#if CYTHON_FAST_THREAD_STATE
+ if (unlikely(tstate->curexc_type))
+#else
+ if (unlikely(PyErr_Occurred()))
+#endif
+ goto bad;
+ if (local_tb) {
+ if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0))
+ goto bad;
+ }
+#endif // __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ Py_XINCREF(local_tb);
+ Py_XINCREF(local_type);
+ Py_XINCREF(local_value);
+ *type = local_type;
+ *value = local_value;
+ *tb = local_tb;
+#if CYTHON_FAST_THREAD_STATE
+ #if CYTHON_USE_EXC_INFO_STACK
+ {
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = local_value;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_tb);
+ #else
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = local_type;
+ exc_info->exc_value = local_value;
+ exc_info->exc_traceback = local_tb;
+ #endif
+ }
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = local_type;
+ tstate->exc_value = local_value;
+ tstate->exc_traceback = local_tb;
+ #endif
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ PyErr_SetHandledException(local_value);
+ Py_XDECREF(local_value);
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_tb);
+#else
+ PyErr_SetExcInfo(local_type, local_value, local_tb);
+#endif
+ return 0;
+#if __PYX_LIMITED_VERSION_HEX <= 0x030C0000
+bad:
+ *type = 0;
+ *value = 0;
+ *tb = 0;
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_value);
+ Py_XDECREF(local_tb);
+ return -1;
+#endif
+}
+
+/* pep479 */
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen) {
+ PyObject *exc, *val, *tb, *cur_exc, *new_exc;
+ __Pyx_PyThreadState_declare
+ int is_async_stopiteration = 0;
+ CYTHON_MAYBE_UNUSED_VAR(in_async_gen);
+ __Pyx_PyThreadState_assign
+ cur_exc = __Pyx_PyErr_CurrentExceptionType();
+ if (likely(!__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopIteration))) {
+ if (in_async_gen && unlikely(__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopAsyncIteration))) {
+ is_async_stopiteration = 1;
+ } else {
+ return;
+ }
+ }
+ __Pyx_GetException(&exc, &val, &tb);
+ Py_XDECREF(exc);
+ Py_XDECREF(tb);
+ new_exc = PyObject_CallFunction(PyExc_RuntimeError, "s",
+ is_async_stopiteration ? "async generator raised StopAsyncIteration" :
+ in_async_gen ? "async generator raised StopIteration" :
+ "generator raised StopIteration");
+ if (!new_exc) {
+ Py_XDECREF(val);
+ return;
+ }
+ PyException_SetCause(new_exc, val); // steals ref to val
+ PyErr_SetObject(PyExc_RuntimeError, new_exc);
+}
+
+/* SliceObject */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(PyObject* obj,
+ Py_ssize_t cstart, Py_ssize_t cstop,
+ PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice,
+ int has_cstart, int has_cstop, CYTHON_UNUSED int wraparound) {
+ __Pyx_TypeName obj_type_name;
+#if CYTHON_USE_TYPE_SLOTS
+ PyMappingMethods* mp = Py_TYPE(obj)->tp_as_mapping;
+ if (likely(mp && mp->mp_subscript))
+#endif
+ {
+ PyObject* result;
+ PyObject *py_slice, *py_start, *py_stop;
+ if (_py_slice) {
+ py_slice = *_py_slice;
+ } else {
+ PyObject* owned_start = NULL;
+ PyObject* owned_stop = NULL;
+ if (_py_start) {
+ py_start = *_py_start;
+ } else {
+ if (has_cstart) {
+ owned_start = py_start = PyLong_FromSsize_t(cstart);
+ if (unlikely(!py_start)) goto bad;
+ } else
+ py_start = Py_None;
+ }
+ if (_py_stop) {
+ py_stop = *_py_stop;
+ } else {
+ if (has_cstop) {
+ owned_stop = py_stop = PyLong_FromSsize_t(cstop);
+ if (unlikely(!py_stop)) {
+ Py_XDECREF(owned_start);
+ goto bad;
+ }
+ } else
+ py_stop = Py_None;
+ }
+ py_slice = PySlice_New(py_start, py_stop, Py_None);
+ Py_XDECREF(owned_start);
+ Py_XDECREF(owned_stop);
+ if (unlikely(!py_slice)) goto bad;
+ }
+#if CYTHON_USE_TYPE_SLOTS
+ result = mp->mp_subscript(obj, py_slice);
+#else
+ result = PyObject_GetItem(obj, py_slice);
+#endif
+ if (!_py_slice) {
+ Py_DECREF(py_slice);
+ }
+ return result;
+ }
+ obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+bad:
+ return NULL;
+}
+
+/* SetItemInt */
+static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) {
+ int r;
+ if (unlikely(!j)) return -1;
+ r = PyObject_SetItem(o, j, v);
+ Py_DECREF(j);
+ return r;
+}
+static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS
+ if (is_list || PyList_CheckExact(o)) {
+ Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o));
+ if ((CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS && !__Pyx_IS_UNIQUELY_REFERENCED(o, unsafe_shared))) {
+ Py_INCREF(v);
+ return PyList_SetItem(o, n, v);
+ } else if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) {
+ PyObject* old;
+ Py_INCREF(v);
+ old = PyList_GET_ITEM(o, n);
+ PyList_SET_ITEM(o, n, v);
+ Py_DECREF(old);
+ return 0;
+ }
+ } else
+#endif
+#if CYTHON_USE_TYPE_SLOTS && !CYTHON_COMPILING_IN_PYPY
+ {
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if (!is_list && mm && mm->mp_ass_subscript) {
+ int r;
+ PyObject *key = PyLong_FromSsize_t(i);
+ if (unlikely(!key)) return -1;
+ r = mm->mp_ass_subscript(o, key, v);
+ Py_DECREF(key);
+ return r;
+ }
+ if (is_list || likely(sm && sm->sq_ass_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
+ if (likely(l >= 0)) {
+ i += l;
+ } else {
+ if (!PyErr_ExceptionMatches(PyExc_OverflowError))
+ return -1;
+ PyErr_Clear();
+ }
+ }
+ return sm->sq_ass_item(o, i, v);
+ }
+ }
+#else
+ if (is_list || !PyMapping_Check(o)) {
+ return PySequence_SetItem(o, i, v);
+ }
+#endif
+ (void)wraparound;
+ (void)boundscheck;
+ return __Pyx_SetItemInt_Generic(o, PyLong_FromSsize_t(i), v);
+}
+
+/* CoroutineSetYieldFrom (used by GeneratorYieldFrom) */
+static void
+__Pyx_Coroutine_Set_Owned_Yield_From(__pyx_CoroutineObject *gen, PyObject *yf) {
+ assert (!gen->yieldfrom);
+#if CYTHON_USE_AM_SEND
+ assert (!gen->yieldfrom_am_send);
+ #if PY_VERSION_HEX < 0x030A00F0
+ if (__Pyx_PyType_HasFeature(Py_TYPE(yf), __Pyx_TPFLAGS_HAVE_AM_SEND))
+ #endif
+ {
+ __Pyx_pyiter_sendfunc am_send;
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+ am_send = __Pyx_PyObject_TryGetSubSlot(yf, tp_as_async, am_send, __Pyx_pyiter_sendfunc);
+ #else
+ __Pyx_PyAsyncMethodsStruct* tp_as_async = (__Pyx_PyAsyncMethodsStruct*) Py_TYPE(yf)->tp_as_async;
+ am_send = tp_as_async ? tp_as_async->am_send : NULL;
+ #endif
+ if (likely(am_send)) {
+ gen->yieldfrom_am_send = am_send;
+ }
+ }
+#endif
+ gen->yieldfrom = yf;
+}
+
+/* dict_setdefault (used by FetchCommonType) */
+static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value) {
+ PyObject* value;
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX >= 0x030C0000
+ PyObject *args[] = {d, key, default_value};
+ value = PyObject_VectorcallMethod(__pyx_mstate_global->__pyx_n_u_setdefault, args, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#elif CYTHON_COMPILING_IN_LIMITED_API
+ value = PyObject_CallMethodObjArgs(d, __pyx_mstate_global->__pyx_n_u_setdefault, key, default_value, NULL);
+#elif PY_VERSION_HEX >= 0x030d0000
+ PyDict_SetDefaultRef(d, key, default_value, &value);
+#else
+ value = PyDict_SetDefault(d, key, default_value);
+ if (unlikely(!value)) return NULL;
+ Py_INCREF(value);
+#endif
+ return value;
+}
+
+/* LimitedApiGetTypeDict (used by SetItemOnTypeDict) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static Py_ssize_t __Pyx_GetTypeDictOffset(void) {
+ PyObject *tp_dictoffset_o;
+ Py_ssize_t tp_dictoffset;
+ tp_dictoffset_o = PyObject_GetAttrString((PyObject*)(&PyType_Type), "__dictoffset__");
+ if (unlikely(!tp_dictoffset_o)) return -1;
+ tp_dictoffset = PyLong_AsSsize_t(tp_dictoffset_o);
+ Py_DECREF(tp_dictoffset_o);
+ if (unlikely(tp_dictoffset == 0)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "'type' doesn't have a dictoffset");
+ return -1;
+ } else if (unlikely(tp_dictoffset < 0)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "'type' has an unexpected negative dictoffset. "
+ "Please report this as Cython bug");
+ return -1;
+ }
+ return tp_dictoffset;
+}
+static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp) {
+ static Py_ssize_t tp_dictoffset = 0;
+ if (unlikely(tp_dictoffset == 0)) {
+ tp_dictoffset = __Pyx_GetTypeDictOffset();
+ if (unlikely(tp_dictoffset == -1 && PyErr_Occurred())) {
+ tp_dictoffset = 0; // try again next time?
+ return NULL;
+ }
+ }
+ return *(PyObject**)((char*)tp + tp_dictoffset);
+}
+#endif
+
+/* SetItemOnTypeDict (used by FixUpExtensionType) */
+static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v) {
+ int result;
+ PyObject *tp_dict;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ tp_dict = __Pyx_GetTypeDict(tp);
+ if (unlikely(!tp_dict)) return -1;
+#else
+ tp_dict = tp->tp_dict;
+#endif
+ result = PyDict_SetItem(tp_dict, k, v);
+ if (likely(!result)) {
+ PyType_Modified(tp);
+ if (unlikely(PyObject_HasAttr(v, __pyx_mstate_global->__pyx_n_u_set_name))) {
+ PyObject *setNameResult = PyObject_CallMethodObjArgs(v, __pyx_mstate_global->__pyx_n_u_set_name, (PyObject *) tp, k, NULL);
+ if (!setNameResult) return -1;
+ Py_DECREF(setNameResult);
+ }
+ }
+ return result;
+}
+
+/* FixUpExtensionType (used by FetchCommonType) */
+static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) {
+#if __PYX_LIMITED_VERSION_HEX > 0x030900B1
+ CYTHON_UNUSED_VAR(spec);
+ CYTHON_UNUSED_VAR(type);
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+#else
+ const PyType_Slot *slot = spec->slots;
+ int changed = 0;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ while (slot && slot->slot && slot->slot != Py_tp_members)
+ slot++;
+ if (slot && slot->slot == Py_tp_members) {
+#if !CYTHON_COMPILING_IN_CPYTHON
+ const
+#endif // !CYTHON_COMPILING_IN_CPYTHON)
+ PyMemberDef *memb = (PyMemberDef*) slot->pfunc;
+ while (memb && memb->name) {
+ if (memb->name[0] == '_' && memb->name[1] == '_') {
+ if (strcmp(memb->name, "__weaklistoffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_weaklistoffset = memb->offset;
+ changed = 1;
+ }
+ else if (strcmp(memb->name, "__dictoffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_dictoffset = memb->offset;
+ changed = 1;
+ }
+#if CYTHON_METH_FASTCALL
+ else if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_vectorcall_offset = memb->offset;
+ changed = 1;
+ }
+#endif // CYTHON_METH_FASTCALL
+#if !CYTHON_COMPILING_IN_PYPY
+ else if (strcmp(memb->name, "__module__") == 0) {
+ PyObject *descr;
+ assert(memb->type == T_OBJECT);
+ assert(memb->flags == 0 || memb->flags == READONLY);
+ descr = PyDescr_NewMember(type, memb);
+ if (unlikely(!descr))
+ return -1;
+ int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr);
+ Py_DECREF(descr);
+ if (unlikely(set_item_result < 0)) {
+ return -1;
+ }
+ changed = 1;
+ }
+#endif // !CYTHON_COMPILING_IN_PYPY
+ }
+ memb++;
+ }
+ }
+#endif // !CYTHON_COMPILING_IN_LIMITED_API
+#if !CYTHON_COMPILING_IN_PYPY
+ slot = spec->slots;
+ while (slot && slot->slot && slot->slot != Py_tp_getset)
+ slot++;
+ if (slot && slot->slot == Py_tp_getset) {
+ PyGetSetDef *getset = (PyGetSetDef*) slot->pfunc;
+ while (getset && getset->name) {
+ if (getset->name[0] == '_' && getset->name[1] == '_' && strcmp(getset->name, "__module__") == 0) {
+ PyObject *descr = PyDescr_NewGetSet(type, getset);
+ if (unlikely(!descr))
+ return -1;
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *pyname = PyUnicode_FromString(getset->name);
+ if (unlikely(!pyname)) {
+ Py_DECREF(descr);
+ return -1;
+ }
+ int set_item_result = __Pyx_SetItemOnTypeDict(type, pyname, descr);
+ Py_DECREF(pyname);
+ #else
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+ int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr);
+ #endif
+ Py_DECREF(descr);
+ if (unlikely(set_item_result < 0)) {
+ return -1;
+ }
+ changed = 1;
+ }
+ ++getset;
+ }
+ }
+#else
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+#endif // !CYTHON_COMPILING_IN_PYPY
+ if (changed)
+ PyType_Modified(type);
+#endif // PY_VERSION_HEX > 0x030900B1
+ return 0;
+}
+
+/* AddModuleRef (used by FetchSharedCythonModule) */
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ static PyObject *__Pyx_PyImport_AddModuleObjectRef(PyObject *name) {
+ PyObject *module_dict = PyImport_GetModuleDict();
+ PyObject *m;
+ if (PyMapping_GetOptionalItem(module_dict, name, &m) < 0) {
+ return NULL;
+ }
+ if (m != NULL && PyModule_Check(m)) {
+ return m;
+ }
+ Py_XDECREF(m);
+ m = PyModule_NewObject(name);
+ if (m == NULL)
+ return NULL;
+ if (PyDict_CheckExact(module_dict)) {
+ PyObject *new_m;
+ (void)PyDict_SetDefaultRef(module_dict, name, m, &new_m);
+ Py_DECREF(m);
+ return new_m;
+ } else {
+ if (PyObject_SetItem(module_dict, name, m) != 0) {
+ Py_DECREF(m);
+ return NULL;
+ }
+ return m;
+ }
+ }
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name) {
+ PyObject *py_name = PyUnicode_FromString(name);
+ if (!py_name) return NULL;
+ PyObject *module = __Pyx_PyImport_AddModuleObjectRef(py_name);
+ Py_DECREF(py_name);
+ return module;
+ }
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name)
+#else
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name) {
+ PyObject *module = PyImport_AddModule(name);
+ Py_XINCREF(module);
+ return module;
+ }
+#endif
+
+/* FetchSharedCythonModule (used by FetchCommonType) */
+static PyObject *__Pyx_FetchSharedCythonABIModule(void) {
+ return __Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME);
+}
+
+/* FetchCommonType (used by CommonTypesMetaclass) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030C0000
+static PyObject* __Pyx_PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) {
+ PyObject *result = __Pyx_PyType_FromModuleAndSpec(module, spec, bases);
+ if (result && metaclass) {
+ PyObject *old_tp = (PyObject*)Py_TYPE(result);
+ Py_INCREF((PyObject*)metaclass);
+#if __PYX_LIMITED_VERSION_HEX >= 0x03090000
+ Py_SET_TYPE(result, metaclass);
+#else
+ result->ob_type = metaclass;
+#endif
+ Py_DECREF(old_tp);
+ }
+ return result;
+}
+#else
+#define __Pyx_PyType_FromMetaclass(me, mo, s, b) PyType_FromMetaclass(me, mo, s, b)
+#endif
+static int __Pyx_VerifyCachedType(PyObject *cached_type,
+ const char *name,
+ Py_ssize_t expected_basicsize) {
+ Py_ssize_t basicsize;
+ if (!PyType_Check(cached_type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s is not a type object", name);
+ return -1;
+ }
+ if (expected_basicsize == 0) {
+ return 0; // size is inherited, nothing useful to check
+ }
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_basicsize;
+ py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__");
+ if (unlikely(!py_basicsize)) return -1;
+ basicsize = PyLong_AsSsize_t(py_basicsize);
+ Py_DECREF(py_basicsize);
+ py_basicsize = NULL;
+ if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) return -1;
+#else
+ basicsize = ((PyTypeObject*) cached_type)->tp_basicsize;
+#endif
+ if (basicsize != expected_basicsize) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s has the wrong size, try recompiling",
+ name);
+ return -1;
+ }
+ return 0;
+}
+static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) {
+ PyObject *abi_module = NULL, *cached_type = NULL, *abi_module_dict, *new_cached_type, *py_object_name;
+ int get_item_ref_result;
+ const char* object_name = strrchr(spec->name, '.');
+ object_name = object_name ? object_name+1 : spec->name;
+ py_object_name = PyUnicode_FromString(object_name);
+ if (!py_object_name) return NULL;
+ abi_module = __Pyx_FetchSharedCythonABIModule();
+ if (!abi_module) goto done;
+ abi_module_dict = PyModule_GetDict(abi_module);
+ if (!abi_module_dict) goto done;
+ get_item_ref_result = __Pyx_PyDict_GetItemRef(abi_module_dict, py_object_name, &cached_type);
+ if (get_item_ref_result == 1) {
+ if (__Pyx_VerifyCachedType(
+ cached_type,
+ object_name,
+ spec->basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ } else if (unlikely(get_item_ref_result == -1)) {
+ goto bad;
+ }
+ cached_type = __Pyx_PyType_FromMetaclass(
+ metaclass,
+ CYTHON_USE_MODULE_STATE ? module : abi_module,
+ spec, bases);
+ if (unlikely(!cached_type)) goto bad;
+ if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad;
+ new_cached_type = __Pyx_PyDict_SetDefault(abi_module_dict, py_object_name, cached_type);
+ if (unlikely(new_cached_type != cached_type)) {
+ if (unlikely(!new_cached_type)) goto bad;
+ Py_DECREF(cached_type);
+ cached_type = new_cached_type;
+ if (__Pyx_VerifyCachedType(
+ cached_type,
+ object_name,
+ spec->basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ } else {
+ Py_DECREF(new_cached_type);
+ }
+done:
+ Py_XDECREF(abi_module);
+ Py_DECREF(py_object_name);
+ assert(cached_type == NULL || PyType_Check(cached_type));
+ return (PyTypeObject *) cached_type;
+bad:
+ Py_XDECREF(cached_type);
+ cached_type = NULL;
+ goto done;
+}
+
+/* CommonTypesMetaclass (used by CoroutineBase) */
+static PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
+ return PyUnicode_FromString(__PYX_ABI_MODULE_NAME);
+}
+#if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject* __pyx_CommonTypesMetaclass_call(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED PyObject *args, CYTHON_UNUSED PyObject *kwds) {
+ PyErr_SetString(PyExc_TypeError, "Cannot instantiate Cython internal types");
+ return NULL;
+}
+static int __pyx_CommonTypesMetaclass_setattr(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED PyObject *attr, CYTHON_UNUSED PyObject *value) {
+ PyErr_SetString(PyExc_TypeError, "Cython internal types are immutable");
+ return -1;
+}
+#endif
+static PyGetSetDef __pyx_CommonTypesMetaclass_getset[] = {
+ {"__module__", __pyx_CommonTypesMetaclass_get_module, NULL, NULL, NULL},
+ {0, 0, 0, 0, 0}
+};
+static PyType_Slot __pyx_CommonTypesMetaclass_slots[] = {
+ {Py_tp_getset, (void *)__pyx_CommonTypesMetaclass_getset},
+ #if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ {Py_tp_call, (void*)__pyx_CommonTypesMetaclass_call},
+ {Py_tp_new, (void*)__pyx_CommonTypesMetaclass_call},
+ {Py_tp_setattro, (void*)__pyx_CommonTypesMetaclass_setattr},
+ #endif
+ {0, 0}
+};
+static PyType_Spec __pyx_CommonTypesMetaclass_spec = {
+ __PYX_TYPE_MODULE_PREFIX "_common_types_metatype",
+ 0,
+ 0,
+ Py_TPFLAGS_IMMUTABLETYPE |
+ Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT,
+ __pyx_CommonTypesMetaclass_slots
+};
+static int __pyx_CommonTypesMetaclass_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ PyObject *bases = PyTuple_Pack(1, &PyType_Type);
+ if (unlikely(!bases)) {
+ return -1;
+ }
+ mstate->__pyx_CommonTypesMetaclassType = __Pyx_FetchCommonTypeFromSpec(NULL, module, &__pyx_CommonTypesMetaclass_spec, bases);
+ Py_DECREF(bases);
+ if (unlikely(mstate->__pyx_CommonTypesMetaclassType == NULL)) {
+ return -1;
+ }
+ return 0;
+}
+
+/* RaiseException (used by CoroutineBase) */
+static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) {
+ PyObject* owned_instance = NULL;
+ if (tb == Py_None) {
+ tb = 0;
+ } else if (tb && !PyTraceBack_Check(tb)) {
+ PyErr_SetString(PyExc_TypeError,
+ "raise: arg 3 must be a traceback or None");
+ goto bad;
+ }
+ if (value == Py_None)
+ value = 0;
+ if (PyExceptionInstance_Check(type)) {
+ if (value) {
+ PyErr_SetString(PyExc_TypeError,
+ "instance exception may not have a separate value");
+ goto bad;
+ }
+ value = type;
+ type = (PyObject*) Py_TYPE(value);
+ } else if (PyExceptionClass_Check(type)) {
+ PyObject *instance_class = NULL;
+ if (value && PyExceptionInstance_Check(value)) {
+ instance_class = (PyObject*) Py_TYPE(value);
+ if (instance_class != type) {
+ int is_subclass = PyObject_IsSubclass(instance_class, type);
+ if (!is_subclass) {
+ instance_class = NULL;
+ } else if (unlikely(is_subclass == -1)) {
+ goto bad;
+ } else {
+ type = instance_class;
+ }
+ }
+ }
+ if (!instance_class) {
+ PyObject *args;
+ if (!value)
+ args = PyTuple_New(0);
+ else if (PyTuple_Check(value)) {
+ Py_INCREF(value);
+ args = value;
+ } else
+ args = PyTuple_Pack(1, value);
+ if (!args)
+ goto bad;
+ owned_instance = PyObject_Call(type, args, NULL);
+ Py_DECREF(args);
+ if (!owned_instance)
+ goto bad;
+ value = owned_instance;
+ if (!PyExceptionInstance_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "calling %R should have returned an instance of "
+ "BaseException, not %R",
+ type, Py_TYPE(value));
+ goto bad;
+ }
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "raise: exception class must be a subclass of BaseException");
+ goto bad;
+ }
+ if (cause) {
+ PyObject *fixed_cause;
+ if (cause == Py_None) {
+ fixed_cause = NULL;
+ } else if (PyExceptionClass_Check(cause)) {
+ fixed_cause = PyObject_CallObject(cause, NULL);
+ if (fixed_cause == NULL)
+ goto bad;
+ } else if (PyExceptionInstance_Check(cause)) {
+ fixed_cause = cause;
+ Py_INCREF(fixed_cause);
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "exception causes must derive from "
+ "BaseException");
+ goto bad;
+ }
+ PyException_SetCause(value, fixed_cause);
+ }
+ PyErr_SetObject(type, value);
+ if (tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyException_SetTraceback(value, tb);
+#elif CYTHON_FAST_THREAD_STATE
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
+ PyObject* tmp_tb = tstate->curexc_traceback;
+ if (tb != tmp_tb) {
+ Py_INCREF(tb);
+ tstate->curexc_traceback = tb;
+ Py_XDECREF(tmp_tb);
+ }
+#else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb);
+ Py_INCREF(tb);
+ PyErr_Restore(tmp_type, tmp_value, tb);
+ Py_XDECREF(tmp_tb);
+#endif
+ }
+bad:
+ Py_XDECREF(owned_instance);
+ return;
+}
+
+/* GetTopmostException (used by SaveResetException) */
+#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE
+static _PyErr_StackItem *
+__Pyx_PyErr_GetTopmostException(PyThreadState *tstate)
+{
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
+ exc_info->previous_item != NULL)
+ {
+ exc_info = exc_info->previous_item;
+ }
+ return exc_info;
+}
+#endif
+
+/* SaveResetException (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ PyObject *exc_value = exc_info->exc_value;
+ if (exc_value == NULL || exc_value == Py_None) {
+ *value = NULL;
+ *type = NULL;
+ *tb = NULL;
+ } else {
+ *value = exc_value;
+ Py_INCREF(*value);
+ *type = (PyObject*) Py_TYPE(exc_value);
+ Py_INCREF(*type);
+ *tb = PyException_GetTraceback(exc_value);
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ *type = exc_info->exc_type;
+ *value = exc_info->exc_value;
+ *tb = exc_info->exc_traceback;
+ Py_XINCREF(*type);
+ Py_XINCREF(*value);
+ Py_XINCREF(*tb);
+ #else
+ *type = tstate->exc_type;
+ *value = tstate->exc_value;
+ *tb = tstate->exc_traceback;
+ Py_XINCREF(*type);
+ Py_XINCREF(*value);
+ Py_XINCREF(*tb);
+ #endif
+}
+static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ PyObject *tmp_value = exc_info->exc_value;
+ exc_info->exc_value = value;
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+ #else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = type;
+ exc_info->exc_value = value;
+ exc_info->exc_traceback = tb;
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = type;
+ tstate->exc_value = value;
+ tstate->exc_traceback = tb;
+ #endif
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+ #endif
+}
+#endif
+
+/* SwapException (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = *value;
+ if (tmp_value == NULL || tmp_value == Py_None) {
+ Py_XDECREF(tmp_value);
+ tmp_value = NULL;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ } else {
+ tmp_type = (PyObject*) Py_TYPE(tmp_value);
+ Py_INCREF(tmp_type);
+ #if CYTHON_COMPILING_IN_CPYTHON
+ tmp_tb = ((PyBaseExceptionObject*) tmp_value)->traceback;
+ Py_XINCREF(tmp_tb);
+ #else
+ tmp_tb = PyException_GetTraceback(tmp_value);
+ #endif
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = *type;
+ exc_info->exc_value = *value;
+ exc_info->exc_traceback = *tb;
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = *type;
+ tstate->exc_value = *value;
+ tstate->exc_traceback = *tb;
+ #endif
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+#else
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyErr_GetExcInfo(&tmp_type, &tmp_value, &tmp_tb);
+ PyErr_SetExcInfo(*type, *value, *tb);
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+#endif
+
+/* CallTypeTraverse (used by CoroutineBase) */
+#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000)
+#else
+static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg) {
+ #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x03090000
+ if (__Pyx_get_runtime_version() < 0x03090000) return 0;
+ #endif
+ if (!always_call) {
+ PyTypeObject *base = __Pyx_PyObject_GetSlot(o, tp_base, PyTypeObject*);
+ unsigned long flags = PyType_GetFlags(base);
+ if (flags & Py_TPFLAGS_HEAPTYPE) {
+ return 0;
+ }
+ }
+ Py_VISIT((PyObject*)Py_TYPE(o));
+ return 0;
+}
+#endif
+
+/* IterNextPlain (used by CoroutineBase) */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void) {
+ if (unlikely(!__pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache))
+ __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache = __Pyx_GetBuiltinName(__pyx_mstate_global->__pyx_n_u_next);
+ return __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache;
+}
+#endif
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator) {
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ PyObject *result;
+ PyObject *next = __Pyx_GetBuiltinNext_LimitedAPI();
+ if (unlikely(!next)) return NULL;
+ result = PyObject_CallFunctionObjArgs(next, iterator, NULL);
+ return result;
+#else
+ (void)__Pyx_GetBuiltinName; // only for early limited API
+ iternextfunc iternext = __Pyx_PyObject_GetIterNextFunc(iterator);
+ assert(iternext);
+ return iternext(iterator);
+#endif
+}
+
+/* PyObjectCall2Args (used by PyObjectCallMethod1) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) {
+ PyObject *args[3] = {NULL, arg1, arg2};
+ return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* PyObjectGetMethod (used by PyObjectCallMethod1) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) {
+ PyObject *attr;
+#if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP
+ __Pyx_TypeName type_name;
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyObject *descr;
+ descrgetfunc f = NULL;
+ PyObject **dictptr, *dict;
+ int meth_found = 0;
+ assert (*method == NULL);
+ if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) {
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+ }
+ if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) {
+ return 0;
+ }
+ descr = _PyType_Lookup(tp, name);
+ if (likely(descr != NULL)) {
+ Py_INCREF(descr);
+#if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR
+ if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR))
+#else
+ #ifdef __Pyx_CyFunction_USED
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr)))
+ #else
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type)))
+ #endif
+#endif
+ {
+ meth_found = 1;
+ } else {
+ f = Py_TYPE(descr)->tp_descr_get;
+ if (f != NULL && PyDescr_IsData(descr)) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+ }
+ }
+ dictptr = _PyObject_GetDictPtr(obj);
+ if (dictptr != NULL && (dict = *dictptr) != NULL) {
+ Py_INCREF(dict);
+ attr = __Pyx_PyDict_GetItemStr(dict, name);
+ if (attr != NULL) {
+ Py_INCREF(attr);
+ Py_DECREF(dict);
+ Py_XDECREF(descr);
+ goto try_unpack;
+ }
+ Py_DECREF(dict);
+ }
+ if (meth_found) {
+ *method = descr;
+ return 1;
+ }
+ if (f != NULL) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+ if (likely(descr != NULL)) {
+ *method = descr;
+ return 0;
+ }
+ type_name = __Pyx_PyType_GetFullyQualifiedName(tp);
+ PyErr_Format(PyExc_AttributeError,
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'",
+ type_name, name);
+ __Pyx_DECREF_TypeName(type_name);
+ return 0;
+#else
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+#endif
+try_unpack:
+#if CYTHON_UNPACK_METHODS
+ if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) {
+ PyObject *function = PyMethod_GET_FUNCTION(attr);
+ Py_INCREF(function);
+ Py_DECREF(attr);
+ *method = function;
+ return 1;
+ }
+#endif
+ *method = attr;
+ return 0;
+}
+#endif
+
+/* PyObjectCallMethod1 (used by CoroutineBase) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
+ PyObject *result = __Pyx_PyObject_CallOneArg(method, arg);
+ Py_DECREF(method);
+ return result;
+}
+#endif
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
+#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
+ PyObject *args[2] = {obj, arg};
+ (void) __Pyx_PyObject_CallOneArg;
+ (void) __Pyx_PyObject_Call2Args;
+ return PyObject_VectorcallMethod(method_name, args, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#else
+ PyObject *method = NULL, *result;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_Call2Args(method, obj, arg);
+ Py_DECREF(method);
+ return result;
+ }
+ if (unlikely(!method)) return NULL;
+ return __Pyx__PyObject_CallMethod1(method, arg);
+#endif
+}
+
+/* PyObjectCallNoArg (used by CoroutineBase) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) {
+ PyObject *arg[2] = {NULL, NULL};
+ return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* ReturnWithStopIteration (used by CoroutineBase) */
+static void __Pyx__ReturnWithStopIteration(PyObject* value, int async);
+static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext) {
+ if (value == Py_None) {
+ if (async || !iternext)
+ PyErr_SetNone(async ? PyExc_StopAsyncIteration : PyExc_StopIteration);
+ return;
+ }
+ __Pyx__ReturnWithStopIteration(value, async);
+}
+static void __Pyx__ReturnWithStopIteration(PyObject* value, int async) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ __Pyx_PyThreadState_declare
+#endif
+ PyObject *exc;
+ PyObject *exc_type = async ? PyExc_StopAsyncIteration : PyExc_StopIteration;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if ((PY_VERSION_HEX >= (0x030C00A6)) || unlikely(PyTuple_Check(value) || PyExceptionInstance_Check(value))) {
+ if (PY_VERSION_HEX >= (0x030e00A1)) {
+ exc = __Pyx_PyObject_CallOneArg(exc_type, value);
+ } else {
+ PyObject *args_tuple = PyTuple_New(1);
+ if (unlikely(!args_tuple)) return;
+ Py_INCREF(value);
+ PyTuple_SET_ITEM(args_tuple, 0, value);
+ exc = PyObject_Call(exc_type, args_tuple, NULL);
+ Py_DECREF(args_tuple);
+ }
+ if (unlikely(!exc)) return;
+ } else {
+ Py_INCREF(value);
+ exc = value;
+ }
+ #if CYTHON_FAST_THREAD_STATE
+ __Pyx_PyThreadState_assign
+ #if CYTHON_USE_EXC_INFO_STACK
+ if (!__pyx_tstate->exc_info->exc_value)
+ #else
+ if (!__pyx_tstate->exc_type)
+ #endif
+ {
+ Py_INCREF(exc_type);
+ __Pyx_ErrRestore(exc_type, exc, NULL);
+ return;
+ }
+ #endif
+#else
+ exc = __Pyx_PyObject_CallOneArg(exc_type, value);
+ if (unlikely(!exc)) return;
+#endif
+ PyErr_SetObject(exc_type, exc);
+ Py_DECREF(exc);
+}
+
+/* CoroutineBase (used by Generator) */
+#if !CYTHON_COMPILING_IN_LIMITED_API
+#include
+#if PY_VERSION_HEX >= 0x030b00a6 && !defined(PYPY_VERSION)
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+#endif // CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE void
+__Pyx_Coroutine_Undelegate(__pyx_CoroutineObject *gen) {
+#if CYTHON_USE_AM_SEND
+ gen->yieldfrom_am_send = NULL;
+#endif
+ Py_CLEAR(gen->yieldfrom);
+}
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *__pyx_tstate, PyObject **pvalue) {
+ PyObject *et, *ev, *tb;
+ PyObject *value = NULL;
+ CYTHON_UNUSED_VAR(__pyx_tstate);
+ __Pyx_ErrFetch(&et, &ev, &tb);
+ if (!et) {
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ Py_INCREF(Py_None);
+ *pvalue = Py_None;
+ return 0;
+ }
+ if (likely(et == PyExc_StopIteration)) {
+ if (!ev) {
+ Py_INCREF(Py_None);
+ value = Py_None;
+ }
+ else if (likely(__Pyx_IS_TYPE(ev, (PyTypeObject*)PyExc_StopIteration))) {
+ #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL
+ value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value);
+ if (unlikely(!value)) goto limited_api_failure;
+ #else
+ value = ((PyStopIterationObject *)ev)->value;
+ Py_INCREF(value);
+ #endif
+ Py_DECREF(ev);
+ }
+ else if (unlikely(PyTuple_Check(ev))) {
+ Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(ev);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(tuple_size < 0)) {
+ Py_XDECREF(tb);
+ Py_DECREF(ev);
+ Py_DECREF(et);
+ return -1;
+ }
+ #endif
+ if (tuple_size >= 1) {
+#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ value = PyTuple_GET_ITEM(ev, 0);
+ Py_INCREF(value);
+#elif CYTHON_ASSUME_SAFE_MACROS
+ value = PySequence_ITEM(ev, 0);
+#else
+ value = PySequence_GetItem(ev, 0);
+ if (!value) goto limited_api_failure;
+#endif
+ } else {
+ Py_INCREF(Py_None);
+ value = Py_None;
+ }
+ Py_DECREF(ev);
+ }
+ else if (!__Pyx_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) {
+ value = ev;
+ }
+ if (likely(value)) {
+ Py_XDECREF(tb);
+ Py_DECREF(et);
+ *pvalue = value;
+ return 0;
+ }
+ } else if (!__Pyx_PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) {
+ __Pyx_ErrRestore(et, ev, tb);
+ return -1;
+ }
+ PyErr_NormalizeException(&et, &ev, &tb);
+ if (unlikely(!PyObject_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration))) {
+ __Pyx_ErrRestore(et, ev, tb);
+ return -1;
+ }
+ Py_XDECREF(tb);
+ Py_DECREF(et);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value);
+#else
+ value = ((PyStopIterationObject *)ev)->value;
+ Py_INCREF(value);
+#endif
+ Py_DECREF(ev);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(!value)) return -1;
+#endif
+ *pvalue = value;
+ return 0;
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL || !CYTHON_ASSUME_SAFE_MACROS
+ limited_api_failure:
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ return -1;
+#endif
+}
+static CYTHON_INLINE
+__Pyx_PySendResult __Pyx_Coroutine_status_from_result(PyObject **retval) {
+ if (*retval) {
+ return PYGEN_NEXT;
+ } else if (likely(__Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, retval) == 0)) {
+ return PYGEN_RETURN;
+ } else {
+ return PYGEN_ERROR;
+ }
+}
+static CYTHON_INLINE
+void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *exc_state) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_CLEAR(exc_state->exc_value);
+#else
+ PyObject *t, *v, *tb;
+ t = exc_state->exc_type;
+ v = exc_state->exc_value;
+ tb = exc_state->exc_traceback;
+ exc_state->exc_type = NULL;
+ exc_state->exc_value = NULL;
+ exc_state->exc_traceback = NULL;
+ Py_XDECREF(t);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+#endif
+}
+#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL)
+static void __Pyx__Coroutine_AlreadyRunningError(__pyx_CoroutineObject *gen) {
+ const char *msg;
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check((PyObject*)gen)) {
+ msg = "coroutine already executing";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact((PyObject*)gen)) {
+ msg = "async generator already executing";
+ #endif
+ } else {
+ msg = "generator already executing";
+ }
+ PyErr_SetString(PyExc_ValueError, msg);
+}
+static void __Pyx_Coroutine_AlreadyTerminatedError(PyObject *gen, PyObject *value, int closing) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ CYTHON_MAYBE_UNUSED_VAR(closing);
+ #ifdef __Pyx_Coroutine_USED
+ if (!closing && __Pyx_Coroutine_Check(gen)) {
+ PyErr_SetString(PyExc_RuntimeError, "cannot reuse already awaited coroutine");
+ } else
+ #endif
+ if (value) {
+ #ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(gen))
+ PyErr_SetNone(PyExc_StopAsyncIteration);
+ else
+ #endif
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+}
+static
+__Pyx_PySendResult __Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, PyObject **result, int closing) {
+ __Pyx_PyThreadState_declare
+ PyThreadState *tstate;
+ __Pyx_ExcInfoStruct *exc_state;
+ PyObject *retval;
+ assert(__Pyx_Coroutine_get_is_running(self)); // Callers should ensure is_running
+ if (unlikely(self->resume_label == -1)) {
+ __Pyx_Coroutine_AlreadyTerminatedError((PyObject*)self, value, closing);
+ return PYGEN_ERROR;
+ }
+#if CYTHON_FAST_THREAD_STATE
+ __Pyx_PyThreadState_assign
+ tstate = __pyx_tstate;
+#else
+ tstate = __Pyx_PyThreadState_Current;
+#endif
+ exc_state = &self->gi_exc_state;
+ if (exc_state->exc_value) {
+ #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ #else
+ PyObject *exc_tb;
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #elif PY_VERSION_HEX >= 0x030B00a4
+ exc_tb = ((PyBaseExceptionObject*) exc_state->exc_value)->traceback;
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+ if (exc_tb) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
+ PyFrameObject *f = tb->tb_frame;
+ assert(f->f_back == NULL);
+ #if PY_VERSION_HEX >= 0x030B00A1
+ f->f_back = PyThreadState_GetFrame(tstate);
+ #else
+ Py_XINCREF(tstate->frame);
+ f->f_back = tstate->frame;
+ #endif
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON
+ Py_DECREF(exc_tb);
+ #endif
+ }
+ #endif
+ }
+#if CYTHON_USE_EXC_INFO_STACK
+ exc_state->previous_item = tstate->exc_info;
+ tstate->exc_info = exc_state;
+#else
+ if (exc_state->exc_type) {
+ __Pyx_ExceptionSwap(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
+ } else {
+ __Pyx_Coroutine_ExceptionClear(exc_state);
+ __Pyx_ExceptionSave(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
+ }
+#endif
+ retval = self->body(self, tstate, value);
+#if CYTHON_USE_EXC_INFO_STACK
+ exc_state = &self->gi_exc_state;
+ tstate->exc_info = exc_state->previous_item;
+ exc_state->previous_item = NULL;
+ __Pyx_Coroutine_ResetFrameBackpointer(exc_state);
+#endif
+ *result = retval;
+ if (self->resume_label == -1) {
+ return likely(retval) ? PYGEN_RETURN : PYGEN_ERROR;
+ }
+ return PYGEN_NEXT;
+}
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API
+ CYTHON_UNUSED_VAR(exc_state);
+#else
+ PyObject *exc_tb;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ if (!exc_state->exc_value) return;
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+ if (likely(exc_tb)) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
+ PyFrameObject *f = tb->tb_frame;
+ Py_CLEAR(f->f_back);
+ #if PY_VERSION_HEX >= 0x030B00a4
+ Py_DECREF(exc_tb);
+ #endif
+ }
+#endif
+}
+#define __Pyx_Coroutine_MethodReturnFromResult(gen, result, retval, iternext)\
+ ((result) == PYGEN_NEXT ? (retval) : __Pyx__Coroutine_MethodReturnFromResult(gen, result, retval, iternext))
+static PyObject *
+__Pyx__Coroutine_MethodReturnFromResult(PyObject* gen, __Pyx_PySendResult result, PyObject *retval, int iternext) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if (likely(result == PYGEN_RETURN)) {
+ int is_async = 0;
+ #ifdef __Pyx_AsyncGen_USED
+ is_async = __Pyx_AsyncGen_CheckExact(gen);
+ #endif
+ __Pyx_ReturnWithStopIteration(retval, is_async, iternext);
+ Py_XDECREF(retval);
+ }
+ return NULL;
+}
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE
+PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) {
+#if PY_VERSION_HEX <= 0x030A00A1
+ return _PyGen_Send(gen, arg);
+#else
+ PyObject *result;
+ if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) {
+ if (PyAsyncGen_CheckExact(gen)) {
+ assert(result == Py_None);
+ PyErr_SetNone(PyExc_StopAsyncIteration);
+ }
+ else if (result == Py_None) {
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+ else {
+#if PY_VERSION_HEX < 0x030d00A1
+ _PyGen_SetStopIterationValue(result);
+#else
+ if (!PyTuple_Check(result) && !PyExceptionInstance_Check(result)) {
+ PyErr_SetObject(PyExc_StopIteration, result);
+ } else {
+ PyObject *exc = __Pyx_PyObject_CallOneArg(PyExc_StopIteration, result);
+ if (likely(exc != NULL)) {
+ PyErr_SetObject(PyExc_StopIteration, exc);
+ Py_DECREF(exc);
+ }
+ }
+#endif
+ }
+ Py_DECREF(result);
+ result = NULL;
+ }
+ return result;
+#endif
+}
+#endif
+static CYTHON_INLINE __Pyx_PySendResult
+__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen, PyObject** retval) {
+ __Pyx_PySendResult result;
+ PyObject *val = NULL;
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ __Pyx_Coroutine_Undelegate(gen);
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, &val);
+ result = __Pyx_Coroutine_SendEx(gen, val, retval, 0);
+ Py_XDECREF(val);
+ return result;
+}
+#if CYTHON_USE_AM_SEND
+static __Pyx_PySendResult
+__Pyx_Coroutine_SendToDelegate(__pyx_CoroutineObject *gen, __Pyx_pyiter_sendfunc gen_am_send, PyObject *value, PyObject **retval) {
+ PyObject *ret = NULL;
+ __Pyx_PySendResult delegate_result, result;
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ delegate_result = gen_am_send(gen->yieldfrom, value, &ret);
+ if (delegate_result == PYGEN_NEXT) {
+ assert (ret != NULL);
+ *retval = ret;
+ return PYGEN_NEXT;
+ }
+ assert (delegate_result != PYGEN_ERROR || ret == NULL);
+ __Pyx_Coroutine_Undelegate(gen);
+ result = __Pyx_Coroutine_SendEx(gen, ret, retval, 0);
+ Py_XDECREF(ret);
+ return result;
+}
+#endif
+static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_AmSend(self, value, &retval);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0);
+}
+static __Pyx_PySendResult
+__Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval) {
+ __Pyx_PySendResult result;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ *retval = __Pyx_Coroutine_AlreadyRunningError(gen);
+ return PYGEN_ERROR;
+ }
+ #if CYTHON_USE_AM_SEND
+ if (gen->yieldfrom_am_send) {
+ result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, value, retval);
+ } else
+ #endif
+ if (gen->yieldfrom) {
+ PyObject *yf = gen->yieldfrom;
+ PyObject *ret;
+ #if !CYTHON_USE_AM_SEND
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, value);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, value);
+ } else
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ ret = __Pyx_async_gen_asend_send(yf, value);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ if (PyCoro_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ #endif
+ #endif
+ {
+ #if !CYTHON_COMPILING_IN_LIMITED_API || __PYX_LIMITED_VERSION_HEX >= 0x03080000
+ if (value == Py_None && PyIter_Check(yf))
+ ret = __Pyx_PyIter_Next_Plain(yf);
+ else
+ #endif
+ ret = __Pyx_PyObject_CallMethod1(yf, __pyx_mstate_global->__pyx_n_u_send, value);
+ }
+ if (likely(ret)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ *retval = ret;
+ return PYGEN_NEXT;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, retval);
+ } else {
+ result = __Pyx_Coroutine_SendEx(gen, value, retval, 0);
+ }
+ __Pyx_Coroutine_unset_is_running(gen);
+ return result;
+}
+static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) {
+ __Pyx_PySendResult result;
+ PyObject *retval = NULL;
+ CYTHON_UNUSED_VAR(gen);
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ result = __Pyx_Coroutine_Close(yf, &retval);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(yf)) {
+ result = __Pyx_Coroutine_Close(yf, &retval);
+ } else
+ if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ result = __Pyx_CoroutineAwait_Close((__pyx_CoroutineAwaitObject*)yf);
+ } else
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ retval = __Pyx_async_gen_asend_close(yf, NULL);
+ result = PYGEN_RETURN;
+ } else
+ if (__pyx_PyAsyncGenAThrow_CheckExact(yf)) {
+ retval = __Pyx_async_gen_athrow_close(yf, NULL);
+ result = PYGEN_RETURN;
+ } else
+ #endif
+ {
+ PyObject *meth;
+ result = PYGEN_RETURN;
+ meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_close);
+ if (unlikely(!meth)) {
+ if (unlikely(PyErr_Occurred())) {
+ PyErr_WriteUnraisable(yf);
+ }
+ } else {
+ retval = __Pyx_PyObject_CallNoArg(meth);
+ Py_DECREF(meth);
+ if (unlikely(!retval)) {
+ result = PYGEN_ERROR;
+ }
+ }
+ }
+ Py_XDECREF(retval);
+ return result == PYGEN_ERROR ? -1 : 0;
+}
+static PyObject *__Pyx_Generator_Next(PyObject *self) {
+ __Pyx_PySendResult result;
+ PyObject *retval = NULL;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ }
+ #if CYTHON_USE_AM_SEND
+ if (gen->yieldfrom_am_send) {
+ result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, Py_None, &retval);
+ } else
+ #endif
+ if (gen->yieldfrom) {
+ PyObject *yf = gen->yieldfrom;
+ PyObject *ret;
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ ret = __Pyx_Generator_Next(yf);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_CheckExact(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, Py_None);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && (PY_VERSION_HEX < 0x030A00A3 || !CYTHON_USE_AM_SEND)
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL);
+ } else
+ #endif
+ ret = __Pyx_PyIter_Next_Plain(yf);
+ if (likely(ret)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return ret;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, &retval);
+ } else {
+ result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0);
+ }
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 1);
+}
+static PyObject *__Pyx_Coroutine_Close_Method(PyObject *self, PyObject *arg) {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result;
+ CYTHON_UNUSED_VAR(arg);
+ result = __Pyx_Coroutine_Close(self, &retval);
+ if (unlikely(result == PYGEN_ERROR))
+ return NULL;
+ Py_XDECREF(retval);
+ Py_RETURN_NONE;
+}
+static __Pyx_PySendResult
+__Pyx_Coroutine_Close(PyObject *self, PyObject **retval) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ __Pyx_PySendResult result;
+ PyObject *yf;
+ int err = 0;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ *retval = __Pyx_Coroutine_AlreadyRunningError(gen);
+ return PYGEN_ERROR;
+ }
+ yf = gen->yieldfrom;
+ if (yf) {
+ Py_INCREF(yf);
+ err = __Pyx_Coroutine_CloseIter(gen, yf);
+ __Pyx_Coroutine_Undelegate(gen);
+ Py_DECREF(yf);
+ }
+ if (err == 0)
+ PyErr_SetNone(PyExc_GeneratorExit);
+ result = __Pyx_Coroutine_SendEx(gen, NULL, retval, 1);
+ if (result == PYGEN_ERROR) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ __Pyx_Coroutine_unset_is_running(gen);
+ if (!__Pyx_PyErr_Occurred()) {
+ return PYGEN_RETURN;
+ } else if (likely(__Pyx_PyErr_ExceptionMatches2(PyExc_GeneratorExit, PyExc_StopIteration))) {
+ __Pyx_PyErr_Clear();
+ return PYGEN_RETURN;
+ }
+ return PYGEN_ERROR;
+ } else if (likely(result == PYGEN_RETURN && *retval == Py_None)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return PYGEN_RETURN;
+ } else {
+ const char *msg;
+ Py_DECREF(*retval);
+ *retval = NULL;
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check(self)) {
+ msg = "coroutine ignored GeneratorExit";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact(self)) {
+ msg = "async generator ignored GeneratorExit";
+ #endif
+ } else {
+ msg = "generator ignored GeneratorExit";
+ }
+ PyErr_SetString(PyExc_RuntimeError, msg);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return PYGEN_ERROR;
+ }
+}
+static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject *val, PyObject *tb,
+ PyObject *args, int close_on_genexit) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ PyObject *yf;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen)))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ yf = gen->yieldfrom;
+ if (yf) {
+ __Pyx_PySendResult result;
+ PyObject *ret;
+ Py_INCREF(yf);
+ if (__Pyx_PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && close_on_genexit) {
+ int err = __Pyx_Coroutine_CloseIter(gen, yf);
+ Py_DECREF(yf);
+ __Pyx_Coroutine_Undelegate(gen);
+ if (err < 0)
+ goto propagate_exception;
+ goto throw_here;
+ }
+ if (0
+ #ifdef __Pyx_Generator_USED
+ || __Pyx_Generator_CheckExact(yf)
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ || __Pyx_Coroutine_Check(yf)
+ #endif
+ ) {
+ ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit);
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ ret = __Pyx__Coroutine_Throw(((__pyx_CoroutineAwaitObject*)yf)->coroutine, typ, val, tb, args, close_on_genexit);
+ #endif
+ } else {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_throw);
+ if (unlikely(!meth)) {
+ Py_DECREF(yf);
+ if (unlikely(PyErr_Occurred())) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return NULL;
+ }
+ __Pyx_Coroutine_Undelegate(gen);
+ goto throw_here;
+ }
+ if (likely(args)) {
+ ret = __Pyx_PyObject_Call(meth, args, NULL);
+ } else {
+ PyObject *cargs[4] = {NULL, typ, val, tb};
+ ret = __Pyx_PyObject_FastCall(meth, cargs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+ }
+ Py_DECREF(meth);
+ }
+ Py_DECREF(yf);
+ if (ret) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return ret;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, &ret);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, ret, 0);
+ }
+throw_here:
+ __Pyx_Raise(typ, val, tb, NULL);
+propagate_exception:
+ {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, NULL, &retval, 0);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0);
+ }
+}
+static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) {
+ PyObject *typ;
+ PyObject *val = NULL;
+ PyObject *tb = NULL;
+ if (unlikely(!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)))
+ return NULL;
+ return __Pyx__Coroutine_Throw(self, typ, val, tb, args, 1);
+}
+static CYTHON_INLINE int __Pyx_Coroutine_traverse_excstate(__Pyx_ExcInfoStruct *exc_state, visitproc visit, void *arg) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_VISIT(exc_state->exc_value);
+#else
+ Py_VISIT(exc_state->exc_type);
+ Py_VISIT(exc_state->exc_value);
+ Py_VISIT(exc_state->exc_traceback);
+#endif
+ return 0;
+}
+static int __Pyx_Coroutine_traverse(__pyx_CoroutineObject *gen, visitproc visit, void *arg) {
+ {
+ int e = __Pyx_call_type_traverse((PyObject*)gen, 1, visit, arg);
+ if (e) return e;
+ }
+ Py_VISIT(gen->closure);
+ Py_VISIT(gen->classobj);
+ Py_VISIT(gen->yieldfrom);
+ return __Pyx_Coroutine_traverse_excstate(&gen->gi_exc_state, visit, arg);
+}
+static int __Pyx_Coroutine_clear(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ Py_CLEAR(gen->closure);
+ Py_CLEAR(gen->classobj);
+ __Pyx_Coroutine_Undelegate(gen);
+ __Pyx_Coroutine_ExceptionClear(&gen->gi_exc_state);
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)gen)->ag_finalizer);
+ }
+#endif
+ Py_CLEAR(gen->gi_code);
+ Py_CLEAR(gen->gi_frame);
+ Py_CLEAR(gen->gi_name);
+ Py_CLEAR(gen->gi_qualname);
+ Py_CLEAR(gen->gi_modulename);
+ return 0;
+}
+static void __Pyx_Coroutine_dealloc(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ PyObject_GC_UnTrack(gen);
+ #if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ if (gen->gi_weakreflist != NULL)
+ #endif
+ PyObject_ClearWeakRefs(self);
+ if (gen->resume_label >= 0) {
+ PyObject_GC_Track(self);
+#if CYTHON_USE_TP_FINALIZE
+ if (unlikely(PyObject_CallFinalizerFromDealloc(self)))
+#else
+ {
+ destructor del = __Pyx_PyObject_GetSlot(gen, tp_del, destructor);
+ if (del) del(self);
+ }
+ if (unlikely(Py_REFCNT(self) > 0))
+#endif
+ {
+ return;
+ }
+ PyObject_GC_UnTrack(self);
+ }
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ /* We have to handle this case for asynchronous generators
+ right here, because this code has to be between UNTRACK
+ and GC_Del. */
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)self)->ag_finalizer);
+ }
+#endif
+ __Pyx_Coroutine_clear(self);
+ __Pyx_PyHeapTypeObject_GC_Del(gen);
+}
+#if CYTHON_USE_TP_FINALIZE
+static void __Pyx_Coroutine_del(PyObject *self) {
+ PyObject *error_type, *error_value, *error_traceback;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ __Pyx_PyThreadState_declare
+ if (gen->resume_label < 0) {
+ return;
+ }
+ __Pyx_PyThreadState_assign
+ __Pyx_ErrFetch(&error_type, &error_value, &error_traceback);
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ __pyx_PyAsyncGenObject *agen = (__pyx_PyAsyncGenObject*)self;
+ PyObject *finalizer = agen->ag_finalizer;
+ if (finalizer && !agen->ag_closed) {
+ PyObject *res = __Pyx_PyObject_CallOneArg(finalizer, self);
+ if (unlikely(!res)) {
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_DECREF(res);
+ }
+ __Pyx_ErrRestore(error_type, error_value, error_traceback);
+ return;
+ }
+ }
+#endif
+ if (unlikely(gen->resume_label == 0 && !error_value)) {
+#ifdef __Pyx_Coroutine_USED
+#ifdef __Pyx_Generator_USED
+ if (!__Pyx_Generator_CheckExact(self))
+#endif
+ {
+ PyObject_GC_UnTrack(self);
+ if (unlikely(PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname) < 0))
+ PyErr_WriteUnraisable(self);
+ PyObject_GC_Track(self);
+ }
+#endif
+ } else {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_Close(self, &retval);
+ if (result == PYGEN_ERROR) {
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_XDECREF(retval);
+ }
+ }
+ __Pyx_ErrRestore(error_type, error_value, error_traceback);
+}
+#endif
+static PyObject *
+__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self, void *context)
+{
+ PyObject *name = self->gi_name;
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
+}
+static int
+__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__name__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_Py_XDECREF_SET(self->gi_name, value);
+ return 0;
+}
+static PyObject *
+__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self, void *context)
+{
+ PyObject *name = self->gi_qualname;
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
+}
+static int
+__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__qualname__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_Py_XDECREF_SET(self->gi_qualname, value);
+ return 0;
+}
+static PyObject *
+__Pyx__Coroutine_get_frame(__pyx_CoroutineObject *self)
+{
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *frame;
+ #if PY_VERSION_HEX >= 0x030d0000
+ Py_BEGIN_CRITICAL_SECTION(self);
+ #endif
+ frame = self->gi_frame;
+ if (!frame) {
+ if (unlikely(!self->gi_code)) {
+ Py_RETURN_NONE;
+ }
+ PyObject *globals = PyDict_New();
+ if (unlikely(!globals)) return NULL;
+ frame = (PyObject *) PyFrame_New(
+ PyThreadState_Get(), /*PyThreadState *tstate,*/
+ (PyCodeObject*) self->gi_code, /*PyCodeObject *code,*/
+ globals, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ Py_DECREF(globals);
+ if (unlikely(!frame))
+ return NULL;
+ if (unlikely(self->gi_frame)) {
+ Py_DECREF(frame);
+ frame = self->gi_frame;
+ } else {
+ self->gi_frame = frame;
+ }
+ }
+ Py_INCREF(frame);
+ #if PY_VERSION_HEX >= 0x030d0000
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return frame;
+#else
+ CYTHON_UNUSED_VAR(self);
+ Py_RETURN_NONE;
+#endif
+}
+static PyObject *
+__Pyx_Coroutine_get_frame(__pyx_CoroutineObject *self, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ PyObject *frame = self->gi_frame;
+ if (frame)
+ return __Pyx_NewRef(frame);
+ return __Pyx__Coroutine_get_frame(self);
+}
+static __pyx_CoroutineObject *__Pyx__Coroutine_New(
+ PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
+ __pyx_CoroutineObject *gen = PyObject_GC_New(__pyx_CoroutineObject, type);
+ if (unlikely(!gen))
+ return NULL;
+ return __Pyx__Coroutine_NewInit(gen, body, code, closure, name, qualname, module_name);
+}
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
+ gen->body = body;
+ gen->closure = closure;
+ Py_XINCREF(closure);
+ gen->is_running = 0;
+ gen->resume_label = 0;
+ gen->classobj = NULL;
+ gen->yieldfrom = NULL;
+ gen->yieldfrom_am_send = NULL;
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_LIMITED_API
+ gen->gi_exc_state.exc_value = NULL;
+ #else
+ gen->gi_exc_state.exc_type = NULL;
+ gen->gi_exc_state.exc_value = NULL;
+ gen->gi_exc_state.exc_traceback = NULL;
+ #endif
+#if CYTHON_USE_EXC_INFO_STACK
+ gen->gi_exc_state.previous_item = NULL;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ gen->gi_weakreflist = NULL;
+#endif
+ Py_XINCREF(qualname);
+ gen->gi_qualname = qualname;
+ Py_XINCREF(name);
+ gen->gi_name = name;
+ Py_XINCREF(module_name);
+ gen->gi_modulename = module_name;
+ Py_XINCREF(code);
+ gen->gi_code = code;
+ gen->gi_frame = NULL;
+ PyObject_GC_Track(gen);
+ return gen;
+}
+static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen) {
+ char result;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ result = gen->is_running;
+ gen->is_running = 1;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return result;
+}
+static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen) {
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ assert(gen->is_running);
+ gen->is_running = 0;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+}
+static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen) {
+ char result;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ result = gen->is_running;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return result;
+}
+static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure) {
+ CYTHON_UNUSED_VAR(closure);
+ char result = __Pyx_Coroutine_get_is_running((__pyx_CoroutineObject*)gen);
+ if (result) Py_RETURN_TRUE;
+ else Py_RETURN_FALSE;
+}
+#if __PYX_HAS_PY_AM_SEND == 2
+static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send) {
+ Py_ssize_t ptr_offset = (char*)(type->tp_as_async) - (char*)type;
+ if (ptr_offset < 0 || ptr_offset > type->tp_basicsize) {
+ return;
+ }
+ memcpy((void*)static_amsend_methods, (void*)(type->tp_as_async), sizeof(*type->tp_as_async));
+ static_amsend_methods->am_send = am_send;
+ type->tp_as_async = __Pyx_SlotTpAsAsync(static_amsend_methods);
+}
+#endif
+static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ __Pyx_TypeName self_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE((PyObject*)self));
+ PyErr_Format(PyExc_TypeError, "cannot pickle '" __Pyx_FMT_TYPENAME "' object",
+ self_type_name);
+ __Pyx_DECREF_TypeName(self_type_name);
+ return NULL;
+}
+
+/* Generator (used by GeneratorYieldFrom) */
+static PyMethodDef __pyx_Generator_methods[] = {
+ {"send", (PyCFunction) __Pyx_Coroutine_Send, METH_O,
+ PyDoc_STR("send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")},
+ {"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS,
+ PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")},
+ {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS,
+ PyDoc_STR("close() -> raise GeneratorExit inside generator.")},
+ {"__reduce_ex__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_O, 0},
+ {"__reduce__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_NOARGS, 0},
+ {0, 0, 0, 0}
+};
+static PyMemberDef __pyx_Generator_memberlist[] = {
+ {"gi_yieldfrom", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY,
+ PyDoc_STR("object being iterated by 'yield from', or None")},
+ {"gi_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL},
+ {"__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0},
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0},
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyGetSetDef __pyx_Generator_getsets[] = {
+ {"__name__", (getter)__Pyx_Coroutine_get_name, (setter)__Pyx_Coroutine_set_name,
+ PyDoc_STR("name of the generator"), 0},
+ {"__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname,
+ PyDoc_STR("qualified name of the generator"), 0},
+ {"gi_frame", (getter)__Pyx_Coroutine_get_frame, NULL,
+ PyDoc_STR("Frame of the generator"), 0},
+ {"gi_running", __Pyx_Coroutine_get_is_running_getter, NULL, NULL, NULL},
+ {0, 0, 0, 0, 0}
+};
+static PyType_Slot __pyx_GeneratorType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse},
+ {Py_tp_iter, (void *)PyObject_SelfIter},
+ {Py_tp_iternext, (void *)__Pyx_Generator_Next},
+ {Py_tp_methods, (void *)__pyx_Generator_methods},
+ {Py_tp_members, (void *)__pyx_Generator_memberlist},
+ {Py_tp_getset, (void *)__pyx_Generator_getsets},
+ {Py_tp_getattro, (void *) PyObject_GenericGetAttr},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+#if __PYX_HAS_PY_AM_SEND == 1
+ {Py_am_send, (void *)__Pyx_Coroutine_AmSend},
+#endif
+ {0, 0},
+};
+static PyType_Spec __pyx_GeneratorType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "generator",
+ sizeof(__pyx_CoroutineObject),
+ 0,
+#if PY_VERSION_HEX >= 0x030C0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_TPFLAGS_MANAGED_WEAKREF |
+#endif
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | __Pyx_TPFLAGS_HAVE_AM_SEND,
+ __pyx_GeneratorType_slots
+};
+#if __PYX_HAS_PY_AM_SEND == 2
+static __Pyx_PyAsyncMethodsStruct __pyx_Generator_as_async;
+#endif
+static int __pyx_Generator_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ mstate->__pyx_GeneratorType = __Pyx_FetchCommonTypeFromSpec(
+ mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_GeneratorType_spec, NULL);
+ if (unlikely(!mstate->__pyx_GeneratorType)) {
+ return -1;
+ }
+#if __PYX_HAS_PY_AM_SEND == 2
+ __Pyx_SetBackportTypeAmSend(mstate->__pyx_GeneratorType, &__pyx_Generator_as_async, &__Pyx_Coroutine_AmSend);
+#endif
+ return 0;
+}
+static PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ PyObject *retval = NULL;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ }
+ __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0);
+ __Pyx_Coroutine_unset_is_running(gen);
+ (void) result;
+ assert (result == PYGEN_RETURN || result == PYGEN_ERROR);
+ assert ((result == PYGEN_RETURN && retval != NULL) || (result == PYGEN_ERROR && retval == NULL));
+ return retval;
+}
+
+/* GeneratorYieldFrom */
+#if CYTHON_USE_TYPE_SLOTS
+static void __Pyx_PyIter_CheckErrorAndDecref(PyObject *source) {
+ __Pyx_TypeName source_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(source));
+ PyErr_Format(PyExc_TypeError,
+ "iter() returned non-iterator of type '" __Pyx_FMT_TYPENAME "'", source_type_name);
+ __Pyx_DECREF_TypeName(source_type_name);
+ Py_DECREF(source);
+}
+#endif
+static CYTHON_INLINE __Pyx_PySendResult __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source, PyObject **retval) {
+ PyObject *source_gen;
+ __Pyx_PySendResult result;
+#ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(source)) {
+ Py_INCREF(source);
+ source_gen = source;
+ result = __Pyx_Coroutine_AmSend(source, Py_None, retval);
+ } else
+#endif
+ {
+#if CYTHON_USE_TYPE_SLOTS
+ if (likely(Py_TYPE(source)->tp_iter)) {
+ source_gen = Py_TYPE(source)->tp_iter(source);
+ if (unlikely(!source_gen)) {
+ *retval = NULL;
+ return PYGEN_ERROR;
+ }
+ if (unlikely(!PyIter_Check(source_gen))) {
+ __Pyx_PyIter_CheckErrorAndDecref(source_gen);
+ *retval = NULL;
+ return PYGEN_ERROR;
+ }
+ } else
+#endif
+ {
+ source_gen = PyObject_GetIter(source);
+ if (unlikely(!source_gen)) {
+ *retval = NULL;
+ return PYGEN_ERROR;
+ }
+ }
+ *retval = __Pyx_PyIter_Next_Plain(source_gen);
+ result = __Pyx_Coroutine_status_from_result(retval);
+ }
+ if (likely(result == PYGEN_NEXT)) {
+ __Pyx_Coroutine_Set_Owned_Yield_From(gen, source_gen);
+ return PYGEN_NEXT;
+ }
+ Py_DECREF(source_gen);
+ return result;
+}
+
+/* append */
+static CYTHON_INLINE int __Pyx_PyObject_Append(PyObject* L, PyObject* x) {
+ if (likely(PyList_CheckExact(L))) {
+ if (unlikely(__Pyx_PyList_Append(L, x) < 0)) return -1;
+ } else {
+ PyObject* retval = __Pyx_PyObject_CallMethod1(L, __pyx_mstate_global->__pyx_n_u_append, x);
+ if (unlikely(!retval))
+ return -1;
+ Py_DECREF(retval);
+ }
+ return 0;
+}
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceAdd : PyNumber_Add)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ long a;
+ const PY_LONG_LONG llb = intval;
+ PY_LONG_LONG lla;
+ if (unlikely(__Pyx_PyLong_IsZero(op1))) {
+ return __Pyx_NewRef(op2);
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op1);
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+ if (likely(size == 1)) {
+ a = (long) digits[0];
+ if (!is_positive) a *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_add(op1, op2);
+ }
+ calculate_long:
+ {
+ long x;
+ x = a + b;
+ return PyLong_FromLong(x);
+ }
+ calculate_long_long:
+ {
+ PY_LONG_LONG llx;
+ llx = lla + llb;
+ return PyLong_FromLongLong(llx);
+ }
+
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_AddObjC(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ double a = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) + (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op1))) {
+ return __Pyx_Unpacked___Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ return __Pyx_Float___Pyx_PyLong_AddObjC(op1, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_AddObjC(op1, op2, inplace);
+}
+#endif
+
+/* py_abs */
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
+#if PY_VERSION_HEX >= 0x030C00A7
+ if (likely(__Pyx_PyLong_IsCompact(n))) {
+ return PyLong_FromSize_t(__Pyx_PyLong_CompactValueUnsigned(n));
+ }
+#else
+ if (likely(Py_SIZE(n) == -1)) {
+ return PyLong_FromUnsignedLong(__Pyx_PyLong_Digits(n)[0]);
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+ {
+ PyObject *copy = _PyLong_Copy((PyLongObject*)n);
+ if (likely(copy)) {
+ #if PY_VERSION_HEX >= 0x030C00A7
+ ((PyLongObject*)copy)->long_value.lv_tag ^= ((PyLongObject*)copy)->long_value.lv_tag & _PyLong_SIGN_MASK;
+ #else
+ __Pyx_SET_SIZE(copy, -Py_SIZE(copy));
+ #endif
+ }
+ return copy;
+ }
+#else
+ return PyNumber_Negative(n);
+#endif
+}
+#endif
+
+/* PyFloatBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_PyFloat_TrueDivideObjC(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check) {
+ const double b = floatval;
+ double a, result;
+ CYTHON_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ if (likely(PyFloat_CheckExact(op1))) {
+ a = __Pyx_PyFloat_AS_DOUBLE(op1);
+
+ } else
+ if (likely(PyLong_CheckExact(op1))) {
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsZero(op1)) {
+ a = 0.0;
+
+ } else if (__Pyx_PyLong_IsCompact(op1)) {
+ a = (double) __Pyx_PyLong_CompactValue(op1);
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(op1);
+ switch (size) {
+ case -2:
+ case 2:
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (1 * PyLong_SHIFT < 53))) {
+ a = (double) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -2)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ case -3:
+ case 3:
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53))) {
+ a = (double) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (3 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -3)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ case -4:
+ case 4:
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (3 * PyLong_SHIFT < 53))) {
+ a = (double) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (4 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -4)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ default:
+ #endif
+ a = PyLong_AsDouble(op1);
+ if (unlikely(a == -1.0 && PyErr_Occurred())) return NULL;
+ #if CYTHON_USE_PYLONG_INTERNALS
+ }
+ }
+ #endif
+ } else {
+ return (inplace ? PyNumber_InPlaceTrueDivide : PyNumber_TrueDivide)(op1, op2);
+ }
+ result = a / b;
+ return PyFloat_FromDouble(result);
+}
+#endif
+
+/* pybytes_as_double (used by pynumber_float) */
+static double __Pyx_SlowPyString_AsDouble(PyObject *obj) {
+ PyObject *float_value = PyFloat_FromString(obj);
+ if (likely(float_value)) {
+ double value = __Pyx_PyFloat_AS_DOUBLE(float_value);
+ Py_DECREF(float_value);
+ return value;
+ }
+ return (double)-1;
+}
+static const char* __Pyx__PyBytes_AsDouble_Copy(const char* start, char* buffer, Py_ssize_t length) {
+ int last_was_punctuation = 1;
+ int parse_error_found = 0;
+ Py_ssize_t i;
+ for (i=0; i < length; i++) {
+ char chr = start[i];
+ int is_punctuation = (chr == '_') | (chr == '.') | (chr == 'e') | (chr == 'E');
+ *buffer = chr;
+ buffer += (chr != '_');
+ parse_error_found |= last_was_punctuation & is_punctuation;
+ last_was_punctuation = is_punctuation;
+ }
+ parse_error_found |= last_was_punctuation;
+ *buffer = '\0';
+ return unlikely(parse_error_found) ? NULL : buffer;
+}
+static double __Pyx__PyBytes_AsDouble_inf_nan(const char* start, Py_ssize_t length) {
+ int matches = 1;
+ char sign = start[0];
+ int is_signed = (sign == '+') | (sign == '-');
+ start += is_signed;
+ length -= is_signed;
+ switch (start[0]) {
+ #ifdef Py_NAN
+ case 'n':
+ case 'N':
+ if (unlikely(length != 3)) goto parse_failure;
+ matches &= (start[1] == 'a' || start[1] == 'A');
+ matches &= (start[2] == 'n' || start[2] == 'N');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_NAN : Py_NAN;
+ #endif
+ case 'i':
+ case 'I':
+ if (unlikely(length < 3)) goto parse_failure;
+ matches &= (start[1] == 'n' || start[1] == 'N');
+ matches &= (start[2] == 'f' || start[2] == 'F');
+ if (likely(length == 3 && matches))
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ if (unlikely(length != 8)) goto parse_failure;
+ matches &= (start[3] == 'i' || start[3] == 'I');
+ matches &= (start[4] == 'n' || start[4] == 'N');
+ matches &= (start[5] == 'i' || start[5] == 'I');
+ matches &= (start[6] == 't' || start[6] == 'T');
+ matches &= (start[7] == 'y' || start[7] == 'Y');
+ if (unlikely(!matches)) goto parse_failure;
+ return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL;
+ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ break;
+ default:
+ goto parse_failure;
+ }
+ return 0.0;
+parse_failure:
+ return -1.0;
+}
+static CYTHON_INLINE int __Pyx__PyBytes_AsDouble_IsSpace(char ch) {
+ return (ch == 0x20) | !((ch < 0x9) | (ch > 0xd));
+}
+CYTHON_UNUSED static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) {
+ double value;
+ Py_ssize_t i, digits;
+ const char *last = start + length;
+ char *end;
+ while (__Pyx__PyBytes_AsDouble_IsSpace(*start))
+ start++;
+ while (start < last - 1 && __Pyx__PyBytes_AsDouble_IsSpace(last[-1]))
+ last--;
+ length = last - start;
+ if (unlikely(length <= 0)) goto fallback;
+ value = __Pyx__PyBytes_AsDouble_inf_nan(start, length);
+ if (unlikely(value == -1.0)) goto fallback;
+ if (value != 0.0) return value;
+ digits = 0;
+ for (i=0; i < length; digits += start[i++] != '_');
+ if (likely(digits == length)) {
+ value = PyOS_string_to_double(start, &end, NULL);
+ } else if (digits < 40) {
+ char number[40];
+ last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+ if (unlikely(!last)) goto fallback;
+ value = PyOS_string_to_double(number, &end, NULL);
+ } else {
+ char *number = (char*) PyMem_Malloc((digits + 1) * sizeof(char));
+ if (unlikely(!number)) goto fallback;
+ last = __Pyx__PyBytes_AsDouble_Copy(start, number, length);
+ if (unlikely(!last)) {
+ PyMem_Free(number);
+ goto fallback;
+ }
+ value = PyOS_string_to_double(number, &end, NULL);
+ PyMem_Free(number);
+ }
+ if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) {
+ return value;
+ }
+fallback:
+ return __Pyx_SlowPyString_AsDouble(obj);
+}
+
+/* pynumber_float */
+static CYTHON_INLINE PyObject* __Pyx__PyNumber_Float(PyObject* obj) {
+ double val;
+ if (PyLong_CheckExact(obj)) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(__Pyx_PyLong_IsCompact(obj))) {
+ val = (double) __Pyx_PyLong_CompactValue(obj);
+ goto no_error;
+ }
+#endif
+ val = PyLong_AsDouble(obj);
+ } else if (PyUnicode_CheckExact(obj)) {
+ val = __Pyx_PyUnicode_AsDouble(obj);
+ } else if (PyBytes_CheckExact(obj)) {
+ val = __Pyx_PyBytes_AsDouble(obj);
+ } else if (PyByteArray_CheckExact(obj)) {
+ val = __Pyx_PyByteArray_AsDouble(obj);
+ } else {
+ return PyNumber_Float(obj);
+ }
+ if (unlikely(val == -1 && PyErr_Occurred())) {
+ return NULL;
+ }
+#if CYTHON_USE_PYLONG_INTERNALS
+no_error:
+#endif
+ return PyFloat_FromDouble(val);
+}
+
+/* PyFloatBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static int __Pyx_PyFloat_BoolEqObjC(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check) {
+ const double b = floatval;
+ double a;
+ CYTHON_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ if (op1 == op2) {
+ return 1;
+ }
+ if (likely(PyFloat_CheckExact(op1))) {
+ a = __Pyx_PyFloat_AS_DOUBLE(op1);
+
+ } else
+ if (likely(PyLong_CheckExact(op1))) {
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsZero(op1)) {
+ a = 0.0;
+
+ } else if (__Pyx_PyLong_IsCompact(op1)) {
+ a = (double) __Pyx_PyLong_CompactValue(op1);
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(op1);
+ switch (size) {
+ case -2:
+ case 2:
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (1 * PyLong_SHIFT < 53))) {
+ a = (double) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -2)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ case -3:
+ case 3:
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53))) {
+ a = (double) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (3 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -3)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ case -4:
+ case 4:
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || (3 * PyLong_SHIFT < 53))) {
+ a = (double) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if ((8 * sizeof(unsigned long) < 53) || (4 * PyLong_SHIFT < 53) || (a < (double) ((PY_LONG_LONG)1 << 53))) {
+ if (size == -4)
+ a = -a;
+ break;
+ }
+ }
+ CYTHON_FALLTHROUGH;
+ default:
+ #endif
+ {
+ PyObject *res =
+ #if CYTHON_USE_TYPE_SLOTS || __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+ __Pyx_PyType_GetSlot((&PyFloat_Type), tp_richcompare, richcmpfunc)
+ #else
+ PyObject_RichCompare
+ #endif
+ (op2, op1,
+ Py_EQ);
+ return __Pyx_PyObject_IsTrueAndDecref(
+ res);
+ }
+ #if CYTHON_USE_PYLONG_INTERNALS
+ }
+ }
+ #endif
+ } else {
+ return __Pyx_PyObject_IsTrueAndDecref(
+ PyObject_RichCompare(op1, op2, Py_EQ));
+ }
+ if (a == b) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+#endif
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceSubtract : PyNumber_Subtract)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ long b;
+ const PY_LONG_LONG lla = intval;
+ PY_LONG_LONG llb;
+ if (unlikely(__Pyx_PyLong_IsZero(op2))) {
+ return __Pyx_NewRef(op1);
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op2);
+ const digit* digits = __Pyx_PyLong_Digits(op2);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op2);
+ if (likely(size == 1)) {
+ b = (long) digits[0];
+ if (!is_positive) b *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ b = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ b = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ b = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_subtract(op1, op2);
+ }
+ calculate_long:
+ {
+ long x;
+ x = a - b;
+ return PyLong_FromLong(x);
+ }
+ calculate_long_long:
+ {
+ PY_LONG_LONG llx;
+ llx = lla - llb;
+ return PyLong_FromLongLong(llx);
+ }
+
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_SubtractCObj(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ double b = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) - (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op2))) {
+ return __Pyx_Unpacked___Pyx_PyLong_SubtractCObj(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op2)) {
+ return __Pyx_Float___Pyx_PyLong_SubtractCObj(op2, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_SubtractCObj(op1, op2, inplace);
+}
+#endif
+
+/* PyObjectVectorCallKwBuilder */
+#if CYTHON_VECTORCALL
+static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ (void)__Pyx_PyObject_FastCallDict;
+ if (__Pyx_PyTuple_SET_ITEM(builder, n, key) != (0)) return -1;
+ Py_INCREF(key);
+ args[n] = value;
+ return 0;
+}
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ (void)__Pyx_VectorcallBuilder_AddArgStr;
+ if (unlikely(!PyUnicode_Check(key))) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ return -1;
+ }
+ return __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n);
+}
+static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ PyObject *pyKey = PyUnicode_FromString(key);
+ if (!pyKey) return -1;
+ return __Pyx_VectorcallBuilder_AddArg(pyKey, value, builder, args, n);
+}
+#else // CYTHON_VECTORCALL
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, CYTHON_UNUSED PyObject **args, CYTHON_UNUSED int n) {
+ if (unlikely(!PyUnicode_Check(key))) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ return -1;
+ }
+ return PyDict_SetItem(builder, key, value);
+}
+#endif
+
+/* PyObjectFastCallMethod */
+#if !CYTHON_VECTORCALL || PY_VERSION_HEX < 0x03090000
+static PyObject *__Pyx_PyObject_FastCallMethod(PyObject *name, PyObject *const *args, size_t nargsf) {
+ PyObject *result;
+ PyObject *attr = PyObject_GetAttr(args[0], name);
+ if (unlikely(!attr))
+ return NULL;
+ result = __Pyx_PyObject_FastCall(attr, args+1, nargsf - 1);
+ Py_DECREF(attr);
+ return result;
+}
+#endif
+
+/* RaiseClosureNameError */
+static void __Pyx_RaiseClosureNameError(const char *varname) {
+ PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
+}
+
+/* PyMethodNew (used by CythonFunctionShared) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(typ);
+ if (!self)
+ return __Pyx_NewRef(func);
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030C0000
+ {
+ PyObject *args[] = {func, self};
+ result = PyObject_Vectorcall(__pyx_mstate_global->__Pyx_CachedMethodType, args, 2, NULL);
+ }
+ #else
+ result = PyObject_CallFunctionObjArgs(__pyx_mstate_global->__Pyx_CachedMethodType, func, self, NULL);
+ #endif
+ return result;
+}
+#else
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) {
+ CYTHON_UNUSED_VAR(typ);
+ if (!self)
+ return __Pyx_NewRef(func);
+ return PyMethod_New(func, self);
+}
+#endif
+
+/* PyVectorcallFastCallDict (used by CythonFunctionShared) */
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ PyObject *res = NULL;
+ PyObject *kwnames;
+ PyObject **newargs;
+ PyObject **kwvalues;
+ Py_ssize_t i;
+ #if CYTHON_AVOID_BORROWED_REFS
+ PyObject *pos;
+ #else
+ Py_ssize_t pos;
+ #endif
+ size_t j;
+ PyObject *key, *value;
+ unsigned long keys_are_strings;
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ Py_ssize_t nkw = PyDict_Size(kw);
+ if (unlikely(nkw == -1)) return NULL;
+ #else
+ Py_ssize_t nkw = PyDict_GET_SIZE(kw);
+ #endif
+ newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0]));
+ if (unlikely(newargs == NULL)) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (j = 0; j < nargs; j++) newargs[j] = args[j];
+ kwnames = PyTuple_New(nkw);
+ if (unlikely(kwnames == NULL)) {
+ PyMem_Free(newargs);
+ return NULL;
+ }
+ kwvalues = newargs + nargs;
+ pos = 0;
+ i = 0;
+ keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;
+ while (__Pyx_PyDict_NextRef(kw, &pos, &key, &value)) {
+ keys_are_strings &=
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ PyType_GetFlags(Py_TYPE(key));
+ #else
+ Py_TYPE(key)->tp_flags;
+ #endif
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(PyTuple_SetItem(kwnames, i, key) < 0)) goto cleanup;
+ #else
+ PyTuple_SET_ITEM(kwnames, i, key);
+ #endif
+ kwvalues[i] = value;
+ i++;
+ }
+ if (unlikely(!keys_are_strings)) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ goto cleanup;
+ }
+ res = vc(func, newargs, nargs, kwnames);
+cleanup:
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(pos);
+ #endif
+ Py_DECREF(kwnames);
+ for (i = 0; i < nkw; i++)
+ Py_DECREF(kwvalues[i]);
+ PyMem_Free(newargs);
+ return res;
+}
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ Py_ssize_t kw_size =
+ likely(kw == NULL) ?
+ 0 :
+#if !CYTHON_ASSUME_SAFE_SIZE
+ PyDict_Size(kw);
+#else
+ PyDict_GET_SIZE(kw);
+#endif
+ if (kw_size == 0) {
+ return vc(func, args, nargs, NULL);
+ }
+#if !CYTHON_ASSUME_SAFE_SIZE
+ else if (unlikely(kw_size == -1)) {
+ return NULL;
+ }
+#endif
+ return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw);
+}
+#endif
+
+/* CythonFunctionShared (used by CythonFunction) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunctionNoMethod(PyObject *func, void (*cfunc)(void)) {
+ if (__Pyx_CyFunction_Check(func)) {
+ return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc;
+ } else if (PyCFunction_Check(func)) {
+ return PyCFunction_GetFunction(func) == (PyCFunction) cfunc;
+ }
+ return 0;
+}
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) {
+ if ((PyObject*)Py_TYPE(func) == __pyx_mstate_global->__Pyx_CachedMethodType) {
+ int result;
+ PyObject *newFunc = PyObject_GetAttr(func, __pyx_mstate_global->__pyx_n_u_func);
+ if (unlikely(!newFunc)) {
+ PyErr_Clear(); // It's only an optimization, so don't throw an error
+ return 0;
+ }
+ result = __Pyx__IsSameCyOrCFunctionNoMethod(newFunc, cfunc);
+ Py_DECREF(newFunc);
+ return result;
+ }
+ return __Pyx__IsSameCyOrCFunctionNoMethod(func, cfunc);
+}
+#else
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) {
+ if (PyMethod_Check(func)) {
+ func = PyMethod_GET_FUNCTION(func);
+ }
+ return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc;
+}
+#endif
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) {
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ __Pyx_Py_XDECREF_SET(
+ __Pyx_CyFunction_GetClassObj(f),
+ ((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#else
+ __Pyx_Py_XDECREF_SET(
+ ((PyCMethodObject *) (f))->mm_class,
+ (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#endif
+}
+static PyObject *
+__Pyx_CyFunction_get_doc_locked(__pyx_CyFunctionObject *op)
+{
+ if (unlikely(op->func_doc == NULL)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func_doc = PyObject_GetAttrString(op->func, "__doc__");
+ if (unlikely(!op->func_doc)) return NULL;
+#else
+ if (((PyCFunctionObject*)op)->m_ml->ml_doc) {
+ op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc);
+ if (unlikely(op->func_doc == NULL))
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+#endif
+ }
+ Py_INCREF(op->func_doc);
+ return op->func_doc;
+}
+static PyObject *
+__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(closure);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_doc_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (value == NULL) {
+ value = Py_None;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_doc, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_name_locked(__pyx_CyFunctionObject *op)
+{
+ if (unlikely(op->func_name == NULL)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func_name = PyObject_GetAttrString(op->func, "__name__");
+#else
+ op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name);
+#endif
+ if (unlikely(op->func_name == NULL))
+ return NULL;
+ }
+ Py_INCREF(op->func_name);
+ return op->func_name;
+}
+static PyObject *
+__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context)
+{
+ PyObject *result = NULL;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_name_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__name__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_name, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ PyObject *result;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ Py_INCREF(op->func_qualname);
+ result = op->func_qualname;
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__qualname__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_qualname, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *
+__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(op->func_dict == NULL)) {
+ op->func_dict = PyDict_New();
+ if (unlikely(op->func_dict == NULL))
+ return NULL;
+ }
+ Py_INCREF(op->func_dict);
+ return op->func_dict;
+}
+#endif
+static PyObject *
+__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(op->func_globals);
+ return op->func_globals;
+}
+static PyObject *
+__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(op);
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+static PyObject *
+__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context)
+{
+ PyObject* result = (op->func_code) ? op->func_code : Py_None;
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(result);
+ return result;
+}
+static int
+__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) {
+ int result = 0;
+ PyObject *res = op->defaults_getter((PyObject *) op);
+ if (unlikely(!res))
+ return -1;
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ op->defaults_tuple = PyTuple_GET_ITEM(res, 0);
+ Py_INCREF(op->defaults_tuple);
+ op->defaults_kwdict = PyTuple_GET_ITEM(res, 1);
+ Py_INCREF(op->defaults_kwdict);
+ #else
+ op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0);
+ if (unlikely(!op->defaults_tuple)) result = -1;
+ else {
+ op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1);
+ if (unlikely(!op->defaults_kwdict)) result = -1;
+ }
+ #endif
+ Py_DECREF(res);
+ return result;
+}
+static int
+__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value) {
+ value = Py_None;
+ } else if (unlikely(value != Py_None && !PyTuple_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__defaults__ must be set to a tuple object");
+ return -1;
+ }
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not "
+ "currently affect the values used in function calls", 1);
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->defaults_tuple, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_defaults_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_tuple;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
+ result = op->defaults_tuple;
+ } else {
+ result = Py_None;
+ }
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) {
+ PyObject* result = NULL;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_defaults_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value) {
+ value = Py_None;
+ } else if (unlikely(value != Py_None && !PyDict_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__kwdefaults__ must be set to a dict object");
+ return -1;
+ }
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not "
+ "currently affect the values used in function calls", 1);
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_kwdefaults_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_kwdict;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
+ result = op->defaults_kwdict;
+ } else {
+ result = Py_None;
+ }
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) {
+ PyObject* result;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_kwdefaults_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value || value == Py_None) {
+ value = NULL;
+ } else if (unlikely(!PyDict_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__annotations__ must be set to a dict object");
+ return -1;
+ }
+ Py_XINCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_annotations, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_annotations_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->func_annotations;
+ if (unlikely(!result)) {
+ result = PyDict_New();
+ if (unlikely(!result)) return NULL;
+ op->func_annotations = result;
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_annotations_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_is_coroutine_value(__pyx_CyFunctionObject *op) {
+ int is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE;
+ if (is_coroutine) {
+ PyObject *is_coroutine_value, *module, *fromlist, *marker = __pyx_mstate_global->__pyx_n_u_is_coroutine;
+ fromlist = PyList_New(1);
+ if (unlikely(!fromlist)) return NULL;
+ Py_INCREF(marker);
+#if CYTHON_ASSUME_SAFE_MACROS
+ PyList_SET_ITEM(fromlist, 0, marker);
+#else
+ if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) {
+ Py_DECREF(marker);
+ Py_DECREF(fromlist);
+ return NULL;
+ }
+#endif
+ module = PyImport_ImportModuleLevelObject(__pyx_mstate_global->__pyx_n_u_asyncio_coroutines, NULL, NULL, fromlist, 0);
+ Py_DECREF(fromlist);
+ if (unlikely(!module)) goto ignore;
+ is_coroutine_value = __Pyx_PyObject_GetAttrStr(module, marker);
+ Py_DECREF(module);
+ if (likely(is_coroutine_value)) {
+ return is_coroutine_value;
+ }
+ignore:
+ PyErr_Clear();
+ }
+ return __Pyx_PyBool_FromLong(is_coroutine);
+}
+static PyObject *
+__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(context);
+ if (op->func_is_coroutine) {
+ return __Pyx_NewRef(op->func_is_coroutine);
+ }
+ result = __Pyx_CyFunction_get_is_coroutine_value(op);
+ if (unlikely(!result))
+ return NULL;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ if (op->func_is_coroutine) {
+ Py_DECREF(result);
+ result = __Pyx_NewRef(op->func_is_coroutine);
+ } else {
+ op->func_is_coroutine = __Pyx_NewRef(result);
+ }
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static void __Pyx_CyFunction_raise_argument_count_error(__pyx_CyFunctionObject *func, const char* message, Py_ssize_t size) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL);
+ if (!py_name) return;
+ PyErr_Format(PyExc_TypeError,
+ "%.200S() %s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ py_name, message, size);
+ Py_DECREF(py_name);
+#else
+ const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name;
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() %s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ name, message, size);
+#endif
+}
+static void __Pyx_CyFunction_raise_type_error(__pyx_CyFunctionObject *func, const char* message) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL);
+ if (!py_name) return;
+ PyErr_Format(PyExc_TypeError,
+ "%.200S() %s",
+ py_name, message);
+ Py_DECREF(py_name);
+#else
+ const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name;
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() %s",
+ name, message);
+#endif
+}
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *
+__Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ return PyObject_GetAttrString(op->func, "__module__");
+}
+static int
+__Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ return PyObject_SetAttrString(op->func, "__module__", value);
+}
+#endif
+static PyGetSetDef __pyx_CyFunction_getsets[] = {
+ {"func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
+ {"__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
+ {"func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
+ {"__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
+ {"__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ {"func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)PyObject_GenericSetDict, 0, 0},
+ {"__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)PyObject_GenericSetDict, 0, 0},
+#else
+ {"func_dict", (getter)PyObject_GenericGetDict, (setter)PyObject_GenericSetDict, 0, 0},
+ {"__dict__", (getter)PyObject_GenericGetDict, (setter)PyObject_GenericSetDict, 0, 0},
+#endif
+ {"func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
+ {"__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
+ {"func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
+ {"__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
+ {"func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
+ {"__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
+ {"func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {"__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {"__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0},
+ {"__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0},
+ {"_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0},
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0},
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyMemberDef __pyx_CyFunction_members[] = {
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ {"__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0},
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ {"__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0},
+#endif
+#if CYTHON_METH_FASTCALL
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0},
+#else
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0},
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0},
+#else
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0},
+#endif
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyObject *
+__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args)
+{
+ PyObject *result = NULL;
+ CYTHON_UNUSED_VAR(args);
+ __Pyx_BEGIN_CRITICAL_SECTION(m);
+ Py_INCREF(m->func_qualname);
+ result = m->func_qualname;
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static PyMethodDef __pyx_CyFunction_methods[] = {
+ {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0},
+ {0, 0, 0, 0}
+};
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist)
+#else
+#define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist)
+#endif
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunctionObject *cf = (PyCFunctionObject*) op;
+#endif
+ if (unlikely(op == NULL))
+ return NULL;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func = PyCFunction_NewEx(ml, (PyObject*)op, module);
+ if (unlikely(!op->func)) return NULL;
+#endif
+ op->flags = flags;
+ __Pyx_CyFunction_weakreflist(op) = NULL;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ cf->m_ml = ml;
+ cf->m_self = (PyObject *) op;
+#endif
+ Py_XINCREF(closure);
+ op->func_closure = closure;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ Py_XINCREF(module);
+ cf->m_module = module;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ op->func_dict = NULL;
+#endif
+ op->func_name = NULL;
+ Py_INCREF(qualname);
+ op->func_qualname = qualname;
+ op->func_doc = NULL;
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ op->func_classobj = NULL;
+#else
+ ((PyCMethodObject*)op)->mm_class = NULL;
+#endif
+ op->func_globals = globals;
+ Py_INCREF(op->func_globals);
+ Py_XINCREF(code);
+ op->func_code = code;
+ op->defaults = NULL;
+ op->defaults_tuple = NULL;
+ op->defaults_kwdict = NULL;
+ op->defaults_getter = NULL;
+ op->func_annotations = NULL;
+ op->func_is_coroutine = NULL;
+#if CYTHON_METH_FASTCALL
+ switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) {
+ case METH_NOARGS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS;
+ break;
+ case METH_O:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O;
+ break;
+ case METH_METHOD | METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD;
+ break;
+ case METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS;
+ break;
+ case METH_VARARGS | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = NULL;
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
+ Py_DECREF(op);
+ return NULL;
+ }
+#endif
+ return (PyObject *) op;
+}
+static int
+__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
+{
+ Py_CLEAR(m->func_closure);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ Py_CLEAR(m->func);
+#else
+ Py_CLEAR(((PyCFunctionObject*)m)->m_module);
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ Py_CLEAR(m->func_dict);
+#elif PY_VERSION_HEX < 0x030d0000
+ _PyObject_ClearManagedDict((PyObject*)m);
+#else
+ PyObject_ClearManagedDict((PyObject*)m);
+#endif
+ Py_CLEAR(m->func_name);
+ Py_CLEAR(m->func_qualname);
+ Py_CLEAR(m->func_doc);
+ Py_CLEAR(m->func_globals);
+ Py_CLEAR(m->func_code);
+#if !CYTHON_COMPILING_IN_LIMITED_API
+#if PY_VERSION_HEX < 0x030900B1
+ Py_CLEAR(__Pyx_CyFunction_GetClassObj(m));
+#else
+ {
+ PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class;
+ ((PyCMethodObject *) (m))->mm_class = NULL;
+ Py_XDECREF(cls);
+ }
+#endif
+#endif
+ Py_CLEAR(m->defaults_tuple);
+ Py_CLEAR(m->defaults_kwdict);
+ Py_CLEAR(m->func_annotations);
+ Py_CLEAR(m->func_is_coroutine);
+ Py_CLEAR(m->defaults);
+ return 0;
+}
+static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m)
+{
+ if (__Pyx_CyFunction_weakreflist(m) != NULL)
+ PyObject_ClearWeakRefs((PyObject *) m);
+ __Pyx_CyFunction_clear(m);
+ __Pyx_PyHeapTypeObject_GC_Del(m);
+}
+static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
+{
+ PyObject_GC_UnTrack(m);
+ __Pyx__CyFunction_dealloc(m);
+}
+static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg)
+{
+ {
+ int e = __Pyx_call_type_traverse((PyObject*)m, 1, visit, arg);
+ if (e) return e;
+ }
+ Py_VISIT(m->func_closure);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(m->func);
+#else
+ Py_VISIT(((PyCFunctionObject*)m)->m_module);
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(m->func_dict);
+#else
+ {
+ int e =
+#if PY_VERSION_HEX < 0x030d0000
+ _PyObject_VisitManagedDict
+#else
+ PyObject_VisitManagedDict
+#endif
+ ((PyObject*)m, visit, arg);
+ if (e != 0) return e;
+ }
+#endif
+ __Pyx_VISIT_CONST(m->func_name);
+ __Pyx_VISIT_CONST(m->func_qualname);
+ Py_VISIT(m->func_doc);
+ Py_VISIT(m->func_globals);
+ __Pyx_VISIT_CONST(m->func_code);
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(__Pyx_CyFunction_GetClassObj(m));
+#endif
+ Py_VISIT(m->defaults_tuple);
+ Py_VISIT(m->defaults_kwdict);
+ Py_VISIT(m->func_is_coroutine);
+ Py_VISIT(m->defaults);
+ return 0;
+}
+static PyObject*
+__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
+{
+ PyObject *repr;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ repr = PyUnicode_FromFormat("",
+ op->func_qualname, (void *)op);
+ __Pyx_END_CRITICAL_SECTION();
+ return repr;
+}
+static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *f = ((__pyx_CyFunctionObject*)func)->func;
+ PyCFunction meth;
+ int flags;
+ meth = PyCFunction_GetFunction(f);
+ if (unlikely(!meth)) return NULL;
+ flags = PyCFunction_GetFlags(f);
+ if (unlikely(flags < 0)) return NULL;
+#else
+ PyCFunctionObject* f = (PyCFunctionObject*)func;
+ PyCFunction meth = f->m_ml->ml_meth;
+ int flags = f->m_ml->ml_flags;
+#endif
+ Py_ssize_t size;
+ switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) {
+ case METH_VARARGS:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0))
+ return (*meth)(self, arg);
+ break;
+ case METH_VARARGS | METH_KEYWORDS:
+ return (*(PyCFunctionWithKeywords)(void(*)(void))meth)(self, arg, kw);
+ case METH_NOARGS:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
+#if CYTHON_ASSUME_SAFE_SIZE
+ size = PyTuple_GET_SIZE(arg);
+#else
+ size = PyTuple_Size(arg);
+ if (unlikely(size < 0)) return NULL;
+#endif
+ if (likely(size == 0))
+ return (*meth)(self, NULL);
+ __Pyx_CyFunction_raise_argument_count_error(
+ (__pyx_CyFunctionObject*)func,
+ "takes no arguments", size);
+ return NULL;
+ }
+ break;
+ case METH_O:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
+#if CYTHON_ASSUME_SAFE_SIZE
+ size = PyTuple_GET_SIZE(arg);
+#else
+ size = PyTuple_Size(arg);
+ if (unlikely(size < 0)) return NULL;
+#endif
+ if (likely(size == 1)) {
+ PyObject *result, *arg0;
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ arg0 = PyTuple_GET_ITEM(arg, 0);
+ #else
+ arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL;
+ #endif
+ result = (*meth)(self, arg0);
+ #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
+ Py_DECREF(arg0);
+ #endif
+ return result;
+ }
+ __Pyx_CyFunction_raise_argument_count_error(
+ (__pyx_CyFunctionObject*)func,
+ "takes exactly one argument", size);
+ return NULL;
+ }
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
+ return NULL;
+ }
+ __Pyx_CyFunction_raise_type_error(
+ (__pyx_CyFunctionObject*)func, "takes no keyword arguments");
+ return NULL;
+}
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) {
+ PyObject *self, *result;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)func)->m_self;
+#endif
+ result = __Pyx_CyFunction_CallMethod(func, self, arg, kw);
+ return result;
+}
+static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) {
+ PyObject *result;
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+ __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc);
+ if (vc) {
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw);
+#else
+ (void) &__Pyx_PyVectorcall_FastCallDict;
+ return PyVectorcall_Call(func, args, kw);
+#endif
+ }
+#endif
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
+ Py_ssize_t argc;
+ PyObject *new_args;
+ PyObject *self;
+#if CYTHON_ASSUME_SAFE_SIZE
+ argc = PyTuple_GET_SIZE(args);
+#else
+ argc = PyTuple_Size(args);
+ if (unlikely(argc < 0)) return NULL;
+#endif
+ new_args = PyTuple_GetSlice(args, 1, argc);
+ if (unlikely(!new_args))
+ return NULL;
+ self = PyTuple_GetItem(args, 0);
+ if (unlikely(!self)) {
+ Py_DECREF(new_args);
+ PyErr_Format(PyExc_TypeError,
+ "unbound method %.200S() needs an argument",
+ cyfunc->func_qualname);
+ return NULL;
+ }
+ result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw);
+ Py_DECREF(new_args);
+ } else {
+ result = __Pyx_CyFunction_Call(func, args, kw);
+ }
+ return result;
+}
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames)
+{
+ int ret = 0;
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
+ if (unlikely(nargs < 1)) {
+ __Pyx_CyFunction_raise_type_error(
+ cyfunc, "needs an argument");
+ return -1;
+ }
+ ret = 1;
+ }
+ if (unlikely(kwnames) && unlikely(__Pyx_PyTuple_GET_SIZE(kwnames))) {
+ __Pyx_CyFunction_raise_type_error(
+ cyfunc, "takes no keyword arguments");
+ return -1;
+ }
+ return ret;
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ if (unlikely(nargs != 0)) {
+ __Pyx_CyFunction_raise_argument_count_error(
+ cyfunc, "takes no arguments", nargs);
+ return NULL;
+ }
+ return meth(self, NULL);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ if (unlikely(nargs != 1)) {
+ __Pyx_CyFunction_raise_argument_count_error(
+ cyfunc, "takes exactly one argument", nargs);
+ return NULL;
+ }
+ return meth(self, args[0]);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ return ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))meth)(self, args, nargs, kwnames);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc);
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ #if PY_VERSION_HEX < 0x030e00A6
+ size_t nargs_value = (size_t) nargs;
+ #else
+ Py_ssize_t nargs_value = nargs;
+ #endif
+ return ((__Pyx_PyCMethod)(void(*)(void))meth)(self, cls, args, nargs_value, kwnames);
+}
+#endif
+static PyType_Slot __pyx_CyFunctionType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc},
+ {Py_tp_repr, (void *)__Pyx_CyFunction_repr},
+ {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod},
+ {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse},
+ {Py_tp_clear, (void *)__Pyx_CyFunction_clear},
+ {Py_tp_methods, (void *)__pyx_CyFunction_methods},
+ {Py_tp_members, (void *)__pyx_CyFunction_members},
+ {Py_tp_getset, (void *)__pyx_CyFunction_getsets},
+ {Py_tp_descr_get, (void *)__Pyx_PyMethod_New},
+ {0, 0},
+};
+static PyType_Spec __pyx_CyFunctionType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "cython_function_or_method",
+ sizeof(__pyx_CyFunctionObject),
+ 0,
+#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR
+ Py_TPFLAGS_METHOD_DESCRIPTOR |
+#endif
+#if CYTHON_METH_FASTCALL
+#if defined(Py_TPFLAGS_HAVE_VECTORCALL)
+ Py_TPFLAGS_HAVE_VECTORCALL |
+#elif defined(_Py_TPFLAGS_HAVE_VECTORCALL)
+ _Py_TPFLAGS_HAVE_VECTORCALL |
+#endif
+#endif // CYTHON_METH_FASTCALL
+#if PY_VERSION_HEX >= 0x030C0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_TPFLAGS_MANAGED_DICT |
+#endif
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
+ __pyx_CyFunctionType_slots
+};
+static int __pyx_CyFunction_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ mstate->__pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(
+ mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_CyFunctionType_spec, NULL);
+ if (unlikely(mstate->__pyx_CyFunctionType == NULL)) {
+ return -1;
+ }
+ return 0;
+}
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func, PyTypeObject *defaults_type) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults = PyObject_CallObject((PyObject*)defaults_type, NULL); // _PyObject_New(defaults_type);
+ if (unlikely(!m->defaults))
+ return NULL;
+ return m->defaults;
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults_tuple = tuple;
+ Py_INCREF(tuple);
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults_kwdict = dict;
+ Py_INCREF(dict);
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->func_annotations = dict;
+ Py_INCREF(dict);
+}
+
+/* CythonFunction */
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+ PyObject *op = __Pyx_CyFunction_Init(
+ PyObject_GC_New(__pyx_CyFunctionObject, __pyx_mstate_global->__pyx_CyFunctionType),
+ ml, flags, qualname, closure, module, globals, code
+ );
+ if (likely(op)) {
+ PyObject_GC_Track(op);
+ }
+ return op;
+}
+
+/* pyfrozenset_new (used by PySetContains) */
+static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) {
+ if (it) {
+ PyObject* result;
+#if CYTHON_COMPILING_IN_PYPY
+ PyObject* args;
+ args = PyTuple_Pack(1, it);
+ if (unlikely(!args))
+ return NULL;
+ result = PyObject_Call((PyObject*)&PyFrozenSet_Type, args, NULL);
+ Py_DECREF(args);
+ return result;
+#else
+ if (PyFrozenSet_CheckExact(it)) {
+ Py_INCREF(it);
+ return it;
+ }
+ result = PyFrozenSet_New(it);
+ if (unlikely(!result))
+ return NULL;
+ if ((__PYX_LIMITED_VERSION_HEX >= 0x030A0000)
+#if CYTHON_COMPILING_IN_LIMITED_API
+ || __Pyx_get_runtime_version() >= 0x030A0000
+#endif
+ )
+ return result;
+ {
+ Py_ssize_t size = __Pyx_PySet_GET_SIZE(result);
+ if (likely(size > 0))
+ return result;
+#if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(size < 0)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+#endif
+ }
+ Py_DECREF(result);
+#endif
+ }
+ return __Pyx_PyObject_CallNoArg((PyObject*) &PyFrozenSet_Type);
+}
+
+/* PySetContains */
+static int __Pyx_PySet_ContainsUnhashable(PyObject *set, PyObject *key) {
+ int result = -1;
+ if (PySet_Check(key) && PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyObject *tmpkey;
+ PyErr_Clear();
+ tmpkey = __Pyx_PyFrozenSet_New(key);
+ if (tmpkey != NULL) {
+ result = PySet_Contains(set, tmpkey);
+ Py_DECREF(tmpkey);
+ }
+ }
+ return result;
+}
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq) {
+ int result = PySet_Contains(set, key);
+ if (unlikely(result < 0)) {
+ result = __Pyx_PySet_ContainsUnhashable(set, key);
+ }
+ return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
+}
+
+/* AllocateExtensionType */
+static PyObject *__Pyx_AllocateExtensionType(PyTypeObject *t, int is_final) {
+ if (is_final || likely(!__Pyx_PyType_HasFeature(t, Py_TPFLAGS_IS_ABSTRACT))) {
+ allocfunc alloc_func = __Pyx_PyType_GetSlot(t, tp_alloc, allocfunc);
+ return alloc_func(t, 0);
+ } else {
+ newfunc tp_new = __Pyx_PyType_TryGetSlot(&PyBaseObject_Type, tp_new, newfunc);
+ #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ if (!tp_new) {
+ PyObject *new_str = PyUnicode_FromString("__new__");
+ if (likely(new_str)) {
+ PyObject *o = PyObject_CallMethodObjArgs((PyObject *)&PyBaseObject_Type, new_str, t, NULL);
+ Py_DECREF(new_str);
+ return o;
+ } else
+ return NULL;
+ } else
+ #endif
+ return tp_new(t, __pyx_mstate_global->__pyx_empty_tuple, 0);
+ }
+}
+
+/* PyObjectCallMethod0 (used by PyType_Ready) */
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
+#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
+ PyObject *args[1] = {obj};
+ (void) __Pyx_PyObject_CallOneArg;
+ (void) __Pyx_PyObject_CallNoArg;
+ return PyObject_VectorcallMethod(method_name, args, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#else
+ PyObject *method = NULL, *result = NULL;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_CallOneArg(method, obj);
+ Py_DECREF(method);
+ return result;
+ }
+ if (unlikely(!method)) goto bad;
+ result = __Pyx_PyObject_CallNoArg(method);
+ Py_DECREF(method);
+bad:
+ return result;
+#endif
+}
+
+/* ValidateBasesTuple (used by PyType_Ready) */
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) {
+ Py_ssize_t i, n;
+#if CYTHON_ASSUME_SAFE_SIZE
+ n = PyTuple_GET_SIZE(bases);
+#else
+ n = PyTuple_Size(bases);
+ if (unlikely(n < 0)) return -1;
+#endif
+ for (i = 1; i < n; i++)
+ {
+ PyTypeObject *b;
+#if CYTHON_AVOID_BORROWED_REFS
+ PyObject *b0 = PySequence_GetItem(bases, i);
+ if (!b0) return -1;
+#elif CYTHON_ASSUME_SAFE_MACROS
+ PyObject *b0 = PyTuple_GET_ITEM(bases, i);
+#else
+ PyObject *b0 = PyTuple_GetItem(bases, i);
+ if (!b0) return -1;
+#endif
+ b = (PyTypeObject*) b0;
+ if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE))
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b);
+ PyErr_Format(PyExc_TypeError,
+ "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name);
+ __Pyx_DECREF_TypeName(b_name);
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ return -1;
+ }
+ if (dictoffset == 0)
+ {
+ Py_ssize_t b_dictoffset = 0;
+#if CYTHON_USE_TYPE_SLOTS
+ b_dictoffset = b->tp_dictoffset;
+#else
+ PyObject *py_b_dictoffset = PyObject_GetAttrString((PyObject*)b, "__dictoffset__");
+ if (!py_b_dictoffset) goto dictoffset_return;
+ b_dictoffset = PyLong_AsSsize_t(py_b_dictoffset);
+ Py_DECREF(py_b_dictoffset);
+ if (b_dictoffset == -1 && PyErr_Occurred()) goto dictoffset_return;
+#endif
+ if (b_dictoffset) {
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b);
+ PyErr_Format(PyExc_TypeError,
+ "extension type '%.200s' has no __dict__ slot, "
+ "but base type '" __Pyx_FMT_TYPENAME "' has: "
+ "either add 'cdef dict __dict__' to the extension type "
+ "or add '__slots__ = [...]' to the base type",
+ type_name, b_name);
+ __Pyx_DECREF_TypeName(b_name);
+ }
+#if !CYTHON_USE_TYPE_SLOTS
+ dictoffset_return:
+#endif
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ return -1;
+ }
+ }
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ }
+ return 0;
+}
+#endif
+
+/* PyType_Ready */
+CYTHON_UNUSED static int __Pyx_PyType_HasMultipleInheritance(PyTypeObject *t) {
+ while (t) {
+ PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*);
+ if (bases) {
+ return 1;
+ }
+ t = __Pyx_PyType_GetSlot(t, tp_base, PyTypeObject*);
+ }
+ return 0;
+}
+static int __Pyx_PyType_Ready(PyTypeObject *t) {
+#if CYTHON_USE_TYPE_SPECS || !CYTHON_COMPILING_IN_CPYTHON || defined(PYSTON_MAJOR_VERSION)
+ (void)__Pyx_PyObject_CallMethod0;
+#if CYTHON_USE_TYPE_SPECS
+ (void)__Pyx_validate_bases_tuple;
+#endif
+ return PyType_Ready(t);
+#else
+ int r;
+ if (!__Pyx_PyType_HasMultipleInheritance(t)) {
+ return PyType_Ready(t);
+ }
+ PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*);
+ if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1))
+ return -1;
+#if !defined(PYSTON_MAJOR_VERSION)
+ {
+ int gc_was_enabled;
+ #if PY_VERSION_HEX >= 0x030A00b1
+ gc_was_enabled = PyGC_Disable();
+ (void)__Pyx_PyObject_CallMethod0;
+ #else
+ PyObject *ret, *py_status;
+ PyObject *gc = NULL;
+ #if (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400) &&\
+ !CYTHON_COMPILING_IN_GRAAL
+ gc = PyImport_GetModule(__pyx_mstate_global->__pyx_kp_u_gc);
+ #endif
+ if (unlikely(!gc)) gc = PyImport_Import(__pyx_mstate_global->__pyx_kp_u_gc);
+ if (unlikely(!gc)) return -1;
+ py_status = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_isenabled);
+ if (unlikely(!py_status)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ gc_was_enabled = __Pyx_PyObject_IsTrue(py_status);
+ Py_DECREF(py_status);
+ if (gc_was_enabled > 0) {
+ ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_disable);
+ if (unlikely(!ret)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ Py_DECREF(ret);
+ } else if (unlikely(gc_was_enabled == -1)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ #endif
+ t->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+#if PY_VERSION_HEX >= 0x030A0000
+ t->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
+#endif
+#else
+ (void)__Pyx_PyObject_CallMethod0;
+#endif
+ r = PyType_Ready(t);
+#if !defined(PYSTON_MAJOR_VERSION)
+ t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE;
+ #if PY_VERSION_HEX >= 0x030A00b1
+ if (gc_was_enabled)
+ PyGC_Enable();
+ #else
+ if (gc_was_enabled) {
+ PyObject *tp, *v, *tb;
+ PyErr_Fetch(&tp, &v, &tb);
+ ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_enable);
+ if (likely(ret || r == -1)) {
+ Py_XDECREF(ret);
+ PyErr_Restore(tp, v, tb);
+ } else {
+ Py_XDECREF(tp);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+ r = -1;
+ }
+ }
+ Py_DECREF(gc);
+ #endif
+ }
+#endif
+ return r;
+#endif
+}
+
+/* HasAttr (used by ImportImpl) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *o, PyObject *n) {
+ PyObject *r;
+ if (unlikely(!PyUnicode_Check(n))) {
+ PyErr_SetString(PyExc_TypeError,
+ "hasattr(): attribute name must be string");
+ return -1;
+ }
+ r = __Pyx_PyObject_GetAttrStrNoError(o, n);
+ if (!r) {
+ return (unlikely(PyErr_Occurred())) ? -1 : 0;
+ } else {
+ Py_DECREF(r);
+ return 1;
+ }
+}
+#endif
+
+/* ImportImpl (used by Import) */
+static int __Pyx__Import_GetModule(PyObject *qualname, PyObject **module) {
+ PyObject *imported_module = PyImport_GetModule(qualname);
+ if (unlikely(!imported_module)) {
+ *module = NULL;
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+ return 0;
+ }
+ *module = imported_module;
+ return 1;
+}
+static int __Pyx__Import_Lookup(PyObject *qualname, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject **module) {
+ PyObject *imported_module;
+ PyObject *top_level_package_name;
+ Py_ssize_t i;
+ int status, module_found;
+ Py_ssize_t dot_index;
+ module_found = __Pyx__Import_GetModule(qualname, &imported_module);
+ if (unlikely(!module_found || module_found == -1)) {
+ *module = NULL;
+ return module_found;
+ }
+ if (imported_names) {
+ for (i = 0; i < len_imported_names; i++) {
+ PyObject *imported_name = imported_names[i];
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+ int has_imported_attribute = PyObject_HasAttr(imported_module, imported_name);
+#else
+ int has_imported_attribute = PyObject_HasAttrWithError(imported_module, imported_name);
+ if (unlikely(has_imported_attribute == -1)) goto error;
+#endif
+ if (!has_imported_attribute) {
+ goto not_found;
+ }
+ }
+ *module = imported_module;
+ return 1;
+ }
+ dot_index = PyUnicode_FindChar(qualname, '.', 0, PY_SSIZE_T_MAX, 1);
+ if (dot_index == -1) {
+ *module = imported_module;
+ return 1;
+ }
+ if (unlikely(dot_index == -2)) goto error;
+ top_level_package_name = PyUnicode_Substring(qualname, 0, dot_index);
+ if (unlikely(!top_level_package_name)) goto error;
+ Py_DECREF(imported_module);
+ status = __Pyx__Import_GetModule(top_level_package_name, module);
+ Py_DECREF(top_level_package_name);
+ return status;
+error:
+ Py_DECREF(imported_module);
+ *module = NULL;
+ return -1;
+not_found:
+ Py_DECREF(imported_module);
+ *module = NULL;
+ return 0;
+}
+static PyObject *__Pyx__Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, PyObject *moddict, int level) {
+ PyObject *module = 0;
+ PyObject *empty_dict = 0;
+ PyObject *from_list = 0;
+ int module_found;
+ if (!qualname) {
+ qualname = name;
+ }
+ module_found = __Pyx__Import_Lookup(qualname, imported_names, len_imported_names, &module);
+ if (likely(module_found == 1)) {
+ return module;
+ } else if (unlikely(module_found == -1)) {
+ return NULL;
+ }
+ empty_dict = PyDict_New();
+ if (unlikely(!empty_dict))
+ goto bad;
+ if (imported_names) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ from_list = __Pyx_PyList_FromArray(imported_names, len_imported_names);
+ if (unlikely(!from_list))
+ goto bad;
+#else
+ from_list = PyList_New(len_imported_names);
+ if (unlikely(!from_list)) goto bad;
+ for (Py_ssize_t i=0; i__pyx_d, level);
+}
+
+/* ImportFrom */
+static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) {
+ PyObject* value = __Pyx_PyObject_GetAttrStr(module, name);
+ if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ const char* module_name_str = 0;
+ PyObject* module_name = 0;
+ PyObject* module_dot = 0;
+ PyObject* full_name = 0;
+ PyErr_Clear();
+ module_name_str = PyModule_GetName(module);
+ if (unlikely(!module_name_str)) { goto modbad; }
+ module_name = PyUnicode_FromString(module_name_str);
+ if (unlikely(!module_name)) { goto modbad; }
+ module_dot = PyUnicode_Concat(module_name, __pyx_mstate_global->__pyx_kp_u__2);
+ if (unlikely(!module_dot)) { goto modbad; }
+ full_name = PyUnicode_Concat(module_dot, name);
+ if (unlikely(!full_name)) { goto modbad; }
+ #if (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) ||\
+ CYTHON_COMPILING_IN_GRAAL
+ {
+ PyObject *modules = PyImport_GetModuleDict();
+ if (unlikely(!modules))
+ goto modbad;
+ value = PyObject_GetItem(modules, full_name);
+ }
+ #else
+ value = PyImport_GetModule(full_name);
+ #endif
+ modbad:
+ Py_XDECREF(full_name);
+ Py_XDECREF(module_dot);
+ Py_XDECREF(module_name);
+ }
+ if (unlikely(!value)) {
+ PyErr_Format(PyExc_ImportError, "cannot import name %S", name);
+ }
+ return value;
+}
+
+/* ListPack */
+static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...) {
+ va_list va;
+ PyObject *l = PyList_New(n);
+ va_start(va, n);
+ if (unlikely(!l)) goto end;
+ for (Py_ssize_t i=0; i__pyx_cython_runtime)) {
+ return c_line;
+ }
+ __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
+ cython_runtime_dict = __Pyx_PyProbablyModule_GetDict(__pyx_mstate_global->__pyx_cython_runtime);
+ if (likely(cython_runtime_dict)) {
+ __PYX_PY_DICT_LOOKUP_IF_MODIFIED(
+ use_cline, cython_runtime_dict,
+ __Pyx_PyDict_SetDefault(cython_runtime_dict, __pyx_mstate_global->__pyx_n_u_cline_in_traceback, Py_False))
+ }
+ if (use_cline == NULL || use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) {
+ c_line = 0;
+ }
+ Py_XDECREF(use_cline);
+ Py_XDECREF(cython_runtime_dict);
+ __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
+ return c_line;
+}
+#endif
+
+/* CodeObjectCache (used by AddTraceback) */
+static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) {
+ int start = 0, mid = 0, end = count - 1;
+ if (end >= 0 && code_line > entries[end].code_line) {
+ return count;
+ }
+ while (start < end) {
+ mid = start + (end - start) / 2;
+ if (code_line < entries[mid].code_line) {
+ end = mid;
+ } else if (code_line > entries[mid].code_line) {
+ start = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ if (code_line <= entries[mid].code_line) {
+ return mid;
+ } else {
+ return mid + 1;
+ }
+}
+static __Pyx_CachedCodeObjectType *__pyx__find_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line) {
+ __Pyx_CachedCodeObjectType* code_object;
+ int pos;
+ if (unlikely(!code_line) || unlikely(!code_cache->entries)) {
+ return NULL;
+ }
+ pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line);
+ if (unlikely(pos >= code_cache->count) || unlikely(code_cache->entries[pos].code_line != code_line)) {
+ return NULL;
+ }
+ code_object = code_cache->entries[pos].code_object;
+ Py_INCREF(code_object);
+ return code_object;
+}
+static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line) {
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS
+ (void)__pyx__find_code_object;
+ return NULL; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just miss.
+#else
+ struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_nonatomic_int_type old_count = __pyx_atomic_incr_acq_rel(&code_cache->accessor_count);
+ if (old_count < 0) {
+ __pyx_atomic_decr_acq_rel(&code_cache->accessor_count);
+ return NULL;
+ }
+#endif
+ __Pyx_CachedCodeObjectType *result = __pyx__find_code_object(code_cache, code_line);
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_decr_acq_rel(&code_cache->accessor_count);
+#endif
+ return result;
+#endif
+}
+static void __pyx__insert_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line, __Pyx_CachedCodeObjectType* code_object)
+{
+ int pos, i;
+ __Pyx_CodeObjectCacheEntry* entries = code_cache->entries;
+ if (unlikely(!code_line)) {
+ return;
+ }
+ if (unlikely(!entries)) {
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry));
+ if (likely(entries)) {
+ code_cache->entries = entries;
+ code_cache->max_count = 64;
+ code_cache->count = 1;
+ entries[0].code_line = code_line;
+ entries[0].code_object = code_object;
+ Py_INCREF(code_object);
+ }
+ return;
+ }
+ pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line);
+ if ((pos < code_cache->count) && unlikely(code_cache->entries[pos].code_line == code_line)) {
+ __Pyx_CachedCodeObjectType* tmp = entries[pos].code_object;
+ entries[pos].code_object = code_object;
+ Py_INCREF(code_object);
+ Py_DECREF(tmp);
+ return;
+ }
+ if (code_cache->count == code_cache->max_count) {
+ int new_max = code_cache->max_count + 64;
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc(
+ code_cache->entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry));
+ if (unlikely(!entries)) {
+ return;
+ }
+ code_cache->entries = entries;
+ code_cache->max_count = new_max;
+ }
+ for (i=code_cache->count; i>pos; i--) {
+ entries[i] = entries[i-1];
+ }
+ entries[pos].code_line = code_line;
+ entries[pos].code_object = code_object;
+ code_cache->count++;
+ Py_INCREF(code_object);
+}
+static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object) {
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS
+ (void)__pyx__insert_code_object;
+ return; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just fail.
+#else
+ struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_nonatomic_int_type expected = 0;
+ if (!__pyx_atomic_int_cmp_exchange(&code_cache->accessor_count, &expected, INT_MIN)) {
+ return;
+ }
+#endif
+ __pyx__insert_code_object(code_cache, code_line, code_object);
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_sub(&code_cache->accessor_count, INT_MIN);
+#endif
+#endif
+}
+
+/* AddTraceback */
+#include "compile.h"
+#include "frameobject.h"
+#include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API && !defined(PYPY_VERSION)
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict,
+ PyObject *firstlineno, PyObject *name) {
+ PyObject *replace = NULL;
+ if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL;
+ if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL;
+ replace = PyObject_GetAttrString(code, "replace");
+ if (likely(replace)) {
+ PyObject *result = PyObject_Call(replace, __pyx_mstate_global->__pyx_empty_tuple, scratch_dict);
+ Py_DECREF(replace);
+ return result;
+ }
+ PyErr_Clear();
+ return NULL;
+}
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL;
+ PyObject *replace = NULL, *getframe = NULL, *frame = NULL;
+ PyObject *exc_type, *exc_value, *exc_traceback;
+ int success = 0;
+ if (c_line) {
+ c_line = __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line);
+ }
+ PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
+ code_object = __pyx_find_code_object(c_line ? -c_line : py_line);
+ if (!code_object) {
+ code_object = Py_CompileString("_getframe()", filename, Py_eval_input);
+ if (unlikely(!code_object)) goto bad;
+ py_py_line = PyLong_FromLong(py_line);
+ if (unlikely(!py_py_line)) goto bad;
+ if (c_line) {
+ py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ } else {
+ py_funcname = PyUnicode_FromString(funcname);
+ }
+ if (unlikely(!py_funcname)) goto bad;
+ dict = PyDict_New();
+ if (unlikely(!dict)) goto bad;
+ {
+ PyObject *old_code_object = code_object;
+ code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname);
+ Py_DECREF(old_code_object);
+ }
+ if (unlikely(!code_object)) goto bad;
+ __pyx_insert_code_object(c_line ? -c_line : py_line, code_object);
+ } else {
+ dict = PyDict_New();
+ }
+ getframe = PySys_GetObject("_getframe");
+ if (unlikely(!getframe)) goto bad;
+ if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad;
+ frame = PyEval_EvalCode(code_object, dict, dict);
+ if (unlikely(!frame) || frame == Py_None) goto bad;
+ success = 1;
+ bad:
+ PyErr_Restore(exc_type, exc_value, exc_traceback);
+ Py_XDECREF(code_object);
+ Py_XDECREF(py_py_line);
+ Py_XDECREF(py_funcname);
+ Py_XDECREF(dict);
+ Py_XDECREF(replace);
+ if (success) {
+ PyTraceBack_Here(
+ (struct _frame*)frame);
+ }
+ Py_XDECREF(frame);
+}
+#else
+static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
+ const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = NULL;
+ PyObject *py_funcname = NULL;
+ if (c_line) {
+ py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ if (!py_funcname) goto bad;
+ funcname = PyUnicode_AsUTF8(py_funcname);
+ if (!funcname) goto bad;
+ }
+ py_code = PyCode_NewEmpty(filename, funcname, py_line);
+ Py_XDECREF(py_funcname);
+ return py_code;
+bad:
+ Py_XDECREF(py_funcname);
+ return NULL;
+}
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = 0;
+ PyFrameObject *py_frame = 0;
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
+ PyObject *ptype, *pvalue, *ptraceback;
+ if (c_line) {
+ c_line = __Pyx_CLineForTraceback(tstate, c_line);
+ }
+ py_code = __pyx_find_code_object(c_line ? -c_line : py_line);
+ if (!py_code) {
+ __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
+ py_code = __Pyx_CreateCodeObjectForTraceback(
+ funcname, c_line, py_line, filename);
+ if (!py_code) {
+ /* If the code object creation fails, then we should clear the
+ fetched exception references and propagate the new exception */
+ Py_XDECREF(ptype);
+ Py_XDECREF(pvalue);
+ Py_XDECREF(ptraceback);
+ goto bad;
+ }
+ __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
+ __pyx_insert_code_object(c_line ? -c_line : py_line, py_code);
+ }
+ py_frame = PyFrame_New(
+ tstate, /*PyThreadState *tstate,*/
+ py_code, /*PyCodeObject *code,*/
+ __pyx_mstate_global->__pyx_d, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ if (!py_frame) goto bad;
+ __Pyx_PyFrame_SetLineNumber(py_frame, py_line);
+ PyTraceBack_Here(py_frame);
+bad:
+ Py_XDECREF(py_code);
+ Py_XDECREF(py_frame);
+}
+#endif
+
+/* Declarations */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #ifdef __cplusplus
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ return ::std::complex< double >(x, y);
+ }
+ #else
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ return x + y*(__pyx_t_double_complex)_Complex_I;
+ }
+ #endif
+#else
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ __pyx_t_double_complex z;
+ z.real = x;
+ z.imag = y;
+ return z;
+ }
+#endif
+
+/* Arithmetic */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+#else
+ static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ return (a.real == b.real) && (a.imag == b.imag);
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real + b.real;
+ z.imag = a.imag + b.imag;
+ return z;
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real - b.real;
+ z.imag = a.imag - b.imag;
+ return z;
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real * b.real - a.imag * b.imag;
+ z.imag = a.real * b.imag + a.imag * b.real;
+ return z;
+ }
+ #if 1
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ if (b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real);
+ } else if (fabs(b.real) >= fabs(b.imag)) {
+ if (b.real == 0 && b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.imag);
+ } else {
+ double r = b.imag / b.real;
+ double s = (double)(1.0) / (b.real + b.imag * r);
+ return __pyx_t_double_complex_from_parts(
+ (a.real + a.imag * r) * s, (a.imag - a.real * r) * s);
+ }
+ } else {
+ double r = b.real / b.imag;
+ double s = (double)(1.0) / (b.imag + b.real * r);
+ return __pyx_t_double_complex_from_parts(
+ (a.real * r + a.imag) * s, (a.imag * r - a.real) * s);
+ }
+ }
+ #else
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ if (b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real);
+ } else {
+ double denom = b.real * b.real + b.imag * b.imag;
+ return __pyx_t_double_complex_from_parts(
+ (a.real * b.real + a.imag * b.imag) / denom,
+ (a.imag * b.real - a.real * b.imag) / denom);
+ }
+ }
+ #endif
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex a) {
+ __pyx_t_double_complex z;
+ z.real = -a.real;
+ z.imag = -a.imag;
+ return z;
+ }
+ static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex a) {
+ return (a.real == 0) && (a.imag == 0);
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex a) {
+ __pyx_t_double_complex z;
+ z.real = a.real;
+ z.imag = -a.imag;
+ return z;
+ }
+ #if 1
+ static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex z) {
+ #if !defined(HAVE_HYPOT) || defined(_MSC_VER)
+ return sqrt(z.real*z.real + z.imag*z.imag);
+ #else
+ return hypot(z.real, z.imag);
+ #endif
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ double r, lnr, theta, z_r, z_theta;
+ if (b.imag == 0 && b.real == (int)b.real) {
+ if (b.real < 0) {
+ double denom = a.real * a.real + a.imag * a.imag;
+ a.real = a.real / denom;
+ a.imag = -a.imag / denom;
+ b.real = -b.real;
+ }
+ switch ((int)b.real) {
+ case 0:
+ z.real = 1;
+ z.imag = 0;
+ return z;
+ case 1:
+ return a;
+ case 2:
+ return __Pyx_c_prod_double(a, a);
+ case 3:
+ z = __Pyx_c_prod_double(a, a);
+ return __Pyx_c_prod_double(z, a);
+ case 4:
+ z = __Pyx_c_prod_double(a, a);
+ return __Pyx_c_prod_double(z, z);
+ }
+ }
+ if (a.imag == 0) {
+ if (a.real == 0) {
+ return a;
+ } else if ((b.imag == 0) && (a.real >= 0)) {
+ z.real = pow(a.real, b.real);
+ z.imag = 0;
+ return z;
+ } else if (a.real > 0) {
+ r = a.real;
+ theta = 0;
+ } else {
+ r = -a.real;
+ theta = atan2(0.0, -1.0);
+ }
+ } else {
+ r = __Pyx_c_abs_double(a);
+ theta = atan2(a.imag, a.real);
+ }
+ lnr = log(r);
+ z_r = exp(lnr * b.real - theta * b.imag);
+ z_theta = theta * b.real + lnr * b.imag;
+ z.real = z_r * cos(z_theta);
+ z.imag = z_r * sin(z_theta);
+ return z;
+ }
+ #endif
+#endif
+
+/* FromPy */
+static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject* o) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ double real=-1.0, imag=-1.0;
+ real = PyComplex_RealAsDouble(o);
+ if (unlikely(real == -1.0 && PyErr_Occurred())) goto end;
+ imag = PyComplex_ImagAsDouble(o);
+ end:
+ return __pyx_t_double_complex_from_parts(
+ (double)real, (double)imag
+ );
+#else
+ Py_complex cval;
+#if !CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_GRAAL
+ if (PyComplex_CheckExact(o))
+ cval = ((PyComplexObject *)o)->cval;
+ else
+#endif
+ cval = PyComplex_AsCComplex(o);
+ return __pyx_t_double_complex_from_parts(
+ (double)cval.real,
+ (double)cval.imag);
+#endif
+}
+
+/* CIntToPy */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const long neg_one = (long) -1, const_zero = (long) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (is_unsigned) {
+ if (sizeof(long) < sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(unsigned long)) {
+ return PyLong_FromUnsignedLong((unsigned long) value);
+#if !CYTHON_COMPILING_IN_PYPY
+ } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
+ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
+#endif
+ }
+ } else {
+ if (sizeof(long) <= sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
+ return PyLong_FromLongLong((PY_LONG_LONG) value);
+ }
+ }
+ {
+ unsigned char *bytes = (unsigned char *)&value;
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4
+ if (is_unsigned) {
+ return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1);
+ } else {
+ return PyLong_FromNativeBytes(bytes, sizeof(value), -1);
+ }
+#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ return _PyLong_FromByteArray(bytes, sizeof(long),
+ little, !is_unsigned);
+#else
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ PyObject *from_bytes, *result = NULL, *kwds = NULL;
+ PyObject *py_bytes = NULL, *order_str = NULL;
+ from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes");
+ if (!from_bytes) return NULL;
+ py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(long));
+ if (!py_bytes) goto limited_bad;
+ order_str = PyUnicode_FromString(little ? "little" : "big");
+ if (!order_str) goto limited_bad;
+ {
+ PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str };
+ if (!is_unsigned) {
+ kwds = __Pyx_MakeVectorcallBuilderKwds(1);
+ if (!kwds) goto limited_bad;
+ if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad;
+ }
+ result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds);
+ }
+ limited_bad:
+ Py_XDECREF(kwds);
+ Py_XDECREF(order_str);
+ Py_XDECREF(py_bytes);
+ Py_XDECREF(from_bytes);
+ return result;
+#endif
+ }
+}
+
+/* FormatTypeName */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static __Pyx_TypeName
+__Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp)
+{
+ PyObject *module = NULL, *name = NULL, *result = NULL;
+ #if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+ name = __Pyx_PyObject_GetAttrStr((PyObject *)tp,
+ __pyx_mstate_global->__pyx_n_u_qualname);
+ #else
+ name = PyType_GetQualName(tp);
+ #endif
+ if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) goto bad;
+ module = __Pyx_PyObject_GetAttrStr((PyObject *)tp,
+ __pyx_mstate_global->__pyx_n_u_module);
+ if (unlikely(module == NULL) || unlikely(!PyUnicode_Check(module))) goto bad;
+ if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) {
+ result = name;
+ name = NULL;
+ goto done;
+ }
+ result = PyUnicode_FromFormat("%U.%U", module, name);
+ if (unlikely(result == NULL)) goto bad;
+ done:
+ Py_XDECREF(name);
+ Py_XDECREF(module);
+ return result;
+ bad:
+ PyErr_Clear();
+ if (name) {
+ result = name;
+ name = NULL;
+ } else {
+ result = __Pyx_NewRef(__pyx_mstate_global->__pyx_kp_u__3);
+ }
+ goto done;
+}
+#endif
+
+/* CIntFromPyVerify (used by CIntFromPy) */
+#define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0)
+#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1)
+#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\
+ {\
+ func_type value = func_value;\
+ if (sizeof(target_type) < sizeof(func_type)) {\
+ if (unlikely(value != (func_type) (target_type) value)) {\
+ func_type zero = 0;\
+ if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\
+ return (target_type) -1;\
+ if (is_unsigned && unlikely(value < zero))\
+ goto raise_neg_overflow;\
+ else\
+ goto raise_overflow;\
+ }\
+ }\
+ return (target_type) value;\
+ }
+
+/* CIntFromPy */
+static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *x) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const long neg_one = (long) -1, const_zero = (long) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (unlikely(!PyLong_Check(x))) {
+ long val;
+ PyObject *tmp = __Pyx_PyNumber_Long(x);
+ if (!tmp) return (long) -1;
+ val = __Pyx_PyLong_As_long(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (unlikely(__Pyx_PyLong_IsNeg(x))) {
+ goto raise_neg_overflow;
+ } else if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_DigitCount(x)) {
+ case 2:
+ if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 2 * PyLong_SHIFT)) {
+ return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 3 * PyLong_SHIFT)) {
+ return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 4 * PyLong_SHIFT)) {
+ return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ }
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (long) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if ((sizeof(long) <= sizeof(unsigned long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if ((sizeof(long) <= sizeof(unsigned PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_SignedDigitCount(x)) {
+ case -2:
+ if ((8 * sizeof(long) - 1 > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) {
+ return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ }
+ }
+#endif
+ if ((sizeof(long) <= sizeof(long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x))
+ } else if ((sizeof(long) <= sizeof(PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+ long val;
+ int ret = -1;
+#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_ssize_t bytes_copied = PyLong_AsNativeBytes(
+ x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0));
+ if (unlikely(bytes_copied == -1)) {
+ } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) {
+ goto raise_overflow;
+ } else {
+ ret = 0;
+ }
+#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray)
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ ret = _PyLong_AsByteArray((PyLongObject *)x,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+#else
+ PyObject *v;
+ PyObject *stepval = NULL, *mask = NULL, *shift = NULL;
+ int bits, remaining_bits, is_negative = 0;
+ int chunk_size = (sizeof(long) < 8) ? 30 : 62;
+ if (likely(PyLong_CheckExact(x))) {
+ v = __Pyx_NewRef(x);
+ } else {
+ v = PyNumber_Long(x);
+ if (unlikely(!v)) return (long) -1;
+ assert(PyLong_CheckExact(v));
+ }
+ {
+ int result = PyObject_RichCompareBool(v, Py_False, Py_LT);
+ if (unlikely(result < 0)) {
+ Py_DECREF(v);
+ return (long) -1;
+ }
+ is_negative = result == 1;
+ }
+ if (is_unsigned && unlikely(is_negative)) {
+ Py_DECREF(v);
+ goto raise_neg_overflow;
+ } else if (is_negative) {
+ stepval = PyNumber_Invert(v);
+ Py_DECREF(v);
+ if (unlikely(!stepval))
+ return (long) -1;
+ } else {
+ stepval = v;
+ }
+ v = NULL;
+ val = (long) 0;
+ mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done;
+ shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done;
+ for (bits = 0; bits < (int) sizeof(long) * 8 - chunk_size; bits += chunk_size) {
+ PyObject *tmp, *digit;
+ long idigit;
+ digit = PyNumber_And(stepval, mask);
+ if (unlikely(!digit)) goto done;
+ idigit = PyLong_AsLong(digit);
+ Py_DECREF(digit);
+ if (unlikely(idigit < 0)) goto done;
+ val |= ((long) idigit) << bits;
+ tmp = PyNumber_Rshift(stepval, shift);
+ if (unlikely(!tmp)) goto done;
+ Py_DECREF(stepval); stepval = tmp;
+ }
+ Py_DECREF(shift); shift = NULL;
+ Py_DECREF(mask); mask = NULL;
+ {
+ long idigit = PyLong_AsLong(stepval);
+ if (unlikely(idigit < 0)) goto done;
+ remaining_bits = ((int) sizeof(long) * 8) - bits - (is_unsigned ? 0 : 1);
+ if (unlikely(idigit >= (1L << remaining_bits)))
+ goto raise_overflow;
+ val |= ((long) idigit) << bits;
+ }
+ if (!is_unsigned) {
+ if (unlikely(val & (((long) 1) << (sizeof(long) * 8 - 1))))
+ goto raise_overflow;
+ if (is_negative)
+ val = ~val;
+ }
+ ret = 0;
+ done:
+ Py_XDECREF(shift);
+ Py_XDECREF(mask);
+ Py_XDECREF(stepval);
+#endif
+ if (unlikely(ret))
+ return (long) -1;
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to long");
+ return (long) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to long");
+ return (long) -1;
+}
+
+/* CIntFromPy */
+static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *x) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const int neg_one = (int) -1, const_zero = (int) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (unlikely(!PyLong_Check(x))) {
+ int val;
+ PyObject *tmp = __Pyx_PyNumber_Long(x);
+ if (!tmp) return (int) -1;
+ val = __Pyx_PyLong_As_int(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (unlikely(__Pyx_PyLong_IsNeg(x))) {
+ goto raise_neg_overflow;
+ } else if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_DigitCount(x)) {
+ case 2:
+ if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 2 * PyLong_SHIFT)) {
+ return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 3 * PyLong_SHIFT)) {
+ return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 4 * PyLong_SHIFT)) {
+ return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ }
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (int) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if ((sizeof(int) <= sizeof(unsigned long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if ((sizeof(int) <= sizeof(unsigned PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_SignedDigitCount(x)) {
+ case -2:
+ if ((8 * sizeof(int) - 1 > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) {
+ return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ }
+ }
+#endif
+ if ((sizeof(int) <= sizeof(long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x))
+ } else if ((sizeof(int) <= sizeof(PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+ int val;
+ int ret = -1;
+#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_ssize_t bytes_copied = PyLong_AsNativeBytes(
+ x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0));
+ if (unlikely(bytes_copied == -1)) {
+ } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) {
+ goto raise_overflow;
+ } else {
+ ret = 0;
+ }
+#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray)
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ ret = _PyLong_AsByteArray((PyLongObject *)x,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+#else
+ PyObject *v;
+ PyObject *stepval = NULL, *mask = NULL, *shift = NULL;
+ int bits, remaining_bits, is_negative = 0;
+ int chunk_size = (sizeof(long) < 8) ? 30 : 62;
+ if (likely(PyLong_CheckExact(x))) {
+ v = __Pyx_NewRef(x);
+ } else {
+ v = PyNumber_Long(x);
+ if (unlikely(!v)) return (int) -1;
+ assert(PyLong_CheckExact(v));
+ }
+ {
+ int result = PyObject_RichCompareBool(v, Py_False, Py_LT);
+ if (unlikely(result < 0)) {
+ Py_DECREF(v);
+ return (int) -1;
+ }
+ is_negative = result == 1;
+ }
+ if (is_unsigned && unlikely(is_negative)) {
+ Py_DECREF(v);
+ goto raise_neg_overflow;
+ } else if (is_negative) {
+ stepval = PyNumber_Invert(v);
+ Py_DECREF(v);
+ if (unlikely(!stepval))
+ return (int) -1;
+ } else {
+ stepval = v;
+ }
+ v = NULL;
+ val = (int) 0;
+ mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done;
+ shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done;
+ for (bits = 0; bits < (int) sizeof(int) * 8 - chunk_size; bits += chunk_size) {
+ PyObject *tmp, *digit;
+ long idigit;
+ digit = PyNumber_And(stepval, mask);
+ if (unlikely(!digit)) goto done;
+ idigit = PyLong_AsLong(digit);
+ Py_DECREF(digit);
+ if (unlikely(idigit < 0)) goto done;
+ val |= ((int) idigit) << bits;
+ tmp = PyNumber_Rshift(stepval, shift);
+ if (unlikely(!tmp)) goto done;
+ Py_DECREF(stepval); stepval = tmp;
+ }
+ Py_DECREF(shift); shift = NULL;
+ Py_DECREF(mask); mask = NULL;
+ {
+ long idigit = PyLong_AsLong(stepval);
+ if (unlikely(idigit < 0)) goto done;
+ remaining_bits = ((int) sizeof(int) * 8) - bits - (is_unsigned ? 0 : 1);
+ if (unlikely(idigit >= (1L << remaining_bits)))
+ goto raise_overflow;
+ val |= ((int) idigit) << bits;
+ }
+ if (!is_unsigned) {
+ if (unlikely(val & (((int) 1) << (sizeof(int) * 8 - 1))))
+ goto raise_overflow;
+ if (is_negative)
+ val = ~val;
+ }
+ ret = 0;
+ done:
+ Py_XDECREF(shift);
+ Py_XDECREF(mask);
+ Py_XDECREF(stepval);
+#endif
+ if (unlikely(ret))
+ return (int) -1;
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to int");
+ return (int) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to int");
+ return (int) -1;
+}
+
+/* FastTypeChecks */
+#if CYTHON_COMPILING_IN_CPYTHON
+static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) {
+ while (a) {
+ a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*);
+ if (a == b)
+ return 1;
+ }
+ return b == &PyBaseObject_Type;
+}
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (a == b) return 1;
+ mro = a->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ return __Pyx_InBases(a, b);
+}
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (cls == a || cls == b) return 1;
+ mro = cls->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ PyObject *base = PyTuple_GET_ITEM(mro, i);
+ if (base == (PyObject *)a || base == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b);
+}
+static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) {
+ if (exc_type1) {
+ return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2);
+ } else {
+ return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2);
+ }
+}
+static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ assert(PyExceptionClass_Check(exc_type));
+ n = PyTuple_GET_SIZE(tuple);
+ for (i=0; i>= 8;
+ ++i;
+ }
+ __Pyx_cached_runtime_version = version;
+ }
+}
+#endif
+static unsigned long __Pyx_get_runtime_version(void) {
+#if __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ return Py_Version & ~0xFFUL;
+#else
+ return __Pyx_cached_runtime_version;
+#endif
+}
+
+/* CheckBinaryVersion */
+static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer) {
+ const unsigned long MAJOR_MINOR = 0xFFFF0000UL;
+ if ((rt_version & MAJOR_MINOR) == (ct_version & MAJOR_MINOR))
+ return 0;
+ if (likely(allow_newer && (rt_version & MAJOR_MINOR) > (ct_version & MAJOR_MINOR)))
+ return 1;
+ {
+ char message[200];
+ PyOS_snprintf(message, sizeof(message),
+ "compile time Python version %d.%d "
+ "of module '%.100s' "
+ "%s "
+ "runtime version %d.%d",
+ (int) (ct_version >> 24), (int) ((ct_version >> 16) & 0xFF),
+ __Pyx_MODULE_NAME,
+ (allow_newer) ? "was newer than" : "does not match",
+ (int) (rt_version >> 24), (int) ((rt_version >> 16) & 0xFF)
+ );
+ return PyErr_WarnEx(NULL, message, 1);
+ }
+}
+
+/* NewCodeObj */
+#if CYTHON_COMPILING_IN_LIMITED_API
+ static PyObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f,
+ PyObject *code, PyObject *c, PyObject* n, PyObject *v,
+ PyObject *fv, PyObject *cell, PyObject* fn,
+ PyObject *name, int fline, PyObject *lnos) {
+ PyObject *exception_table = NULL;
+ PyObject *types_module=NULL, *code_type=NULL, *result=NULL;
+ #if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+ PyObject *version_info;
+ PyObject *py_minor_version = NULL;
+ #endif
+ long minor_version = 0;
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ minor_version = 11;
+ #else
+ if (!(version_info = PySys_GetObject("version_info"))) goto end;
+ if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end;
+ minor_version = PyLong_AsLong(py_minor_version);
+ Py_DECREF(py_minor_version);
+ if (minor_version == -1 && PyErr_Occurred()) goto end;
+ #endif
+ if (!(types_module = PyImport_ImportModule("types"))) goto end;
+ if (!(code_type = PyObject_GetAttrString(types_module, "CodeType"))) goto end;
+ if (minor_version <= 7) {
+ (void)p;
+ result = PyObject_CallFunction(code_type, "iiiiiOOOOOOiOOO", a, k, l, s, f, code,
+ c, n, v, fn, name, fline, lnos, fv, cell);
+ } else if (minor_version <= 10) {
+ result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOiOOO", a,p, k, l, s, f, code,
+ c, n, v, fn, name, fline, lnos, fv, cell);
+ } else {
+ if (!(exception_table = PyBytes_FromStringAndSize(NULL, 0))) goto end;
+ result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOOiOOOO", a,p, k, l, s, f, code,
+ c, n, v, fn, name, name, fline, lnos, exception_table, fv, cell);
+ }
+ end:
+ Py_XDECREF(code_type);
+ Py_XDECREF(exception_table);
+ Py_XDECREF(types_module);
+ if (type) {
+ PyErr_Restore(type, value, traceback);
+ }
+ return result;
+ }
+#elif PY_VERSION_HEX >= 0x030B0000
+ static PyCodeObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f,
+ PyObject *code, PyObject *c, PyObject* n, PyObject *v,
+ PyObject *fv, PyObject *cell, PyObject* fn,
+ PyObject *name, int fline, PyObject *lnos) {
+ PyCodeObject *result;
+ result =
+ #if PY_VERSION_HEX >= 0x030C0000
+ PyUnstable_Code_NewWithPosOnlyArgs
+ #else
+ PyCode_NewWithPosOnlyArgs
+ #endif
+ (a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, name, fline, lnos, __pyx_mstate_global->__pyx_empty_bytes);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030c00A1
+ if (likely(result))
+ result->_co_firsttraceable = 0;
+ #endif
+ return result;
+ }
+#elif !CYTHON_COMPILING_IN_PYPY
+ #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#else
+ #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#endif
+static PyObject* __Pyx_PyCode_New(
+ const __Pyx_PyCode_New_function_description descr,
+ PyObject * const *varnames,
+ PyObject *filename,
+ PyObject *funcname,
+ PyObject *line_table,
+ PyObject *tuple_dedup_map
+) {
+ PyObject *code_obj = NULL, *varnames_tuple_dedup = NULL, *code_bytes = NULL;
+ Py_ssize_t var_count = (Py_ssize_t) descr.nlocals;
+ PyObject *varnames_tuple = PyTuple_New(var_count);
+ if (unlikely(!varnames_tuple)) return NULL;
+ for (Py_ssize_t i=0; i < var_count; i++) {
+ Py_INCREF(varnames[i]);
+ if (__Pyx_PyTuple_SET_ITEM(varnames_tuple, i, varnames[i]) != (0)) goto done;
+ }
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ varnames_tuple_dedup = PyDict_GetItem(tuple_dedup_map, varnames_tuple);
+ if (!varnames_tuple_dedup) {
+ if (unlikely(PyDict_SetItem(tuple_dedup_map, varnames_tuple, varnames_tuple) < 0)) goto done;
+ varnames_tuple_dedup = varnames_tuple;
+ }
+ #else
+ varnames_tuple_dedup = PyDict_SetDefault(tuple_dedup_map, varnames_tuple, varnames_tuple);
+ if (unlikely(!varnames_tuple_dedup)) goto done;
+ #endif
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_INCREF(varnames_tuple_dedup);
+ #endif
+ if (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table != NULL && !CYTHON_COMPILING_IN_GRAAL) {
+ Py_ssize_t line_table_length = __Pyx_PyBytes_GET_SIZE(line_table);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(line_table_length == -1)) goto done;
+ #endif
+ Py_ssize_t code_len = (line_table_length * 2 + 4) & ~3LL;
+ code_bytes = PyBytes_FromStringAndSize(NULL, code_len);
+ if (unlikely(!code_bytes)) goto done;
+ char* c_code_bytes = PyBytes_AsString(code_bytes);
+ if (unlikely(!c_code_bytes)) goto done;
+ memset(c_code_bytes, 0, (size_t) code_len);
+ }
+ code_obj = (PyObject*) __Pyx__PyCode_New(
+ (int) descr.argcount,
+ (int) descr.num_posonly_args,
+ (int) descr.num_kwonly_args,
+ (int) descr.nlocals,
+ 0,
+ (int) descr.flags,
+ code_bytes ? code_bytes : __pyx_mstate_global->__pyx_empty_bytes,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ varnames_tuple_dedup,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ filename,
+ funcname,
+ (int) descr.first_line,
+ (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table) ? line_table : __pyx_mstate_global->__pyx_empty_bytes
+ );
+done:
+ Py_XDECREF(code_bytes);
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(varnames_tuple_dedup);
+ #endif
+ Py_DECREF(varnames_tuple);
+ return code_obj;
+}
+
+/* DecompressString */
+static PyObject *__Pyx_DecompressString(const char *s, Py_ssize_t length, int algo) {
+ PyObject *module, *decompress, *compressed_bytes, *decompressed;
+ const char* module_name = algo == 3 ? "compression.zstd" : algo == 2 ? "bz2" : "zlib";
+ PyObject *methodname = PyUnicode_FromString("decompress");
+ if (unlikely(!methodname)) return NULL;
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030e0000
+ if (algo == 3) {
+ PyObject *fromlist = Py_BuildValue("[O]", methodname);
+ if (unlikely(!fromlist)) return NULL;
+ module = PyImport_ImportModuleLevel("compression.zstd", NULL, NULL, fromlist, 0);
+ Py_DECREF(fromlist);
+ } else
+ #endif
+ module = PyImport_ImportModule(module_name);
+ if (unlikely(!module)) goto import_failed;
+ decompress = PyObject_GetAttr(module, methodname);
+ if (unlikely(!decompress)) goto import_failed;
+ {
+ #ifdef __cplusplus
+ char *memview_bytes = const_cast(s);
+ #else
+ #if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wcast-qual"
+ #elif !defined(__INTEL_COMPILER) && defined(__GNUC__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-qual"
+ #endif
+ char *memview_bytes = (char*) s;
+ #if defined(__clang__)
+ #pragma clang diagnostic pop
+ #elif !defined(__INTEL_COMPILER) && defined(__GNUC__)
+ #pragma GCC diagnostic pop
+ #endif
+ #endif
+ #if CYTHON_COMPILING_IN_LIMITED_API && !defined(PyBUF_READ)
+ int memview_flags = 0x100;
+ #else
+ int memview_flags = PyBUF_READ;
+ #endif
+ compressed_bytes = PyMemoryView_FromMemory(memview_bytes, length, memview_flags);
+ }
+ if (unlikely(!compressed_bytes)) {
+ Py_DECREF(decompress);
+ goto bad;
+ }
+ decompressed = PyObject_CallFunctionObjArgs(decompress, compressed_bytes, NULL);
+ Py_DECREF(compressed_bytes);
+ Py_DECREF(decompress);
+ Py_DECREF(module);
+ Py_DECREF(methodname);
+ return decompressed;
+import_failed:
+ PyErr_Format(PyExc_ImportError,
+ "Failed to import '%.20s.decompress' - cannot initialise module strings. "
+ "String compression was configured with the C macro 'CYTHON_COMPRESS_STRINGS=%d'.",
+ module_name, algo);
+bad:
+ Py_XDECREF(module);
+ Py_DECREF(methodname);
+ return NULL;
+}
+
+#include
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) {
+ size_t len = strlen(s);
+ if (unlikely(len > (size_t) PY_SSIZE_T_MAX)) {
+ PyErr_SetString(PyExc_OverflowError, "byte string is too long");
+ return -1;
+ }
+ return (Py_ssize_t) len;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) {
+ Py_ssize_t len = __Pyx_ssize_strlen(c_str);
+ if (unlikely(len < 0)) return NULL;
+ return __Pyx_PyUnicode_FromStringAndSize(c_str, len);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char* c_str) {
+ Py_ssize_t len = __Pyx_ssize_strlen(c_str);
+ if (unlikely(len < 0)) return NULL;
+ return PyByteArray_FromStringAndSize(c_str, len);
+}
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) {
+ Py_ssize_t ignore;
+ return __Pyx_PyObject_AsStringAndSize(o, &ignore);
+}
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+ if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {
+ const char* result;
+ Py_ssize_t unicode_length;
+ CYTHON_MAYBE_UNUSED_VAR(unicode_length); // only for __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ #if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ if (unlikely(PyArg_Parse(o, "s#", &result, length) < 0)) return NULL;
+ #else
+ result = PyUnicode_AsUTF8AndSize(o, length);
+ #endif
+ #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ unicode_length = PyUnicode_GetLength(o);
+ if (unlikely(unicode_length < 0)) return NULL;
+ if (unlikely(unicode_length != *length)) {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+ #endif
+ return result;
+ }
+#else
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ if (likely(PyUnicode_IS_ASCII(o))) {
+ *length = PyUnicode_GET_LENGTH(o);
+ return PyUnicode_AsUTF8(o);
+ } else {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+#else
+ return PyUnicode_AsUTF8AndSize(o, length);
+#endif
+#endif
+}
+#endif
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+ if (PyUnicode_Check(o)) {
+ return __Pyx_PyUnicode_AsStringAndSize(o, length);
+ } else
+#endif
+ if (PyByteArray_Check(o)) {
+#if (CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS) || (CYTHON_COMPILING_IN_PYPY && (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)))
+ *length = PyByteArray_GET_SIZE(o);
+ return PyByteArray_AS_STRING(o);
+#else
+ *length = PyByteArray_Size(o);
+ if (*length == -1) return NULL;
+ return PyByteArray_AsString(o);
+#endif
+ } else
+ {
+ char* result;
+ int r = PyBytes_AsStringAndSize(o, &result, length);
+ if (unlikely(r < 0)) {
+ return NULL;
+ } else {
+ return result;
+ }
+ }
+}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
+ int is_true = x == Py_True;
+ if (is_true | (x == Py_False) | (x == Py_None)) return is_true;
+ else return PyObject_IsTrue(x);
+}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) {
+ int retval;
+ if (unlikely(!x)) return -1;
+ retval = __Pyx_PyObject_IsTrue(x);
+ Py_DECREF(x);
+ return retval;
+}
+static PyObject* __Pyx_PyNumber_LongWrongResultType(PyObject* result) {
+ __Pyx_TypeName result_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(result));
+ if (PyLong_Check(result)) {
+ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
+ "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). "
+ "The ability to return an instance of a strict subclass of int is deprecated, "
+ "and may be removed in a future version of Python.",
+ result_type_name)) {
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+ }
+ __Pyx_DECREF_TypeName(result_type_name);
+ return result;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "__int__ returned non-int (type " __Pyx_FMT_TYPENAME ")",
+ result_type_name);
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x) {
+#if CYTHON_USE_TYPE_SLOTS
+ PyNumberMethods *m;
+#endif
+ PyObject *res = NULL;
+ if (likely(PyLong_Check(x)))
+ return __Pyx_NewRef(x);
+#if CYTHON_USE_TYPE_SLOTS
+ m = Py_TYPE(x)->tp_as_number;
+ if (likely(m && m->nb_int)) {
+ res = m->nb_int(x);
+ }
+#else
+ if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) {
+ res = PyNumber_Long(x);
+ }
+#endif
+ if (likely(res)) {
+ if (unlikely(!PyLong_CheckExact(res))) {
+ return __Pyx_PyNumber_LongWrongResultType(res);
+ }
+ }
+ else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "an integer is required");
+ }
+ return res;
+}
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
+ Py_ssize_t ival;
+ PyObject *x;
+ if (likely(PyLong_CheckExact(b))) {
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(__Pyx_PyLong_IsCompact(b))) {
+ return __Pyx_PyLong_CompactValue(b);
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(b);
+ const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(b);
+ switch (size) {
+ case 2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ }
+ }
+ #endif
+ return PyLong_AsSsize_t(b);
+ }
+ x = PyNumber_Index(b);
+ if (!x) return -1;
+ ival = PyLong_AsSsize_t(x);
+ Py_DECREF(x);
+ return ival;
+}
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) {
+ if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) {
+ return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o);
+ } else {
+ Py_ssize_t ival;
+ PyObject *x;
+ x = PyNumber_Index(o);
+ if (!x) return -1;
+ ival = PyLong_AsLong(x);
+ Py_DECREF(x);
+ return ival;
+ }
+}
+static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b) {
+ CYTHON_UNUSED_VAR(b);
+ return __Pyx_NewRef(Py_None);
+}
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
+ return __Pyx_NewRef(b ? Py_True: Py_False);
+}
+static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t ival) {
+ return PyLong_FromSize_t(ival);
+}
+
+
+/* MultiPhaseInitModuleState */
+#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE
+#ifndef CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+#if (CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX >= 0x030C0000)
+ #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 1
+#else
+ #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 0
+#endif
+#endif
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE && !CYTHON_ATOMICS
+#error "Module state with PEP489 requires atomics. Currently that's one of\
+ C11, C++11, gcc atomic intrinsics or MSVC atomic intrinsics"
+#endif
+#if !CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+#define __Pyx_ModuleStateLookup_Lock()
+#define __Pyx_ModuleStateLookup_Unlock()
+#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d0000
+static PyMutex __Pyx_ModuleStateLookup_mutex = {0};
+#define __Pyx_ModuleStateLookup_Lock() PyMutex_Lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() PyMutex_Unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(__cplusplus) && __cplusplus >= 201103L
+#include
+static std::mutex __Pyx_ModuleStateLookup_mutex;
+#define __Pyx_ModuleStateLookup_Lock() __Pyx_ModuleStateLookup_mutex.lock()
+#define __Pyx_ModuleStateLookup_Unlock() __Pyx_ModuleStateLookup_mutex.unlock()
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201112L) && !defined(__STDC_NO_THREADS__)
+#include
+static mtx_t __Pyx_ModuleStateLookup_mutex;
+static once_flag __Pyx_ModuleStateLookup_mutex_once_flag = ONCE_FLAG_INIT;
+static void __Pyx_ModuleStateLookup_initialize_mutex(void) {
+ mtx_init(&__Pyx_ModuleStateLookup_mutex, mtx_plain);
+}
+#define __Pyx_ModuleStateLookup_Lock()\
+ call_once(&__Pyx_ModuleStateLookup_mutex_once_flag, __Pyx_ModuleStateLookup_initialize_mutex);\
+ mtx_lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() mtx_unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(HAVE_PTHREAD_H)
+#include
+static pthread_mutex_t __Pyx_ModuleStateLookup_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define __Pyx_ModuleStateLookup_Lock() pthread_mutex_lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() pthread_mutex_unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(_WIN32)
+#include // synchapi.h on its own doesn't work
+static SRWLOCK __Pyx_ModuleStateLookup_mutex = SRWLOCK_INIT;
+#define __Pyx_ModuleStateLookup_Lock() AcquireSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() ReleaseSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex)
+#else
+#error "No suitable lock available for CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE.\
+ Requires C standard >= C11, or C++ standard >= C++11,\
+ or pthreads, or the Windows 32 API, or Python >= 3.13."
+#endif
+typedef struct {
+ int64_t id;
+ PyObject *module;
+} __Pyx_InterpreterIdAndModule;
+typedef struct {
+ char interpreter_id_as_index;
+ Py_ssize_t count;
+ Py_ssize_t allocated;
+ __Pyx_InterpreterIdAndModule table[1];
+} __Pyx_ModuleStateLookupData;
+#define __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE 32
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static __pyx_atomic_int_type __Pyx_ModuleStateLookup_read_counter = 0;
+#endif
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static __pyx_atomic_ptr_type __Pyx_ModuleStateLookup_data = 0;
+#else
+static __Pyx_ModuleStateLookupData* __Pyx_ModuleStateLookup_data = NULL;
+#endif
+static __Pyx_InterpreterIdAndModule* __Pyx_State_FindModuleStateLookupTableLowerBound(
+ __Pyx_InterpreterIdAndModule* table,
+ Py_ssize_t count,
+ int64_t interpreterId) {
+ __Pyx_InterpreterIdAndModule* begin = table;
+ __Pyx_InterpreterIdAndModule* end = begin + count;
+ if (begin->id == interpreterId) {
+ return begin;
+ }
+ while ((end - begin) > __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) {
+ __Pyx_InterpreterIdAndModule* halfway = begin + (end - begin)/2;
+ if (halfway->id == interpreterId) {
+ return halfway;
+ }
+ if (halfway->id < interpreterId) {
+ begin = halfway;
+ } else {
+ end = halfway;
+ }
+ }
+ for (; begin < end; ++begin) {
+ if (begin->id >= interpreterId) return begin;
+ }
+ return begin;
+}
+static PyObject *__Pyx_State_FindModule(CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return NULL;
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData* data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data);
+ {
+ __pyx_atomic_incr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+ if (likely(data)) {
+ __Pyx_ModuleStateLookupData* new_data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_acquire(&__Pyx_ModuleStateLookup_data);
+ if (likely(data == new_data)) {
+ goto read_finished;
+ }
+ }
+ __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+ __Pyx_ModuleStateLookup_Lock();
+ __pyx_atomic_incr_relaxed(&__Pyx_ModuleStateLookup_read_counter);
+ data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data);
+ __Pyx_ModuleStateLookup_Unlock();
+ }
+ read_finished:;
+#else
+ __Pyx_ModuleStateLookupData* data = __Pyx_ModuleStateLookup_data;
+#endif
+ __Pyx_InterpreterIdAndModule* found = NULL;
+ if (unlikely(!data)) goto end;
+ if (data->interpreter_id_as_index) {
+ if (interpreter_id < data->count) {
+ found = data->table+interpreter_id;
+ }
+ } else {
+ found = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ data->table, data->count, interpreter_id);
+ }
+ end:
+ {
+ PyObject *result=NULL;
+ if (found && found->id == interpreter_id) {
+ result = found->module;
+ }
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+#endif
+ return result;
+ }
+}
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static void __Pyx_ModuleStateLookup_wait_until_no_readers(void) {
+ while (__pyx_atomic_load(&__Pyx_ModuleStateLookup_read_counter) != 0);
+}
+#else
+#define __Pyx_ModuleStateLookup_wait_until_no_readers()
+#endif
+static int __Pyx_State_AddModuleInterpIdAsIndex(__Pyx_ModuleStateLookupData **old_data, PyObject* module, int64_t interpreter_id) {
+ Py_ssize_t to_allocate = (*old_data)->allocated;
+ while (to_allocate <= interpreter_id) {
+ if (to_allocate == 0) to_allocate = 1;
+ else to_allocate *= 2;
+ }
+ __Pyx_ModuleStateLookupData *new_data = *old_data;
+ if (to_allocate != (*old_data)->allocated) {
+ new_data = (__Pyx_ModuleStateLookupData *)realloc(
+ *old_data,
+ sizeof(__Pyx_ModuleStateLookupData)+(to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule));
+ if (!new_data) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ for (Py_ssize_t i = new_data->allocated; i < to_allocate; ++i) {
+ new_data->table[i].id = i;
+ new_data->table[i].module = NULL;
+ }
+ new_data->allocated = to_allocate;
+ }
+ new_data->table[interpreter_id].module = module;
+ if (new_data->count < interpreter_id+1) {
+ new_data->count = interpreter_id+1;
+ }
+ *old_data = new_data;
+ return 0;
+}
+static void __Pyx_State_ConvertFromInterpIdAsIndex(__Pyx_ModuleStateLookupData *data) {
+ __Pyx_InterpreterIdAndModule *read = data->table;
+ __Pyx_InterpreterIdAndModule *write = data->table;
+ __Pyx_InterpreterIdAndModule *end = read + data->count;
+ for (; readmodule) {
+ write->id = read->id;
+ write->module = read->module;
+ ++write;
+ }
+ }
+ data->count = write - data->table;
+ for (; writeid = 0;
+ write->module = NULL;
+ }
+ data->interpreter_id_as_index = 0;
+}
+static int __Pyx_State_AddModule(PyObject* module, CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return -1;
+ int result = 0;
+ __Pyx_ModuleStateLookup_Lock();
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData *old_data = (__Pyx_ModuleStateLookupData *)
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0);
+#else
+ __Pyx_ModuleStateLookupData *old_data = __Pyx_ModuleStateLookup_data;
+#endif
+ __Pyx_ModuleStateLookupData *new_data = old_data;
+ if (!new_data) {
+ new_data = (__Pyx_ModuleStateLookupData *)calloc(1, sizeof(__Pyx_ModuleStateLookupData));
+ if (!new_data) {
+ result = -1;
+ PyErr_NoMemory();
+ goto end;
+ }
+ new_data->allocated = 1;
+ new_data->interpreter_id_as_index = 1;
+ }
+ __Pyx_ModuleStateLookup_wait_until_no_readers();
+ if (new_data->interpreter_id_as_index) {
+ if (interpreter_id < __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) {
+ result = __Pyx_State_AddModuleInterpIdAsIndex(&new_data, module, interpreter_id);
+ goto end;
+ }
+ __Pyx_State_ConvertFromInterpIdAsIndex(new_data);
+ }
+ {
+ Py_ssize_t insert_at = 0;
+ {
+ __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ new_data->table, new_data->count, interpreter_id);
+ assert(lower_bound);
+ insert_at = lower_bound - new_data->table;
+ if (unlikely(insert_at < new_data->count && lower_bound->id == interpreter_id)) {
+ lower_bound->module = module;
+ goto end; // already in table, nothing more to do
+ }
+ }
+ if (new_data->count+1 >= new_data->allocated) {
+ Py_ssize_t to_allocate = (new_data->count+1)*2;
+ new_data =
+ (__Pyx_ModuleStateLookupData*)realloc(
+ new_data,
+ sizeof(__Pyx_ModuleStateLookupData) +
+ (to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule));
+ if (!new_data) {
+ result = -1;
+ new_data = old_data;
+ PyErr_NoMemory();
+ goto end;
+ }
+ new_data->allocated = to_allocate;
+ }
+ ++new_data->count;
+ int64_t last_id = interpreter_id;
+ PyObject *last_module = module;
+ for (Py_ssize_t i=insert_at; icount; ++i) {
+ int64_t current_id = new_data->table[i].id;
+ new_data->table[i].id = last_id;
+ last_id = current_id;
+ PyObject *current_module = new_data->table[i].module;
+ new_data->table[i].module = last_module;
+ last_module = current_module;
+ }
+ }
+ end:
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, new_data);
+#else
+ __Pyx_ModuleStateLookup_data = new_data;
+#endif
+ __Pyx_ModuleStateLookup_Unlock();
+ return result;
+}
+static int __Pyx_State_RemoveModule(CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return -1;
+ __Pyx_ModuleStateLookup_Lock();
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData *data = (__Pyx_ModuleStateLookupData *)
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0);
+#else
+ __Pyx_ModuleStateLookupData *data = __Pyx_ModuleStateLookup_data;
+#endif
+ if (data->interpreter_id_as_index) {
+ if (interpreter_id < data->count) {
+ data->table[interpreter_id].module = NULL;
+ }
+ goto done;
+ }
+ {
+ __Pyx_ModuleStateLookup_wait_until_no_readers();
+ __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ data->table, data->count, interpreter_id);
+ if (!lower_bound) goto done;
+ if (lower_bound->id != interpreter_id) goto done;
+ __Pyx_InterpreterIdAndModule *end = data->table+data->count;
+ for (;lower_boundid = (lower_bound+1)->id;
+ lower_bound->module = (lower_bound+1)->module;
+ }
+ }
+ --data->count;
+ if (data->count == 0) {
+ free(data);
+ data = NULL;
+ }
+ done:
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, data);
+#else
+ __Pyx_ModuleStateLookup_data = data;
+#endif
+ __Pyx_ModuleStateLookup_Unlock();
+ return 0;
+}
+#endif
+
+/* #### Code section: utility_code_pragmas_end ### */
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+
+
+/* #### Code section: end ### */
+#endif /* Py_PYTHON_H */
diff --git a/lib/python3.12/site-packages/fontTools/misc/bezierTools.py b/lib/python3.12/site-packages/fontTools/misc/bezierTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..efdbdaaeaf3bad275204dabda4cb8f024b31ed91
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/bezierTools.py
@@ -0,0 +1,1500 @@
+# -*- coding: utf-8 -*-
+"""fontTools.misc.bezierTools.py -- tools for working with Bezier path segments.
+"""
+
+from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea
+from fontTools.misc.transform import Identity
+import math
+from collections import namedtuple
+
+try:
+ import cython
+except (AttributeError, ImportError):
+ # if cython not installed, use mock module with no-op decorators and types
+ from fontTools.misc import cython
+COMPILED = cython.compiled
+
+
+EPSILON = 1e-9
+
+
+Intersection = namedtuple("Intersection", ["pt", "t1", "t2"])
+
+
+__all__ = [
+ "approximateCubicArcLength",
+ "approximateCubicArcLengthC",
+ "approximateQuadraticArcLength",
+ "approximateQuadraticArcLengthC",
+ "calcCubicArcLength",
+ "calcCubicArcLengthC",
+ "calcQuadraticArcLength",
+ "calcQuadraticArcLengthC",
+ "calcCubicBounds",
+ "calcQuadraticBounds",
+ "splitLine",
+ "splitQuadratic",
+ "splitCubic",
+ "splitQuadraticAtT",
+ "splitCubicAtT",
+ "splitCubicAtTC",
+ "splitCubicIntoTwoAtTC",
+ "solveQuadratic",
+ "solveCubic",
+ "quadraticPointAtT",
+ "cubicPointAtT",
+ "cubicPointAtTC",
+ "linePointAtT",
+ "segmentPointAtT",
+ "lineLineIntersections",
+ "curveLineIntersections",
+ "curveCurveIntersections",
+ "segmentSegmentIntersections",
+]
+
+
+def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005):
+ """Calculates the arc length for a cubic Bezier segment.
+
+ Whereas :func:`approximateCubicArcLength` approximates the length, this
+ function calculates it by "measuring", recursively dividing the curve
+ until the divided segments are shorter than ``tolerance``.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+ tolerance: Controls the precision of the calcuation.
+
+ Returns:
+ Arc length value.
+ """
+ return calcCubicArcLengthC(
+ complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance
+ )
+
+
+def _split_cubic_into_two(p0, p1, p2, p3):
+ mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ return (
+ (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
+ (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
+ )
+
+
+@cython.returns(cython.double)
+@cython.locals(
+ p0=cython.complex,
+ p1=cython.complex,
+ p2=cython.complex,
+ p3=cython.complex,
+)
+@cython.locals(mult=cython.double, arch=cython.double, box=cython.double)
+def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
+ arch = abs(p0 - p3)
+ box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
+ if arch * mult + EPSILON >= box:
+ return (arch + box) * 0.5
+ else:
+ one, two = _split_cubic_into_two(p0, p1, p2, p3)
+ return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(
+ mult, *two
+ )
+
+
+@cython.returns(cython.double)
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+)
+@cython.locals(
+ tolerance=cython.double,
+ mult=cython.double,
+)
+def calcCubicArcLengthC(pt1, pt2, pt3, pt4, tolerance=0.005):
+ """Calculates the arc length for a cubic Bezier segment.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
+ tolerance: Controls the precision of the calcuation.
+
+ Returns:
+ Arc length value.
+ """
+ mult = 1.0 + 1.5 * tolerance # The 1.5 is a empirical hack; no math
+ return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
+
+
+epsilonDigits = 6
+epsilon = 1e-10
+
+
+@cython.cfunc
+@cython.inline
+@cython.returns(cython.double)
+@cython.locals(v1=cython.complex, v2=cython.complex)
+def _dot(v1, v2):
+ return (v1 * v2.conjugate()).real
+
+
+@cython.cfunc
+@cython.inline
+@cython.returns(cython.double)
+@cython.locals(x=cython.double)
+def _intSecAtan(x):
+ # In : sympy.integrate(sp.sec(sp.atan(x)))
+ # Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
+ return x * math.sqrt(x**2 + 1) / 2 + math.asinh(x) / 2
+
+
+def calcQuadraticArcLength(pt1, pt2, pt3):
+ """Calculates the arc length for a quadratic Bezier segment.
+
+ Args:
+ pt1: Start point of the Bezier as 2D tuple.
+ pt2: Handle point of the Bezier as 2D tuple.
+ pt3: End point of the Bezier as 2D tuple.
+
+ Returns:
+ Arc length value.
+
+ Example::
+
+ >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment
+ 0.0
+ >>> calcQuadraticArcLength((0, 0), (50, 0), (80, 0)) # collinear points
+ 80.0
+ >>> calcQuadraticArcLength((0, 0), (0, 50), (0, 80)) # collinear points vertical
+ 80.0
+ >>> calcQuadraticArcLength((0, 0), (50, 20), (100, 40)) # collinear points
+ 107.70329614269008
+ >>> calcQuadraticArcLength((0, 0), (0, 100), (100, 0))
+ 154.02976155645263
+ >>> calcQuadraticArcLength((0, 0), (0, 50), (100, 0))
+ 120.21581243984076
+ >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))
+ 102.53273816445825
+ >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0)) # collinear points, control point outside
+ 66.66666666666667
+ >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0)) # collinear points, looping back
+ 40.0
+ """
+ return calcQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
+
+
+@cython.returns(cython.double)
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ d0=cython.complex,
+ d1=cython.complex,
+ d=cython.complex,
+ n=cython.complex,
+)
+@cython.locals(
+ scale=cython.double,
+ origDist=cython.double,
+ a=cython.double,
+ b=cython.double,
+ x0=cython.double,
+ x1=cython.double,
+ Len=cython.double,
+)
+def calcQuadraticArcLengthC(pt1, pt2, pt3):
+ """Calculates the arc length for a quadratic Bezier segment.
+
+ Args:
+ pt1: Start point of the Bezier as a complex number.
+ pt2: Handle point of the Bezier as a complex number.
+ pt3: End point of the Bezier as a complex number.
+
+ Returns:
+ Arc length value.
+ """
+ # Analytical solution to the length of a quadratic bezier.
+ # Documentation: https://github.com/fonttools/fonttools/issues/3055
+ d0 = pt2 - pt1
+ d1 = pt3 - pt2
+ d = d1 - d0
+ n = d * 1j
+ scale = abs(n)
+ if scale == 0.0:
+ return abs(pt3 - pt1)
+ origDist = _dot(n, d0)
+ if abs(origDist) < epsilon:
+ if _dot(d0, d1) >= 0:
+ return abs(pt3 - pt1)
+ a, b = abs(d0), abs(d1)
+ return (a * a + b * b) / (a + b)
+ x0 = _dot(d, d0) / origDist
+ x1 = _dot(d, d1) / origDist
+ Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
+ return Len
+
+
+def approximateQuadraticArcLength(pt1, pt2, pt3):
+ """Calculates the arc length for a quadratic Bezier segment.
+
+ Uses Gauss-Legendre quadrature for a branch-free approximation.
+ See :func:`calcQuadraticArcLength` for a slower but more accurate result.
+
+ Args:
+ pt1: Start point of the Bezier as 2D tuple.
+ pt2: Handle point of the Bezier as 2D tuple.
+ pt3: End point of the Bezier as 2D tuple.
+
+ Returns:
+ Approximate arc length value.
+ """
+ return approximateQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
+
+
+@cython.returns(cython.double)
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+)
+@cython.locals(
+ v0=cython.double,
+ v1=cython.double,
+ v2=cython.double,
+)
+def approximateQuadraticArcLengthC(pt1, pt2, pt3):
+ """Calculates the arc length for a quadratic Bezier segment.
+
+ Uses Gauss-Legendre quadrature for a branch-free approximation.
+ See :func:`calcQuadraticArcLength` for a slower but more accurate result.
+
+ Args:
+ pt1: Start point of the Bezier as a complex number.
+ pt2: Handle point of the Bezier as a complex number.
+ pt3: End point of the Bezier as a complex number.
+
+ Returns:
+ Approximate arc length value.
+ """
+ # This, essentially, approximates the length-of-derivative function
+ # to be integrated with the best-matching fifth-degree polynomial
+ # approximation of it.
+ #
+ # https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Legendre_quadrature
+
+ # abs(BezierCurveC[2].diff(t).subs({t:T})) for T in sorted(.5, .5±sqrt(3/5)/2),
+ # weighted 5/18, 8/18, 5/18 respectively.
+ v0 = abs(
+ -0.492943519233745 * pt1 + 0.430331482911935 * pt2 + 0.0626120363218102 * pt3
+ )
+ v1 = abs(pt3 - pt1) * 0.4444444444444444
+ v2 = abs(
+ -0.0626120363218102 * pt1 - 0.430331482911935 * pt2 + 0.492943519233745 * pt3
+ )
+
+ return v0 + v1 + v2
+
+
+def calcQuadraticBounds(pt1, pt2, pt3):
+ """Calculates the bounding rectangle for a quadratic Bezier segment.
+
+ Args:
+ pt1: Start point of the Bezier as a 2D tuple.
+ pt2: Handle point of the Bezier as a 2D tuple.
+ pt3: End point of the Bezier as a 2D tuple.
+
+ Returns:
+ A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+
+ Example::
+
+ >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
+ (0, 0, 100, 50.0)
+ >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
+ (0.0, 0.0, 100, 100)
+ """
+ (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
+ ax2 = ax * 2.0
+ ay2 = ay * 2.0
+ roots = []
+ if ax2 != 0:
+ roots.append(-bx / ax2)
+ if ay2 != 0:
+ roots.append(-by / ay2)
+ points = [
+ (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+ for t in roots
+ if 0 <= t < 1
+ ] + [pt1, pt3]
+ return calcBounds(points)
+
+
+def approximateCubicArcLength(pt1, pt2, pt3, pt4):
+ """Approximates the arc length for a cubic Bezier segment.
+
+ Uses Gauss-Lobatto quadrature with n=5 points to approximate arc length.
+ See :func:`calcCubicArcLength` for a slower but more accurate result.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+
+ Returns:
+ Arc length value.
+
+ Example::
+
+ >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))
+ 190.04332968932817
+ >>> approximateCubicArcLength((0, 0), (50, 0), (100, 50), (100, 100))
+ 154.8852074945903
+ >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (150, 0)) # line; exact result should be 150.
+ 149.99999999999991
+ >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (-50, 0)) # cusp; exact result should be 150.
+ 136.9267662156362
+ >>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp
+ 154.80848416537057
+ """
+ return approximateCubicArcLengthC(
+ complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4)
+ )
+
+
+@cython.returns(cython.double)
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+)
+@cython.locals(
+ v0=cython.double,
+ v1=cython.double,
+ v2=cython.double,
+ v3=cython.double,
+ v4=cython.double,
+)
+def approximateCubicArcLengthC(pt1, pt2, pt3, pt4):
+ """Approximates the arc length for a cubic Bezier segment.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
+
+ Returns:
+ Arc length value.
+ """
+ # This, essentially, approximates the length-of-derivative function
+ # to be integrated with the best-matching seventh-degree polynomial
+ # approximation of it.
+ #
+ # https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Lobatto_rules
+
+ # abs(BezierCurveC[3].diff(t).subs({t:T})) for T in sorted(0, .5±(3/7)**.5/2, .5, 1),
+ # weighted 1/20, 49/180, 32/90, 49/180, 1/20 respectively.
+ v0 = abs(pt2 - pt1) * 0.15
+ v1 = abs(
+ -0.558983582205757 * pt1
+ + 0.325650248872424 * pt2
+ + 0.208983582205757 * pt3
+ + 0.024349751127576 * pt4
+ )
+ v2 = abs(pt4 - pt1 + pt3 - pt2) * 0.26666666666666666
+ v3 = abs(
+ -0.024349751127576 * pt1
+ - 0.208983582205757 * pt2
+ - 0.325650248872424 * pt3
+ + 0.558983582205757 * pt4
+ )
+ v4 = abs(pt4 - pt3) * 0.15
+
+ return v0 + v1 + v2 + v3 + v4
+
+
+def calcCubicBounds(pt1, pt2, pt3, pt4):
+ """Calculates the bounding rectangle for a quadratic Bezier segment.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+
+ Returns:
+ A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+
+ Example::
+
+ >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
+ (0, 0, 100, 75.0)
+ >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
+ (0.0, 0.0, 100, 100)
+ >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
+ 35.566243 0.000000 64.433757 75.000000
+ """
+ (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
+ # calc first derivative
+ ax3 = ax * 3.0
+ ay3 = ay * 3.0
+ bx2 = bx * 2.0
+ by2 = by * 2.0
+ xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
+ yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
+ roots = xRoots + yRoots
+
+ points = [
+ (
+ ax * t * t * t + bx * t * t + cx * t + dx,
+ ay * t * t * t + by * t * t + cy * t + dy,
+ )
+ for t in roots
+ ] + [pt1, pt4]
+ return calcBounds(points)
+
+
+def splitLine(pt1, pt2, where, isHorizontal):
+ """Split a line at a given coordinate.
+
+ Args:
+ pt1: Start point of line as 2D tuple.
+ pt2: End point of line as 2D tuple.
+ where: Position at which to split the line.
+ isHorizontal: Direction of the ray splitting the line. If true,
+ ``where`` is interpreted as a Y coordinate; if false, then
+ ``where`` is interpreted as an X coordinate.
+
+ Returns:
+ A list of two line segments (each line segment being two 2D tuples)
+ if the line was successfully split, or a list containing the original
+ line.
+
+ Example::
+
+ >>> printSegments(splitLine((0, 0), (100, 100), 50, True))
+ ((0, 0), (50, 50))
+ ((50, 50), (100, 100))
+ >>> printSegments(splitLine((0, 0), (100, 100), 100, True))
+ ((0, 0), (100, 100))
+ >>> printSegments(splitLine((0, 0), (100, 100), 0, True))
+ ((0, 0), (0, 0))
+ ((0, 0), (100, 100))
+ >>> printSegments(splitLine((0, 0), (100, 100), 0, False))
+ ((0, 0), (0, 0))
+ ((0, 0), (100, 100))
+ >>> printSegments(splitLine((100, 0), (0, 0), 50, False))
+ ((100, 0), (50, 0))
+ ((50, 0), (0, 0))
+ >>> printSegments(splitLine((0, 100), (0, 0), 50, True))
+ ((0, 100), (0, 50))
+ ((0, 50), (0, 0))
+ """
+ pt1x, pt1y = pt1
+ pt2x, pt2y = pt2
+
+ ax = pt2x - pt1x
+ ay = pt2y - pt1y
+
+ bx = pt1x
+ by = pt1y
+
+ a = (ax, ay)[isHorizontal]
+
+ if a == 0:
+ return [(pt1, pt2)]
+ t = (where - (bx, by)[isHorizontal]) / a
+ if 0 <= t < 1:
+ midPt = ax * t + bx, ay * t + by
+ return [(pt1, midPt), (midPt, pt2)]
+ else:
+ return [(pt1, pt2)]
+
+
+def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
+ """Split a quadratic Bezier curve at a given coordinate.
+
+ Args:
+ pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
+ where: Position at which to split the curve.
+ isHorizontal: Direction of the ray splitting the curve. If true,
+ ``where`` is interpreted as a Y coordinate; if false, then
+ ``where`` is interpreted as an X coordinate.
+
+ Returns:
+ A list of two curve segments (each curve segment being three 2D tuples)
+ if the curve was successfully split, or a list containing the original
+ curve.
+
+ Example::
+
+ >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
+ ((0, 0), (50, 100), (100, 0))
+ >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))
+ ((0, 0), (25, 50), (50, 50))
+ ((50, 50), (75, 50), (100, 0))
+ >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))
+ ((0, 0), (12.5, 25), (25, 37.5))
+ ((25, 37.5), (62.5, 75), (100, 0))
+ >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))
+ ((0, 0), (7.32233, 14.6447), (14.6447, 25))
+ ((14.6447, 25), (50, 75), (85.3553, 25))
+ ((85.3553, 25), (92.6777, 14.6447), (100, -7.10543e-15))
+ >>> # XXX I'm not at all sure if the following behavior is desirable:
+ >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))
+ ((0, 0), (25, 50), (50, 50))
+ ((50, 50), (50, 50), (50, 50))
+ ((50, 50), (75, 50), (100, 0))
+ """
+ a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
+ solutions = solveQuadratic(
+ a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+ )
+ solutions = sorted(t for t in solutions if 0 <= t < 1)
+ if not solutions:
+ return [(pt1, pt2, pt3)]
+ return _splitQuadraticAtT(a, b, c, *solutions)
+
+
+def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
+ """Split a cubic Bezier curve at a given coordinate.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+ where: Position at which to split the curve.
+ isHorizontal: Direction of the ray splitting the curve. If true,
+ ``where`` is interpreted as a Y coordinate; if false, then
+ ``where`` is interpreted as an X coordinate.
+
+ Returns:
+ A list of two curve segments (each curve segment being four 2D tuples)
+ if the curve was successfully split, or a list containing the original
+ curve.
+
+ Example::
+
+ >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
+ ((0, 0), (25, 100), (75, 100), (100, 0))
+ >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))
+ ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+ ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
+ >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))
+ ((0, 0), (2.29379, 9.17517), (4.79804, 17.5085), (7.47414, 25))
+ ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667), (92.5259, 25))
+ ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))
+ """
+ a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
+ solutions = solveCubic(
+ a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+ )
+ solutions = sorted(t for t in solutions if 0 <= t < 1)
+ if not solutions:
+ return [(pt1, pt2, pt3, pt4)]
+ return _splitCubicAtT(a, b, c, d, *solutions)
+
+
+def splitQuadraticAtT(pt1, pt2, pt3, *ts):
+ """Split a quadratic Bezier curve at one or more values of t.
+
+ Args:
+ pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
+ *ts: Positions at which to split the curve.
+
+ Returns:
+ A list of curve segments (each curve segment being three 2D tuples).
+
+ Examples::
+
+ >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
+ ((0, 0), (25, 50), (50, 50))
+ ((50, 50), (75, 50), (100, 0))
+ >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))
+ ((0, 0), (25, 50), (50, 50))
+ ((50, 50), (62.5, 50), (75, 37.5))
+ ((75, 37.5), (87.5, 25), (100, 0))
+ """
+ a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
+ return _splitQuadraticAtT(a, b, c, *ts)
+
+
+def splitCubicAtT(pt1, pt2, pt3, pt4, *ts):
+ """Split a cubic Bezier curve at one or more values of t.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+ *ts: Positions at which to split the curve.
+
+ Returns:
+ A list of curve segments (each curve segment being four 2D tuples).
+
+ Examples::
+
+ >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
+ ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+ ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
+ >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))
+ ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+ ((50, 75), (59.375, 75), (68.75, 68.75), (77.3438, 56.25))
+ ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))
+ """
+ a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
+ split = _splitCubicAtT(a, b, c, d, *ts)
+
+ # the split impl can introduce floating point errors; we know the first
+ # segment should always start at pt1 and the last segment should end at pt4,
+ # so we set those values directly before returning.
+ split[0] = (pt1, *split[0][1:])
+ split[-1] = (*split[-1][:-1], pt4)
+ return split
+
+
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+ a=cython.complex,
+ b=cython.complex,
+ c=cython.complex,
+ d=cython.complex,
+)
+def splitCubicAtTC(pt1, pt2, pt3, pt4, *ts):
+ """Split a cubic Bezier curve at one or more values of t.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers..
+ *ts: Positions at which to split the curve.
+
+ Yields:
+ Curve segments (each curve segment being four complex numbers).
+ """
+ a, b, c, d = calcCubicParametersC(pt1, pt2, pt3, pt4)
+ yield from _splitCubicAtTC(a, b, c, d, *ts)
+
+
+@cython.returns(cython.complex)
+@cython.locals(
+ t=cython.double,
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+ pointAtT=cython.complex,
+ off1=cython.complex,
+ off2=cython.complex,
+)
+@cython.locals(
+ t2=cython.double, _1_t=cython.double, _1_t_2=cython.double, _2_t_1_t=cython.double
+)
+def splitCubicIntoTwoAtTC(pt1, pt2, pt3, pt4, t):
+ """Split a cubic Bezier curve at t.
+
+ Args:
+ pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
+ t: Position at which to split the curve.
+
+ Returns:
+ A tuple of two curve segments (each curve segment being four complex numbers).
+ """
+ t2 = t * t
+ _1_t = 1 - t
+ _1_t_2 = _1_t * _1_t
+ _2_t_1_t = 2 * t * _1_t
+ pointAtT = (
+ _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+ )
+ off1 = _1_t_2 * pt1 + _2_t_1_t * pt2 + t2 * pt3
+ off2 = _1_t_2 * pt2 + _2_t_1_t * pt3 + t2 * pt4
+
+ pt2 = pt1 + (pt2 - pt1) * t
+ pt3 = pt4 + (pt3 - pt4) * _1_t
+
+ return ((pt1, pt2, off1, pointAtT), (pointAtT, off2, pt3, pt4))
+
+
+def _splitQuadraticAtT(a, b, c, *ts):
+ ts = list(ts)
+ segments = []
+ ts.insert(0, 0.0)
+ ts.append(1.0)
+ ax, ay = a
+ bx, by = b
+ cx, cy = c
+ for i in range(len(ts) - 1):
+ t1 = ts[i]
+ t2 = ts[i + 1]
+ delta = t2 - t1
+ # calc new a, b and c
+ delta_2 = delta * delta
+ a1x = ax * delta_2
+ a1y = ay * delta_2
+ b1x = (2 * ax * t1 + bx) * delta
+ b1y = (2 * ay * t1 + by) * delta
+ t1_2 = t1 * t1
+ c1x = ax * t1_2 + bx * t1 + cx
+ c1y = ay * t1_2 + by * t1 + cy
+
+ pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
+ segments.append((pt1, pt2, pt3))
+ return segments
+
+
+def _splitCubicAtT(a, b, c, d, *ts):
+ ts = list(ts)
+ ts.insert(0, 0.0)
+ ts.append(1.0)
+ segments = []
+ ax, ay = a
+ bx, by = b
+ cx, cy = c
+ dx, dy = d
+ for i in range(len(ts) - 1):
+ t1 = ts[i]
+ t2 = ts[i + 1]
+ delta = t2 - t1
+
+ delta_2 = delta * delta
+ delta_3 = delta * delta_2
+ t1_2 = t1 * t1
+ t1_3 = t1 * t1_2
+
+ # calc new a, b, c and d
+ a1x = ax * delta_3
+ a1y = ay * delta_3
+ b1x = (3 * ax * t1 + bx) * delta_2
+ b1y = (3 * ay * t1 + by) * delta_2
+ c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
+ c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
+ d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
+ d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
+ pt1, pt2, pt3, pt4 = calcCubicPoints(
+ (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
+ )
+ segments.append((pt1, pt2, pt3, pt4))
+ return segments
+
+
+@cython.locals(
+ a=cython.complex,
+ b=cython.complex,
+ c=cython.complex,
+ d=cython.complex,
+ t1=cython.double,
+ t2=cython.double,
+ delta=cython.double,
+ delta_2=cython.double,
+ delta_3=cython.double,
+ a1=cython.complex,
+ b1=cython.complex,
+ c1=cython.complex,
+ d1=cython.complex,
+)
+def _splitCubicAtTC(a, b, c, d, *ts):
+ ts = list(ts)
+ ts.insert(0, 0.0)
+ ts.append(1.0)
+ for i in range(len(ts) - 1):
+ t1 = ts[i]
+ t2 = ts[i + 1]
+ delta = t2 - t1
+
+ delta_2 = delta * delta
+ delta_3 = delta * delta_2
+ t1_2 = t1 * t1
+ t1_3 = t1 * t1_2
+
+ # calc new a, b, c and d
+ a1 = a * delta_3
+ b1 = (3 * a * t1 + b) * delta_2
+ c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
+ d1 = a * t1_3 + b * t1_2 + c * t1 + d
+ pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1)
+ yield (pt1, pt2, pt3, pt4)
+
+
+#
+# Equation solvers.
+#
+
+from math import sqrt, acos, cos, pi
+
+
+def solveQuadratic(a, b, c, sqrt=sqrt):
+ """Solve a quadratic equation.
+
+ Solves *a*x*x + b*x + c = 0* where a, b and c are real.
+
+ Args:
+ a: coefficient of *x²*
+ b: coefficient of *x*
+ c: constant term
+
+ Returns:
+ A list of roots. Note that the returned list is neither guaranteed to
+ be sorted nor to contain unique values!
+ """
+ if abs(a) < epsilon:
+ if abs(b) < epsilon:
+ # We have a non-equation; therefore, we have no valid solution
+ roots = []
+ else:
+ # We have a linear equation with 1 root.
+ roots = [-c / b]
+ else:
+ # We have a true quadratic equation. Apply the quadratic formula to find two roots.
+ DD = b * b - 4.0 * a * c
+ if DD >= 0.0:
+ rDD = sqrt(DD)
+ roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
+ else:
+ # complex roots, ignore
+ roots = []
+ return roots
+
+
+def solveCubic(a, b, c, d):
+ """Solve a cubic equation.
+
+ Solves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.
+
+ Args:
+ a: coefficient of *x³*
+ b: coefficient of *x²*
+ c: coefficient of *x*
+ d: constant term
+
+ Returns:
+ A list of roots. Note that the returned list is neither guaranteed to
+ be sorted nor to contain unique values!
+
+ Examples::
+
+ >>> solveCubic(1, 1, -6, 0)
+ [-3.0, -0.0, 2.0]
+ >>> solveCubic(-10.0, -9.0, 48.0, -29.0)
+ [-2.9, 1.0, 1.0]
+ >>> solveCubic(-9.875, -9.0, 47.625, -28.75)
+ [-2.911392, 1.0, 1.0]
+ >>> solveCubic(1.0, -4.5, 6.75, -3.375)
+ [1.5, 1.5, 1.5]
+ >>> solveCubic(-12.0, 18.0, -9.0, 1.50023651123)
+ [0.5, 0.5, 0.5]
+ >>> solveCubic(
+ ... 9.0, 0.0, 0.0, -7.62939453125e-05
+ ... ) == [-0.0, -0.0, -0.0]
+ True
+ """
+ #
+ # adapted from:
+ # CUBIC.C - Solve a cubic polynomial
+ # public domain by Ross Cottrell
+ # found at: http://www.strangecreations.com/library/snippets/Cubic.C
+ #
+ if abs(a) < epsilon:
+ # don't just test for zero; for very small values of 'a' solveCubic()
+ # returns unreliable results, so we fall back to quad.
+ return solveQuadratic(b, c, d)
+ a = float(a)
+ a1 = b / a
+ a2 = c / a
+ a3 = d / a
+
+ Q = (a1 * a1 - 3.0 * a2) / 9.0
+ R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0
+
+ R2 = R * R
+ Q3 = Q * Q * Q
+ R2 = 0 if R2 < epsilon else R2
+ Q3 = 0 if abs(Q3) < epsilon else Q3
+
+ R2_Q3 = R2 - Q3
+
+ if R2 == 0.0 and Q3 == 0.0:
+ x = round(-a1 / 3.0, epsilonDigits)
+ return [x, x, x]
+ elif R2_Q3 <= epsilon * 0.5:
+ # The epsilon * .5 above ensures that Q3 is not zero.
+ theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+ rQ2 = -2.0 * sqrt(Q)
+ a1_3 = a1 / 3.0
+ x0 = rQ2 * cos(theta / 3.0) - a1_3
+ x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
+ x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3
+ x0, x1, x2 = sorted([x0, x1, x2])
+ # Merge roots that are close-enough
+ if x1 - x0 < epsilon and x2 - x1 < epsilon:
+ x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
+ elif x1 - x0 < epsilon:
+ x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
+ x2 = round(x2, epsilonDigits)
+ elif x2 - x1 < epsilon:
+ x0 = round(x0, epsilonDigits)
+ x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
+ else:
+ x0 = round(x0, epsilonDigits)
+ x1 = round(x1, epsilonDigits)
+ x2 = round(x2, epsilonDigits)
+ return [x0, x1, x2]
+ else:
+ x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
+ x = x + Q / x
+ if R >= 0.0:
+ x = -x
+ x = round(x - a1 / 3.0, epsilonDigits)
+ return [x]
+
+
+#
+# Conversion routines for points to parameters and vice versa
+#
+
+
+def calcQuadraticParameters(pt1, pt2, pt3):
+ x2, y2 = pt2
+ x3, y3 = pt3
+ cx, cy = pt1
+ bx = (x2 - cx) * 2.0
+ by = (y2 - cy) * 2.0
+ ax = x3 - cx - bx
+ ay = y3 - cy - by
+ return (ax, ay), (bx, by), (cx, cy)
+
+
+def calcCubicParameters(pt1, pt2, pt3, pt4):
+ x2, y2 = pt2
+ x3, y3 = pt3
+ x4, y4 = pt4
+ dx, dy = pt1
+ cx = (x2 - dx) * 3.0
+ cy = (y2 - dy) * 3.0
+ bx = (x3 - x2) * 3.0 - cx
+ by = (y3 - y2) * 3.0 - cy
+ ax = x4 - dx - cx - bx
+ ay = y4 - dy - cy - by
+ return (ax, ay), (bx, by), (cx, cy), (dx, dy)
+
+
+@cython.cfunc
+@cython.inline
+@cython.locals(
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+ a=cython.complex,
+ b=cython.complex,
+ c=cython.complex,
+)
+def calcCubicParametersC(pt1, pt2, pt3, pt4):
+ c = (pt2 - pt1) * 3.0
+ b = (pt3 - pt2) * 3.0 - c
+ a = pt4 - pt1 - c - b
+ return (a, b, c, pt1)
+
+
+def calcQuadraticPoints(a, b, c):
+ ax, ay = a
+ bx, by = b
+ cx, cy = c
+ x1 = cx
+ y1 = cy
+ x2 = (bx * 0.5) + cx
+ y2 = (by * 0.5) + cy
+ x3 = ax + bx + cx
+ y3 = ay + by + cy
+ return (x1, y1), (x2, y2), (x3, y3)
+
+
+def calcCubicPoints(a, b, c, d):
+ ax, ay = a
+ bx, by = b
+ cx, cy = c
+ dx, dy = d
+ x1 = dx
+ y1 = dy
+ x2 = (cx / 3.0) + dx
+ y2 = (cy / 3.0) + dy
+ x3 = (bx + cx) / 3.0 + x2
+ y3 = (by + cy) / 3.0 + y2
+ x4 = ax + dx + cx + bx
+ y4 = ay + dy + cy + by
+ return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
+
+
+@cython.cfunc
+@cython.inline
+@cython.locals(
+ a=cython.complex,
+ b=cython.complex,
+ c=cython.complex,
+ d=cython.complex,
+ p2=cython.complex,
+ p3=cython.complex,
+ p4=cython.complex,
+)
+def calcCubicPointsC(a, b, c, d):
+ p2 = c * (1 / 3) + d
+ p3 = (b + c) * (1 / 3) + p2
+ p4 = a + b + c + d
+ return (d, p2, p3, p4)
+
+
+#
+# Point at time
+#
+
+
+def linePointAtT(pt1, pt2, t):
+ """Finds the point at time `t` on a line.
+
+ Args:
+ pt1, pt2: Coordinates of the line as 2D tuples.
+ t: The time along the line.
+
+ Returns:
+ A 2D tuple with the coordinates of the point.
+ """
+ return ((pt1[0] * (1 - t) + pt2[0] * t), (pt1[1] * (1 - t) + pt2[1] * t))
+
+
+def quadraticPointAtT(pt1, pt2, pt3, t):
+ """Finds the point at time `t` on a quadratic curve.
+
+ Args:
+ pt1, pt2, pt3: Coordinates of the curve as 2D tuples.
+ t: The time along the curve.
+
+ Returns:
+ A 2D tuple with the coordinates of the point.
+ """
+ x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0]
+ y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1]
+ return (x, y)
+
+
+def cubicPointAtT(pt1, pt2, pt3, pt4, t):
+ """Finds the point at time `t` on a cubic curve.
+
+ Args:
+ pt1, pt2, pt3, pt4: Coordinates of the curve as 2D tuples.
+ t: The time along the curve.
+
+ Returns:
+ A 2D tuple with the coordinates of the point.
+ """
+ t2 = t * t
+ _1_t = 1 - t
+ _1_t_2 = _1_t * _1_t
+ x = (
+ _1_t_2 * _1_t * pt1[0]
+ + 3 * (_1_t_2 * t * pt2[0] + _1_t * t2 * pt3[0])
+ + t2 * t * pt4[0]
+ )
+ y = (
+ _1_t_2 * _1_t * pt1[1]
+ + 3 * (_1_t_2 * t * pt2[1] + _1_t * t2 * pt3[1])
+ + t2 * t * pt4[1]
+ )
+ return (x, y)
+
+
+@cython.returns(cython.complex)
+@cython.locals(
+ t=cython.double,
+ pt1=cython.complex,
+ pt2=cython.complex,
+ pt3=cython.complex,
+ pt4=cython.complex,
+)
+@cython.locals(t2=cython.double, _1_t=cython.double, _1_t_2=cython.double)
+def cubicPointAtTC(pt1, pt2, pt3, pt4, t):
+ """Finds the point at time `t` on a cubic curve.
+
+ Args:
+ pt1, pt2, pt3, pt4: Coordinates of the curve as complex numbers.
+ t: The time along the curve.
+
+ Returns:
+ A complex number with the coordinates of the point.
+ """
+ t2 = t * t
+ _1_t = 1 - t
+ _1_t_2 = _1_t * _1_t
+ return _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
+
+
+def segmentPointAtT(seg, t):
+ if len(seg) == 2:
+ return linePointAtT(*seg, t)
+ elif len(seg) == 3:
+ return quadraticPointAtT(*seg, t)
+ elif len(seg) == 4:
+ return cubicPointAtT(*seg, t)
+ raise ValueError("Unknown curve degree")
+
+
+#
+# Intersection finders
+#
+
+
+def _line_t_of_pt(s, e, pt):
+ sx, sy = s
+ ex, ey = e
+ px, py = pt
+ if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon:
+ # Line is a point!
+ return -1
+ # Use the largest
+ if abs(sx - ex) > abs(sy - ey):
+ return (px - sx) / (ex - sx)
+ else:
+ return (py - sy) / (ey - sy)
+
+
+def _both_points_are_on_same_side_of_origin(a, b, origin):
+ xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+ yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+ return not (xDiff <= 0.0 and yDiff <= 0.0)
+
+
+def lineLineIntersections(s1, e1, s2, e2):
+ """Finds intersections between two line segments.
+
+ Args:
+ s1, e1: Coordinates of the first line as 2D tuples.
+ s2, e2: Coordinates of the second line as 2D tuples.
+
+ Returns:
+ A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+ and ``t2`` attributes containing the intersection point, time on first
+ segment and time on second segment respectively.
+
+ Examples::
+
+ >>> a = lineLineIntersections( (310,389), (453, 222), (289, 251), (447, 367))
+ >>> len(a)
+ 1
+ >>> intersection = a[0]
+ >>> intersection.pt
+ (374.44882952482897, 313.73458370177315)
+ >>> (intersection.t1, intersection.t2)
+ (0.45069111555824465, 0.5408153767394238)
+ """
+ s1x, s1y = s1
+ e1x, e1y = e1
+ s2x, s2y = s2
+ e2x, e2y = e2
+ if (
+ math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+ ): # Parallel vertical
+ return []
+ if (
+ math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+ ): # Parallel horizontal
+ return []
+ if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
+ return []
+ if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
+ return []
+ if math.isclose(e1x, s1x):
+ x = s1x
+ slope34 = (e2y - s2y) / (e2x - s2x)
+ y = slope34 * (x - s2x) + s2y
+ pt = (x, y)
+ return [
+ Intersection(
+ pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ )
+ ]
+ if math.isclose(s2x, e2x):
+ x = s2x
+ slope12 = (e1y - s1y) / (e1x - s1x)
+ y = slope12 * (x - s1x) + s1y
+ pt = (x, y)
+ return [
+ Intersection(
+ pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ )
+ ]
+
+ slope12 = (e1y - s1y) / (e1x - s1x)
+ slope34 = (e2y - s2y) / (e2x - s2x)
+ if math.isclose(slope12, slope34):
+ return []
+ x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+ y = slope12 * (x - s1x) + s1y
+ pt = (x, y)
+ if _both_points_are_on_same_side_of_origin(
+ pt, e1, s1
+ ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+ return [
+ Intersection(
+ pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+ )
+ ]
+ return []
+
+
+def _alignment_transformation(segment):
+ # Returns a transformation which aligns a segment horizontally at the
+ # origin. Apply this transformation to curves and root-find to find
+ # intersections with the segment.
+ start = segment[0]
+ end = segment[-1]
+ angle = math.atan2(end[1] - start[1], end[0] - start[0])
+ return Identity.rotate(-angle).translate(-start[0], -start[1])
+
+
+def _curve_line_intersections_t(curve, line):
+ aligned_curve = _alignment_transformation(line).transformPoints(curve)
+ if len(curve) == 3:
+ a, b, c = calcQuadraticParameters(*aligned_curve)
+ intersections = solveQuadratic(a[1], b[1], c[1])
+ elif len(curve) == 4:
+ a, b, c, d = calcCubicParameters(*aligned_curve)
+ intersections = solveCubic(a[1], b[1], c[1], d[1])
+ else:
+ raise ValueError("Unknown curve degree")
+ return sorted(i for i in intersections if 0.0 <= i <= 1)
+
+
+def curveLineIntersections(curve, line):
+ """Finds intersections between a curve and a line.
+
+ Args:
+ curve: List of coordinates of the curve segment as 2D tuples.
+ line: List of coordinates of the line segment as 2D tuples.
+
+ Returns:
+ A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+ and ``t2`` attributes containing the intersection point, time on first
+ segment and time on second segment respectively.
+
+ Examples::
+ >>> curve = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
+ >>> line = [ (25, 260), (230, 20) ]
+ >>> intersections = curveLineIntersections(curve, line)
+ >>> len(intersections)
+ 3
+ >>> intersections[0].pt
+ (84.9000930760723, 189.87306176459828)
+ """
+ if len(curve) == 3:
+ pointFinder = quadraticPointAtT
+ elif len(curve) == 4:
+ pointFinder = cubicPointAtT
+ else:
+ raise ValueError("Unknown curve degree")
+ intersections = []
+ for t in _curve_line_intersections_t(curve, line):
+ pt = pointFinder(*curve, t)
+ # Back-project the point onto the line, to avoid problems with
+ # numerical accuracy in the case of vertical and horizontal lines
+ line_t = _line_t_of_pt(*line, pt)
+ pt = linePointAtT(*line, line_t)
+ intersections.append(Intersection(pt=pt, t1=t, t2=line_t))
+ return intersections
+
+
+def _curve_bounds(c):
+ if len(c) == 3:
+ return calcQuadraticBounds(*c)
+ elif len(c) == 4:
+ return calcCubicBounds(*c)
+ raise ValueError("Unknown curve degree")
+
+
+def _split_segment_at_t(c, t):
+ if len(c) == 2:
+ s, e = c
+ midpoint = linePointAtT(s, e, t)
+ return [(s, midpoint), (midpoint, e)]
+ if len(c) == 3:
+ return splitQuadraticAtT(*c, t)
+ elif len(c) == 4:
+ return splitCubicAtT(*c, t)
+ raise ValueError("Unknown curve degree")
+
+
+def _curve_curve_intersections_t(
+ curve1, curve2, precision=1e-3, range1=None, range2=None
+):
+ bounds1 = _curve_bounds(curve1)
+ bounds2 = _curve_bounds(curve2)
+
+ if not range1:
+ range1 = (0.0, 1.0)
+ if not range2:
+ range2 = (0.0, 1.0)
+
+ # If bounds don't intersect, go home
+ intersects, _ = sectRect(bounds1, bounds2)
+ if not intersects:
+ return []
+
+ def midpoint(r):
+ return 0.5 * (r[0] + r[1])
+
+ # If they do overlap but they're tiny, approximate
+ if rectArea(bounds1) < precision and rectArea(bounds2) < precision:
+ return [(midpoint(range1), midpoint(range2))]
+
+ c11, c12 = _split_segment_at_t(curve1, 0.5)
+ c11_range = (range1[0], midpoint(range1))
+ c12_range = (midpoint(range1), range1[1])
+
+ c21, c22 = _split_segment_at_t(curve2, 0.5)
+ c21_range = (range2[0], midpoint(range2))
+ c22_range = (midpoint(range2), range2[1])
+
+ found = []
+ found.extend(
+ _curve_curve_intersections_t(
+ c11, c21, precision, range1=c11_range, range2=c21_range
+ )
+ )
+ found.extend(
+ _curve_curve_intersections_t(
+ c12, c21, precision, range1=c12_range, range2=c21_range
+ )
+ )
+ found.extend(
+ _curve_curve_intersections_t(
+ c11, c22, precision, range1=c11_range, range2=c22_range
+ )
+ )
+ found.extend(
+ _curve_curve_intersections_t(
+ c12, c22, precision, range1=c12_range, range2=c22_range
+ )
+ )
+
+ unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision))
+ seen = set()
+ unique_values = []
+
+ for ts in found:
+ key = unique_key(ts)
+ if key in seen:
+ continue
+ seen.add(key)
+ unique_values.append(ts)
+
+ return unique_values
+
+
+def _is_linelike(segment):
+ maybeline = _alignment_transformation(segment).transformPoints(segment)
+ return all(math.isclose(p[1], 0.0) for p in maybeline)
+
+
+def curveCurveIntersections(curve1, curve2):
+ """Finds intersections between a curve and a curve.
+
+ Args:
+ curve1: List of coordinates of the first curve segment as 2D tuples.
+ curve2: List of coordinates of the second curve segment as 2D tuples.
+
+ Returns:
+ A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+ and ``t2`` attributes containing the intersection point, time on first
+ segment and time on second segment respectively.
+
+ Examples::
+ >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
+ >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
+ >>> intersections = curveCurveIntersections(curve1, curve2)
+ >>> len(intersections)
+ 3
+ >>> intersections[0].pt
+ (81.7831487395506, 109.88904552375288)
+ """
+ if _is_linelike(curve1):
+ line1 = curve1[0], curve1[-1]
+ if _is_linelike(curve2):
+ line2 = curve2[0], curve2[-1]
+ return lineLineIntersections(*line1, *line2)
+ else:
+ hits = curveLineIntersections(curve2, line1)
+ # curve is passed first to this fn but is the second segment, so
+ # we need to swap t1/t2 in the result
+ return [Intersection(pt=x.pt, t1=x.t2, t2=x.t1) for x in hits]
+ elif _is_linelike(curve2):
+ line2 = curve2[0], curve2[-1]
+ return curveLineIntersections(curve1, line2)
+
+ intersection_ts = _curve_curve_intersections_t(curve1, curve2)
+ return [
+ Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+ for ts in intersection_ts
+ ]
+
+
+def segmentSegmentIntersections(seg1, seg2):
+ """Finds intersections between two segments.
+
+ Args:
+ seg1: List of coordinates of the first segment as 2D tuples.
+ seg2: List of coordinates of the second segment as 2D tuples.
+
+ Returns:
+ A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+ and ``t2`` attributes containing the intersection point, time on first
+ segment and time on second segment respectively.
+
+ Examples::
+ >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
+ >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
+ >>> intersections = segmentSegmentIntersections(curve1, curve2)
+ >>> len(intersections)
+ 3
+ >>> intersections[0].pt
+ (81.7831487395506, 109.88904552375288)
+ >>> curve3 = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
+ >>> line = [ (25, 260), (230, 20) ]
+ >>> intersections = segmentSegmentIntersections(curve3, line)
+ >>> len(intersections)
+ 3
+ >>> intersections[0].pt
+ (84.9000930760723, 189.87306176459828)
+
+ """
+ # Arrange by degree
+ swapped = False
+ if len(seg2) > len(seg1):
+ seg2, seg1 = seg1, seg2
+ swapped = True
+ if len(seg1) > 2:
+ if len(seg2) > 2:
+ intersections = curveCurveIntersections(seg1, seg2)
+ else:
+ intersections = curveLineIntersections(seg1, seg2)
+ elif len(seg1) == 2 and len(seg2) == 2:
+ intersections = lineLineIntersections(*seg1, *seg2)
+ else:
+ raise ValueError("Couldn't work out which intersection function to use")
+ if not swapped:
+ return intersections
+ return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
+
+
+def _segmentrepr(obj):
+ """
+ >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+ '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
+ """
+ try:
+ it = iter(obj)
+ except TypeError:
+ return "%g" % obj
+ else:
+ return "(%s)" % ", ".join(_segmentrepr(x) for x in it)
+
+
+def printSegments(segments):
+ """Helper for the doctests, displaying each segment in a list of
+ segments on a single line as a tuple.
+ """
+ for segment in segments:
+ print(_segmentrepr(segment))
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/classifyTools.py b/lib/python3.12/site-packages/fontTools/misc/classifyTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..aed7ca68c4e75fb9c120a9bc479093039e823b30
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/classifyTools.py
@@ -0,0 +1,170 @@
+""" fontTools.misc.classifyTools.py -- tools for classifying things.
+"""
+
+
+class Classifier(object):
+ """
+ Main Classifier object, used to classify things into similar sets.
+ """
+
+ def __init__(self, sort=True):
+ self._things = set() # set of all things known so far
+ self._sets = [] # list of class sets produced so far
+ self._mapping = {} # map from things to their class set
+ self._dirty = False
+ self._sort = sort
+
+ def add(self, set_of_things):
+ """
+ Add a set to the classifier. Any iterable is accepted.
+ """
+ if not set_of_things:
+ return
+
+ self._dirty = True
+
+ things, sets, mapping = self._things, self._sets, self._mapping
+
+ s = set(set_of_things)
+ intersection = s.intersection(things) # existing things
+ s.difference_update(intersection) # new things
+ difference = s
+ del s
+
+ # Add new class for new things
+ if difference:
+ things.update(difference)
+ sets.append(difference)
+ for thing in difference:
+ mapping[thing] = difference
+ del difference
+
+ while intersection:
+ # Take one item and process the old class it belongs to
+ old_class = mapping[next(iter(intersection))]
+ old_class_intersection = old_class.intersection(intersection)
+
+ # Update old class to remove items from new set
+ old_class.difference_update(old_class_intersection)
+
+ # Remove processed items from todo list
+ intersection.difference_update(old_class_intersection)
+
+ # Add new class for the intersection with old class
+ sets.append(old_class_intersection)
+ for thing in old_class_intersection:
+ mapping[thing] = old_class_intersection
+ del old_class_intersection
+
+ def update(self, list_of_sets):
+ """
+ Add a a list of sets to the classifier. Any iterable of iterables is accepted.
+ """
+ for s in list_of_sets:
+ self.add(s)
+
+ def _process(self):
+ if not self._dirty:
+ return
+
+ # Do any deferred processing
+ sets = self._sets
+ self._sets = [s for s in sets if s]
+
+ if self._sort:
+ self._sets = sorted(self._sets, key=lambda s: (-len(s), sorted(s)))
+
+ self._dirty = False
+
+ # Output methods
+
+ def getThings(self):
+ """Returns the set of all things known so far.
+
+ The return value belongs to the Classifier object and should NOT
+ be modified while the classifier is still in use.
+ """
+ self._process()
+ return self._things
+
+ def getMapping(self):
+ """Returns the mapping from things to their class set.
+
+ The return value belongs to the Classifier object and should NOT
+ be modified while the classifier is still in use.
+ """
+ self._process()
+ return self._mapping
+
+ def getClasses(self):
+ """Returns the list of class sets.
+
+ The return value belongs to the Classifier object and should NOT
+ be modified while the classifier is still in use.
+ """
+ self._process()
+ return self._sets
+
+
+def classify(list_of_sets, sort=True):
+ """
+ Takes a iterable of iterables (list of sets from here on; but any
+ iterable works.), and returns the smallest list of sets such that
+ each set, is either a subset, or is disjoint from, each of the input
+ sets.
+
+ In other words, this function classifies all the things present in
+ any of the input sets, into similar classes, based on which sets
+ things are a member of.
+
+ If sort=True, return class sets are sorted by decreasing size and
+ their natural sort order within each class size. Otherwise, class
+ sets are returned in the order that they were identified, which is
+ generally not significant.
+
+ >>> classify([]) == ([], {})
+ True
+ >>> classify([[]]) == ([], {})
+ True
+ >>> classify([[], []]) == ([], {})
+ True
+ >>> classify([[1]]) == ([{1}], {1: {1}})
+ True
+ >>> classify([[1,2]]) == ([{1, 2}], {1: {1, 2}, 2: {1, 2}})
+ True
+ >>> classify([[1],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+ True
+ >>> classify([[1,2],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+ True
+ >>> classify([[1,2],[2,4]]) == ([{1}, {2}, {4}], {1: {1}, 2: {2}, 4: {4}})
+ True
+ >>> classify([[1,2],[2,4,5]]) == (
+ ... [{4, 5}, {1}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+ True
+ >>> classify([[1,2],[2,4,5]], sort=False) == (
+ ... [{1}, {4, 5}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+ True
+ >>> classify([[1,2,9],[2,4,5]], sort=False) == (
+ ... [{1, 9}, {4, 5}, {2}], {1: {1, 9}, 2: {2}, 4: {4, 5}, 5: {4, 5},
+ ... 9: {1, 9}})
+ True
+ >>> classify([[1,2,9,15],[2,4,5]], sort=False) == (
+ ... [{1, 9, 15}, {4, 5}, {2}], {1: {1, 9, 15}, 2: {2}, 4: {4, 5},
+ ... 5: {4, 5}, 9: {1, 9, 15}, 15: {1, 9, 15}})
+ True
+ >>> classes, mapping = classify([[1,2,9,15],[2,4,5],[15,5]], sort=False)
+ >>> set([frozenset(c) for c in classes]) == set(
+ ... [frozenset(s) for s in ({1, 9}, {4}, {2}, {5}, {15})])
+ True
+ >>> mapping == {1: {1, 9}, 2: {2}, 4: {4}, 5: {5}, 9: {1, 9}, 15: {15}}
+ True
+ """
+ classifier = Classifier(sort=sort)
+ classifier.update(list_of_sets)
+ return classifier.getClasses(), classifier.getMapping()
+
+
+if __name__ == "__main__":
+ import sys, doctest
+
+ sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/cliTools.py b/lib/python3.12/site-packages/fontTools/misc/cliTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a64235bf075ebd52b58044a15d475058bd1fdd2
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/cliTools.py
@@ -0,0 +1,53 @@
+"""Collection of utilities for command-line interfaces and console scripts."""
+
+import os
+import re
+
+
+numberAddedRE = re.compile(r"#\d+$")
+
+
+def makeOutputFileName(
+ input, outputDir=None, extension=None, overWrite=False, suffix=""
+):
+ """Generates a suitable file name for writing output.
+
+ Often tools will want to take a file, do some kind of transformation to it,
+ and write it out again. This function determines an appropriate name for the
+ output file, through one or more of the following steps:
+
+ - changing the output directory
+ - appending suffix before file extension
+ - replacing the file extension
+ - suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid
+ overwriting an existing file.
+
+ Args:
+ input: Name of input file.
+ outputDir: Optionally, a new directory to write the file into.
+ suffix: Optionally, a string suffix is appended to file name before
+ the extension.
+ extension: Optionally, a replacement for the current file extension.
+ overWrite: Overwriting an existing file is permitted if true; if false
+ and the proposed filename exists, a new name will be generated by
+ adding an appropriate number suffix.
+
+ Returns:
+ str: Suitable output filename
+ """
+ dirName, fileName = os.path.split(input)
+ fileName, ext = os.path.splitext(fileName)
+ if outputDir:
+ dirName = outputDir
+ fileName = numberAddedRE.split(fileName)[0]
+ if extension is None:
+ extension = os.path.splitext(input)[1]
+ output = os.path.join(dirName, fileName + suffix + extension)
+ n = 1
+ if not overWrite:
+ while os.path.exists(output):
+ output = os.path.join(
+ dirName, fileName + suffix + "#" + repr(n) + extension
+ )
+ n += 1
+ return output
diff --git a/lib/python3.12/site-packages/fontTools/misc/configTools.py b/lib/python3.12/site-packages/fontTools/misc/configTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..c928840a5154a21024ab6ff6e766ea346d43e741
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/configTools.py
@@ -0,0 +1,351 @@
+"""
+Code of the config system; not related to fontTools or fonts in particular.
+
+The options that are specific to fontTools are in :mod:`fontTools.config`.
+
+To create your own config system, you need to create an instance of
+:class:`Options`, and a subclass of :class:`AbstractConfig` with its
+``options`` class variable set to your instance of Options.
+
+"""
+
+from __future__ import annotations
+
+import logging
+from dataclasses import dataclass
+from typing import (
+ Any,
+ Callable,
+ ClassVar,
+ Dict,
+ Iterable,
+ Mapping,
+ MutableMapping,
+ Optional,
+ Set,
+ Union,
+)
+
+
+log = logging.getLogger(__name__)
+
+__all__ = [
+ "AbstractConfig",
+ "ConfigAlreadyRegisteredError",
+ "ConfigError",
+ "ConfigUnknownOptionError",
+ "ConfigValueParsingError",
+ "ConfigValueValidationError",
+ "Option",
+ "Options",
+]
+
+
+class ConfigError(Exception):
+ """Base exception for the config module."""
+
+
+class ConfigAlreadyRegisteredError(ConfigError):
+ """Raised when a module tries to register a configuration option that
+ already exists.
+
+ Should not be raised too much really, only when developing new fontTools
+ modules.
+ """
+
+ def __init__(self, name):
+ super().__init__(f"Config option {name} is already registered.")
+
+
+class ConfigValueParsingError(ConfigError):
+ """Raised when a configuration value cannot be parsed."""
+
+ def __init__(self, name, value):
+ super().__init__(
+ f"Config option {name}: value cannot be parsed (given {repr(value)})"
+ )
+
+
+class ConfigValueValidationError(ConfigError):
+ """Raised when a configuration value cannot be validated."""
+
+ def __init__(self, name, value):
+ super().__init__(
+ f"Config option {name}: value is invalid (given {repr(value)})"
+ )
+
+
+class ConfigUnknownOptionError(ConfigError):
+ """Raised when a configuration option is unknown."""
+
+ def __init__(self, option_or_name):
+ name = (
+ f"'{option_or_name.name}' (id={id(option_or_name)})>"
+ if isinstance(option_or_name, Option)
+ else f"'{option_or_name}'"
+ )
+ super().__init__(f"Config option {name} is unknown")
+
+
+# eq=False because Options are unique, not fungible objects
+@dataclass(frozen=True, eq=False)
+class Option:
+ name: str
+ """Unique name identifying the option (e.g. package.module:MY_OPTION)."""
+ help: str
+ """Help text for this option."""
+ default: Any
+ """Default value for this option."""
+ parse: Callable[[str], Any]
+ """Turn input (e.g. string) into proper type. Only when reading from file."""
+ validate: Optional[Callable[[Any], bool]] = None
+ """Return true if the given value is an acceptable value."""
+
+ @staticmethod
+ def parse_optional_bool(v: str) -> Optional[bool]:
+ s = str(v).lower()
+ if s in {"0", "no", "false"}:
+ return False
+ if s in {"1", "yes", "true"}:
+ return True
+ if s in {"auto", "none"}:
+ return None
+ raise ValueError("invalid optional bool: {v!r}")
+
+ @staticmethod
+ def validate_optional_bool(v: Any) -> bool:
+ return v is None or isinstance(v, bool)
+
+
+class Options(Mapping):
+ """Registry of available options for a given config system.
+
+ Define new options using the :meth:`register()` method.
+
+ Access existing options using the Mapping interface.
+ """
+
+ __options: Dict[str, Option]
+
+ def __init__(self, other: "Options" = None) -> None:
+ self.__options = {}
+ if other is not None:
+ for option in other.values():
+ self.register_option(option)
+
+ def register(
+ self,
+ name: str,
+ help: str,
+ default: Any,
+ parse: Callable[[str], Any],
+ validate: Optional[Callable[[Any], bool]] = None,
+ ) -> Option:
+ """Create and register a new option."""
+ return self.register_option(Option(name, help, default, parse, validate))
+
+ def register_option(self, option: Option) -> Option:
+ """Register a new option."""
+ name = option.name
+ if name in self.__options:
+ raise ConfigAlreadyRegisteredError(name)
+ self.__options[name] = option
+ return option
+
+ def is_registered(self, option: Option) -> bool:
+ """Return True if the same option object is already registered."""
+ return self.__options.get(option.name) is option
+
+ def __getitem__(self, key: str) -> Option:
+ return self.__options.__getitem__(key)
+
+ def __iter__(self) -> Iterator[str]:
+ return self.__options.__iter__()
+
+ def __len__(self) -> int:
+ return self.__options.__len__()
+
+ def __repr__(self) -> str:
+ return (
+ f"{self.__class__.__name__}({{\n"
+ + "".join(
+ f" {k!r}: Option(default={v.default!r}, ...),\n"
+ for k, v in self.__options.items()
+ )
+ + "})"
+ )
+
+
+_USE_GLOBAL_DEFAULT = object()
+
+
+class AbstractConfig(MutableMapping):
+ """
+ Create a set of config values, optionally pre-filled with values from
+ the given dictionary or pre-existing config object.
+
+ The class implements the MutableMapping protocol keyed by option name (`str`).
+ For convenience its methods accept either Option or str as the key parameter.
+
+ .. seealso:: :meth:`set()`
+
+ This config class is abstract because it needs its ``options`` class
+ var to be set to an instance of :class:`Options` before it can be
+ instanciated and used.
+
+ .. code:: python
+
+ class MyConfig(AbstractConfig):
+ options = Options()
+
+ MyConfig.register_option( "test:option_name", "This is an option", 0, int, lambda v: isinstance(v, int))
+
+ cfg = MyConfig({"test:option_name": 10})
+
+ """
+
+ options: ClassVar[Options]
+
+ @classmethod
+ def register_option(
+ cls,
+ name: str,
+ help: str,
+ default: Any,
+ parse: Callable[[str], Any],
+ validate: Optional[Callable[[Any], bool]] = None,
+ ) -> Option:
+ """Register an available option in this config system."""
+ return cls.options.register(
+ name, help=help, default=default, parse=parse, validate=validate
+ )
+
+ _values: Dict[str, Any]
+
+ def __init__(
+ self,
+ values: Union[
+ AbstractConfig, Dict[Union[Option, str], Any], Mapping[str, Any]
+ ] = {},
+ parse_values: bool = False,
+ skip_unknown: bool = False,
+ ):
+ self._values = {}
+ values_dict = values._values if isinstance(values, AbstractConfig) else values
+ for name, value in values_dict.items():
+ self.set(name, value, parse_values, skip_unknown)
+
+ def _resolve_option(self, option_or_name: Union[Option, str]) -> Option:
+ if isinstance(option_or_name, Option):
+ option = option_or_name
+ if not self.options.is_registered(option):
+ raise ConfigUnknownOptionError(option)
+ return option
+ elif isinstance(option_or_name, str):
+ name = option_or_name
+ try:
+ return self.options[name]
+ except KeyError:
+ raise ConfigUnknownOptionError(name)
+ else:
+ raise TypeError(
+ "expected Option or str, found "
+ f"{type(option_or_name).__name__}: {option_or_name!r}"
+ )
+
+ def set(
+ self,
+ option_or_name: Union[Option, str],
+ value: Any,
+ parse_values: bool = False,
+ skip_unknown: bool = False,
+ ):
+ """Set the value of an option.
+
+ Args:
+ * `option_or_name`: an `Option` object or its name (`str`).
+ * `value`: the value to be assigned to given option.
+ * `parse_values`: parse the configuration value from a string into
+ its proper type, as per its `Option` object. The default
+ behavior is to raise `ConfigValueValidationError` when the value
+ is not of the right type. Useful when reading options from a
+ file type that doesn't support as many types as Python.
+ * `skip_unknown`: skip unknown configuration options. The default
+ behaviour is to raise `ConfigUnknownOptionError`. Useful when
+ reading options from a configuration file that has extra entries
+ (e.g. for a later version of fontTools)
+ """
+ try:
+ option = self._resolve_option(option_or_name)
+ except ConfigUnknownOptionError as e:
+ if skip_unknown:
+ log.debug(str(e))
+ return
+ raise
+
+ # Can be useful if the values come from a source that doesn't have
+ # strict typing (.ini file? Terminal input?)
+ if parse_values:
+ try:
+ value = option.parse(value)
+ except Exception as e:
+ raise ConfigValueParsingError(option.name, value) from e
+
+ if option.validate is not None and not option.validate(value):
+ raise ConfigValueValidationError(option.name, value)
+
+ self._values[option.name] = value
+
+ def get(
+ self, option_or_name: Union[Option, str], default: Any = _USE_GLOBAL_DEFAULT
+ ) -> Any:
+ """
+ Get the value of an option. The value which is returned is the first
+ provided among:
+
+ 1. a user-provided value in the options's ``self._values`` dict
+ 2. a caller-provided default value to this method call
+ 3. the global default for the option provided in ``fontTools.config``
+
+ This is to provide the ability to migrate progressively from config
+ options passed as arguments to fontTools APIs to config options read
+ from the current TTFont, e.g.
+
+ .. code:: python
+
+ def fontToolsAPI(font, some_option):
+ value = font.cfg.get("someLib.module:SOME_OPTION", some_option)
+ # use value
+
+ That way, the function will work the same for users of the API that
+ still pass the option to the function call, but will favour the new
+ config mechanism if the given font specifies a value for that option.
+ """
+ option = self._resolve_option(option_or_name)
+ if option.name in self._values:
+ return self._values[option.name]
+ if default is not _USE_GLOBAL_DEFAULT:
+ return default
+ return option.default
+
+ def copy(self):
+ return self.__class__(self._values)
+
+ def __getitem__(self, option_or_name: Union[Option, str]) -> Any:
+ return self.get(option_or_name)
+
+ def __setitem__(self, option_or_name: Union[Option, str], value: Any) -> None:
+ return self.set(option_or_name, value)
+
+ def __delitem__(self, option_or_name: Union[Option, str]) -> None:
+ option = self._resolve_option(option_or_name)
+ del self._values[option.name]
+
+ def __iter__(self) -> Iterable[str]:
+ return self._values.__iter__()
+
+ def __len__(self) -> int:
+ return len(self._values)
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}({repr(self._values)})"
diff --git a/lib/python3.12/site-packages/fontTools/misc/cython.py b/lib/python3.12/site-packages/fontTools/misc/cython.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a42d94a3591e0e8e47f184b303e4aec0a6337ef
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/cython.py
@@ -0,0 +1,27 @@
+""" Exports a no-op 'cython' namespace similar to
+https://github.com/cython/cython/blob/master/Cython/Shadow.py
+
+This allows to optionally compile @cython decorated functions
+(when cython is available at built time), or run the same code
+as pure-python, without runtime dependency on cython module.
+
+We only define the symbols that we use. E.g. see fontTools.cu2qu
+"""
+
+from types import SimpleNamespace
+
+
+def _empty_decorator(x):
+ return x
+
+
+compiled = False
+
+for name in ("double", "complex", "int"):
+ globals()[name] = None
+
+for name in ("cfunc", "inline"):
+ globals()[name] = _empty_decorator
+
+locals = lambda **_: _empty_decorator
+returns = lambda _: _empty_decorator
diff --git a/lib/python3.12/site-packages/fontTools/misc/dictTools.py b/lib/python3.12/site-packages/fontTools/misc/dictTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd3d394c25bc0f5ab49b502fdb614d01a9fef281
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/dictTools.py
@@ -0,0 +1,83 @@
+"""Misc dict tools."""
+
+__all__ = ["hashdict"]
+
+
+# https://stackoverflow.com/questions/1151658/python-hashable-dicts
+class hashdict(dict):
+ """
+ hashable dict implementation, suitable for use as a key into
+ other dicts.
+
+ >>> h1 = hashdict({"apples": 1, "bananas":2})
+ >>> h2 = hashdict({"bananas": 3, "mangoes": 5})
+ >>> h1+h2
+ hashdict(apples=1, bananas=3, mangoes=5)
+ >>> d1 = {}
+ >>> d1[h1] = "salad"
+ >>> d1[h1]
+ 'salad'
+ >>> d1[h2]
+ Traceback (most recent call last):
+ ...
+ KeyError: hashdict(bananas=3, mangoes=5)
+
+ based on answers from
+ http://stackoverflow.com/questions/1151658/python-hashable-dicts
+
+ """
+
+ def __key(self):
+ return tuple(sorted(self.items()))
+
+ def __repr__(self):
+ return "{0}({1})".format(
+ self.__class__.__name__,
+ ", ".join("{0}={1}".format(str(i[0]), repr(i[1])) for i in self.__key()),
+ )
+
+ def __hash__(self):
+ return hash(self.__key())
+
+ def __setitem__(self, key, value):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def __delitem__(self, key):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def clear(self):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def pop(self, *args, **kwargs):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def popitem(self, *args, **kwargs):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def setdefault(self, *args, **kwargs):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ def update(self, *args, **kwargs):
+ raise TypeError(
+ "{0} does not support item assignment".format(self.__class__.__name__)
+ )
+
+ # update is not ok because it mutates the object
+ # __add__ is ok because it creates a new object
+ # while the new object is under construction, it's ok to mutate it
+ def __add__(self, right):
+ result = hashdict(self)
+ dict.update(result, right)
+ return result
diff --git a/lib/python3.12/site-packages/fontTools/misc/eexec.py b/lib/python3.12/site-packages/fontTools/misc/eexec.py
new file mode 100644
index 0000000000000000000000000000000000000000..cafa312cdaa4696b0624438e06418ade95438441
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/eexec.py
@@ -0,0 +1,119 @@
+"""
+PostScript Type 1 fonts make use of two types of encryption: charstring
+encryption and ``eexec`` encryption. Charstring encryption is used for
+the charstrings themselves, while ``eexec`` is used to encrypt larger
+sections of the font program, such as the ``Private`` and ``CharStrings``
+dictionaries. Despite the different names, the algorithm is the same,
+although ``eexec`` encryption uses a fixed initial key R=55665.
+
+The algorithm uses cipher feedback, meaning that the ciphertext is used
+to modify the key. Because of this, the routines in this module return
+the new key at the end of the operation.
+
+"""
+
+from fontTools.misc.textTools import bytechr, bytesjoin, byteord
+
+
+def _decryptChar(cipher, R):
+ cipher = byteord(cipher)
+ plain = ((cipher ^ (R >> 8))) & 0xFF
+ R = ((cipher + R) * 52845 + 22719) & 0xFFFF
+ return bytechr(plain), R
+
+
+def _encryptChar(plain, R):
+ plain = byteord(plain)
+ cipher = ((plain ^ (R >> 8))) & 0xFF
+ R = ((cipher + R) * 52845 + 22719) & 0xFFFF
+ return bytechr(cipher), R
+
+
+def decrypt(cipherstring, R):
+ r"""
+ Decrypts a string using the Type 1 encryption algorithm.
+
+ Args:
+ cipherstring: String of ciphertext.
+ R: Initial key.
+
+ Returns:
+ decryptedStr: Plaintext string.
+ R: Output key for subsequent decryptions.
+
+ Examples::
+
+ >>> testStr = b"\0\0asdadads asds\265"
+ >>> decryptedStr, R = decrypt(testStr, 12321)
+ >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+ True
+ >>> R == 36142
+ True
+ """
+ plainList = []
+ for cipher in cipherstring:
+ plain, R = _decryptChar(cipher, R)
+ plainList.append(plain)
+ plainstring = bytesjoin(plainList)
+ return plainstring, int(R)
+
+
+def encrypt(plainstring, R):
+ r"""
+ Encrypts a string using the Type 1 encryption algorithm.
+
+ Note that the algorithm as described in the Type 1 specification requires the
+ plaintext to be prefixed with a number of random bytes. (For ``eexec`` the
+ number of random bytes is set to 4.) This routine does *not* add the random
+ prefix to its input.
+
+ Args:
+ plainstring: String of plaintext.
+ R: Initial key.
+
+ Returns:
+ cipherstring: Ciphertext string.
+ R: Output key for subsequent encryptions.
+
+ Examples::
+
+ >>> testStr = b"\0\0asdadads asds\265"
+ >>> decryptedStr, R = decrypt(testStr, 12321)
+ >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+ True
+ >>> R == 36142
+ True
+
+ >>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+ >>> encryptedStr, R = encrypt(testStr, 12321)
+ >>> encryptedStr == b"\0\0asdadads asds\265"
+ True
+ >>> R == 36142
+ True
+ """
+ cipherList = []
+ for plain in plainstring:
+ cipher, R = _encryptChar(plain, R)
+ cipherList.append(cipher)
+ cipherstring = bytesjoin(cipherList)
+ return cipherstring, int(R)
+
+
+def hexString(s):
+ import binascii
+
+ return binascii.hexlify(s)
+
+
+def deHexString(h):
+ import binascii
+
+ h = bytesjoin(h.split())
+ return binascii.unhexlify(h)
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/encodingTools.py b/lib/python3.12/site-packages/fontTools/misc/encodingTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b2651d3b1ce222060fa67abaeac4da8030618fa
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/encodingTools.py
@@ -0,0 +1,72 @@
+"""fontTools.misc.encodingTools.py -- tools for working with OpenType encodings.
+"""
+
+import fontTools.encodings.codecs
+
+# Map keyed by platformID, then platEncID, then possibly langID
+_encodingMap = {
+ 0: { # Unicode
+ 0: "utf_16_be",
+ 1: "utf_16_be",
+ 2: "utf_16_be",
+ 3: "utf_16_be",
+ 4: "utf_16_be",
+ 5: "utf_16_be",
+ 6: "utf_16_be",
+ },
+ 1: { # Macintosh
+ # See
+ # https://github.com/fonttools/fonttools/issues/236
+ 0: { # Macintosh, platEncID==0, keyed by langID
+ 15: "mac_iceland",
+ 17: "mac_turkish",
+ 18: "mac_croatian",
+ 24: "mac_latin2",
+ 25: "mac_latin2",
+ 26: "mac_latin2",
+ 27: "mac_latin2",
+ 28: "mac_latin2",
+ 36: "mac_latin2",
+ 37: "mac_romanian",
+ 38: "mac_latin2",
+ 39: "mac_latin2",
+ 40: "mac_latin2",
+ Ellipsis: "mac_roman", # Other
+ },
+ 1: "x_mac_japanese_ttx",
+ 2: "x_mac_trad_chinese_ttx",
+ 3: "x_mac_korean_ttx",
+ 6: "mac_greek",
+ 7: "mac_cyrillic",
+ 25: "x_mac_simp_chinese_ttx",
+ 29: "mac_latin2",
+ 35: "mac_turkish",
+ 37: "mac_iceland",
+ },
+ 2: { # ISO
+ 0: "ascii",
+ 1: "utf_16_be",
+ 2: "latin1",
+ },
+ 3: { # Microsoft
+ 0: "utf_16_be",
+ 1: "utf_16_be",
+ 2: "shift_jis",
+ 3: "gb2312",
+ 4: "big5",
+ 5: "euc_kr",
+ 6: "johab",
+ 10: "utf_16_be",
+ },
+}
+
+
+def getEncoding(platformID, platEncID, langID, default=None):
+ """Returns the Python encoding name for OpenType platformID/encodingID/langID
+ triplet. If encoding for these values is not known, by default None is
+ returned. That can be overriden by passing a value to the default argument.
+ """
+ encoding = _encodingMap.get(platformID, {}).get(platEncID, default)
+ if isinstance(encoding, dict):
+ encoding = encoding.get(langID, encoding[Ellipsis])
+ return encoding
diff --git a/lib/python3.12/site-packages/fontTools/misc/enumTools.py b/lib/python3.12/site-packages/fontTools/misc/enumTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..e947342f200c7c935724256eaee38c44c492ab78
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/enumTools.py
@@ -0,0 +1,23 @@
+"""Enum-related utilities, including backports for older Python versions."""
+
+from __future__ import annotations
+
+from enum import Enum
+
+
+__all__ = ["StrEnum"]
+
+# StrEnum is only available in Python 3.11+
+try:
+ from enum import StrEnum
+except ImportError:
+
+ class StrEnum(str, Enum):
+ """
+ Minimal backport of Python 3.11's StrEnum for older versions.
+
+ An Enum where all members are also strings.
+ """
+
+ def __str__(self) -> str:
+ return self.value
diff --git a/lib/python3.12/site-packages/fontTools/misc/etree.py b/lib/python3.12/site-packages/fontTools/misc/etree.py
new file mode 100644
index 0000000000000000000000000000000000000000..743546061c45724d09c0ca2990383df821026ea7
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/etree.py
@@ -0,0 +1,456 @@
+"""Shim module exporting the same ElementTree API for lxml and
+xml.etree backends.
+
+When lxml is installed, it is automatically preferred over the built-in
+xml.etree module.
+On Python 2.7, the cElementTree module is preferred over the pure-python
+ElementTree module.
+
+Besides exporting a unified interface, this also defines extra functions
+or subclasses built-in ElementTree classes to add features that are
+only availble in lxml, like OrderedDict for attributes, pretty_print and
+iterwalk.
+"""
+
+from fontTools.misc.textTools import tostr
+
+
+XML_DECLARATION = """"""
+
+__all__ = [
+ # public symbols
+ "Comment",
+ "dump",
+ "Element",
+ "ElementTree",
+ "fromstring",
+ "fromstringlist",
+ "iselement",
+ "iterparse",
+ "parse",
+ "ParseError",
+ "PI",
+ "ProcessingInstruction",
+ "QName",
+ "SubElement",
+ "tostring",
+ "tostringlist",
+ "TreeBuilder",
+ "XML",
+ "XMLParser",
+ "register_namespace",
+]
+
+try:
+ from lxml.etree import *
+
+ _have_lxml = True
+except ImportError:
+ try:
+ from xml.etree.cElementTree import *
+
+ # the cElementTree version of XML function doesn't support
+ # the optional 'parser' keyword argument
+ from xml.etree.ElementTree import XML
+ except ImportError: # pragma: no cover
+ from xml.etree.ElementTree import *
+ _have_lxml = False
+
+ _Attrib = dict
+
+ if isinstance(Element, type):
+ _Element = Element
+ else:
+ # in py27, cElementTree.Element cannot be subclassed, so
+ # we need to import the pure-python class
+ from xml.etree.ElementTree import Element as _Element
+
+ class Element(_Element):
+ """Element subclass that keeps the order of attributes."""
+
+ def __init__(self, tag, attrib=_Attrib(), **extra):
+ super(Element, self).__init__(tag)
+ self.attrib = _Attrib()
+ if attrib:
+ self.attrib.update(attrib)
+ if extra:
+ self.attrib.update(extra)
+
+ def SubElement(parent, tag, attrib=_Attrib(), **extra):
+ """Must override SubElement as well otherwise _elementtree.SubElement
+ fails if 'parent' is a subclass of Element object.
+ """
+ element = parent.__class__(tag, attrib, **extra)
+ parent.append(element)
+ return element
+
+ def _iterwalk(element, events, tag):
+ include = tag is None or element.tag == tag
+ if include and "start" in events:
+ yield ("start", element)
+ for e in element:
+ for item in _iterwalk(e, events, tag):
+ yield item
+ if include:
+ yield ("end", element)
+
+ def iterwalk(element_or_tree, events=("end",), tag=None):
+ """A tree walker that generates events from an existing tree as
+ if it was parsing XML data with iterparse().
+ Drop-in replacement for lxml.etree.iterwalk.
+ """
+ if iselement(element_or_tree):
+ element = element_or_tree
+ else:
+ element = element_or_tree.getroot()
+ if tag == "*":
+ tag = None
+ for item in _iterwalk(element, events, tag):
+ yield item
+
+ _ElementTree = ElementTree
+
+ class ElementTree(_ElementTree):
+ """ElementTree subclass that adds 'pretty_print' and 'doctype'
+ arguments to the 'write' method.
+ Currently these are only supported for the default XML serialization
+ 'method', and not also for "html" or "text", for these are delegated
+ to the base class.
+ """
+
+ def write(
+ self,
+ file_or_filename,
+ encoding=None,
+ xml_declaration=False,
+ method=None,
+ doctype=None,
+ pretty_print=False,
+ ):
+ if method and method != "xml":
+ # delegate to super-class
+ super(ElementTree, self).write(
+ file_or_filename,
+ encoding=encoding,
+ xml_declaration=xml_declaration,
+ method=method,
+ )
+ return
+
+ if encoding is not None and encoding.lower() == "unicode":
+ if xml_declaration:
+ raise ValueError(
+ "Serialisation to unicode must not request an XML declaration"
+ )
+ write_declaration = False
+ encoding = "unicode"
+ elif xml_declaration is None:
+ # by default, write an XML declaration only for non-standard encodings
+ write_declaration = encoding is not None and encoding.upper() not in (
+ "ASCII",
+ "UTF-8",
+ "UTF8",
+ "US-ASCII",
+ )
+ else:
+ write_declaration = xml_declaration
+
+ if encoding is None:
+ encoding = "ASCII"
+
+ if pretty_print:
+ # NOTE this will modify the tree in-place
+ _indent(self._root)
+
+ with _get_writer(file_or_filename, encoding) as write:
+ if write_declaration:
+ write(XML_DECLARATION % encoding.upper())
+ if pretty_print:
+ write("\n")
+ if doctype:
+ write(_tounicode(doctype))
+ if pretty_print:
+ write("\n")
+
+ qnames, namespaces = _namespaces(self._root)
+ _serialize_xml(write, self._root, qnames, namespaces)
+
+ import io
+
+ def tostring(
+ element,
+ encoding=None,
+ xml_declaration=None,
+ method=None,
+ doctype=None,
+ pretty_print=False,
+ ):
+ """Custom 'tostring' function that uses our ElementTree subclass, with
+ pretty_print support.
+ """
+ stream = io.StringIO() if encoding == "unicode" else io.BytesIO()
+ ElementTree(element).write(
+ stream,
+ encoding=encoding,
+ xml_declaration=xml_declaration,
+ method=method,
+ doctype=doctype,
+ pretty_print=pretty_print,
+ )
+ return stream.getvalue()
+
+ # serialization support
+
+ import re
+
+ # Valid XML strings can include any Unicode character, excluding control
+ # characters, the surrogate blocks, FFFE, and FFFF:
+ # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+ # Here we reversed the pattern to match only the invalid characters.
+ _invalid_xml_string = re.compile(
+ "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]"
+ )
+
+ def _tounicode(s):
+ """Test if a string is valid user input and decode it to unicode string
+ using ASCII encoding if it's a bytes string.
+ Reject all bytes/unicode input that contains non-XML characters.
+ Reject all bytes input that contains non-ASCII characters.
+ """
+ try:
+ s = tostr(s, encoding="ascii", errors="strict")
+ except UnicodeDecodeError:
+ raise ValueError(
+ "Bytes strings can only contain ASCII characters. "
+ "Use unicode strings for non-ASCII characters."
+ )
+ except AttributeError:
+ _raise_serialization_error(s)
+ if s and _invalid_xml_string.search(s):
+ raise ValueError(
+ "All strings must be XML compatible: Unicode or ASCII, "
+ "no NULL bytes or control characters"
+ )
+ return s
+
+ import contextlib
+
+ @contextlib.contextmanager
+ def _get_writer(file_or_filename, encoding):
+ # returns text write method and release all resources after using
+ try:
+ write = file_or_filename.write
+ except AttributeError:
+ # file_or_filename is a file name
+ f = open(
+ file_or_filename,
+ "w",
+ encoding="utf-8" if encoding == "unicode" else encoding,
+ errors="xmlcharrefreplace",
+ )
+ with f:
+ yield f.write
+ else:
+ # file_or_filename is a file-like object
+ # encoding determines if it is a text or binary writer
+ if encoding == "unicode":
+ # use a text writer as is
+ yield write
+ else:
+ # wrap a binary writer with TextIOWrapper
+ detach_buffer = False
+ if isinstance(file_or_filename, io.BufferedIOBase):
+ buf = file_or_filename
+ elif isinstance(file_or_filename, io.RawIOBase):
+ buf = io.BufferedWriter(file_or_filename)
+ detach_buffer = True
+ else:
+ # This is to handle passed objects that aren't in the
+ # IOBase hierarchy, but just have a write method
+ buf = io.BufferedIOBase()
+ buf.writable = lambda: True
+ buf.write = write
+ try:
+ # TextIOWrapper uses this methods to determine
+ # if BOM (for UTF-16, etc) should be added
+ buf.seekable = file_or_filename.seekable
+ buf.tell = file_or_filename.tell
+ except AttributeError:
+ pass
+ wrapper = io.TextIOWrapper(
+ buf,
+ encoding=encoding,
+ errors="xmlcharrefreplace",
+ newline="\n",
+ )
+ try:
+ yield wrapper.write
+ finally:
+ # Keep the original file open when the TextIOWrapper and
+ # the BufferedWriter are destroyed
+ wrapper.detach()
+ if detach_buffer:
+ buf.detach()
+
+ from xml.etree.ElementTree import _namespace_map
+
+ def _namespaces(elem):
+ # identify namespaces used in this tree
+
+ # maps qnames to *encoded* prefix:local names
+ qnames = {None: None}
+
+ # maps uri:s to prefixes
+ namespaces = {}
+
+ def add_qname(qname):
+ # calculate serialized qname representation
+ try:
+ qname = _tounicode(qname)
+ if qname[:1] == "{":
+ uri, tag = qname[1:].rsplit("}", 1)
+ prefix = namespaces.get(uri)
+ if prefix is None:
+ prefix = _namespace_map.get(uri)
+ if prefix is None:
+ prefix = "ns%d" % len(namespaces)
+ else:
+ prefix = _tounicode(prefix)
+ if prefix != "xml":
+ namespaces[uri] = prefix
+ if prefix:
+ qnames[qname] = "%s:%s" % (prefix, tag)
+ else:
+ qnames[qname] = tag # default element
+ else:
+ qnames[qname] = qname
+ except TypeError:
+ _raise_serialization_error(qname)
+
+ # populate qname and namespaces table
+ for elem in elem.iter():
+ tag = elem.tag
+ if isinstance(tag, QName):
+ if tag.text not in qnames:
+ add_qname(tag.text)
+ elif isinstance(tag, str):
+ if tag not in qnames:
+ add_qname(tag)
+ elif tag is not None and tag is not Comment and tag is not PI:
+ _raise_serialization_error(tag)
+ for key, value in elem.items():
+ if isinstance(key, QName):
+ key = key.text
+ if key not in qnames:
+ add_qname(key)
+ if isinstance(value, QName) and value.text not in qnames:
+ add_qname(value.text)
+ text = elem.text
+ if isinstance(text, QName) and text.text not in qnames:
+ add_qname(text.text)
+ return qnames, namespaces
+
+ def _serialize_xml(write, elem, qnames, namespaces, **kwargs):
+ tag = elem.tag
+ text = elem.text
+ if tag is Comment:
+ write("" % _tounicode(text))
+ elif tag is ProcessingInstruction:
+ write("%s?>" % _tounicode(text))
+ else:
+ tag = qnames[_tounicode(tag) if tag is not None else None]
+ if tag is None:
+ if text:
+ write(_escape_cdata(text))
+ for e in elem:
+ _serialize_xml(write, e, qnames, None)
+ else:
+ write("<" + tag)
+ if namespaces:
+ for uri, prefix in sorted(
+ namespaces.items(), key=lambda x: x[1]
+ ): # sort on prefix
+ if prefix:
+ prefix = ":" + prefix
+ write(' xmlns%s="%s"' % (prefix, _escape_attrib(uri)))
+ attrs = elem.attrib
+ if attrs:
+ # try to keep existing attrib order
+ if len(attrs) <= 1 or type(attrs) is _Attrib:
+ items = attrs.items()
+ else:
+ # if plain dict, use lexical order
+ items = sorted(attrs.items())
+ for k, v in items:
+ if isinstance(k, QName):
+ k = _tounicode(k.text)
+ else:
+ k = _tounicode(k)
+ if isinstance(v, QName):
+ v = qnames[_tounicode(v.text)]
+ else:
+ v = _escape_attrib(v)
+ write(' %s="%s"' % (qnames[k], v))
+ if text is not None or len(elem):
+ write(">")
+ if text:
+ write(_escape_cdata(text))
+ for e in elem:
+ _serialize_xml(write, e, qnames, None)
+ write("" + tag + ">")
+ else:
+ write("/>")
+ if elem.tail:
+ write(_escape_cdata(elem.tail))
+
+ def _raise_serialization_error(text):
+ raise TypeError("cannot serialize %r (type %s)" % (text, type(text).__name__))
+
+ def _escape_cdata(text):
+ # escape character data
+ try:
+ text = _tounicode(text)
+ # it's worth avoiding do-nothing calls for short strings
+ if "&" in text:
+ text = text.replace("&", "&")
+ if "<" in text:
+ text = text.replace("<", "<")
+ if ">" in text:
+ text = text.replace(">", ">")
+ return text
+ except (TypeError, AttributeError):
+ _raise_serialization_error(text)
+
+ def _escape_attrib(text):
+ # escape attribute value
+ try:
+ text = _tounicode(text)
+ if "&" in text:
+ text = text.replace("&", "&")
+ if "<" in text:
+ text = text.replace("<", "<")
+ if ">" in text:
+ text = text.replace(">", ">")
+ if '"' in text:
+ text = text.replace('"', """)
+ if "\n" in text:
+ text = text.replace("\n", "
")
+ return text
+ except (TypeError, AttributeError):
+ _raise_serialization_error(text)
+
+ def _indent(elem, level=0):
+ # From http://effbot.org/zone/element-lib.htm#prettyprint
+ i = "\n" + level * " "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for elem in elem:
+ _indent(elem, level + 1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
diff --git a/lib/python3.12/site-packages/fontTools/misc/filenames.py b/lib/python3.12/site-packages/fontTools/misc/filenames.py
new file mode 100644
index 0000000000000000000000000000000000000000..ddedc5210fb3f81e7b26a8950b6155173ad0069b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filenames.py
@@ -0,0 +1,245 @@
+"""
+This module implements the algorithm for converting between a "user name" -
+something that a user can choose arbitrarily inside a font editor - and a file
+name suitable for use in a wide range of operating systems and filesystems.
+
+The `UFO 3 specification `_
+provides an example of an algorithm for such conversion, which avoids illegal
+characters, reserved file names, ambiguity between upper- and lower-case
+characters, and clashes with existing files.
+
+This code was originally copied from
+`ufoLib `_
+by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
+
+- Erik van Blokland
+- Tal Leming
+- Just van Rossum
+"""
+
+illegalCharacters = r"\" * + / : < > ? [ \ ] | \0".split(" ")
+illegalCharacters += [chr(i) for i in range(1, 32)]
+illegalCharacters += [chr(0x7F)]
+reservedFileNames = "CON PRN AUX CLOCK$ NUL A:-Z: COM1".lower().split(" ")
+reservedFileNames += "LPT1 LPT2 LPT3 COM2 COM3 COM4".lower().split(" ")
+maxFileNameLength = 255
+
+
+class NameTranslationError(Exception):
+ pass
+
+
+def userNameToFileName(userName, existing=[], prefix="", suffix=""):
+ """Converts from a user name to a file name.
+
+ Takes care to avoid illegal characters, reserved file names, ambiguity between
+ upper- and lower-case characters, and clashes with existing files.
+
+ Args:
+ userName (str): The input file name.
+ existing: A case-insensitive list of all existing file names.
+ prefix: Prefix to be prepended to the file name.
+ suffix: Suffix to be appended to the file name.
+
+ Returns:
+ A suitable filename.
+
+ Raises:
+ NameTranslationError: If no suitable name could be generated.
+
+ Examples::
+
+ >>> userNameToFileName("a") == "a"
+ True
+ >>> userNameToFileName("A") == "A_"
+ True
+ >>> userNameToFileName("AE") == "A_E_"
+ True
+ >>> userNameToFileName("Ae") == "A_e"
+ True
+ >>> userNameToFileName("ae") == "ae"
+ True
+ >>> userNameToFileName("aE") == "aE_"
+ True
+ >>> userNameToFileName("a.alt") == "a.alt"
+ True
+ >>> userNameToFileName("A.alt") == "A_.alt"
+ True
+ >>> userNameToFileName("A.Alt") == "A_.A_lt"
+ True
+ >>> userNameToFileName("A.aLt") == "A_.aL_t"
+ True
+ >>> userNameToFileName(u"A.alT") == "A_.alT_"
+ True
+ >>> userNameToFileName("T_H") == "T__H_"
+ True
+ >>> userNameToFileName("T_h") == "T__h"
+ True
+ >>> userNameToFileName("t_h") == "t_h"
+ True
+ >>> userNameToFileName("F_F_I") == "F__F__I_"
+ True
+ >>> userNameToFileName("f_f_i") == "f_f_i"
+ True
+ >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
+ True
+ >>> userNameToFileName(".notdef") == "_notdef"
+ True
+ >>> userNameToFileName("con") == "_con"
+ True
+ >>> userNameToFileName("CON") == "C_O_N_"
+ True
+ >>> userNameToFileName("con.alt") == "_con.alt"
+ True
+ >>> userNameToFileName("alt.con") == "alt._con"
+ True
+ """
+ # the incoming name must be a str
+ if not isinstance(userName, str):
+ raise ValueError("The value for userName must be a string.")
+ # establish the prefix and suffix lengths
+ prefixLength = len(prefix)
+ suffixLength = len(suffix)
+ # replace an initial period with an _
+ # if no prefix is to be added
+ if not prefix and userName[0] == ".":
+ userName = "_" + userName[1:]
+ # filter the user name
+ filteredUserName = []
+ for character in userName:
+ # replace illegal characters with _
+ if character in illegalCharacters:
+ character = "_"
+ # add _ to all non-lower characters
+ elif character != character.lower():
+ character += "_"
+ filteredUserName.append(character)
+ userName = "".join(filteredUserName)
+ # clip to 255
+ sliceLength = maxFileNameLength - prefixLength - suffixLength
+ userName = userName[:sliceLength]
+ # test for illegal files names
+ parts = []
+ for part in userName.split("."):
+ if part.lower() in reservedFileNames:
+ part = "_" + part
+ parts.append(part)
+ userName = ".".join(parts)
+ # test for clash
+ fullName = prefix + userName + suffix
+ if fullName.lower() in existing:
+ fullName = handleClash1(userName, existing, prefix, suffix)
+ # finished
+ return fullName
+
+
+def handleClash1(userName, existing=[], prefix="", suffix=""):
+ """
+ existing should be a case-insensitive list
+ of all existing file names.
+
+ >>> prefix = ("0" * 5) + "."
+ >>> suffix = "." + ("0" * 10)
+ >>> existing = ["a" * 5]
+
+ >>> e = list(existing)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000001.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000002.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000001.0000000000')
+ True
+ """
+ # if the prefix length + user name length + suffix length + 15 is at
+ # or past the maximum length, silce 15 characters off of the user name
+ prefixLength = len(prefix)
+ suffixLength = len(suffix)
+ if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
+ l = prefixLength + len(userName) + suffixLength + 15
+ sliceLength = maxFileNameLength - l
+ userName = userName[:sliceLength]
+ finalName = None
+ # try to add numbers to create a unique name
+ counter = 1
+ while finalName is None:
+ name = userName + str(counter).zfill(15)
+ fullName = prefix + name + suffix
+ if fullName.lower() not in existing:
+ finalName = fullName
+ break
+ else:
+ counter += 1
+ if counter >= 999999999999999:
+ break
+ # if there is a clash, go to the next fallback
+ if finalName is None:
+ finalName = handleClash2(existing, prefix, suffix)
+ # finished
+ return finalName
+
+
+def handleClash2(existing=[], prefix="", suffix=""):
+ """
+ existing should be a case-insensitive list
+ of all existing file names.
+
+ >>> prefix = ("0" * 5) + "."
+ >>> suffix = "." + ("0" * 10)
+ >>> existing = [prefix + str(i) + suffix for i in range(100)]
+
+ >>> e = list(existing)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.100.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.remove(prefix + "1" + suffix)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.1.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.remove(prefix + "2" + suffix)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.2.0000000000')
+ True
+ """
+ # calculate the longest possible string
+ maxLength = maxFileNameLength - len(prefix) - len(suffix)
+ maxValue = int("9" * maxLength)
+ # try to find a number
+ finalName = None
+ counter = 1
+ while finalName is None:
+ fullName = prefix + str(counter) + suffix
+ if fullName.lower() not in existing:
+ finalName = fullName
+ break
+ else:
+ counter += 1
+ if counter >= maxValue:
+ break
+ # raise an error if nothing has been found
+ if finalName is None:
+ raise NameTranslationError("No unique name could be found.")
+ # finished
+ return finalName
+
+
+if __name__ == "__main__":
+ import doctest
+ import sys
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__init__.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f9ae249b32db77a678e72deb5bde31cd5c50d91
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/__init__.py
@@ -0,0 +1,68 @@
+"""Minimal, stdlib-only replacement for [`pyfilesystem2`][1] API for use by `fontTools.ufoLib`.
+
+This package is a partial reimplementation of the `fs` package by Will McGugan, used under the
+MIT license. See LICENSE.external for details.
+
+Note this only exports a **subset** of the `pyfilesystem2` API, in particular the modules,
+classes and functions that are currently used directly by `fontTools.ufoLib`.
+
+It opportunistically tries to import the relevant modules from the upstream `fs` package
+when this is available. Otherwise it falls back to the replacement modules within this package.
+
+As of version 4.59.0, the `fonttools[ufo]` extra no longer requires the `fs` package, thus
+this `fontTools.misc.filesystem` package is used by default.
+
+Client code can either replace `import fs` with `from fontTools.misc import filesystem as fs`
+if that happens to work (no guarantee), or they can continue to use `fs` but they will have
+to specify it as an explicit dependency of their project.
+
+[1]: https://github.com/PyFilesystem/pyfilesystem2
+"""
+
+from __future__ import annotations
+
+try:
+ __import__("fs")
+except ImportError:
+ from . import _base as base
+ from . import _copy as copy
+ from . import _errors as errors
+ from . import _info as info
+ from . import _osfs as osfs
+ from . import _path as path
+ from . import _subfs as subfs
+ from . import _tempfs as tempfs
+ from . import _tools as tools
+ from . import _walk as walk
+ from . import _zipfs as zipfs
+
+ _haveFS = False
+else:
+ import fs.base as base
+ import fs.copy as copy
+ import fs.errors as errors
+ import fs.info as info
+ import fs.osfs as osfs
+ import fs.path as path
+ import fs.subfs as subfs
+ import fs.tempfs as tempfs
+ import fs.tools as tools
+ import fs.walk as walk
+ import fs.zipfs as zipfs
+
+ _haveFS = True
+
+
+__all__ = [
+ "base",
+ "copy",
+ "errors",
+ "info",
+ "osfs",
+ "path",
+ "subfs",
+ "tempfs",
+ "tools",
+ "walk",
+ "zipfs",
+]
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..31737585fbe5e4dfbf5ebae4a07c29913a08683e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_base.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_base.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c17d7e1519717be56499a7627ebbc833d2f4f77e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_base.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_copy.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_copy.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9d1f32d3b46a3f721d1f1e16f4404e9af461b7f7
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_copy.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_errors.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_errors.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ff94b84112f65644adf9329231b0936e6771fb27
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_errors.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_info.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_info.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..229e57389876198a4b607852097c8a02a3857d79
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_info.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_osfs.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_osfs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..83a8665b811e546662f6686d0ced4cc4b40a2d8a
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_osfs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_path.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_path.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..eed44db10596623f0f9202e5a10520777df3b2fc
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_path.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_subfs.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_subfs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7ff11ac6349d6d95118221c0cc153b349067db26
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_subfs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tempfs.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tempfs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0522c7d4543f588bfe6849b9a59e21299a37f098
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tempfs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tools.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e3fcd74e33315b58d4d42efddc7ca37703f3db63
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_tools.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_walk.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_walk.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a3d731599fcba7a58f5821e494f825d9305ce9b3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_walk.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_zipfs.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_zipfs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..370c255e6896020f4aae180f86dbe52c76a048b3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/filesystem/__pycache__/_zipfs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_base.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_base.py
new file mode 100644
index 0000000000000000000000000000000000000000..14603d5b2656dcc4428db64c026206888a73e3b8
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_base.py
@@ -0,0 +1,134 @@
+from __future__ import annotations
+
+import typing
+from abc import ABC, abstractmethod
+
+from ._copy import copy_dir, copy_file
+from ._errors import (
+ DestinationExists,
+ DirectoryExpected,
+ FileExpected,
+ FilesystemClosed,
+ NoSysPath,
+ ResourceNotFound,
+)
+from ._path import dirname
+from ._walk import BoundWalker
+
+if typing.TYPE_CHECKING:
+ from typing import IO, Any, Collection, Iterator, Self, Type
+
+ from ._info import Info
+ from ._subfs import SubFS
+
+
+class FS(ABC):
+ """Abstract base class for custom filesystems."""
+
+ _closed: bool = False
+
+ @abstractmethod
+ def open(self, path: str, mode: str = "rb", **kwargs) -> IO[Any]: ...
+
+ @abstractmethod
+ def exists(self, path: str) -> bool: ...
+
+ @abstractmethod
+ def isdir(self, path: str) -> bool: ...
+
+ @abstractmethod
+ def isfile(self, path: str) -> bool: ...
+
+ @abstractmethod
+ def listdir(self, path: str) -> list[str]: ...
+
+ @abstractmethod
+ def makedir(self, path: str, recreate: bool = False) -> SubFS: ...
+
+ @abstractmethod
+ def makedirs(self, path: str, recreate: bool = False) -> SubFS: ...
+
+ @abstractmethod
+ def getinfo(self, path: str, namespaces: Collection[str] | None = None) -> Info: ...
+
+ @abstractmethod
+ def remove(self, path: str) -> None: ...
+
+ @abstractmethod
+ def removedir(self, path: str) -> None: ...
+
+ @abstractmethod
+ def removetree(self, path: str) -> None: ...
+
+ @abstractmethod
+ def movedir(self, src: str, dst: str, create: bool = False) -> None: ...
+
+ def getsyspath(self, path: str) -> str:
+ raise NoSysPath(f"the filesystem {self!r} has no system path")
+
+ def close(self):
+ self._closed = True
+
+ def isclosed(self) -> bool:
+ return self._closed
+
+ def __enter__(self) -> Self:
+ return self
+
+ def __exit__(self, exc_type, exc, tb):
+ self.close()
+ return False # never swallow exceptions
+
+ def check(self):
+ if self._closed:
+ raise FilesystemClosed(f"the filesystem {self!r} is closed")
+
+ def opendir(self, path: str, *, factory: Type[SubFS] | None = None) -> SubFS:
+ """Return a sub‑filesystem rooted at `path`."""
+ if factory is None:
+ from ._subfs import SubFS
+
+ factory = SubFS
+ return factory(self, path)
+
+ def scandir(
+ self, path: str, namespaces: Collection[str] | None = None
+ ) -> Iterator[Info]:
+ return (self.getinfo(f"{path}/{p}", namespaces) for p in self.listdir(path))
+
+ @property
+ def walk(self) -> BoundWalker:
+ return BoundWalker(self)
+
+ def readbytes(self, path: str) -> bytes:
+ with self.open(path, "rb") as f:
+ return f.read()
+
+ def writebytes(self, path: str, data: bytes):
+ with self.open(path, "wb") as f:
+ f.write(data)
+
+ def create(self, path: str, wipe: bool = False):
+ if not wipe and self.exists(path):
+ return False
+ with self.open(path, "wb"):
+ pass # 'touch' empty file
+ return True
+
+ def copy(self, src_path: str, dst_path: str, overwrite=False):
+ if not self.exists(src_path):
+ raise ResourceNotFound(f"{src_path!r} does not exist")
+ elif not self.isfile(src_path):
+ raise FileExpected(f"path {src_path!r} should be a file")
+ if not overwrite and self.exists(dst_path):
+ raise DestinationExists(f"destination {dst_path!r} already exists")
+ if not self.isdir(dirname(dst_path)):
+ raise DirectoryExpected(f"path {dirname(dst_path)!r} should be a directory")
+ copy_file(self, src_path, self, dst_path)
+
+ def copydir(self, src_path: str, dst_path: str, create=False):
+ if not create and not self.exists(dst_path):
+ raise ResourceNotFound(f"{dst_path!r} does not exist")
+ if not self.isdir(src_path):
+ raise DirectoryExpected(f"path {src_path!r} should be a directory")
+ copy_dir(self, src_path, self, dst_path)
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_copy.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_copy.py
new file mode 100644
index 0000000000000000000000000000000000000000..194f9ffbb456dfc8c915ea82b4179f89a2e7a21e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_copy.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+import typing
+
+from ._errors import IllegalDestination
+from ._path import combine, frombase, isbase
+from ._tools import copy_file_data
+
+if typing.TYPE_CHECKING:
+ from ._base import FS
+
+
+def copy_file(src_fs: FS, src_path: str, dst_fs: FS, dst_path: str):
+ if src_fs is dst_fs and src_path == dst_path:
+ raise IllegalDestination(f"cannot copy {src_path!r} to itself")
+
+ with src_fs.open(src_path, "rb") as src_file:
+ with dst_fs.open(dst_path, "wb") as dst_file:
+ copy_file_data(src_file, dst_file)
+
+
+def copy_structure(
+ src_fs: FS,
+ dst_fs: FS,
+ src_root: str = "/",
+ dst_root: str = "/",
+):
+ if src_fs is dst_fs and isbase(src_root, dst_root):
+ raise IllegalDestination(f"cannot copy {src_fs!r} to itself")
+
+ dst_fs.makedirs(dst_root, recreate=True)
+ for dir_path in src_fs.walk.dirs(src_root):
+ dst_fs.makedir(combine(dst_root, frombase(src_root, dir_path)), recreate=True)
+
+
+def copy_dir(src_fs: FS, src_path: str, dst_fs: FS, dst_path: str):
+ copy_structure(src_fs, dst_fs, src_path, dst_path)
+
+ for file_path in src_fs.walk.files(src_path):
+ copy_path = combine(dst_path, frombase(src_path, file_path))
+ copy_file(src_fs, file_path, dst_fs, copy_path)
+
+
+def copy_fs(src_fs: FS, dst_fs: FS):
+ copy_dir(src_fs, "/", dst_fs, "/")
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_errors.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..5017d563377146915824c718cdcf0fb5d5b9b570
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_errors.py
@@ -0,0 +1,54 @@
+class FSError(Exception):
+ pass
+
+
+class CreateFailed(FSError):
+ pass
+
+
+class FilesystemClosed(FSError):
+ pass
+
+
+class MissingInfoNamespace(FSError):
+ pass
+
+
+class NoSysPath(FSError):
+ pass
+
+
+class OperationFailed(FSError):
+ pass
+
+
+class IllegalDestination(OperationFailed):
+ pass
+
+
+class ResourceError(FSError):
+ pass
+
+
+class ResourceNotFound(ResourceError):
+ pass
+
+
+class DirectoryExpected(ResourceError):
+ pass
+
+
+class DirectoryNotEmpty(ResourceError):
+ pass
+
+
+class FileExpected(ResourceError):
+ pass
+
+
+class DestinationExists(ResourceError):
+ pass
+
+
+class ResourceReadOnly(ResourceError):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_info.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_info.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c204c83c47e221d64fb829fbbdd25bfdfe3edaa
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_info.py
@@ -0,0 +1,75 @@
+from __future__ import annotations
+
+import typing
+from datetime import datetime, timezone
+
+from ._errors import MissingInfoNamespace
+
+if typing.TYPE_CHECKING:
+ from collections.abc import Mapping
+ from typing import Any
+
+
+def epoch_to_datetime(t: int | None) -> datetime | None:
+ """Convert epoch time to a UTC datetime."""
+ if t is None:
+ return None
+ return datetime.fromtimestamp(t, tz=timezone.utc)
+
+
+class Info:
+ __slots__ = ["raw", "namespaces"]
+
+ def __init__(self, raw_info: Mapping[str, Any]):
+ self.raw = raw_info
+ self.namespaces = frozenset(raw_info.keys())
+
+ def get(self, namespace: str, key: str, default: Any | None = None) -> Any | None:
+ try:
+ return self.raw[namespace].get(key, default)
+ except KeyError:
+ raise MissingInfoNamespace(f"Namespace {namespace!r} does not exist")
+
+ @property
+ def name(self) -> str:
+ return self.get("basic", "name")
+
+ @property
+ def is_dir(self) -> bool:
+ return self.get("basic", "is_dir")
+
+ @property
+ def is_file(self) -> bool:
+ return not self.is_dir
+
+ @property
+ def accessed(self) -> datetime | None:
+ return epoch_to_datetime(self.get("details", "accessed"))
+
+ @property
+ def modified(self) -> datetime | None:
+ return epoch_to_datetime(self.get("details", "modified"))
+
+ @property
+ def size(self) -> int | None:
+ return self.get("details", "size")
+
+ @property
+ def type(self) -> int | None:
+ return self.get("details", "type")
+
+ @property
+ def created(self) -> datetime | None:
+ return epoch_to_datetime(self.get("details", "created"))
+
+ @property
+ def metadata_changed(self) -> datetime | None:
+ return epoch_to_datetime(self.get("details", "metadata_changed"))
+
+ def __str__(self) -> str:
+ if self.is_dir:
+ return "".format(self.name)
+ else:
+ return "".format(self.name)
+
+ __repr__ = __str__
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_osfs.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_osfs.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f533bb8da4fce4f76a6a0abf6a154f74635d96c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_osfs.py
@@ -0,0 +1,164 @@
+from __future__ import annotations
+
+import errno
+import platform
+import shutil
+import stat
+import typing
+from os import PathLike
+from pathlib import Path
+
+from ._base import FS
+from ._errors import (
+ CreateFailed,
+ DirectoryExpected,
+ DirectoryNotEmpty,
+ FileExpected,
+ IllegalDestination,
+ ResourceError,
+ ResourceNotFound,
+)
+from ._info import Info
+from ._path import isbase
+
+if typing.TYPE_CHECKING:
+ from collections.abc import Collection
+ from typing import IO, Any
+
+ from ._subfs import SubFS
+
+
+_WINDOWS_PLATFORM = platform.system() == "Windows"
+
+
+class OSFS(FS):
+ """Filesystem for a directory on the local disk.
+
+ A thin layer on top of `pathlib.Path`.
+ """
+
+ def __init__(self, root: str | PathLike, create: bool = False):
+ super().__init__()
+ self._root = Path(root).resolve()
+ if create:
+ self._root.mkdir(parents=True, exist_ok=True)
+ else:
+ if not self._root.is_dir():
+ raise CreateFailed(
+ f"unable to create OSFS: {root!r} does not exist or is not a directory"
+ )
+
+ def _abs(self, rel_path: str) -> Path:
+ self.check()
+ return (self._root / rel_path.strip("/")).resolve()
+
+ def open(self, path: str, mode: str = "rb", **kwargs) -> IO[Any]:
+ try:
+ return self._abs(path).open(mode, **kwargs)
+ except FileNotFoundError:
+ raise ResourceNotFound(f"No such file or directory: {path!r}")
+
+ def exists(self, path: str) -> bool:
+ return self._abs(path).exists()
+
+ def isdir(self, path: str) -> bool:
+ return self._abs(path).is_dir()
+
+ def isfile(self, path: str) -> bool:
+ return self._abs(path).is_file()
+
+ def listdir(self, path: str) -> list[str]:
+ return [p.name for p in self._abs(path).iterdir()]
+
+ def _mkdir(self, path: str, parents: bool = False, exist_ok: bool = False) -> SubFS:
+ self._abs(path).mkdir(parents=parents, exist_ok=exist_ok)
+ return self.opendir(path)
+
+ def makedir(self, path: str, recreate: bool = False) -> SubFS:
+ return self._mkdir(path, parents=False, exist_ok=recreate)
+
+ def makedirs(self, path: str, recreate: bool = False) -> SubFS:
+ return self._mkdir(path, parents=True, exist_ok=recreate)
+
+ def getinfo(self, path: str, namespaces: Collection[str] | None = None) -> Info:
+ path = self._abs(path)
+ if not path.exists():
+ raise ResourceNotFound(f"No such file or directory: {str(path)!r}")
+ info = {
+ "basic": {
+ "name": path.name,
+ "is_dir": path.is_dir(),
+ }
+ }
+ namespaces = namespaces or ()
+ if "details" in namespaces:
+ stat_result = path.stat()
+ details = info["details"] = {
+ "accessed": stat_result.st_atime,
+ "modified": stat_result.st_mtime,
+ "size": stat_result.st_size,
+ "type": stat.S_IFMT(stat_result.st_mode),
+ "created": getattr(stat_result, "st_birthtime", None),
+ }
+ ctime_key = "created" if _WINDOWS_PLATFORM else "metadata_changed"
+ details[ctime_key] = stat_result.st_ctime
+ return Info(info)
+
+ def remove(self, path: str):
+ path = self._abs(path)
+ try:
+ path.unlink()
+ except FileNotFoundError:
+ raise ResourceNotFound(f"No such file or directory: {str(path)!r}")
+ except OSError as e:
+ if path.is_dir():
+ raise FileExpected(f"path {str(path)!r} should be a file")
+ else:
+ raise ResourceError(f"unable to remove {str(path)!r}: {e}")
+
+ def removedir(self, path: str):
+ try:
+ self._abs(path).rmdir()
+ except NotADirectoryError:
+ raise DirectoryExpected(f"path {path!r} should be a directory")
+ except OSError as e:
+ if e.errno == errno.ENOTEMPTY:
+ raise DirectoryNotEmpty(f"Directory not empty: {path!r}")
+ else:
+ raise ResourceError(f"unable to remove {path!r}: {e}")
+
+ def removetree(self, path: str):
+ shutil.rmtree(self._abs(path))
+
+ def movedir(self, src_dir: str, dst_dir: str, create: bool = False):
+ if isbase(src_dir, dst_dir):
+ raise IllegalDestination(f"cannot move {src_dir!r} to {dst_dir!r}")
+ src_path = self._abs(src_dir)
+ if not src_path.exists():
+ raise ResourceNotFound(f"Source {src_dir!r} does not exist")
+ elif not src_path.is_dir():
+ raise DirectoryExpected(f"Source {src_dir!r} should be a directory")
+ dst_path = self._abs(dst_dir)
+ if not create and not dst_path.exists():
+ raise ResourceNotFound(f"Destination {dst_dir!r} does not exist")
+ if dst_path.is_file():
+ raise DirectoryExpected(f"Destination {dst_dir!r} should be a directory")
+ if create:
+ dst_path.parent.mkdir(parents=True, exist_ok=True)
+ if dst_path.exists():
+ if list(dst_path.iterdir()):
+ raise DirectoryNotEmpty(f"Destination {dst_dir!r} is not empty")
+ elif _WINDOWS_PLATFORM:
+ # on Unix os.rename silently replaces an empty dst_dir whereas on
+ # Windows it always raises FileExistsError, empty or not.
+ dst_path.rmdir()
+ src_path.rename(dst_path)
+
+ def getsyspath(self, path: str) -> str:
+ return str(self._abs(path))
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}({str(self._root)!r})"
+
+ def __str__(self) -> str:
+ return f"<{self.__class__.__name__.lower()} '{self._root}'>"
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_path.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_path.py
new file mode 100644
index 0000000000000000000000000000000000000000..e89c00b3d97c88339187c3cda03445a7822807e0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_path.py
@@ -0,0 +1,67 @@
+import os
+import platform
+
+_WINDOWS_PLATFORM = platform.system() == "Windows"
+
+
+def combine(path1: str, path2) -> str:
+ if not path1:
+ return path2
+ return "{}/{}".format(path1.rstrip("/"), path2.lstrip("/"))
+
+
+def split(path: str) -> tuple[str, str]:
+ if "/" not in path:
+ return ("", path)
+ split = path.rsplit("/", 1)
+ return (split[0] or "/", split[1])
+
+
+def dirname(path: str) -> str:
+ return split(path)[0]
+
+
+def basename(path: str) -> str:
+ return split(path)[1]
+
+
+def forcedir(path: str) -> str:
+ # Ensure the path ends with a trailing forward slash.
+ if not path.endswith("/"):
+ return path + "/"
+ return path
+
+
+def abspath(path: str) -> str:
+ # FS objects have no concept of a *current directory*. This simply
+ # ensures the path starts with a forward slash.
+ if not path.startswith("/"):
+ return "/" + path
+ return path
+
+
+def isbase(path1: str, path2: str) -> bool:
+ # Check if `path1` is a base or prefix of `path2`.
+ _path1 = forcedir(abspath(path1))
+ _path2 = forcedir(abspath(path2))
+ return _path2.startswith(_path1)
+
+
+def frombase(path1: str, path2: str) -> str:
+ # Get the final path of `path2` that isn't in `path1`.
+ if not isbase(path1, path2):
+ raise ValueError(f"path1 must be a prefix of path2: {path1!r} vs {path2!r}")
+ return path2[len(path1) :]
+
+
+def relpath(path: str) -> str:
+ return path.lstrip("/")
+
+
+def normpath(path: str) -> str:
+ normalized = os.path.normpath(path)
+ if _WINDOWS_PLATFORM:
+ # os.path.normpath converts backslashes to forward slashes on Windows
+ # but we want forward slashes, so we convert them back
+ normalized = normalized.replace("\\", "/")
+ return normalized
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_subfs.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_subfs.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc7d8825af100384d5b4f0522eaafc6dd1a626ac
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_subfs.py
@@ -0,0 +1,92 @@
+from __future__ import annotations
+
+import typing
+from pathlib import PurePosixPath
+
+from ._base import FS
+from ._errors import DirectoryExpected, ResourceNotFound
+
+if typing.TYPE_CHECKING:
+ from collections.abc import Collection
+ from typing import IO, Any
+
+ from ._info import Info
+
+
+class SubFS(FS):
+ """Maps a sub-directory of another filesystem."""
+
+ def __init__(self, parent: FS, sub_path: str):
+ super().__init__()
+ self._parent = parent
+ self._prefix = PurePosixPath(sub_path).as_posix().rstrip("/")
+ if not parent.exists(self._prefix):
+ raise ResourceNotFound(f"No such file or directory: {sub_path!r}")
+ elif not parent.isdir(self._prefix):
+ raise DirectoryExpected(f"{sub_path!r} is not a directory")
+
+ def delegate_fs(self):
+ return self._parent
+
+ def _full(self, rel: str) -> str:
+ self.check()
+ return f"{self._prefix}/{PurePosixPath(rel).as_posix()}".lstrip("/")
+
+ def open(self, path: str, mode: str = "rb", **kwargs) -> IO[Any]:
+ return self._parent.open(self._full(path), mode, **kwargs)
+
+ def exists(self, path: str) -> bool:
+ return self._parent.exists(self._full(path))
+
+ def isdir(self, path: str) -> bool:
+ return self._parent.isdir(self._full(path))
+
+ def isfile(self, path: str) -> bool:
+ return self._parent.isfile(self._full(path))
+
+ def listdir(self, path: str) -> list[str]:
+ return self._parent.listdir(self._full(path))
+
+ def makedir(self, path: str, recreate: bool = False):
+ return self._parent.makedir(self._full(path), recreate=recreate)
+
+ def makedirs(self, path: str, recreate: bool = False):
+ return self._parent.makedirs(self._full(path), recreate=recreate)
+
+ def getinfo(self, path: str, namespaces: Collection[str] | None = None) -> Info:
+ return self._parent.getinfo(self._full(path), namespaces=namespaces)
+
+ def remove(self, path: str):
+ return self._parent.remove(self._full(path))
+
+ def removedir(self, path: str):
+ return self._parent.removedir(self._full(path))
+
+ def removetree(self, path: str):
+ return self._parent.removetree(self._full(path))
+
+ def movedir(self, src: str, dst: str, create: bool = False):
+ self._parent.movedir(self._full(src), self._full(dst), create=create)
+
+ def getsyspath(self, path: str) -> str:
+ return self._parent.getsyspath(self._full(path))
+
+ def readbytes(self, path: str) -> bytes:
+ return self._parent.readbytes(self._full(path))
+
+ def writebytes(self, path: str, data: bytes):
+ self._parent.writebytes(self._full(path), data)
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}({self._parent!r}, {self._prefix!r})"
+
+ def __str__(self) -> str:
+ return f"{self._parent}/{self._prefix}"
+
+
+class ClosingSubFS(SubFS):
+ """Like SubFS, but auto-closes the parent filesystem when closed."""
+
+ def close(self):
+ super().close()
+ self._parent.close()
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_tempfs.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_tempfs.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f968c45f0211f004491f6207545f9343a47378d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_tempfs.py
@@ -0,0 +1,34 @@
+from __future__ import annotations
+
+import shutil
+import tempfile
+
+from ._errors import OperationFailed
+from ._osfs import OSFS
+
+
+class TempFS(OSFS):
+ def __init__(self, auto_clean: bool = True, ignore_clean_errors: bool = True):
+ self.auto_clean = auto_clean
+ self.ignore_clean_errors = ignore_clean_errors
+ self._temp_dir = tempfile.mkdtemp("__temp_fs__")
+ self._cleaned = False
+ super().__init__(self._temp_dir)
+
+ def close(self):
+ if self.auto_clean:
+ self.clean()
+ super().close()
+
+ def clean(self):
+ if self._cleaned:
+ return
+
+ try:
+ shutil.rmtree(self._temp_dir)
+ except Exception as e:
+ if not self.ignore_clean_errors:
+ raise OperationFailed(
+ f"failed to remove temporary directory: {self._temp_dir!r}"
+ ) from e
+ self._cleaned = True
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_tools.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b02ac0d2533064246312e1127a1a56d2f6d6a39
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_tools.py
@@ -0,0 +1,34 @@
+from __future__ import annotations
+
+import typing
+from pathlib import PurePosixPath
+
+from ._errors import DirectoryNotEmpty
+
+if typing.TYPE_CHECKING:
+ from typing import IO
+
+ from ._base import FS
+
+
+def remove_empty(fs: FS, path: str):
+ """Remove all empty parents."""
+ path = PurePosixPath(path)
+ root = PurePosixPath("/")
+ try:
+ while path != root:
+ fs.removedir(path.as_posix())
+ path = path.parent
+ except DirectoryNotEmpty:
+ pass
+
+
+def copy_file_data(src_file: IO, dst_file: IO, chunk_size: int | None = None):
+ """Copy data from one file object to another."""
+ _chunk_size = 1024 * 1024 if chunk_size is None else chunk_size
+ read = src_file.read
+ write = dst_file.write
+ # in iter(callable, sentilel), callable is called until it returns the sentinel;
+ # this allows to copy `chunk_size` bytes at a time.
+ for chunk in iter(lambda: read(_chunk_size) or None, None):
+ write(chunk)
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_walk.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_walk.py
new file mode 100644
index 0000000000000000000000000000000000000000..e372e618a4bfa9b0087e82e875e2909475cb40ea
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_walk.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+import typing
+from collections import deque
+from collections.abc import Collection, Iterator
+
+from ._path import combine
+
+if typing.TYPE_CHECKING:
+ from typing import Callable
+
+ from ._base import FS
+ from ._info import Info
+
+
+class BoundWalker:
+ def __init__(self, fs: FS):
+ self._fs = fs
+
+ def _iter_walk(
+ self, path: str, namespaces: Collection[str] | None = None
+ ) -> Iterator[tuple[str, Info | None]]:
+ """Walk files using a *breadth first* search."""
+ queue = deque([path])
+ push = queue.appendleft
+ pop = queue.pop
+ _scan = self._fs.scandir
+ _combine = combine
+
+ while queue:
+ dir_path = pop()
+ for info in _scan(dir_path, namespaces=namespaces):
+ if info.is_dir:
+ yield dir_path, info
+ push(_combine(dir_path, info.name))
+ else:
+ yield dir_path, info
+ yield path, None
+
+ def _filter(
+ self,
+ include: Callable[[str, Info], bool] = lambda path, info: True,
+ path: str = "/",
+ namespaces: Collection[str] | None = None,
+ ) -> Iterator[str]:
+ _combine = combine
+ for path, info in self._iter_walk(path, namespaces):
+ if info is not None and include(path, info):
+ yield _combine(path, info.name)
+
+ def files(self, path: str = "/") -> Iterator[str]:
+ yield from self._filter(lambda _, info: info.is_file, path)
+
+ def dirs(self, path: str = "/") -> Iterator[str]:
+ yield from self._filter(lambda _, info: info.is_dir, path)
diff --git a/lib/python3.12/site-packages/fontTools/misc/filesystem/_zipfs.py b/lib/python3.12/site-packages/fontTools/misc/filesystem/_zipfs.py
new file mode 100644
index 0000000000000000000000000000000000000000..1635a6d0abd33798c6d2b5acedc0a1406486889f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/filesystem/_zipfs.py
@@ -0,0 +1,204 @@
+from __future__ import annotations
+
+import io
+import os
+import shutil
+import stat
+import typing
+import zipfile
+from datetime import datetime
+
+from ._base import FS
+from ._errors import FileExpected, ResourceNotFound, ResourceReadOnly
+from ._info import Info
+from ._path import dirname, forcedir, normpath, relpath
+from ._tempfs import TempFS
+
+if typing.TYPE_CHECKING:
+ from collections.abc import Collection
+ from typing import IO, Any
+
+ from ._subfs import SubFS
+
+
+class ZipFS(FS):
+ """Read and write zip files."""
+
+ def __new__(
+ cls, file: str | os.PathLike, write: bool = False, encoding: str = "utf-8"
+ ):
+ if write:
+ return WriteZipFS(file, encoding)
+ else:
+ return ReadZipFS(file, encoding)
+
+ if typing.TYPE_CHECKING:
+
+ def __init__(
+ self, file: str | os.PathLike, write: bool = False, encoding: str = "utf-8"
+ ):
+ pass
+
+
+class ReadZipFS(FS):
+ """A readable zip file."""
+
+ def __init__(self, file: str | os.PathLike, encoding: str = "utf-8"):
+ super().__init__()
+ self._file = os.fspath(file)
+ self.encoding = encoding # unused
+ self._zip = zipfile.ZipFile(file, "r")
+ self._directory_fs = None
+
+ def __repr__(self) -> str:
+ return f"ReadZipFS({self._file!r})"
+
+ def __str__(self) -> str:
+ return f""
+
+ def _path_to_zip_name(self, path: str) -> str:
+ """Convert a path to a zip file name."""
+ path = relpath(normpath(path))
+ if self._directory.isdir(path):
+ path = forcedir(path)
+ return path
+
+ @property
+ def _directory(self) -> TempFS:
+ if self._directory_fs is None:
+ self._directory_fs = _fs = TempFS()
+ for zip_name in self._zip.namelist():
+ resource_name = zip_name
+ if resource_name.endswith("/"):
+ _fs.makedirs(resource_name, recreate=True)
+ else:
+ _fs.makedirs(dirname(resource_name), recreate=True)
+ _fs.create(resource_name)
+ return self._directory_fs
+
+ def close(self):
+ super(ReadZipFS, self).close()
+ self._zip.close()
+ if self._directory_fs is not None:
+ self._directory_fs.close()
+
+ def getinfo(self, path: str, namespaces: Collection[str] | None = None) -> Info:
+ namespaces = namespaces or ()
+ raw_info = {}
+
+ if path == "/":
+ raw_info["basic"] = {"name": "", "is_dir": True}
+ if "details" in namespaces:
+ raw_info["details"] = {"type": stat.S_IFDIR}
+ else:
+ basic_info = self._directory.getinfo(path)
+ raw_info["basic"] = {"name": basic_info.name, "is_dir": basic_info.is_dir}
+
+ if "details" in namespaces:
+ zip_name = self._path_to_zip_name(path)
+ try:
+ zip_info = self._zip.getinfo(zip_name)
+ except KeyError:
+ pass
+ else:
+ if "details" in namespaces:
+ raw_info["details"] = {
+ "size": zip_info.file_size,
+ "type": int(
+ stat.S_IFDIR if basic_info.is_dir else stat.S_IFREG
+ ),
+ "modified": datetime(*zip_info.date_time).timestamp(),
+ }
+
+ return Info(raw_info)
+
+ def exists(self, path: str) -> bool:
+ self.check()
+ return self._directory.exists(path)
+
+ def isdir(self, path: str) -> bool:
+ self.check()
+ return self._directory.isdir(path)
+
+ def isfile(self, path: str) -> bool:
+ self.check()
+ return self._directory.isfile(path)
+
+ def listdir(self, path: str) -> str:
+ self.check()
+ return self._directory.listdir(path)
+
+ def makedir(self, path: str, recreate: bool = False) -> SubFS:
+ self.check()
+ raise ResourceReadOnly(path)
+
+ def makedirs(self, path: str, recreate: bool = False) -> SubFS:
+ self.check()
+ raise ResourceReadOnly(path)
+
+ def remove(self, path: str):
+ self.check()
+ raise ResourceReadOnly(path)
+
+ def removedir(self, path: str):
+ self.check()
+ raise ResourceReadOnly(path)
+
+ def removetree(self, path: str):
+ self.check()
+ raise ResourceReadOnly(path)
+
+ def movedir(self, src: str, dst: str, create: bool = False):
+ self.check()
+ raise ResourceReadOnly(src)
+
+ def readbytes(self, path: str) -> bytes:
+ self.check()
+ if not self._directory.isfile(path):
+ raise ResourceNotFound(path)
+ zip_name = self._path_to_zip_name(path)
+ zip_bytes = self._zip.read(zip_name)
+ return zip_bytes
+
+ def open(self, path: str, mode: str = "rb", **kwargs) -> IO[Any]:
+ self.check()
+ if self._directory.isdir(path):
+ raise FileExpected(f"{path!r} is a directory")
+
+ zip_mode = mode[0]
+ if zip_mode == "r" and not self._directory.exists(path):
+ raise ResourceNotFound(f"No such file or directory: {path!r}")
+
+ if any(m in mode for m in "wax+"):
+ raise ResourceReadOnly(path)
+
+ zip_name = self._path_to_zip_name(path)
+ stream = self._zip.open(zip_name, zip_mode)
+ if "b" in mode:
+ if kwargs:
+ raise ValueError("encoding args invalid for binary operation")
+ return stream
+ # Text mode
+ return io.TextIOWrapper(stream, **kwargs)
+
+
+class WriteZipFS(TempFS):
+ """A writable zip file."""
+
+ def __init__(self, file: str | os.PathLike, encoding: str = "utf-8"):
+ super().__init__()
+ self._file = os.fspath(file)
+ self.encoding = encoding # unused
+
+ def __repr__(self) -> str:
+ return f"WriteZipFS({self._file!r})"
+
+ def __str__(self) -> str:
+ return f""
+
+ def close(self):
+ base_name = os.path.splitext(self._file)[0]
+ shutil.make_archive(base_name, format="zip", root_dir=self._temp_dir)
+ if self._file != base_name + ".zip":
+ shutil.move(base_name + ".zip", self._file)
+ super().close()
diff --git a/lib/python3.12/site-packages/fontTools/misc/fixedTools.py b/lib/python3.12/site-packages/fontTools/misc/fixedTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..b7086c8ac1e0409b6223b32946727db0cbf534f3
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/fixedTools.py
@@ -0,0 +1,253 @@
+"""
+The `OpenType specification `_
+defines two fixed-point data types:
+
+``Fixed``
+ A 32-bit signed fixed-point number with a 16 bit twos-complement
+ magnitude component and 16 fractional bits.
+``F2DOT14``
+ A 16-bit signed fixed-point number with a 2 bit twos-complement
+ magnitude component and 14 fractional bits.
+
+To support reading and writing data with these data types, this module provides
+functions for converting between fixed-point, float and string representations.
+
+.. data:: MAX_F2DOT14
+
+ The maximum value that can still fit in an F2Dot14. (1.99993896484375)
+"""
+
+from .roundTools import otRound, nearestMultipleShortestRepr
+import logging
+
+log = logging.getLogger(__name__)
+
+__all__ = [
+ "MAX_F2DOT14",
+ "fixedToFloat",
+ "floatToFixed",
+ "floatToFixedToFloat",
+ "floatToFixedToStr",
+ "fixedToStr",
+ "strToFixed",
+ "strToFixedToFloat",
+ "ensureVersionIsLong",
+ "versionToFixed",
+]
+
+
+MAX_F2DOT14 = 0x7FFF / (1 << 14)
+
+
+def fixedToFloat(value: float, precisionBits: int) -> float:
+ """Converts a fixed-point number to a float given the number of
+ precision bits.
+
+ Args:
+ value (int): Number in fixed-point format.
+ precisionBits (int): Number of precision bits.
+
+ Returns:
+ Floating point value.
+
+ Examples::
+
+ >>> import math
+ >>> f = fixedToFloat(-10139, precisionBits=14)
+ >>> math.isclose(f, -0.61883544921875)
+ True
+ """
+ return value / (1 << precisionBits)
+
+
+def floatToFixed(value, precisionBits):
+ """Converts a float to a fixed-point number given the number of
+ precision bits.
+
+ Args:
+ value (float): Floating point value.
+ precisionBits (int): Number of precision bits.
+
+ Returns:
+ int: Fixed-point representation.
+
+ Examples::
+
+ >>> floatToFixed(-0.61883544921875, precisionBits=14)
+ -10139
+ >>> floatToFixed(-0.61884, precisionBits=14)
+ -10139
+ """
+ return otRound(value * (1 << precisionBits))
+
+
+def floatToFixedToFloat(value, precisionBits):
+ """Converts a float to a fixed-point number and back again.
+
+ By converting the float to fixed, rounding it, and converting it back
+ to float again, this returns a floating point values which is exactly
+ representable in fixed-point format.
+
+ Note: this **is** equivalent to ``fixedToFloat(floatToFixed(value))``.
+
+ Args:
+ value (float): The input floating point value.
+ precisionBits (int): Number of precision bits.
+
+ Returns:
+ float: The transformed and rounded value.
+
+ Examples::
+ >>> import math
+ >>> f1 = -0.61884
+ >>> f2 = floatToFixedToFloat(-0.61884, precisionBits=14)
+ >>> f1 != f2
+ True
+ >>> math.isclose(f2, -0.61883544921875)
+ True
+ """
+ scale = 1 << precisionBits
+ return otRound(value * scale) / scale
+
+
+def fixedToStr(value, precisionBits):
+ """Converts a fixed-point number to a string representing a decimal float.
+
+ This chooses the float that has the shortest decimal representation (the least
+ number of fractional decimal digits).
+
+ For example, to convert a fixed-point number in a 2.14 format, use
+ ``precisionBits=14``::
+
+ >>> fixedToStr(-10139, precisionBits=14)
+ '-0.61884'
+
+ This is pretty slow compared to the simple division used in ``fixedToFloat``.
+ Use sporadically when you need to serialize or print the fixed-point number in
+ a human-readable form.
+ It uses nearestMultipleShortestRepr under the hood.
+
+ Args:
+ value (int): The fixed-point value to convert.
+ precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+ Returns:
+ str: A string representation of the value.
+ """
+ scale = 1 << precisionBits
+ return nearestMultipleShortestRepr(value / scale, factor=1.0 / scale)
+
+
+def strToFixed(string, precisionBits):
+ """Converts a string representing a decimal float to a fixed-point number.
+
+ Args:
+ string (str): A string representing a decimal float.
+ precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+ Returns:
+ int: Fixed-point representation.
+
+ Examples::
+
+ >>> ## to convert a float string to a 2.14 fixed-point number:
+ >>> strToFixed('-0.61884', precisionBits=14)
+ -10139
+ """
+ value = float(string)
+ return otRound(value * (1 << precisionBits))
+
+
+def strToFixedToFloat(string, precisionBits):
+ """Convert a string to a decimal float with fixed-point rounding.
+
+ This first converts string to a float, then turns it into a fixed-point
+ number with ``precisionBits`` fractional binary digits, then back to a
+ float again.
+
+ This is simply a shorthand for fixedToFloat(floatToFixed(float(s))).
+
+ Args:
+ string (str): A string representing a decimal float.
+ precisionBits (int): Number of precision bits.
+
+ Returns:
+ float: The transformed and rounded value.
+
+ Examples::
+
+ >>> import math
+ >>> s = '-0.61884'
+ >>> bits = 14
+ >>> f = strToFixedToFloat(s, precisionBits=bits)
+ >>> math.isclose(f, -0.61883544921875)
+ True
+ >>> f == fixedToFloat(floatToFixed(float(s), precisionBits=bits), precisionBits=bits)
+ True
+ """
+ value = float(string)
+ scale = 1 << precisionBits
+ return otRound(value * scale) / scale
+
+
+def floatToFixedToStr(value, precisionBits):
+ """Convert float to string with fixed-point rounding.
+
+ This uses the shortest decimal representation (ie. the least
+ number of fractional decimal digits) to represent the equivalent
+ fixed-point number with ``precisionBits`` fractional binary digits.
+ It uses nearestMultipleShortestRepr under the hood.
+
+ >>> floatToFixedToStr(-0.61883544921875, precisionBits=14)
+ '-0.61884'
+
+ Args:
+ value (float): The float value to convert.
+ precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+ Returns:
+ str: A string representation of the value.
+
+ """
+ scale = 1 << precisionBits
+ return nearestMultipleShortestRepr(value, factor=1.0 / scale)
+
+
+def ensureVersionIsLong(value):
+ """Ensure a table version is an unsigned long.
+
+ OpenType table version numbers are expressed as a single unsigned long
+ comprising of an unsigned short major version and unsigned short minor
+ version. This function detects if the value to be used as a version number
+ looks too small (i.e. is less than ``0x10000``), and converts it to
+ fixed-point using :func:`floatToFixed` if so.
+
+ Args:
+ value (Number): a candidate table version number.
+
+ Returns:
+ int: A table version number, possibly corrected to fixed-point.
+ """
+ if value < 0x10000:
+ newValue = floatToFixed(value, 16)
+ log.warning(
+ "Table version value is a float: %.4f; " "fix to use hex instead: 0x%08x",
+ value,
+ newValue,
+ )
+ value = newValue
+ return value
+
+
+def versionToFixed(value):
+ """Ensure a table version number is fixed-point.
+
+ Args:
+ value (str): a candidate table version number.
+
+ Returns:
+ int: A table version number, possibly corrected to fixed-point.
+ """
+ value = int(value, 0) if value.startswith("0") else float(value)
+ value = ensureVersionIsLong(value)
+ return value
diff --git a/lib/python3.12/site-packages/fontTools/misc/intTools.py b/lib/python3.12/site-packages/fontTools/misc/intTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ca29854aae85750bdd7d25efc25ffd59392dc8e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/intTools.py
@@ -0,0 +1,25 @@
+__all__ = ["popCount", "bit_count", "bit_indices"]
+
+
+try:
+ bit_count = int.bit_count
+except AttributeError:
+
+ def bit_count(v):
+ return bin(v).count("1")
+
+
+"""Return number of 1 bits (population count) of the absolute value of an integer.
+
+See https://docs.python.org/3.10/library/stdtypes.html#int.bit_count
+"""
+popCount = bit_count # alias
+
+
+def bit_indices(v):
+ """Return list of indices where bits are set, 0 being the index of the least significant bit.
+
+ >>> bit_indices(0b101)
+ [0, 2]
+ """
+ return [i for i, b in enumerate(bin(v)[::-1]) if b == "1"]
diff --git a/lib/python3.12/site-packages/fontTools/misc/iterTools.py b/lib/python3.12/site-packages/fontTools/misc/iterTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..d7b8305322c8d7d9ef847bd626b3e4077e04e6f7
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/iterTools.py
@@ -0,0 +1,12 @@
+from itertools import *
+
+# Python 3.12:
+if "batched" not in globals():
+ # https://docs.python.org/3/library/itertools.html#itertools.batched
+ def batched(iterable, n):
+ # batched('ABCDEFG', 3) --> ABC DEF G
+ if n < 1:
+ raise ValueError("n must be at least one")
+ it = iter(iterable)
+ while batch := tuple(islice(it, n)):
+ yield batch
diff --git a/lib/python3.12/site-packages/fontTools/misc/lazyTools.py b/lib/python3.12/site-packages/fontTools/misc/lazyTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..91cb80c99262d7d3b6a998847df8de295470d9b9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/lazyTools.py
@@ -0,0 +1,42 @@
+from collections import UserDict, UserList
+
+__all__ = ["LazyDict", "LazyList"]
+
+
+class LazyDict(UserDict):
+ def __init__(self, data):
+ super().__init__()
+ self.data = data
+
+ def __getitem__(self, k):
+ v = self.data[k]
+ if callable(v):
+ v = v(k)
+ self.data[k] = v
+ return v
+
+
+class LazyList(UserList):
+ def __getitem__(self, k):
+ if isinstance(k, slice):
+ indices = range(*k.indices(len(self)))
+ return [self[i] for i in indices]
+ v = self.data[k]
+ if callable(v):
+ v = v(k)
+ self.data[k] = v
+ return v
+
+ def __add__(self, other):
+ if isinstance(other, LazyList):
+ other = list(other)
+ elif isinstance(other, list):
+ pass
+ else:
+ return NotImplemented
+ return list(self) + other
+
+ def __radd__(self, other):
+ if not isinstance(other, list):
+ return NotImplemented
+ return other + list(self)
diff --git a/lib/python3.12/site-packages/fontTools/misc/loggingTools.py b/lib/python3.12/site-packages/fontTools/misc/loggingTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..be6c2d369b62b23e82397515ad9416ed38868f1e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/loggingTools.py
@@ -0,0 +1,543 @@
+import sys
+import logging
+import timeit
+from functools import wraps
+from collections.abc import Mapping, Callable
+import warnings
+from logging import PercentStyle
+
+
+# default logging level used by Timer class
+TIME_LEVEL = logging.DEBUG
+
+# per-level format strings used by the default formatter
+# (the level name is not printed for INFO and DEBUG messages)
+DEFAULT_FORMATS = {
+ "*": "%(levelname)s: %(message)s",
+ "INFO": "%(message)s",
+ "DEBUG": "%(message)s",
+}
+
+
+class LevelFormatter(logging.Formatter):
+ """Log formatter with level-specific formatting.
+
+ Formatter class which optionally takes a dict of logging levels to
+ format strings, allowing to customise the log records appearance for
+ specific levels.
+
+
+ Attributes:
+ fmt: A dictionary mapping logging levels to format strings.
+ The ``*`` key identifies the default format string.
+ datefmt: As per py:class:`logging.Formatter`
+ style: As per py:class:`logging.Formatter`
+
+ >>> import sys
+ >>> handler = logging.StreamHandler(sys.stdout)
+ >>> formatter = LevelFormatter(
+ ... fmt={
+ ... '*': '[%(levelname)s] %(message)s',
+ ... 'DEBUG': '%(name)s [%(levelname)s] %(message)s',
+ ... 'INFO': '%(message)s',
+ ... })
+ >>> handler.setFormatter(formatter)
+ >>> log = logging.getLogger('test')
+ >>> log.setLevel(logging.DEBUG)
+ >>> log.addHandler(handler)
+ >>> log.debug('this uses a custom format string')
+ test [DEBUG] this uses a custom format string
+ >>> log.info('this also uses a custom format string')
+ this also uses a custom format string
+ >>> log.warning("this one uses the default format string")
+ [WARNING] this one uses the default format string
+ """
+
+ def __init__(self, fmt=None, datefmt=None, style="%"):
+ if style != "%":
+ raise ValueError(
+ "only '%' percent style is supported in both python 2 and 3"
+ )
+ if fmt is None:
+ fmt = DEFAULT_FORMATS
+ if isinstance(fmt, str):
+ default_format = fmt
+ custom_formats = {}
+ elif isinstance(fmt, Mapping):
+ custom_formats = dict(fmt)
+ default_format = custom_formats.pop("*", None)
+ else:
+ raise TypeError("fmt must be a str or a dict of str: %r" % fmt)
+ super(LevelFormatter, self).__init__(default_format, datefmt)
+ self.default_format = self._fmt
+ self.custom_formats = {}
+ for level, fmt in custom_formats.items():
+ level = logging._checkLevel(level)
+ self.custom_formats[level] = fmt
+
+ def format(self, record):
+ if self.custom_formats:
+ fmt = self.custom_formats.get(record.levelno, self.default_format)
+ if self._fmt != fmt:
+ self._fmt = fmt
+ # for python >= 3.2, _style needs to be set if _fmt changes
+ if PercentStyle:
+ self._style = PercentStyle(fmt)
+ return super(LevelFormatter, self).format(record)
+
+
+def configLogger(**kwargs):
+ """A more sophisticated logging system configuation manager.
+
+ This is more or less the same as :py:func:`logging.basicConfig`,
+ with some additional options and defaults.
+
+ The default behaviour is to create a ``StreamHandler`` which writes to
+ sys.stderr, set a formatter using the ``DEFAULT_FORMATS`` strings, and add
+ the handler to the top-level library logger ("fontTools").
+
+ A number of optional keyword arguments may be specified, which can alter
+ the default behaviour.
+
+ Args:
+
+ logger: Specifies the logger name or a Logger instance to be
+ configured. (Defaults to "fontTools" logger). Unlike ``basicConfig``,
+ this function can be called multiple times to reconfigure a logger.
+ If the logger or any of its children already exists before the call is
+ made, they will be reset before the new configuration is applied.
+ filename: Specifies that a ``FileHandler`` be created, using the
+ specified filename, rather than a ``StreamHandler``.
+ filemode: Specifies the mode to open the file, if filename is
+ specified. (If filemode is unspecified, it defaults to ``a``).
+ format: Use the specified format string for the handler. This
+ argument also accepts a dictionary of format strings keyed by
+ level name, to allow customising the records appearance for
+ specific levels. The special ``'*'`` key is for 'any other' level.
+ datefmt: Use the specified date/time format.
+ level: Set the logger level to the specified level.
+ stream: Use the specified stream to initialize the StreamHandler. Note
+ that this argument is incompatible with ``filename`` - if both
+ are present, ``stream`` is ignored.
+ handlers: If specified, this should be an iterable of already created
+ handlers, which will be added to the logger. Any handler in the
+ list which does not have a formatter assigned will be assigned the
+ formatter created in this function.
+ filters: If specified, this should be an iterable of already created
+ filters. If the ``handlers`` do not already have filters assigned,
+ these filters will be added to them.
+ propagate: All loggers have a ``propagate`` attribute which determines
+ whether to continue searching for handlers up the logging hierarchy.
+ If not provided, the "propagate" attribute will be set to ``False``.
+ """
+ # using kwargs to enforce keyword-only arguments in py2.
+ handlers = kwargs.pop("handlers", None)
+ if handlers is None:
+ if "stream" in kwargs and "filename" in kwargs:
+ raise ValueError(
+ "'stream' and 'filename' should not be " "specified together"
+ )
+ else:
+ if "stream" in kwargs or "filename" in kwargs:
+ raise ValueError(
+ "'stream' or 'filename' should not be "
+ "specified together with 'handlers'"
+ )
+ if handlers is None:
+ filename = kwargs.pop("filename", None)
+ mode = kwargs.pop("filemode", "a")
+ if filename:
+ h = logging.FileHandler(filename, mode)
+ else:
+ stream = kwargs.pop("stream", None)
+ h = logging.StreamHandler(stream)
+ handlers = [h]
+ # By default, the top-level library logger is configured.
+ logger = kwargs.pop("logger", "fontTools")
+ if not logger or isinstance(logger, str):
+ # empty "" or None means the 'root' logger
+ logger = logging.getLogger(logger)
+ # before (re)configuring, reset named logger and its children (if exist)
+ _resetExistingLoggers(parent=logger.name)
+ # use DEFAULT_FORMATS if 'format' is None
+ fs = kwargs.pop("format", None)
+ dfs = kwargs.pop("datefmt", None)
+ # XXX: '%' is the only format style supported on both py2 and 3
+ style = kwargs.pop("style", "%")
+ fmt = LevelFormatter(fs, dfs, style)
+ filters = kwargs.pop("filters", [])
+ for h in handlers:
+ if h.formatter is None:
+ h.setFormatter(fmt)
+ if not h.filters:
+ for f in filters:
+ h.addFilter(f)
+ logger.addHandler(h)
+ if logger.name != "root":
+ # stop searching up the hierarchy for handlers
+ logger.propagate = kwargs.pop("propagate", False)
+ # set a custom severity level
+ level = kwargs.pop("level", None)
+ if level is not None:
+ logger.setLevel(level)
+ if kwargs:
+ keys = ", ".join(kwargs.keys())
+ raise ValueError("Unrecognised argument(s): %s" % keys)
+
+
+def _resetExistingLoggers(parent="root"):
+ """Reset the logger named 'parent' and all its children to their initial
+ state, if they already exist in the current configuration.
+ """
+ root = logging.root
+ # get sorted list of all existing loggers
+ existing = sorted(root.manager.loggerDict.keys())
+ if parent == "root":
+ # all the existing loggers are children of 'root'
+ loggers_to_reset = [parent] + existing
+ elif parent not in existing:
+ # nothing to do
+ return
+ elif parent in existing:
+ loggers_to_reset = [parent]
+ # collect children, starting with the entry after parent name
+ i = existing.index(parent) + 1
+ prefixed = parent + "."
+ pflen = len(prefixed)
+ num_existing = len(existing)
+ while i < num_existing:
+ if existing[i][:pflen] == prefixed:
+ loggers_to_reset.append(existing[i])
+ i += 1
+ for name in loggers_to_reset:
+ if name == "root":
+ root.setLevel(logging.WARNING)
+ for h in root.handlers[:]:
+ root.removeHandler(h)
+ for f in root.filters[:]:
+ root.removeFilters(f)
+ root.disabled = False
+ else:
+ logger = root.manager.loggerDict[name]
+ logger.level = logging.NOTSET
+ logger.handlers = []
+ logger.filters = []
+ logger.propagate = True
+ logger.disabled = False
+
+
+class Timer(object):
+ """Keeps track of overall time and split/lap times.
+
+ >>> import time
+ >>> timer = Timer()
+ >>> time.sleep(0.01)
+ >>> print("First lap:", timer.split())
+ First lap: ...
+ >>> time.sleep(0.02)
+ >>> print("Second lap:", timer.split())
+ Second lap: ...
+ >>> print("Overall time:", timer.time())
+ Overall time: ...
+
+ Can be used as a context manager inside with-statements.
+
+ >>> with Timer() as t:
+ ... time.sleep(0.01)
+ >>> print("%0.3f seconds" % t.elapsed)
+ 0... seconds
+
+ If initialised with a logger, it can log the elapsed time automatically
+ upon exiting the with-statement.
+
+ >>> import logging
+ >>> log = logging.getLogger("my-fancy-timer-logger")
+ >>> configLogger(logger=log, level="DEBUG", format="%(message)s", stream=sys.stdout)
+ >>> with Timer(log, 'do something'):
+ ... time.sleep(0.01)
+ Took ... to do something
+
+ The same Timer instance, holding a reference to a logger, can be reused
+ in multiple with-statements, optionally with different messages or levels.
+
+ >>> timer = Timer(log)
+ >>> with timer():
+ ... time.sleep(0.01)
+ elapsed time: ...s
+ >>> with timer('redo it', level=logging.INFO):
+ ... time.sleep(0.02)
+ Took ... to redo it
+
+ It can also be used as a function decorator to log the time elapsed to run
+ the decorated function.
+
+ >>> @timer()
+ ... def test1():
+ ... time.sleep(0.01)
+ >>> @timer('run test 2', level=logging.INFO)
+ ... def test2():
+ ... time.sleep(0.02)
+ >>> test1()
+ Took ... to run 'test1'
+ >>> test2()
+ Took ... to run test 2
+ """
+
+ # timeit.default_timer choses the most accurate clock for each platform
+ _time: Callable[[], float] = staticmethod(timeit.default_timer)
+ default_msg = "elapsed time: %(time).3fs"
+ default_format = "Took %(time).3fs to %(msg)s"
+
+ def __init__(self, logger=None, msg=None, level=None, start=None):
+ self.reset(start)
+ if logger is None:
+ for arg in ("msg", "level"):
+ if locals().get(arg) is not None:
+ raise ValueError("'%s' can't be specified without a 'logger'" % arg)
+ self.logger = logger
+ self.level = level if level is not None else TIME_LEVEL
+ self.msg = msg
+
+ def reset(self, start=None):
+ """Reset timer to 'start_time' or the current time."""
+ if start is None:
+ self.start = self._time()
+ else:
+ self.start = start
+ self.last = self.start
+ self.elapsed = 0.0
+
+ def time(self):
+ """Return the overall time (in seconds) since the timer started."""
+ return self._time() - self.start
+
+ def split(self):
+ """Split and return the lap time (in seconds) in between splits."""
+ current = self._time()
+ self.elapsed = current - self.last
+ self.last = current
+ return self.elapsed
+
+ def formatTime(self, msg, time):
+ """Format 'time' value in 'msg' and return formatted string.
+ If 'msg' contains a '%(time)' format string, try to use that.
+ Otherwise, use the predefined 'default_format'.
+ If 'msg' is empty or None, fall back to 'default_msg'.
+ """
+ if not msg:
+ msg = self.default_msg
+ if msg.find("%(time)") < 0:
+ msg = self.default_format % {"msg": msg, "time": time}
+ else:
+ try:
+ msg = msg % {"time": time}
+ except (KeyError, ValueError):
+ pass # skip if the format string is malformed
+ return msg
+
+ def __enter__(self):
+ """Start a new lap"""
+ self.last = self._time()
+ self.elapsed = 0.0
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """End the current lap. If timer has a logger, log the time elapsed,
+ using the format string in self.msg (or the default one).
+ """
+ time = self.split()
+ if self.logger is None or exc_type:
+ # if there's no logger attached, or if any exception occurred in
+ # the with-statement, exit without logging the time
+ return
+ message = self.formatTime(self.msg, time)
+ # Allow log handlers to see the individual parts to facilitate things
+ # like a server accumulating aggregate stats.
+ msg_parts = {"msg": self.msg, "time": time}
+ self.logger.log(self.level, message, msg_parts)
+
+ def __call__(self, func_or_msg=None, **kwargs):
+ """If the first argument is a function, return a decorator which runs
+ the wrapped function inside Timer's context manager.
+ Otherwise, treat the first argument as a 'msg' string and return an updated
+ Timer instance, referencing the same logger.
+ A 'level' keyword can also be passed to override self.level.
+ """
+ if isinstance(func_or_msg, Callable):
+ func = func_or_msg
+ # use the function name when no explicit 'msg' is provided
+ if not self.msg:
+ self.msg = "run '%s'" % func.__name__
+
+ @wraps(func)
+ def wrapper(*args, **kwds):
+ with self:
+ return func(*args, **kwds)
+
+ return wrapper
+ else:
+ msg = func_or_msg or kwargs.get("msg")
+ level = kwargs.get("level", self.level)
+ return self.__class__(self.logger, msg, level)
+
+ def __float__(self):
+ return self.elapsed
+
+ def __int__(self):
+ return int(self.elapsed)
+
+ def __str__(self):
+ return "%.3f" % self.elapsed
+
+
+class ChannelsFilter(logging.Filter):
+ """Provides a hierarchical filter for log entries based on channel names.
+
+ Filters out records emitted from a list of enabled channel names,
+ including their children. It works the same as the ``logging.Filter``
+ class, but allows the user to specify multiple channel names.
+
+ >>> import sys
+ >>> handler = logging.StreamHandler(sys.stdout)
+ >>> handler.setFormatter(logging.Formatter("%(message)s"))
+ >>> filter = ChannelsFilter("A.B", "C.D")
+ >>> handler.addFilter(filter)
+ >>> root = logging.getLogger()
+ >>> root.addHandler(handler)
+ >>> root.setLevel(level=logging.DEBUG)
+ >>> logging.getLogger('A.B').debug('this record passes through')
+ this record passes through
+ >>> logging.getLogger('A.B.C').debug('records from children also pass')
+ records from children also pass
+ >>> logging.getLogger('C.D').debug('this one as well')
+ this one as well
+ >>> logging.getLogger('A.B.').debug('also this one')
+ also this one
+ >>> logging.getLogger('A.F').debug('but this one does not!')
+ >>> logging.getLogger('C.DE').debug('neither this one!')
+ """
+
+ def __init__(self, *names):
+ self.names = names
+ self.num = len(names)
+ self.lengths = {n: len(n) for n in names}
+
+ def filter(self, record):
+ if self.num == 0:
+ return True
+ for name in self.names:
+ nlen = self.lengths[name]
+ if name == record.name:
+ return True
+ elif record.name.find(name, 0, nlen) == 0 and record.name[nlen] == ".":
+ return True
+ return False
+
+
+class CapturingLogHandler(logging.Handler):
+ def __init__(self, logger, level):
+ super(CapturingLogHandler, self).__init__(level=level)
+ self.records = []
+ if isinstance(logger, str):
+ self.logger = logging.getLogger(logger)
+ else:
+ self.logger = logger
+
+ def __enter__(self):
+ self.original_disabled = self.logger.disabled
+ self.original_level = self.logger.level
+ self.original_propagate = self.logger.propagate
+
+ self.logger.addHandler(self)
+ self.logger.setLevel(self.level)
+ self.logger.disabled = False
+ self.logger.propagate = False
+
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.logger.removeHandler(self)
+ self.logger.setLevel(self.original_level)
+ self.logger.disabled = self.original_disabled
+ self.logger.propagate = self.original_propagate
+
+ return self
+
+ def emit(self, record):
+ self.records.append(record)
+
+ def assertRegex(self, regexp, msg=None):
+ import re
+
+ pattern = re.compile(regexp)
+ for r in self.records:
+ if pattern.search(r.getMessage()):
+ return True
+ if msg is None:
+ msg = "Pattern '%s' not found in logger records" % regexp
+ assert 0, msg
+
+
+class LogMixin(object):
+ """Mixin class that adds logging functionality to another class.
+
+ You can define a new class that subclasses from ``LogMixin`` as well as
+ other base classes through multiple inheritance.
+ All instances of that class will have a ``log`` property that returns
+ a ``logging.Logger`` named after their respective ``.``.
+
+ For example:
+
+ >>> class BaseClass(object):
+ ... pass
+ >>> class MyClass(LogMixin, BaseClass):
+ ... pass
+ >>> a = MyClass()
+ >>> isinstance(a.log, logging.Logger)
+ True
+ >>> print(a.log.name)
+ fontTools.misc.loggingTools.MyClass
+ >>> class AnotherClass(MyClass):
+ ... pass
+ >>> b = AnotherClass()
+ >>> isinstance(b.log, logging.Logger)
+ True
+ >>> print(b.log.name)
+ fontTools.misc.loggingTools.AnotherClass
+ """
+
+ @property
+ def log(self):
+ if not hasattr(self, "_log"):
+ name = ".".join((self.__class__.__module__, self.__class__.__name__))
+ self._log = logging.getLogger(name)
+ return self._log
+
+
+def deprecateArgument(name, msg, category=UserWarning):
+ """Raise a warning about deprecated function argument 'name'."""
+ warnings.warn("%r is deprecated; %s" % (name, msg), category=category, stacklevel=3)
+
+
+def deprecateFunction(msg, category=UserWarning):
+ """Decorator to raise a warning when a deprecated function is called."""
+
+ def decorator(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ warnings.warn(
+ "%r is deprecated; %s" % (func.__name__, msg),
+ category=category,
+ stacklevel=2,
+ )
+ return func(*args, **kwargs)
+
+ return wrapper
+
+ return decorator
+
+
+if __name__ == "__main__":
+ import doctest
+
+ sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/macCreatorType.py b/lib/python3.12/site-packages/fontTools/misc/macCreatorType.py
new file mode 100644
index 0000000000000000000000000000000000000000..36b15aca51c564c7a9c05ebfcff8f17925ec1630
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/macCreatorType.py
@@ -0,0 +1,56 @@
+from fontTools.misc.textTools import Tag, bytesjoin, strjoin
+
+try:
+ import xattr
+except ImportError:
+ xattr = None
+
+
+def _reverseString(s):
+ s = list(s)
+ s.reverse()
+ return strjoin(s)
+
+
+def getMacCreatorAndType(path):
+ """Returns file creator and file type codes for a path.
+
+ Args:
+ path (str): A file path.
+
+ Returns:
+ A tuple of two :py:class:`fontTools.textTools.Tag` objects, the first
+ representing the file creator and the second representing the
+ file type.
+ """
+ if xattr is not None:
+ try:
+ finderInfo = xattr.getxattr(path, "com.apple.FinderInfo")
+ except (KeyError, IOError):
+ pass
+ else:
+ fileType = Tag(finderInfo[:4])
+ fileCreator = Tag(finderInfo[4:8])
+ return fileCreator, fileType
+ return None, None
+
+
+def setMacCreatorAndType(path, fileCreator, fileType):
+ """Set file creator and file type codes for a path.
+
+ Note that if the ``xattr`` module is not installed, no action is
+ taken but no error is raised.
+
+ Args:
+ path (str): A file path.
+ fileCreator: A four-character file creator tag.
+ fileType: A four-character file type tag.
+
+ """
+ if xattr is not None:
+ from fontTools.misc.textTools import pad
+
+ if not all(len(s) == 4 for s in (fileCreator, fileType)):
+ raise TypeError("arg must be string of 4 chars")
+ finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
+ xattr.setxattr(path, "com.apple.FinderInfo", finderInfo)
diff --git a/lib/python3.12/site-packages/fontTools/misc/macRes.py b/lib/python3.12/site-packages/fontTools/misc/macRes.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5a6cfe4789a351204d0ce6fa2abb5651487c5c0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/macRes.py
@@ -0,0 +1,261 @@
+from io import BytesIO
+import struct
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytesjoin, tostr
+from collections import OrderedDict
+from collections.abc import MutableMapping
+
+
+class ResourceError(Exception):
+ pass
+
+
+class ResourceReader(MutableMapping):
+ """Reader for Mac OS resource forks.
+
+ Parses a resource fork and returns resources according to their type.
+ If run on OS X, this will open the resource fork in the filesystem.
+ Otherwise, it will open the file itself and attempt to read it as
+ though it were a resource fork.
+
+ The returned object can be indexed by type and iterated over,
+ returning in each case a list of py:class:`Resource` objects
+ representing all the resources of a certain type.
+
+ """
+
+ def __init__(self, fileOrPath):
+ """Open a file
+
+ Args:
+ fileOrPath: Either an object supporting a ``read`` method, an
+ ``os.PathLike`` object, or a string.
+ """
+ self._resources = OrderedDict()
+ if hasattr(fileOrPath, "read"):
+ self.file = fileOrPath
+ else:
+ try:
+ # try reading from the resource fork (only works on OS X)
+ self.file = self.openResourceFork(fileOrPath)
+ self._readFile()
+ return
+ except (ResourceError, IOError):
+ # if it fails, use the data fork
+ self.file = self.openDataFork(fileOrPath)
+ self._readFile()
+
+ @staticmethod
+ def openResourceFork(path):
+ if hasattr(path, "__fspath__"): # support os.PathLike objects
+ path = path.__fspath__()
+ with open(path + "/..namedfork/rsrc", "rb") as resfork:
+ data = resfork.read()
+ infile = BytesIO(data)
+ infile.name = path
+ return infile
+
+ @staticmethod
+ def openDataFork(path):
+ with open(path, "rb") as datafork:
+ data = datafork.read()
+ infile = BytesIO(data)
+ infile.name = path
+ return infile
+
+ def _readFile(self):
+ self._readHeaderAndMap()
+ self._readTypeList()
+
+ def _read(self, numBytes, offset=None):
+ if offset is not None:
+ try:
+ self.file.seek(offset)
+ except OverflowError:
+ raise ResourceError("Failed to seek offset ('offset' is too large)")
+ if self.file.tell() != offset:
+ raise ResourceError("Failed to seek offset (reached EOF)")
+ try:
+ data = self.file.read(numBytes)
+ except OverflowError:
+ raise ResourceError("Cannot read resource ('numBytes' is too large)")
+ if len(data) != numBytes:
+ raise ResourceError("Cannot read resource (not enough data)")
+ return data
+
+ def _readHeaderAndMap(self):
+ self.file.seek(0)
+ headerData = self._read(ResourceForkHeaderSize)
+ sstruct.unpack(ResourceForkHeader, headerData, self)
+ # seek to resource map, skip reserved
+ mapOffset = self.mapOffset + 22
+ resourceMapData = self._read(ResourceMapHeaderSize, mapOffset)
+ sstruct.unpack(ResourceMapHeader, resourceMapData, self)
+ self.absTypeListOffset = self.mapOffset + self.typeListOffset
+ self.absNameListOffset = self.mapOffset + self.nameListOffset
+
+ def _readTypeList(self):
+ absTypeListOffset = self.absTypeListOffset
+ numTypesData = self._read(2, absTypeListOffset)
+ (self.numTypes,) = struct.unpack(">H", numTypesData)
+ absTypeListOffset2 = absTypeListOffset + 2
+ for i in range(self.numTypes + 1):
+ resTypeItemOffset = absTypeListOffset2 + ResourceTypeItemSize * i
+ resTypeItemData = self._read(ResourceTypeItemSize, resTypeItemOffset)
+ item = sstruct.unpack(ResourceTypeItem, resTypeItemData)
+ resType = tostr(item["type"], encoding="mac-roman")
+ refListOffset = absTypeListOffset + item["refListOffset"]
+ numRes = item["numRes"] + 1
+ resources = self._readReferenceList(resType, refListOffset, numRes)
+ self._resources[resType] = resources
+
+ def _readReferenceList(self, resType, refListOffset, numRes):
+ resources = []
+ for i in range(numRes):
+ refOffset = refListOffset + ResourceRefItemSize * i
+ refData = self._read(ResourceRefItemSize, refOffset)
+ res = Resource(resType)
+ res.decompile(refData, self)
+ resources.append(res)
+ return resources
+
+ def __getitem__(self, resType):
+ return self._resources[resType]
+
+ def __delitem__(self, resType):
+ del self._resources[resType]
+
+ def __setitem__(self, resType, resources):
+ self._resources[resType] = resources
+
+ def __len__(self):
+ return len(self._resources)
+
+ def __iter__(self):
+ return iter(self._resources)
+
+ def keys(self):
+ return self._resources.keys()
+
+ @property
+ def types(self):
+ """A list of the types of resources in the resource fork."""
+ return list(self._resources.keys())
+
+ def countResources(self, resType):
+ """Return the number of resources of a given type."""
+ try:
+ return len(self[resType])
+ except KeyError:
+ return 0
+
+ def getIndices(self, resType):
+ """Returns a list of indices of resources of a given type."""
+ numRes = self.countResources(resType)
+ if numRes:
+ return list(range(1, numRes + 1))
+ else:
+ return []
+
+ def getNames(self, resType):
+ """Return list of names of all resources of a given type."""
+ return [res.name for res in self.get(resType, []) if res.name is not None]
+
+ def getIndResource(self, resType, index):
+ """Return resource of given type located at an index ranging from 1
+ to the number of resources for that type, or None if not found.
+ """
+ if index < 1:
+ return None
+ try:
+ res = self[resType][index - 1]
+ except (KeyError, IndexError):
+ return None
+ return res
+
+ def getNamedResource(self, resType, name):
+ """Return the named resource of given type, else return None."""
+ name = tostr(name, encoding="mac-roman")
+ for res in self.get(resType, []):
+ if res.name == name:
+ return res
+ return None
+
+ def close(self):
+ if not self.file.closed:
+ self.file.close()
+
+
+class Resource(object):
+ """Represents a resource stored within a resource fork.
+
+ Attributes:
+ type: resource type.
+ data: resource data.
+ id: ID.
+ name: resource name.
+ attr: attributes.
+ """
+
+ def __init__(
+ self, resType=None, resData=None, resID=None, resName=None, resAttr=None
+ ):
+ self.type = resType
+ self.data = resData
+ self.id = resID
+ self.name = resName
+ self.attr = resAttr
+
+ def decompile(self, refData, reader):
+ sstruct.unpack(ResourceRefItem, refData, self)
+ # interpret 3-byte dataOffset as (padded) ULONG to unpack it with struct
+ (self.dataOffset,) = struct.unpack(">L", bytesjoin([b"\0", self.dataOffset]))
+ absDataOffset = reader.dataOffset + self.dataOffset
+ (dataLength,) = struct.unpack(">L", reader._read(4, absDataOffset))
+ self.data = reader._read(dataLength)
+ if self.nameOffset == -1:
+ return
+ absNameOffset = reader.absNameListOffset + self.nameOffset
+ (nameLength,) = struct.unpack("B", reader._read(1, absNameOffset))
+ (name,) = struct.unpack(">%ss" % nameLength, reader._read(nameLength))
+ self.name = tostr(name, encoding="mac-roman")
+
+
+ResourceForkHeader = """
+ > # big endian
+ dataOffset: L
+ mapOffset: L
+ dataLen: L
+ mapLen: L
+"""
+
+ResourceForkHeaderSize = sstruct.calcsize(ResourceForkHeader)
+
+ResourceMapHeader = """
+ > # big endian
+ attr: H
+ typeListOffset: H
+ nameListOffset: H
+"""
+
+ResourceMapHeaderSize = sstruct.calcsize(ResourceMapHeader)
+
+ResourceTypeItem = """
+ > # big endian
+ type: 4s
+ numRes: H
+ refListOffset: H
+"""
+
+ResourceTypeItemSize = sstruct.calcsize(ResourceTypeItem)
+
+ResourceRefItem = """
+ > # big endian
+ id: h
+ nameOffset: h
+ attr: B
+ dataOffset: 3s
+ reserved: L
+"""
+
+ResourceRefItemSize = sstruct.calcsize(ResourceRefItem)
diff --git a/lib/python3.12/site-packages/fontTools/misc/plistlib/__init__.py b/lib/python3.12/site-packages/fontTools/misc/plistlib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..066eef38fc720265366afee9a8cd415fc560459e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/plistlib/__init__.py
@@ -0,0 +1,681 @@
+import collections.abc
+import re
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ List,
+ Mapping,
+ MutableMapping,
+ Optional,
+ Sequence,
+ Type,
+ Union,
+ IO,
+)
+import warnings
+from io import BytesIO
+from datetime import datetime
+from base64 import b64encode, b64decode
+from numbers import Integral
+from types import SimpleNamespace
+from functools import singledispatch
+
+from fontTools.misc import etree
+
+from fontTools.misc.textTools import tostr
+
+
+# By default, we
+# - deserialize elements as bytes and
+# - serialize bytes as elements.
+# Before, on Python 2, we
+# - deserialized elements as plistlib.Data objects, in order to
+# distinguish them from the built-in str type (which is bytes on python2)
+# - serialized bytes as elements (they must have only contained
+# ASCII characters in this case)
+# You can pass use_builtin_types=[True|False] to the load/dump etc. functions
+# to enforce a specific treatment.
+# NOTE that unicode type always maps to element, and plistlib.Data
+# always maps to element, regardless of use_builtin_types.
+USE_BUILTIN_TYPES = True
+
+XML_DECLARATION = b""""""
+
+PLIST_DOCTYPE = (
+ b''
+)
+
+
+# Date should conform to a subset of ISO 8601:
+# YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'
+_date_parser = re.compile(
+ r"(?P\d\d\d\d)"
+ r"(?:-(?P\d\d)"
+ r"(?:-(?P\d\d)"
+ r"(?:T(?P\d\d)"
+ r"(?::(?P\d\d)"
+ r"(?::(?P\d\d))"
+ r"?)?)?)?)?Z",
+ re.ASCII,
+)
+
+
+def _date_from_string(s: str) -> datetime:
+ order = ("year", "month", "day", "hour", "minute", "second")
+ m = _date_parser.match(s)
+ if m is None:
+ raise ValueError(f"Expected ISO 8601 date string, but got '{s:r}'.")
+ gd = m.groupdict()
+ lst = []
+ for key in order:
+ val = gd[key]
+ if val is None:
+ break
+ lst.append(int(val))
+ # NOTE: mypy doesn't know that lst is 6 elements long.
+ return datetime(*lst) # type:ignore
+
+
+def _date_to_string(d: datetime) -> str:
+ return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
+ d.year,
+ d.month,
+ d.day,
+ d.hour,
+ d.minute,
+ d.second,
+ )
+
+
+class Data:
+ """Represents binary data when ``use_builtin_types=False.``
+
+ This class wraps binary data loaded from a plist file when the
+ ``use_builtin_types`` argument to the loading function (:py:func:`fromtree`,
+ :py:func:`load`, :py:func:`loads`) is false.
+
+ The actual binary data is retrieved using the ``data`` attribute.
+ """
+
+ def __init__(self, data: bytes) -> None:
+ if not isinstance(data, bytes):
+ raise TypeError("Expected bytes, found %s" % type(data).__name__)
+ self.data = data
+
+ @classmethod
+ def fromBase64(cls, data: Union[bytes, str]) -> "Data":
+ return cls(b64decode(data))
+
+ def asBase64(self, maxlinelength: int = 76, indent_level: int = 1) -> bytes:
+ return _encode_base64(
+ self.data, maxlinelength=maxlinelength, indent_level=indent_level
+ )
+
+ def __eq__(self, other: Any) -> bool:
+ if isinstance(other, self.__class__):
+ return self.data == other.data
+ elif isinstance(other, bytes):
+ return self.data == other
+ else:
+ return NotImplemented
+
+ def __repr__(self) -> str:
+ return "%s(%s)" % (self.__class__.__name__, repr(self.data))
+
+
+def _encode_base64(
+ data: bytes, maxlinelength: Optional[int] = 76, indent_level: int = 1
+) -> bytes:
+ data = b64encode(data)
+ if data and maxlinelength:
+ # split into multiple lines right-justified to 'maxlinelength' chars
+ indent = b"\n" + b" " * indent_level
+ max_length = max(16, maxlinelength - len(indent))
+ chunks = []
+ for i in range(0, len(data), max_length):
+ chunks.append(indent)
+ chunks.append(data[i : i + max_length])
+ chunks.append(indent)
+ data = b"".join(chunks)
+ return data
+
+
+# Mypy does not support recursive type aliases as of 0.782, Pylance does.
+# https://github.com/python/mypy/issues/731
+# https://devblogs.microsoft.com/python/pylance-introduces-five-new-features-that-enable-type-magic-for-python-developers/#1-support-for-recursive-type-aliases
+PlistEncodable = Union[
+ bool,
+ bytes,
+ Data,
+ datetime,
+ float,
+ Integral,
+ Mapping[str, Any],
+ Sequence[Any],
+ str,
+]
+
+
+class PlistTarget:
+ """Event handler using the ElementTree Target API that can be
+ passed to a XMLParser to produce property list objects from XML.
+ It is based on the CPython plistlib module's _PlistParser class,
+ but does not use the expat parser.
+
+ >>> from fontTools.misc import etree
+ >>> parser = etree.XMLParser(target=PlistTarget())
+ >>> result = etree.XML(
+ ... ""
+ ... " something"
+ ... " blah"
+ ... "",
+ ... parser=parser)
+ >>> result == {"something": "blah"}
+ True
+
+ Links:
+ https://github.com/python/cpython/blob/main/Lib/plistlib.py
+ http://lxml.de/parsing.html#the-target-parser-interface
+ """
+
+ def __init__(
+ self,
+ use_builtin_types: Optional[bool] = None,
+ dict_type: Type[MutableMapping[str, Any]] = dict,
+ ) -> None:
+ self.stack: List[PlistEncodable] = []
+ self.current_key: Optional[str] = None
+ self.root: Optional[PlistEncodable] = None
+ if use_builtin_types is None:
+ self._use_builtin_types = USE_BUILTIN_TYPES
+ else:
+ if use_builtin_types is False:
+ warnings.warn(
+ "Setting use_builtin_types to False is deprecated and will be "
+ "removed soon.",
+ DeprecationWarning,
+ )
+ self._use_builtin_types = use_builtin_types
+ self._dict_type = dict_type
+
+ def start(self, tag: str, attrib: Mapping[str, str]) -> None:
+ self._data: List[str] = []
+ handler = _TARGET_START_HANDLERS.get(tag)
+ if handler is not None:
+ handler(self)
+
+ def end(self, tag: str) -> None:
+ handler = _TARGET_END_HANDLERS.get(tag)
+ if handler is not None:
+ handler(self)
+
+ def data(self, data: str) -> None:
+ self._data.append(data)
+
+ def close(self) -> PlistEncodable:
+ if self.root is None:
+ raise ValueError("No root set.")
+ return self.root
+
+ # helpers
+
+ def add_object(self, value: PlistEncodable) -> None:
+ if self.current_key is not None:
+ stack_top = self.stack[-1]
+ if not isinstance(stack_top, collections.abc.MutableMapping):
+ raise ValueError("unexpected element: %r" % stack_top)
+ stack_top[self.current_key] = value
+ self.current_key = None
+ elif not self.stack:
+ # this is the root object
+ self.root = value
+ else:
+ stack_top = self.stack[-1]
+ if not isinstance(stack_top, list):
+ raise ValueError("unexpected element: %r" % stack_top)
+ stack_top.append(value)
+
+ def get_data(self) -> str:
+ data = "".join(self._data)
+ self._data = []
+ return data
+
+
+# event handlers
+
+
+def start_dict(self: PlistTarget) -> None:
+ d = self._dict_type()
+ self.add_object(d)
+ self.stack.append(d)
+
+
+def end_dict(self: PlistTarget) -> None:
+ if self.current_key:
+ raise ValueError("missing value for key '%s'" % self.current_key)
+ self.stack.pop()
+
+
+def end_key(self: PlistTarget) -> None:
+ if self.current_key or not isinstance(self.stack[-1], collections.abc.Mapping):
+ raise ValueError("unexpected key")
+ self.current_key = self.get_data()
+
+
+def start_array(self: PlistTarget) -> None:
+ a: List[PlistEncodable] = []
+ self.add_object(a)
+ self.stack.append(a)
+
+
+def end_array(self: PlistTarget) -> None:
+ self.stack.pop()
+
+
+def end_true(self: PlistTarget) -> None:
+ self.add_object(True)
+
+
+def end_false(self: PlistTarget) -> None:
+ self.add_object(False)
+
+
+def end_integer(self: PlistTarget) -> None:
+ self.add_object(int(self.get_data()))
+
+
+def end_real(self: PlistTarget) -> None:
+ self.add_object(float(self.get_data()))
+
+
+def end_string(self: PlistTarget) -> None:
+ self.add_object(self.get_data())
+
+
+def end_data(self: PlistTarget) -> None:
+ if self._use_builtin_types:
+ self.add_object(b64decode(self.get_data()))
+ else:
+ self.add_object(Data.fromBase64(self.get_data()))
+
+
+def end_date(self: PlistTarget) -> None:
+ self.add_object(_date_from_string(self.get_data()))
+
+
+_TARGET_START_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
+ "dict": start_dict,
+ "array": start_array,
+}
+
+_TARGET_END_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
+ "dict": end_dict,
+ "array": end_array,
+ "key": end_key,
+ "true": end_true,
+ "false": end_false,
+ "integer": end_integer,
+ "real": end_real,
+ "string": end_string,
+ "data": end_data,
+ "date": end_date,
+}
+
+
+# functions to build element tree from plist data
+
+
+def _string_element(value: str, ctx: SimpleNamespace) -> etree.Element:
+ el = etree.Element("string")
+ el.text = value
+ return el
+
+
+def _bool_element(value: bool, ctx: SimpleNamespace) -> etree.Element:
+ if value:
+ return etree.Element("true")
+ return etree.Element("false")
+
+
+def _integer_element(value: int, ctx: SimpleNamespace) -> etree.Element:
+ if -1 << 63 <= value < 1 << 64:
+ el = etree.Element("integer")
+ el.text = "%d" % value
+ return el
+ raise OverflowError(value)
+
+
+def _real_element(value: float, ctx: SimpleNamespace) -> etree.Element:
+ el = etree.Element("real")
+ el.text = repr(value)
+ return el
+
+
+def _dict_element(
+ d: Mapping[str, PlistEncodable], ctx: SimpleNamespace
+) -> etree.Element:
+ el = etree.Element("dict")
+ items = d.items()
+ if ctx.sort_keys:
+ items = sorted(items) # type: ignore
+ ctx.indent_level += 1
+ for key, value in items:
+ if not isinstance(key, str):
+ if ctx.skipkeys:
+ continue
+ raise TypeError("keys must be strings")
+ k = etree.SubElement(el, "key")
+ k.text = tostr(key, "utf-8")
+ el.append(_make_element(value, ctx))
+ ctx.indent_level -= 1
+ return el
+
+
+def _array_element(
+ array: Sequence[PlistEncodable], ctx: SimpleNamespace
+) -> etree.Element:
+ el = etree.Element("array")
+ if len(array) == 0:
+ return el
+ ctx.indent_level += 1
+ for value in array:
+ el.append(_make_element(value, ctx))
+ ctx.indent_level -= 1
+ return el
+
+
+def _date_element(date: datetime, ctx: SimpleNamespace) -> etree.Element:
+ el = etree.Element("date")
+ el.text = _date_to_string(date)
+ return el
+
+
+def _data_element(data: bytes, ctx: SimpleNamespace) -> etree.Element:
+ el = etree.Element("data")
+ # NOTE: mypy is confused about whether el.text should be str or bytes.
+ el.text = _encode_base64( # type: ignore
+ data,
+ maxlinelength=(76 if ctx.pretty_print else None),
+ indent_level=ctx.indent_level,
+ )
+ return el
+
+
+def _string_or_data_element(raw_bytes: bytes, ctx: SimpleNamespace) -> etree.Element:
+ if ctx.use_builtin_types:
+ return _data_element(raw_bytes, ctx)
+ else:
+ try:
+ string = raw_bytes.decode(encoding="ascii", errors="strict")
+ except UnicodeDecodeError:
+ raise ValueError(
+ "invalid non-ASCII bytes; use unicode string instead: %r" % raw_bytes
+ )
+ return _string_element(string, ctx)
+
+
+# The following is probably not entirely correct. The signature should take `Any`
+# and return `NoReturn`. At the time of this writing, neither mypy nor Pyright
+# can deal with singledispatch properly and will apply the signature of the base
+# function to all others. Being slightly dishonest makes it type-check and return
+# usable typing information for the optimistic case.
+@singledispatch
+def _make_element(value: PlistEncodable, ctx: SimpleNamespace) -> etree.Element:
+ raise TypeError("unsupported type: %s" % type(value))
+
+
+_make_element.register(str)(_string_element)
+_make_element.register(bool)(_bool_element)
+_make_element.register(Integral)(_integer_element)
+_make_element.register(float)(_real_element)
+_make_element.register(collections.abc.Mapping)(_dict_element)
+_make_element.register(list)(_array_element)
+_make_element.register(tuple)(_array_element)
+_make_element.register(datetime)(_date_element)
+_make_element.register(bytes)(_string_or_data_element)
+_make_element.register(bytearray)(_data_element)
+_make_element.register(Data)(lambda v, ctx: _data_element(v.data, ctx))
+
+
+# Public functions to create element tree from plist-compatible python
+# data structures and viceversa, for use when (de)serializing GLIF xml.
+
+
+def totree(
+ value: PlistEncodable,
+ sort_keys: bool = True,
+ skipkeys: bool = False,
+ use_builtin_types: Optional[bool] = None,
+ pretty_print: bool = True,
+ indent_level: int = 1,
+) -> etree.Element:
+ """Convert a value derived from a plist into an XML tree.
+
+ Args:
+ value: Any kind of value to be serialized to XML.
+ sort_keys: Whether keys of dictionaries should be sorted.
+ skipkeys (bool): Whether to silently skip non-string dictionary
+ keys.
+ use_builtin_types (bool): If true, byte strings will be
+ encoded in Base-64 and wrapped in a ``data`` tag; if
+ false, they will be either stored as ASCII strings or an
+ exception raised if they cannot be decoded as such. Defaults
+ to ``True`` if not present. Deprecated.
+ pretty_print (bool): Whether to indent the output.
+ indent_level (int): Level of indentation when serializing.
+
+ Returns: an ``etree`` ``Element`` object.
+
+ Raises:
+ ``TypeError``
+ if non-string dictionary keys are serialized
+ and ``skipkeys`` is false.
+ ``ValueError``
+ if non-ASCII binary data is present
+ and `use_builtin_types` is false.
+ """
+ if use_builtin_types is None:
+ use_builtin_types = USE_BUILTIN_TYPES
+ else:
+ use_builtin_types = use_builtin_types
+ context = SimpleNamespace(
+ sort_keys=sort_keys,
+ skipkeys=skipkeys,
+ use_builtin_types=use_builtin_types,
+ pretty_print=pretty_print,
+ indent_level=indent_level,
+ )
+ return _make_element(value, context)
+
+
+def fromtree(
+ tree: etree.Element,
+ use_builtin_types: Optional[bool] = None,
+ dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+ """Convert an XML tree to a plist structure.
+
+ Args:
+ tree: An ``etree`` ``Element``.
+ use_builtin_types: If True, binary data is deserialized to
+ bytes strings. If False, it is wrapped in :py:class:`Data`
+ objects. Defaults to True if not provided. Deprecated.
+ dict_type: What type to use for dictionaries.
+
+ Returns: An object (usually a dictionary).
+ """
+ target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
+ for action, element in etree.iterwalk(tree, events=("start", "end")):
+ if action == "start":
+ target.start(element.tag, element.attrib)
+ elif action == "end":
+ # if there are no children, parse the leaf's data
+ if not len(element):
+ # always pass str, not None
+ target.data(element.text or "")
+ target.end(element.tag)
+ return target.close()
+
+
+# python3 plistlib API
+
+
+def load(
+ fp: IO[bytes],
+ use_builtin_types: Optional[bool] = None,
+ dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+ """Load a plist file into an object.
+
+ Args:
+ fp: An opened file.
+ use_builtin_types: If True, binary data is deserialized to
+ bytes strings. If False, it is wrapped in :py:class:`Data`
+ objects. Defaults to True if not provided. Deprecated.
+ dict_type: What type to use for dictionaries.
+
+ Returns:
+ An object (usually a dictionary) representing the top level of
+ the plist file.
+ """
+
+ if not hasattr(fp, "read"):
+ raise AttributeError("'%s' object has no attribute 'read'" % type(fp).__name__)
+ target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
+ parser = etree.XMLParser(target=target)
+ result = etree.parse(fp, parser=parser)
+ # lxml returns the target object directly, while ElementTree wraps
+ # it as the root of an ElementTree object
+ try:
+ return result.getroot()
+ except AttributeError:
+ return result
+
+
+def loads(
+ value: bytes,
+ use_builtin_types: Optional[bool] = None,
+ dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+ """Load a plist file from a string into an object.
+
+ Args:
+ value: A bytes string containing a plist.
+ use_builtin_types: If True, binary data is deserialized to
+ bytes strings. If False, it is wrapped in :py:class:`Data`
+ objects. Defaults to True if not provided. Deprecated.
+ dict_type: What type to use for dictionaries.
+
+ Returns:
+ An object (usually a dictionary) representing the top level of
+ the plist file.
+ """
+
+ fp = BytesIO(value)
+ return load(fp, use_builtin_types=use_builtin_types, dict_type=dict_type)
+
+
+def dump(
+ value: PlistEncodable,
+ fp: IO[bytes],
+ sort_keys: bool = True,
+ skipkeys: bool = False,
+ use_builtin_types: Optional[bool] = None,
+ pretty_print: bool = True,
+) -> None:
+ """Write a Python object to a plist file.
+
+ Args:
+ value: An object to write.
+ fp: A file opened for writing.
+ sort_keys (bool): Whether keys of dictionaries should be sorted.
+ skipkeys (bool): Whether to silently skip non-string dictionary
+ keys.
+ use_builtin_types (bool): If true, byte strings will be
+ encoded in Base-64 and wrapped in a ``data`` tag; if
+ false, they will be either stored as ASCII strings or an
+ exception raised if they cannot be represented. Defaults
+ pretty_print (bool): Whether to indent the output.
+ indent_level (int): Level of indentation when serializing.
+
+ Raises:
+ ``TypeError``
+ if non-string dictionary keys are serialized
+ and ``skipkeys`` is false.
+ ``ValueError``
+ if non-representable binary data is present
+ and `use_builtin_types` is false.
+ """
+
+ if not hasattr(fp, "write"):
+ raise AttributeError("'%s' object has no attribute 'write'" % type(fp).__name__)
+ root = etree.Element("plist", version="1.0")
+ el = totree(
+ value,
+ sort_keys=sort_keys,
+ skipkeys=skipkeys,
+ use_builtin_types=use_builtin_types,
+ pretty_print=pretty_print,
+ )
+ root.append(el)
+ tree = etree.ElementTree(root)
+ # we write the doctype ourselves instead of using the 'doctype' argument
+ # of 'write' method, becuse lxml will force adding a '\n' even when
+ # pretty_print is False.
+ if pretty_print:
+ header = b"\n".join((XML_DECLARATION, PLIST_DOCTYPE, b""))
+ else:
+ header = XML_DECLARATION + PLIST_DOCTYPE
+ fp.write(header)
+ tree.write( # type: ignore
+ fp,
+ encoding="utf-8",
+ pretty_print=pretty_print,
+ xml_declaration=False,
+ )
+
+
+def dumps(
+ value: PlistEncodable,
+ sort_keys: bool = True,
+ skipkeys: bool = False,
+ use_builtin_types: Optional[bool] = None,
+ pretty_print: bool = True,
+) -> bytes:
+ """Write a Python object to a string in plist format.
+
+ Args:
+ value: An object to write.
+ sort_keys (bool): Whether keys of dictionaries should be sorted.
+ skipkeys (bool): Whether to silently skip non-string dictionary
+ keys.
+ use_builtin_types (bool): If true, byte strings will be
+ encoded in Base-64 and wrapped in a ``data`` tag; if
+ false, they will be either stored as strings or an
+ exception raised if they cannot be represented. Defaults
+ pretty_print (bool): Whether to indent the output.
+ indent_level (int): Level of indentation when serializing.
+
+ Returns:
+ string: A plist representation of the Python object.
+
+ Raises:
+ ``TypeError``
+ if non-string dictionary keys are serialized
+ and ``skipkeys`` is false.
+ ``ValueError``
+ if non-representable binary data is present
+ and `use_builtin_types` is false.
+ """
+ fp = BytesIO()
+ dump(
+ value,
+ fp,
+ sort_keys=sort_keys,
+ skipkeys=skipkeys,
+ use_builtin_types=use_builtin_types,
+ pretty_print=pretty_print,
+ )
+ return fp.getvalue()
diff --git a/lib/python3.12/site-packages/fontTools/misc/plistlib/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/misc/plistlib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b2427c0961e8b2cdee056cd0b983a3292bf1aaf2
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/misc/plistlib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/misc/plistlib/py.typed b/lib/python3.12/site-packages/fontTools/misc/plistlib/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/python3.12/site-packages/fontTools/misc/psCharStrings.py b/lib/python3.12/site-packages/fontTools/misc/psCharStrings.py
new file mode 100644
index 0000000000000000000000000000000000000000..db837248dea6525c87beee5c3a860d86e41fe44a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/psCharStrings.py
@@ -0,0 +1,1511 @@
+"""psCharStrings.py -- module implementing various kinds of CharStrings:
+CFF dictionary data and Type1/Type2 CharStrings.
+"""
+
+from fontTools.misc.fixedTools import (
+ fixedToFloat,
+ floatToFixed,
+ floatToFixedToStr,
+ strToFixedToFloat,
+)
+from fontTools.misc.textTools import bytechr, byteord, bytesjoin, strjoin
+from fontTools.pens.boundsPen import BoundsPen
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+def read_operator(self, b0, data, index):
+ if b0 == 12:
+ op = (b0, byteord(data[index]))
+ index = index + 1
+ else:
+ op = b0
+ try:
+ operator = self.operators[op]
+ except KeyError:
+ return None, index
+ value = self.handle_operator(operator)
+ return value, index
+
+
+def read_byte(self, b0, data, index):
+ return b0 - 139, index
+
+
+def read_smallInt1(self, b0, data, index):
+ b1 = byteord(data[index])
+ return (b0 - 247) * 256 + b1 + 108, index + 1
+
+
+def read_smallInt2(self, b0, data, index):
+ b1 = byteord(data[index])
+ return -(b0 - 251) * 256 - b1 - 108, index + 1
+
+
+def read_shortInt(self, b0, data, index):
+ (value,) = struct.unpack(">h", data[index : index + 2])
+ return value, index + 2
+
+
+def read_longInt(self, b0, data, index):
+ (value,) = struct.unpack(">l", data[index : index + 4])
+ return value, index + 4
+
+
+def read_fixed1616(self, b0, data, index):
+ (value,) = struct.unpack(">l", data[index : index + 4])
+ return fixedToFloat(value, precisionBits=16), index + 4
+
+
+def read_reserved(self, b0, data, index):
+ assert NotImplementedError
+ return NotImplemented, index
+
+
+def read_realNumber(self, b0, data, index):
+ number = ""
+ while True:
+ b = byteord(data[index])
+ index = index + 1
+ nibble0 = (b & 0xF0) >> 4
+ nibble1 = b & 0x0F
+ if nibble0 == 0xF:
+ break
+ number = number + realNibbles[nibble0]
+ if nibble1 == 0xF:
+ break
+ number = number + realNibbles[nibble1]
+ return float(number), index
+
+
+t1OperandEncoding = [None] * 256
+t1OperandEncoding[0:32] = (32) * [read_operator]
+t1OperandEncoding[32:247] = (247 - 32) * [read_byte]
+t1OperandEncoding[247:251] = (251 - 247) * [read_smallInt1]
+t1OperandEncoding[251:255] = (255 - 251) * [read_smallInt2]
+t1OperandEncoding[255] = read_longInt
+assert len(t1OperandEncoding) == 256
+
+t2OperandEncoding = t1OperandEncoding[:]
+t2OperandEncoding[28] = read_shortInt
+t2OperandEncoding[255] = read_fixed1616
+
+cffDictOperandEncoding = t2OperandEncoding[:]
+cffDictOperandEncoding[29] = read_longInt
+cffDictOperandEncoding[30] = read_realNumber
+cffDictOperandEncoding[255] = read_reserved
+
+
+realNibbles = [
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ ".",
+ "E",
+ "E-",
+ None,
+ "-",
+]
+realNibblesDict = {v: i for i, v in enumerate(realNibbles)}
+
+maxOpStack = 193
+
+
+def buildOperatorDict(operatorList):
+ oper = {}
+ opc = {}
+ for item in operatorList:
+ if len(item) == 2:
+ oper[item[0]] = item[1]
+ else:
+ oper[item[0]] = item[1:]
+ if isinstance(item[0], tuple):
+ opc[item[1]] = item[0]
+ else:
+ opc[item[1]] = (item[0],)
+ return oper, opc
+
+
+t2Operators = [
+ # opcode name
+ (1, "hstem"),
+ (3, "vstem"),
+ (4, "vmoveto"),
+ (5, "rlineto"),
+ (6, "hlineto"),
+ (7, "vlineto"),
+ (8, "rrcurveto"),
+ (10, "callsubr"),
+ (11, "return"),
+ (14, "endchar"),
+ (15, "vsindex"),
+ (16, "blend"),
+ (18, "hstemhm"),
+ (19, "hintmask"),
+ (20, "cntrmask"),
+ (21, "rmoveto"),
+ (22, "hmoveto"),
+ (23, "vstemhm"),
+ (24, "rcurveline"),
+ (25, "rlinecurve"),
+ (26, "vvcurveto"),
+ (27, "hhcurveto"),
+ # (28, 'shortint'), # not really an operator
+ (29, "callgsubr"),
+ (30, "vhcurveto"),
+ (31, "hvcurveto"),
+ ((12, 0), "ignore"), # dotsection. Yes, there a few very early OTF/CFF
+ # fonts with this deprecated operator. Just ignore it.
+ ((12, 3), "and"),
+ ((12, 4), "or"),
+ ((12, 5), "not"),
+ ((12, 8), "store"),
+ ((12, 9), "abs"),
+ ((12, 10), "add"),
+ ((12, 11), "sub"),
+ ((12, 12), "div"),
+ ((12, 13), "load"),
+ ((12, 14), "neg"),
+ ((12, 15), "eq"),
+ ((12, 18), "drop"),
+ ((12, 20), "put"),
+ ((12, 21), "get"),
+ ((12, 22), "ifelse"),
+ ((12, 23), "random"),
+ ((12, 24), "mul"),
+ ((12, 26), "sqrt"),
+ ((12, 27), "dup"),
+ ((12, 28), "exch"),
+ ((12, 29), "index"),
+ ((12, 30), "roll"),
+ ((12, 34), "hflex"),
+ ((12, 35), "flex"),
+ ((12, 36), "hflex1"),
+ ((12, 37), "flex1"),
+]
+
+
+def getIntEncoder(format):
+ if format == "cff":
+ twoByteOp = bytechr(28)
+ fourByteOp = bytechr(29)
+ elif format == "t1":
+ twoByteOp = None
+ fourByteOp = bytechr(255)
+ else:
+ assert format == "t2"
+ twoByteOp = bytechr(28)
+ fourByteOp = None
+
+ def encodeInt(
+ value,
+ fourByteOp=fourByteOp,
+ bytechr=bytechr,
+ pack=struct.pack,
+ unpack=struct.unpack,
+ twoByteOp=twoByteOp,
+ ):
+ if -107 <= value <= 107:
+ code = bytechr(value + 139)
+ elif 108 <= value <= 1131:
+ value = value - 108
+ code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
+ elif -1131 <= value <= -108:
+ value = -value - 108
+ code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
+ elif twoByteOp is not None and -32768 <= value <= 32767:
+ code = twoByteOp + pack(">h", value)
+ elif fourByteOp is None:
+ # Backwards compatible hack: due to a previous bug in FontTools,
+ # 16.16 fixed numbers were written out as 4-byte ints. When
+ # these numbers were small, they were wrongly written back as
+ # small ints instead of 4-byte ints, breaking round-tripping.
+ # This here workaround doesn't do it any better, since we can't
+ # distinguish anymore between small ints that were supposed to
+ # be small fixed numbers and small ints that were just small
+ # ints. Hence the warning.
+ log.warning(
+ "4-byte T2 number got passed to the "
+ "IntType handler. This should happen only when reading in "
+ "old XML files.\n"
+ )
+ code = bytechr(255) + pack(">l", value)
+ else:
+ code = fourByteOp + pack(">l", value)
+ return code
+
+ return encodeInt
+
+
+encodeIntCFF = getIntEncoder("cff")
+encodeIntT1 = getIntEncoder("t1")
+encodeIntT2 = getIntEncoder("t2")
+
+
+def encodeFixed(f, pack=struct.pack):
+ """For T2 only"""
+ value = floatToFixed(f, precisionBits=16)
+ if value & 0xFFFF == 0: # check if the fractional part is zero
+ return encodeIntT2(value >> 16) # encode only the integer part
+ else:
+ return b"\xff" + pack(">l", value) # encode the entire fixed point value
+
+
+realZeroBytes = bytechr(30) + bytechr(0xF)
+
+
+def encodeFloat(f):
+ # For CFF only, used in cffLib
+ if f == 0.0: # 0.0 == +0.0 == -0.0
+ return realZeroBytes
+ # Note: 14 decimal digits seems to be the limitation for CFF real numbers
+ # in macOS. However, we use 8 here to match the implementation of AFDKO.
+ s = "%.8G" % f
+ if s[:2] == "0.":
+ s = s[1:]
+ elif s[:3] == "-0.":
+ s = "-" + s[2:]
+ elif s.endswith("000"):
+ significantDigits = s.rstrip("0")
+ s = "%sE%d" % (significantDigits, len(s) - len(significantDigits))
+ else:
+ dotIndex = s.find(".")
+ eIndex = s.find("E")
+ if dotIndex != -1 and eIndex != -1:
+ integerPart = s[:dotIndex]
+ fractionalPart = s[dotIndex + 1 : eIndex]
+ exponent = int(s[eIndex + 1 :])
+ newExponent = exponent - len(fractionalPart)
+ if newExponent == 1:
+ s = "%s%s0" % (integerPart, fractionalPart)
+ else:
+ s = "%s%sE%d" % (integerPart, fractionalPart, newExponent)
+ if s.startswith((".0", "-.0")):
+ sign, s = s.split(".", 1)
+ s = "%s%sE-%d" % (sign, s.lstrip("0"), len(s))
+ nibbles = []
+ while s:
+ c = s[0]
+ s = s[1:]
+ if c == "E":
+ c2 = s[:1]
+ if c2 == "-":
+ s = s[1:]
+ c = "E-"
+ elif c2 == "+":
+ s = s[1:]
+ if s.startswith("0"):
+ s = s[1:]
+ nibbles.append(realNibblesDict[c])
+ nibbles.append(0xF)
+ if len(nibbles) % 2:
+ nibbles.append(0xF)
+ d = bytechr(30)
+ for i in range(0, len(nibbles), 2):
+ d = d + bytechr(nibbles[i] << 4 | nibbles[i + 1])
+ return d
+
+
+class CharStringCompileError(Exception):
+ pass
+
+
+class SimpleT2Decompiler(object):
+ def __init__(self, localSubrs, globalSubrs, private=None, blender=None):
+ self.localSubrs = localSubrs
+ self.localBias = calcSubrBias(localSubrs)
+ self.globalSubrs = globalSubrs
+ self.globalBias = calcSubrBias(globalSubrs)
+ self.private = private
+ self.blender = blender
+ self.reset()
+
+ def reset(self):
+ self.callingStack = []
+ self.operandStack = []
+ self.hintCount = 0
+ self.hintMaskBytes = 0
+ self.numRegions = 0
+ self.vsIndex = 0
+
+ def execute(self, charString, *, pushToStack=None):
+ self.callingStack.append(charString)
+ needsDecompilation = charString.needsDecompilation()
+ if needsDecompilation:
+ program = []
+ pushToProgram = program.append
+ else:
+ pushToProgram = lambda x: None
+ if pushToStack is None:
+ pushToStack = self.operandStack.append
+ index = 0
+ while True:
+ token, isOperator, index = charString.getToken(index)
+ if token is None:
+ break # we're done!
+ pushToProgram(token)
+ if isOperator:
+ handlerName = "op_" + token
+ handler = getattr(self, handlerName, None)
+ if handler is not None:
+ rv = handler(index)
+ if rv:
+ hintMaskBytes, index = rv
+ pushToProgram(hintMaskBytes)
+ else:
+ self.popall()
+ else:
+ pushToStack(token)
+ if needsDecompilation:
+ charString.setProgram(program)
+ del self.callingStack[-1]
+
+ def pop(self):
+ value = self.operandStack[-1]
+ del self.operandStack[-1]
+ return value
+
+ def popall(self):
+ stack = self.operandStack[:]
+ self.operandStack[:] = []
+ return stack
+
+ def push(self, value):
+ self.operandStack.append(value)
+
+ def op_return(self, index):
+ if self.operandStack:
+ pass
+
+ def op_endchar(self, index):
+ pass
+
+ def op_ignore(self, index):
+ pass
+
+ def op_callsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.localSubrs[subrIndex + self.localBias]
+ self.execute(subr)
+
+ def op_callgsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.globalSubrs[subrIndex + self.globalBias]
+ self.execute(subr)
+
+ def op_hstem(self, index):
+ self.countHints()
+
+ def op_vstem(self, index):
+ self.countHints()
+
+ def op_hstemhm(self, index):
+ self.countHints()
+
+ def op_vstemhm(self, index):
+ self.countHints()
+
+ def op_hintmask(self, index):
+ if not self.hintMaskBytes:
+ self.countHints()
+ self.hintMaskBytes = (self.hintCount + 7) // 8
+ hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
+ return hintMaskBytes, index
+
+ op_cntrmask = op_hintmask
+
+ def countHints(self):
+ args = self.popall()
+ self.hintCount = self.hintCount + len(args) // 2
+
+ # misc
+ def op_and(self, index):
+ raise NotImplementedError
+
+ def op_or(self, index):
+ raise NotImplementedError
+
+ def op_not(self, index):
+ raise NotImplementedError
+
+ def op_store(self, index):
+ raise NotImplementedError
+
+ def op_abs(self, index):
+ raise NotImplementedError
+
+ def op_add(self, index):
+ raise NotImplementedError
+
+ def op_sub(self, index):
+ raise NotImplementedError
+
+ def op_div(self, index):
+ raise NotImplementedError
+
+ def op_load(self, index):
+ raise NotImplementedError
+
+ def op_neg(self, index):
+ raise NotImplementedError
+
+ def op_eq(self, index):
+ raise NotImplementedError
+
+ def op_drop(self, index):
+ raise NotImplementedError
+
+ def op_put(self, index):
+ raise NotImplementedError
+
+ def op_get(self, index):
+ raise NotImplementedError
+
+ def op_ifelse(self, index):
+ raise NotImplementedError
+
+ def op_random(self, index):
+ raise NotImplementedError
+
+ def op_mul(self, index):
+ raise NotImplementedError
+
+ def op_sqrt(self, index):
+ raise NotImplementedError
+
+ def op_dup(self, index):
+ raise NotImplementedError
+
+ def op_exch(self, index):
+ raise NotImplementedError
+
+ def op_index(self, index):
+ raise NotImplementedError
+
+ def op_roll(self, index):
+ raise NotImplementedError
+
+ def op_blend(self, index):
+ if self.numRegions == 0:
+ self.numRegions = self.private.getNumRegions()
+ numBlends = self.pop()
+ numOps = numBlends * (self.numRegions + 1)
+ if self.blender is None:
+ del self.operandStack[
+ -(numOps - numBlends) :
+ ] # Leave the default operands on the stack.
+ else:
+ argi = len(self.operandStack) - numOps
+ end_args = tuplei = argi + numBlends
+ while argi < end_args:
+ next_ti = tuplei + self.numRegions
+ deltas = self.operandStack[tuplei:next_ti]
+ delta = self.blender(self.vsIndex, deltas)
+ self.operandStack[argi] += delta
+ tuplei = next_ti
+ argi += 1
+ self.operandStack[end_args:] = []
+
+ def op_vsindex(self, index):
+ vi = self.pop()
+ self.vsIndex = vi
+ self.numRegions = self.private.getNumRegions(vi)
+
+
+t1Operators = [
+ # opcode name
+ (1, "hstem"),
+ (3, "vstem"),
+ (4, "vmoveto"),
+ (5, "rlineto"),
+ (6, "hlineto"),
+ (7, "vlineto"),
+ (8, "rrcurveto"),
+ (9, "closepath"),
+ (10, "callsubr"),
+ (11, "return"),
+ (13, "hsbw"),
+ (14, "endchar"),
+ (21, "rmoveto"),
+ (22, "hmoveto"),
+ (30, "vhcurveto"),
+ (31, "hvcurveto"),
+ ((12, 0), "dotsection"),
+ ((12, 1), "vstem3"),
+ ((12, 2), "hstem3"),
+ ((12, 6), "seac"),
+ ((12, 7), "sbw"),
+ ((12, 12), "div"),
+ ((12, 16), "callothersubr"),
+ ((12, 17), "pop"),
+ ((12, 33), "setcurrentpoint"),
+]
+
+
+class T2StackUseExtractor(SimpleT2Decompiler):
+
+ def execute(self, charString):
+ maxStackUse = 0
+
+ def pushToStack(value):
+ nonlocal maxStackUse
+ self.operandStack.append(value)
+ maxStackUse = max(maxStackUse, len(self.operandStack))
+
+ super().execute(charString, pushToStack=pushToStack)
+ return maxStackUse
+
+
+class T2WidthExtractor(SimpleT2Decompiler):
+ def __init__(
+ self,
+ localSubrs,
+ globalSubrs,
+ nominalWidthX,
+ defaultWidthX,
+ private=None,
+ blender=None,
+ ):
+ SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private, blender)
+ self.nominalWidthX = nominalWidthX
+ self.defaultWidthX = defaultWidthX
+
+ def reset(self):
+ SimpleT2Decompiler.reset(self)
+ self.gotWidth = 0
+ self.width = 0
+
+ def popallWidth(self, evenOdd=0):
+ args = self.popall()
+ if not self.gotWidth:
+ if evenOdd ^ (len(args) % 2):
+ # For CFF2 charstrings, this should never happen
+ assert (
+ self.defaultWidthX is not None
+ ), "CFF2 CharStrings must not have an initial width value"
+ self.width = self.nominalWidthX + args[0]
+ args = args[1:]
+ else:
+ self.width = self.defaultWidthX
+ self.gotWidth = 1
+ return args
+
+ def countHints(self):
+ args = self.popallWidth()
+ self.hintCount = self.hintCount + len(args) // 2
+
+ def op_rmoveto(self, index):
+ self.popallWidth()
+
+ def op_hmoveto(self, index):
+ self.popallWidth(1)
+
+ def op_vmoveto(self, index):
+ self.popallWidth(1)
+
+ def op_endchar(self, index):
+ self.popallWidth()
+
+
+class T2OutlineExtractor(T2WidthExtractor):
+ def __init__(
+ self,
+ pen,
+ localSubrs,
+ globalSubrs,
+ nominalWidthX,
+ defaultWidthX,
+ private=None,
+ blender=None,
+ ):
+ T2WidthExtractor.__init__(
+ self,
+ localSubrs,
+ globalSubrs,
+ nominalWidthX,
+ defaultWidthX,
+ private,
+ blender,
+ )
+ self.pen = pen
+ self.subrLevel = 0
+
+ def reset(self):
+ T2WidthExtractor.reset(self)
+ self.currentPoint = (0, 0)
+ self.sawMoveTo = 0
+ self.subrLevel = 0
+
+ def execute(self, charString):
+ self.subrLevel += 1
+ super().execute(charString)
+ self.subrLevel -= 1
+ if self.subrLevel == 0:
+ self.endPath()
+
+ def _nextPoint(self, point):
+ x, y = self.currentPoint
+ point = x + point[0], y + point[1]
+ self.currentPoint = point
+ return point
+
+ def rMoveTo(self, point):
+ self.pen.moveTo(self._nextPoint(point))
+ self.sawMoveTo = 1
+
+ def rLineTo(self, point):
+ if not self.sawMoveTo:
+ self.rMoveTo((0, 0))
+ self.pen.lineTo(self._nextPoint(point))
+
+ def rCurveTo(self, pt1, pt2, pt3):
+ if not self.sawMoveTo:
+ self.rMoveTo((0, 0))
+ nextPoint = self._nextPoint
+ self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3))
+
+ def closePath(self):
+ if self.sawMoveTo:
+ self.pen.closePath()
+ self.sawMoveTo = 0
+
+ def endPath(self):
+ # In T2 there are no open paths, so always do a closePath when
+ # finishing a sub path. We avoid spurious calls to closePath()
+ # because its a real T1 op we're emulating in T2 whereas
+ # endPath() is just a means to that emulation
+ if self.sawMoveTo:
+ self.closePath()
+
+ #
+ # hint operators
+ #
+ # def op_hstem(self, index):
+ # self.countHints()
+ # def op_vstem(self, index):
+ # self.countHints()
+ # def op_hstemhm(self, index):
+ # self.countHints()
+ # def op_vstemhm(self, index):
+ # self.countHints()
+ # def op_hintmask(self, index):
+ # self.countHints()
+ # def op_cntrmask(self, index):
+ # self.countHints()
+
+ #
+ # path constructors, moveto
+ #
+ def op_rmoveto(self, index):
+ self.endPath()
+ self.rMoveTo(self.popallWidth())
+
+ def op_hmoveto(self, index):
+ self.endPath()
+ self.rMoveTo((self.popallWidth(1)[0], 0))
+
+ def op_vmoveto(self, index):
+ self.endPath()
+ self.rMoveTo((0, self.popallWidth(1)[0]))
+
+ def op_endchar(self, index):
+ self.endPath()
+ args = self.popallWidth()
+ if args:
+ from fontTools.encodings.StandardEncoding import StandardEncoding
+
+ # endchar can do seac accent bulding; The T2 spec says it's deprecated,
+ # but recent software that shall remain nameless does output it.
+ adx, ady, bchar, achar = args
+ baseGlyph = StandardEncoding[bchar]
+ self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
+ accentGlyph = StandardEncoding[achar]
+ self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
+
+ #
+ # path constructors, lines
+ #
+ def op_rlineto(self, index):
+ args = self.popall()
+ for i in range(0, len(args), 2):
+ point = args[i : i + 2]
+ self.rLineTo(point)
+
+ def op_hlineto(self, index):
+ self.alternatingLineto(1)
+
+ def op_vlineto(self, index):
+ self.alternatingLineto(0)
+
+ #
+ # path constructors, curves
+ #
+ def op_rrcurveto(self, index):
+ """{dxa dya dxb dyb dxc dyc}+ rrcurveto"""
+ args = self.popall()
+ for i in range(0, len(args), 6):
+ (
+ dxa,
+ dya,
+ dxb,
+ dyb,
+ dxc,
+ dyc,
+ ) = args[i : i + 6]
+ self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc))
+
+ def op_rcurveline(self, index):
+ """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
+ args = self.popall()
+ for i in range(0, len(args) - 2, 6):
+ dxb, dyb, dxc, dyc, dxd, dyd = args[i : i + 6]
+ self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
+ self.rLineTo(args[-2:])
+
+ def op_rlinecurve(self, index):
+ """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
+ args = self.popall()
+ lineArgs = args[:-6]
+ for i in range(0, len(lineArgs), 2):
+ self.rLineTo(lineArgs[i : i + 2])
+ dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
+ self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
+
+ def op_vvcurveto(self, index):
+ "dx1? {dya dxb dyb dyc}+ vvcurveto"
+ args = self.popall()
+ if len(args) % 2:
+ dx1 = args[0]
+ args = args[1:]
+ else:
+ dx1 = 0
+ for i in range(0, len(args), 4):
+ dya, dxb, dyb, dyc = args[i : i + 4]
+ self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc))
+ dx1 = 0
+
+ def op_hhcurveto(self, index):
+ """dy1? {dxa dxb dyb dxc}+ hhcurveto"""
+ args = self.popall()
+ if len(args) % 2:
+ dy1 = args[0]
+ args = args[1:]
+ else:
+ dy1 = 0
+ for i in range(0, len(args), 4):
+ dxa, dxb, dyb, dxc = args[i : i + 4]
+ self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0))
+ dy1 = 0
+
+ def op_vhcurveto(self, index):
+ """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
+ {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
+ """
+ args = self.popall()
+ while args:
+ args = self.vcurveto(args)
+ if args:
+ args = self.hcurveto(args)
+
+ def op_hvcurveto(self, index):
+ """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
+ {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+ """
+ args = self.popall()
+ while args:
+ args = self.hcurveto(args)
+ if args:
+ args = self.vcurveto(args)
+
+ #
+ # path constructors, flex
+ #
+ def op_hflex(self, index):
+ dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall()
+ dy1 = dy3 = dy4 = dy6 = 0
+ dy5 = -dy2
+ self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
+ self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
+
+ def op_flex(self, index):
+ dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall()
+ self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
+ self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
+
+ def op_hflex1(self, index):
+ dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall()
+ dy3 = dy4 = 0
+ dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5)
+
+ self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
+ self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
+
+ def op_flex1(self, index):
+ dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall()
+ dx = dx1 + dx2 + dx3 + dx4 + dx5
+ dy = dy1 + dy2 + dy3 + dy4 + dy5
+ if abs(dx) > abs(dy):
+ dx6 = d6
+ dy6 = -dy
+ else:
+ dx6 = -dx
+ dy6 = d6
+ self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
+ self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
+
+ # misc
+ def op_and(self, index):
+ raise NotImplementedError
+
+ def op_or(self, index):
+ raise NotImplementedError
+
+ def op_not(self, index):
+ raise NotImplementedError
+
+ def op_store(self, index):
+ raise NotImplementedError
+
+ def op_abs(self, index):
+ raise NotImplementedError
+
+ def op_add(self, index):
+ raise NotImplementedError
+
+ def op_sub(self, index):
+ raise NotImplementedError
+
+ def op_div(self, index):
+ num2 = self.pop()
+ num1 = self.pop()
+ d1 = num1 // num2
+ d2 = num1 / num2
+ if d1 == d2:
+ self.push(d1)
+ else:
+ self.push(d2)
+
+ def op_load(self, index):
+ raise NotImplementedError
+
+ def op_neg(self, index):
+ raise NotImplementedError
+
+ def op_eq(self, index):
+ raise NotImplementedError
+
+ def op_drop(self, index):
+ raise NotImplementedError
+
+ def op_put(self, index):
+ raise NotImplementedError
+
+ def op_get(self, index):
+ raise NotImplementedError
+
+ def op_ifelse(self, index):
+ raise NotImplementedError
+
+ def op_random(self, index):
+ raise NotImplementedError
+
+ def op_mul(self, index):
+ raise NotImplementedError
+
+ def op_sqrt(self, index):
+ raise NotImplementedError
+
+ def op_dup(self, index):
+ raise NotImplementedError
+
+ def op_exch(self, index):
+ raise NotImplementedError
+
+ def op_index(self, index):
+ raise NotImplementedError
+
+ def op_roll(self, index):
+ raise NotImplementedError
+
+ #
+ # miscellaneous helpers
+ #
+ def alternatingLineto(self, isHorizontal):
+ args = self.popall()
+ for arg in args:
+ if isHorizontal:
+ point = (arg, 0)
+ else:
+ point = (0, arg)
+ self.rLineTo(point)
+ isHorizontal = not isHorizontal
+
+ def vcurveto(self, args):
+ dya, dxb, dyb, dxc = args[:4]
+ args = args[4:]
+ if len(args) == 1:
+ dyc = args[0]
+ args = []
+ else:
+ dyc = 0
+ self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc))
+ return args
+
+ def hcurveto(self, args):
+ dxa, dxb, dyb, dyc = args[:4]
+ args = args[4:]
+ if len(args) == 1:
+ dxc = args[0]
+ args = []
+ else:
+ dxc = 0
+ self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc))
+ return args
+
+
+class T1OutlineExtractor(T2OutlineExtractor):
+ def __init__(self, pen, subrs):
+ self.pen = pen
+ self.subrs = subrs
+ self.reset()
+
+ def reset(self):
+ self.flexing = 0
+ self.width = 0
+ self.sbx = 0
+ T2OutlineExtractor.reset(self)
+
+ def endPath(self):
+ if self.sawMoveTo:
+ self.pen.endPath()
+ self.sawMoveTo = 0
+
+ def popallWidth(self, evenOdd=0):
+ return self.popall()
+
+ def exch(self):
+ stack = self.operandStack
+ stack[-1], stack[-2] = stack[-2], stack[-1]
+
+ #
+ # path constructors
+ #
+ def op_rmoveto(self, index):
+ if self.flexing:
+ return
+ self.endPath()
+ self.rMoveTo(self.popall())
+
+ def op_hmoveto(self, index):
+ if self.flexing:
+ # We must add a parameter to the stack if we are flexing
+ self.push(0)
+ return
+ self.endPath()
+ self.rMoveTo((self.popall()[0], 0))
+
+ def op_vmoveto(self, index):
+ if self.flexing:
+ # We must add a parameter to the stack if we are flexing
+ self.push(0)
+ self.exch()
+ return
+ self.endPath()
+ self.rMoveTo((0, self.popall()[0]))
+
+ def op_closepath(self, index):
+ self.closePath()
+
+ def op_setcurrentpoint(self, index):
+ args = self.popall()
+ x, y = args
+ self.currentPoint = x, y
+
+ def op_endchar(self, index):
+ self.endPath()
+
+ def op_hsbw(self, index):
+ sbx, wx = self.popall()
+ self.width = wx
+ self.sbx = sbx
+ self.currentPoint = sbx, self.currentPoint[1]
+
+ def op_sbw(self, index):
+ self.popall() # XXX
+
+ #
+ def op_callsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.subrs[subrIndex]
+ self.execute(subr)
+
+ def op_callothersubr(self, index):
+ subrIndex = self.pop()
+ nArgs = self.pop()
+ # print nArgs, subrIndex, "callothersubr"
+ if subrIndex == 0 and nArgs == 3:
+ self.doFlex()
+ self.flexing = 0
+ elif subrIndex == 1 and nArgs == 0:
+ self.flexing = 1
+ # ignore...
+
+ def op_pop(self, index):
+ pass # ignore...
+
+ def doFlex(self):
+ finaly = self.pop()
+ finalx = self.pop()
+ self.pop() # flex height is unused
+
+ p3y = self.pop()
+ p3x = self.pop()
+ bcp4y = self.pop()
+ bcp4x = self.pop()
+ bcp3y = self.pop()
+ bcp3x = self.pop()
+ p2y = self.pop()
+ p2x = self.pop()
+ bcp2y = self.pop()
+ bcp2x = self.pop()
+ bcp1y = self.pop()
+ bcp1x = self.pop()
+ rpy = self.pop()
+ rpx = self.pop()
+
+ # call rrcurveto
+ self.push(bcp1x + rpx)
+ self.push(bcp1y + rpy)
+ self.push(bcp2x)
+ self.push(bcp2y)
+ self.push(p2x)
+ self.push(p2y)
+ self.op_rrcurveto(None)
+
+ # call rrcurveto
+ self.push(bcp3x)
+ self.push(bcp3y)
+ self.push(bcp4x)
+ self.push(bcp4y)
+ self.push(p3x)
+ self.push(p3y)
+ self.op_rrcurveto(None)
+
+ # Push back final coords so subr 0 can find them
+ self.push(finalx)
+ self.push(finaly)
+
+ def op_dotsection(self, index):
+ self.popall() # XXX
+
+ def op_hstem3(self, index):
+ self.popall() # XXX
+
+ def op_seac(self, index):
+ "asb adx ady bchar achar seac"
+ from fontTools.encodings.StandardEncoding import StandardEncoding
+
+ asb, adx, ady, bchar, achar = self.popall()
+ baseGlyph = StandardEncoding[bchar]
+ self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
+ accentGlyph = StandardEncoding[achar]
+ adx = adx + self.sbx - asb # seac weirdness
+ self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
+
+ def op_vstem3(self, index):
+ self.popall() # XXX
+
+
+class T2CharString(object):
+ operandEncoding = t2OperandEncoding
+ operators, opcodes = buildOperatorDict(t2Operators)
+ decompilerClass = SimpleT2Decompiler
+ outlineExtractor = T2OutlineExtractor
+
+ def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
+ if program is None:
+ program = []
+ self.bytecode = bytecode
+ self.program = program
+ self.private = private
+ self.globalSubrs = globalSubrs if globalSubrs is not None else []
+ self._cur_vsindex = None
+
+ def getNumRegions(self, vsindex=None):
+ pd = self.private
+ assert pd is not None
+ if vsindex is not None:
+ self._cur_vsindex = vsindex
+ elif self._cur_vsindex is None:
+ self._cur_vsindex = pd.vsindex if hasattr(pd, "vsindex") else 0
+ return pd.getNumRegions(self._cur_vsindex)
+
+ def __repr__(self):
+ if self.bytecode is None:
+ return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
+ else:
+ return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
+
+ def getIntEncoder(self):
+ return encodeIntT2
+
+ def getFixedEncoder(self):
+ return encodeFixed
+
+ def decompile(self):
+ if not self.needsDecompilation():
+ return
+ subrs = getattr(self.private, "Subrs", [])
+ decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
+ decompiler.execute(self)
+
+ def draw(self, pen, blender=None):
+ subrs = getattr(self.private, "Subrs", [])
+ extractor = self.outlineExtractor(
+ pen,
+ subrs,
+ self.globalSubrs,
+ self.private.nominalWidthX,
+ self.private.defaultWidthX,
+ self.private,
+ blender,
+ )
+ extractor.execute(self)
+ self.width = extractor.width
+
+ def calcBounds(self, glyphSet):
+ boundsPen = BoundsPen(glyphSet)
+ self.draw(boundsPen)
+ return boundsPen.bounds
+
+ def compile(self, isCFF2=False):
+ if self.bytecode is not None:
+ return
+ opcodes = self.opcodes
+ program = self.program
+
+ if isCFF2:
+ # If present, remove return and endchar operators.
+ if program and program[-1] in ("return", "endchar"):
+ program = program[:-1]
+ elif program and not isinstance(program[-1], str):
+ raise CharStringCompileError(
+ "T2CharString or Subr has items on the stack after last operator."
+ )
+
+ bytecode = []
+ encodeInt = self.getIntEncoder()
+ encodeFixed = self.getFixedEncoder()
+ i = 0
+ end = len(program)
+ while i < end:
+ token = program[i]
+ i = i + 1
+ if isinstance(token, str):
+ try:
+ bytecode.extend(bytechr(b) for b in opcodes[token])
+ except KeyError:
+ raise CharStringCompileError("illegal operator: %s" % token)
+ if token in ("hintmask", "cntrmask"):
+ bytecode.append(program[i]) # hint mask
+ i = i + 1
+ elif isinstance(token, int):
+ bytecode.append(encodeInt(token))
+ elif isinstance(token, float):
+ bytecode.append(encodeFixed(token))
+ else:
+ assert 0, "unsupported type: %s" % type(token)
+ try:
+ bytecode = bytesjoin(bytecode)
+ except TypeError:
+ log.error(bytecode)
+ raise
+ self.setBytecode(bytecode)
+
+ def needsDecompilation(self):
+ return self.bytecode is not None
+
+ def setProgram(self, program):
+ self.program = program
+ self.bytecode = None
+
+ def setBytecode(self, bytecode):
+ self.bytecode = bytecode
+ self.program = None
+
+ def getToken(self, index, len=len, byteord=byteord, isinstance=isinstance):
+ if self.bytecode is not None:
+ if index >= len(self.bytecode):
+ return None, 0, 0
+ b0 = byteord(self.bytecode[index])
+ index = index + 1
+ handler = self.operandEncoding[b0]
+ token, index = handler(self, b0, self.bytecode, index)
+ else:
+ if index >= len(self.program):
+ return None, 0, 0
+ token = self.program[index]
+ index = index + 1
+ isOperator = isinstance(token, str)
+ return token, isOperator, index
+
+ def getBytes(self, index, nBytes):
+ if self.bytecode is not None:
+ newIndex = index + nBytes
+ bytes = self.bytecode[index:newIndex]
+ index = newIndex
+ else:
+ bytes = self.program[index]
+ index = index + 1
+ assert len(bytes) == nBytes
+ return bytes, index
+
+ def handle_operator(self, operator):
+ return operator
+
+ def toXML(self, xmlWriter, ttFont=None):
+ from fontTools.misc.textTools import num2binary
+
+ if self.bytecode is not None:
+ xmlWriter.dumphex(self.bytecode)
+ else:
+ index = 0
+ args = []
+ while True:
+ token, isOperator, index = self.getToken(index)
+ if token is None:
+ break
+ if isOperator:
+ if token in ("hintmask", "cntrmask"):
+ hintMask, isOperator, index = self.getToken(index)
+ bits = []
+ for byte in hintMask:
+ bits.append(num2binary(byteord(byte), 8))
+ hintMask = strjoin(bits)
+ line = " ".join(args + [token, hintMask])
+ else:
+ line = " ".join(args + [token])
+ xmlWriter.write(line)
+ xmlWriter.newline()
+ args = []
+ else:
+ if isinstance(token, float):
+ token = floatToFixedToStr(token, precisionBits=16)
+ else:
+ token = str(token)
+ args.append(token)
+ if args:
+ # NOTE: only CFF2 charstrings/subrs can have numeric arguments on
+ # the stack after the last operator. Compiling this would fail if
+ # this is part of CFF 1.0 table.
+ line = " ".join(args)
+ xmlWriter.write(line)
+
+ def fromXML(self, name, attrs, content):
+ from fontTools.misc.textTools import binary2num, readHex
+
+ if attrs.get("raw"):
+ self.setBytecode(readHex(content))
+ return
+ content = strjoin(content)
+ content = content.split()
+ program = []
+ end = len(content)
+ i = 0
+ while i < end:
+ token = content[i]
+ i = i + 1
+ try:
+ token = int(token)
+ except ValueError:
+ try:
+ token = strToFixedToFloat(token, precisionBits=16)
+ except ValueError:
+ program.append(token)
+ if token in ("hintmask", "cntrmask"):
+ mask = content[i]
+ maskBytes = b""
+ for j in range(0, len(mask), 8):
+ maskBytes = maskBytes + bytechr(binary2num(mask[j : j + 8]))
+ program.append(maskBytes)
+ i = i + 1
+ else:
+ program.append(token)
+ else:
+ program.append(token)
+ self.setProgram(program)
+
+
+class T1CharString(T2CharString):
+ operandEncoding = t1OperandEncoding
+ operators, opcodes = buildOperatorDict(t1Operators)
+
+ def __init__(self, bytecode=None, program=None, subrs=None):
+ super().__init__(bytecode, program)
+ self.subrs = subrs
+
+ def getIntEncoder(self):
+ return encodeIntT1
+
+ def getFixedEncoder(self):
+ def encodeFixed(value):
+ raise TypeError("Type 1 charstrings don't support floating point operands")
+
+ def decompile(self):
+ if self.bytecode is None:
+ return
+ program = []
+ index = 0
+ while True:
+ token, isOperator, index = self.getToken(index)
+ if token is None:
+ break
+ program.append(token)
+ self.setProgram(program)
+
+ def draw(self, pen):
+ extractor = T1OutlineExtractor(pen, self.subrs)
+ extractor.execute(self)
+ self.width = extractor.width
+
+
+class DictDecompiler(object):
+ operandEncoding = cffDictOperandEncoding
+
+ def __init__(self, strings, parent=None):
+ self.stack = []
+ self.strings = strings
+ self.dict = {}
+ self.parent = parent
+
+ def getDict(self):
+ assert len(self.stack) == 0, "non-empty stack"
+ return self.dict
+
+ def decompile(self, data):
+ index = 0
+ lenData = len(data)
+ push = self.stack.append
+ while index < lenData:
+ b0 = byteord(data[index])
+ index = index + 1
+ handler = self.operandEncoding[b0]
+ value, index = handler(self, b0, data, index)
+ if value is not None:
+ push(value)
+
+ def pop(self):
+ value = self.stack[-1]
+ del self.stack[-1]
+ return value
+
+ def popall(self):
+ args = self.stack[:]
+ del self.stack[:]
+ return args
+
+ def handle_operator(self, operator):
+ operator, argType = operator
+ if isinstance(argType, tuple):
+ value = ()
+ for i in range(len(argType) - 1, -1, -1):
+ arg = argType[i]
+ arghandler = getattr(self, "arg_" + arg)
+ value = (arghandler(operator),) + value
+ else:
+ arghandler = getattr(self, "arg_" + argType)
+ value = arghandler(operator)
+ if operator == "blend":
+ self.stack.extend(value)
+ else:
+ self.dict[operator] = value
+
+ def arg_number(self, name):
+ if isinstance(self.stack[0], list):
+ out = self.arg_blend_number(self.stack)
+ else:
+ out = self.pop()
+ return out
+
+ def arg_blend_number(self, name):
+ out = []
+ blendArgs = self.pop()
+ numMasters = len(blendArgs)
+ out.append(blendArgs)
+ out.append("blend")
+ dummy = self.popall()
+ return blendArgs
+
+ def arg_SID(self, name):
+ return self.strings[self.pop()]
+
+ def arg_array(self, name):
+ return self.popall()
+
+ def arg_blendList(self, name):
+ """
+ There may be non-blend args at the top of the stack. We first calculate
+ where the blend args start in the stack. These are the last
+ numMasters*numBlends) +1 args.
+ The blend args starts with numMasters relative coordinate values, the BlueValues in the list from the default master font. This is followed by
+ numBlends list of values. Each of value in one of these lists is the
+ Variable Font delta for the matching region.
+
+ We re-arrange this to be a list of numMaster entries. Each entry starts with the corresponding default font relative value, and is followed by
+ the delta values. We then convert the default values, the first item in each entry, to an absolute value.
+ """
+ vsindex = self.dict.get("vsindex", 0)
+ numMasters = (
+ self.parent.getNumRegions(vsindex) + 1
+ ) # only a PrivateDict has blended ops.
+ numBlends = self.pop()
+ args = self.popall()
+ numArgs = len(args)
+ # The spec says that there should be no non-blended Blue Values,.
+ assert numArgs == numMasters * numBlends
+ value = [None] * numBlends
+ numDeltas = numMasters - 1
+ i = 0
+ prevVal = 0
+ while i < numBlends:
+ newVal = args[i] + prevVal
+ prevVal = newVal
+ masterOffset = numBlends + (i * numDeltas)
+ blendList = [newVal] + args[masterOffset : masterOffset + numDeltas]
+ value[i] = blendList
+ i += 1
+ return value
+
+ def arg_delta(self, name):
+ valueList = self.popall()
+ out = []
+ if valueList and isinstance(valueList[0], list):
+ # arg_blendList() has already converted these to absolute values.
+ out = valueList
+ else:
+ current = 0
+ for v in valueList:
+ current = current + v
+ out.append(current)
+ return out
+
+
+def calcSubrBias(subrs):
+ nSubrs = len(subrs)
+ if nSubrs < 1240:
+ bias = 107
+ elif nSubrs < 33900:
+ bias = 1131
+ else:
+ bias = 32768
+ return bias
diff --git a/lib/python3.12/site-packages/fontTools/misc/psLib.py b/lib/python3.12/site-packages/fontTools/misc/psLib.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bfdb4ae9fcc0c49b77830d2be8c46274e315bd4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/psLib.py
@@ -0,0 +1,398 @@
+from fontTools.misc.textTools import bytechr, byteord, bytesjoin, tobytes, tostr
+from fontTools.misc import eexec
+from .psOperators import (
+ PSOperators,
+ ps_StandardEncoding,
+ ps_array,
+ ps_boolean,
+ ps_dict,
+ ps_integer,
+ ps_literal,
+ ps_mark,
+ ps_name,
+ ps_operator,
+ ps_procedure,
+ ps_procmark,
+ ps_real,
+ ps_string,
+)
+import re
+from collections.abc import Callable
+from string import whitespace
+import logging
+
+
+log = logging.getLogger(__name__)
+
+ps_special = b"()<>[]{}%" # / is one too, but we take care of that one differently
+
+skipwhiteRE = re.compile(bytesjoin([b"[", whitespace, b"]*"]))
+endofthingPat = bytesjoin([b"[^][(){}<>/%", whitespace, b"]*"])
+endofthingRE = re.compile(endofthingPat)
+commentRE = re.compile(b"%[^\n\r]*")
+
+# XXX This not entirely correct as it doesn't allow *nested* embedded parens:
+stringPat = rb"""
+ \(
+ (
+ (
+ [^()]* \ [()]
+ )
+ |
+ (
+ [^()]* \( [^()]* \)
+ )
+ )*
+ [^()]*
+ \)
+"""
+stringPat = b"".join(stringPat.split())
+stringRE = re.compile(stringPat)
+
+hexstringRE = re.compile(bytesjoin([b"<[", whitespace, b"0-9A-Fa-f]*>"]))
+
+
+class PSTokenError(Exception):
+ pass
+
+
+class PSError(Exception):
+ pass
+
+
+class PSTokenizer(object):
+ def __init__(self, buf=b"", encoding="ascii"):
+ # Force self.buf to be a byte string
+ buf = tobytes(buf)
+ self.buf = buf
+ self.len = len(buf)
+ self.pos = 0
+ self.closed = False
+ self.encoding = encoding
+
+ def read(self, n=-1):
+ """Read at most 'n' bytes from the buffer, or less if the read
+ hits EOF before obtaining 'n' bytes.
+ If 'n' is negative or omitted, read all data until EOF is reached.
+ """
+ if self.closed:
+ raise ValueError("I/O operation on closed file")
+ if n is None or n < 0:
+ newpos = self.len
+ else:
+ newpos = min(self.pos + n, self.len)
+ r = self.buf[self.pos : newpos]
+ self.pos = newpos
+ return r
+
+ def close(self):
+ if not self.closed:
+ self.closed = True
+ del self.buf, self.pos
+
+ def getnexttoken(
+ self,
+ # localize some stuff, for performance
+ len=len,
+ ps_special=ps_special,
+ stringmatch=stringRE.match,
+ hexstringmatch=hexstringRE.match,
+ commentmatch=commentRE.match,
+ endmatch=endofthingRE.match,
+ ):
+ self.skipwhite()
+ if self.pos >= self.len:
+ return None, None
+ pos = self.pos
+ buf = self.buf
+ char = bytechr(byteord(buf[pos]))
+ if char in ps_special:
+ if char in b"{}[]":
+ tokentype = "do_special"
+ token = char
+ elif char == b"%":
+ tokentype = "do_comment"
+ _, nextpos = commentmatch(buf, pos).span()
+ token = buf[pos:nextpos]
+ elif char == b"(":
+ tokentype = "do_string"
+ m = stringmatch(buf, pos)
+ if m is None:
+ raise PSTokenError("bad string at character %d" % pos)
+ _, nextpos = m.span()
+ token = buf[pos:nextpos]
+ elif char == b"<":
+ tokentype = "do_hexstring"
+ m = hexstringmatch(buf, pos)
+ if m is None:
+ raise PSTokenError("bad hexstring at character %d" % pos)
+ _, nextpos = m.span()
+ token = buf[pos:nextpos]
+ else:
+ raise PSTokenError("bad token at character %d" % pos)
+ else:
+ if char == b"/":
+ tokentype = "do_literal"
+ m = endmatch(buf, pos + 1)
+ else:
+ tokentype = ""
+ m = endmatch(buf, pos)
+ if m is None:
+ raise PSTokenError("bad token at character %d" % pos)
+ _, nextpos = m.span()
+ token = buf[pos:nextpos]
+ self.pos = pos + len(token)
+ token = tostr(token, encoding=self.encoding)
+ return tokentype, token
+
+ def skipwhite(self, whitematch=skipwhiteRE.match):
+ _, nextpos = whitematch(self.buf, self.pos).span()
+ self.pos = nextpos
+
+ def starteexec(self):
+ self.pos = self.pos + 1
+ self.dirtybuf = self.buf[self.pos :]
+ self.buf, R = eexec.decrypt(self.dirtybuf, 55665)
+ self.len = len(self.buf)
+ self.pos = 4
+
+ def stopeexec(self):
+ if not hasattr(self, "dirtybuf"):
+ return
+ self.buf = self.dirtybuf
+ del self.dirtybuf
+
+
+class PSInterpreter(PSOperators):
+ def __init__(self, encoding="ascii"):
+ systemdict = {}
+ userdict = {}
+ self.encoding = encoding
+ self.dictstack = [systemdict, userdict]
+ self.stack = []
+ self.proclevel = 0
+ self.procmark = ps_procmark()
+ self.fillsystemdict()
+
+ def fillsystemdict(self):
+ systemdict = self.dictstack[0]
+ systemdict["["] = systemdict["mark"] = self.mark = ps_mark()
+ systemdict["]"] = ps_operator("]", self.do_makearray)
+ systemdict["true"] = ps_boolean(1)
+ systemdict["false"] = ps_boolean(0)
+ systemdict["StandardEncoding"] = ps_array(ps_StandardEncoding)
+ systemdict["FontDirectory"] = ps_dict({})
+ self.suckoperators(systemdict, self.__class__)
+
+ def suckoperators(self, systemdict, klass):
+ for name in dir(klass):
+ attr = getattr(self, name)
+ if isinstance(attr, Callable) and name[:3] == "ps_":
+ name = name[3:]
+ systemdict[name] = ps_operator(name, attr)
+ for baseclass in klass.__bases__:
+ self.suckoperators(systemdict, baseclass)
+
+ def interpret(self, data, getattr=getattr):
+ tokenizer = self.tokenizer = PSTokenizer(data, self.encoding)
+ getnexttoken = tokenizer.getnexttoken
+ do_token = self.do_token
+ handle_object = self.handle_object
+ try:
+ while 1:
+ tokentype, token = getnexttoken()
+ if not token:
+ break
+ if tokentype:
+ handler = getattr(self, tokentype)
+ object = handler(token)
+ else:
+ object = do_token(token)
+ if object is not None:
+ handle_object(object)
+ tokenizer.close()
+ self.tokenizer = None
+ except:
+ if self.tokenizer is not None:
+ log.debug(
+ "ps error:\n"
+ "- - - - - - -\n"
+ "%s\n"
+ ">>>\n"
+ "%s\n"
+ "- - - - - - -",
+ self.tokenizer.buf[self.tokenizer.pos - 50 : self.tokenizer.pos],
+ self.tokenizer.buf[self.tokenizer.pos : self.tokenizer.pos + 50],
+ )
+ raise
+
+ def handle_object(self, object):
+ if not (self.proclevel or object.literal or object.type == "proceduretype"):
+ if object.type != "operatortype":
+ object = self.resolve_name(object.value)
+ if object.literal:
+ self.push(object)
+ else:
+ if object.type == "proceduretype":
+ self.call_procedure(object)
+ else:
+ object.function()
+ else:
+ self.push(object)
+
+ def call_procedure(self, proc):
+ handle_object = self.handle_object
+ for item in proc.value:
+ handle_object(item)
+
+ def resolve_name(self, name):
+ dictstack = self.dictstack
+ for i in range(len(dictstack) - 1, -1, -1):
+ if name in dictstack[i]:
+ return dictstack[i][name]
+ raise PSError("name error: " + str(name))
+
+ def do_token(
+ self,
+ token,
+ int=int,
+ float=float,
+ ps_name=ps_name,
+ ps_integer=ps_integer,
+ ps_real=ps_real,
+ ):
+ try:
+ num = int(token)
+ except (ValueError, OverflowError):
+ try:
+ num = float(token)
+ except (ValueError, OverflowError):
+ if "#" in token:
+ hashpos = token.find("#")
+ try:
+ base = int(token[:hashpos])
+ num = int(token[hashpos + 1 :], base)
+ except (ValueError, OverflowError):
+ return ps_name(token)
+ else:
+ return ps_integer(num)
+ else:
+ return ps_name(token)
+ else:
+ return ps_real(num)
+ else:
+ return ps_integer(num)
+
+ def do_comment(self, token):
+ pass
+
+ def do_literal(self, token):
+ return ps_literal(token[1:])
+
+ def do_string(self, token):
+ return ps_string(token[1:-1])
+
+ def do_hexstring(self, token):
+ hexStr = "".join(token[1:-1].split())
+ if len(hexStr) % 2:
+ hexStr = hexStr + "0"
+ cleanstr = []
+ for i in range(0, len(hexStr), 2):
+ cleanstr.append(chr(int(hexStr[i : i + 2], 16)))
+ cleanstr = "".join(cleanstr)
+ return ps_string(cleanstr)
+
+ def do_special(self, token):
+ if token == "{":
+ self.proclevel = self.proclevel + 1
+ return self.procmark
+ elif token == "}":
+ proc = []
+ while 1:
+ topobject = self.pop()
+ if topobject == self.procmark:
+ break
+ proc.append(topobject)
+ self.proclevel = self.proclevel - 1
+ proc.reverse()
+ return ps_procedure(proc)
+ elif token == "[":
+ return self.mark
+ elif token == "]":
+ return ps_name("]")
+ else:
+ raise PSTokenError("huh?")
+
+ def push(self, object):
+ self.stack.append(object)
+
+ def pop(self, *types):
+ stack = self.stack
+ if not stack:
+ raise PSError("stack underflow")
+ object = stack[-1]
+ if types:
+ if object.type not in types:
+ raise PSError(
+ "typecheck, expected %s, found %s" % (repr(types), object.type)
+ )
+ del stack[-1]
+ return object
+
+ def do_makearray(self):
+ array = []
+ while 1:
+ topobject = self.pop()
+ if topobject == self.mark:
+ break
+ array.append(topobject)
+ array.reverse()
+ self.push(ps_array(array))
+
+ def close(self):
+ """Remove circular references."""
+ del self.stack
+ del self.dictstack
+
+
+def unpack_item(item):
+ tp = type(item.value)
+ if tp == dict:
+ newitem = {}
+ for key, value in item.value.items():
+ newitem[key] = unpack_item(value)
+ elif tp == list:
+ newitem = [None] * len(item.value)
+ for i in range(len(item.value)):
+ newitem[i] = unpack_item(item.value[i])
+ if item.type == "proceduretype":
+ newitem = tuple(newitem)
+ else:
+ newitem = item.value
+ return newitem
+
+
+def suckfont(data, encoding="ascii"):
+ m = re.search(rb"/FontName\s+/([^ \t\n\r]+)\s+def", data)
+ if m:
+ fontName = m.group(1)
+ fontName = fontName.decode()
+ else:
+ fontName = None
+ interpreter = PSInterpreter(encoding=encoding)
+ interpreter.interpret(
+ b"/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop"
+ )
+ interpreter.interpret(data)
+ fontdir = interpreter.dictstack[0]["FontDirectory"].value
+ if fontName in fontdir:
+ rawfont = fontdir[fontName]
+ else:
+ # fall back, in case fontName wasn't found
+ fontNames = list(fontdir.keys())
+ if len(fontNames) > 1:
+ fontNames.remove("Helvetica")
+ fontNames.sort()
+ rawfont = fontdir[fontNames[0]]
+ interpreter.close()
+ return unpack_item(rawfont)
diff --git a/lib/python3.12/site-packages/fontTools/misc/psOperators.py b/lib/python3.12/site-packages/fontTools/misc/psOperators.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0b975eab14486cab9f7f8e60d2c2c9de8cb7c48
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/psOperators.py
@@ -0,0 +1,572 @@
+_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
+
+
+class ps_object(object):
+ literal = 1
+ access = 0
+ value = None
+
+ def __init__(self, value):
+ self.value = value
+ self.type = self.__class__.__name__[3:] + "type"
+
+ def __repr__(self):
+ return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
+
+
+class ps_operator(ps_object):
+ literal = 0
+
+ def __init__(self, name, function):
+ self.name = name
+ self.function = function
+ self.type = self.__class__.__name__[3:] + "type"
+
+ def __repr__(self):
+ return "" % self.name
+
+
+class ps_procedure(ps_object):
+ literal = 0
+
+ def __repr__(self):
+ return ""
+
+ def __str__(self):
+ psstring = "{"
+ for i in range(len(self.value)):
+ if i:
+ psstring = psstring + " " + str(self.value[i])
+ else:
+ psstring = psstring + str(self.value[i])
+ return psstring + "}"
+
+
+class ps_name(ps_object):
+ literal = 0
+
+ def __str__(self):
+ if self.literal:
+ return "/" + self.value
+ else:
+ return self.value
+
+
+class ps_literal(ps_object):
+ def __str__(self):
+ return "/" + self.value
+
+
+class ps_array(ps_object):
+ def __str__(self):
+ psstring = "["
+ for i in range(len(self.value)):
+ item = self.value[i]
+ access = _accessstrings[item.access]
+ if access:
+ access = " " + access
+ if i:
+ psstring = psstring + " " + str(item) + access
+ else:
+ psstring = psstring + str(item) + access
+ return psstring + "]"
+
+ def __repr__(self):
+ return ""
+
+
+_type1_pre_eexec_order = [
+ "FontInfo",
+ "FontName",
+ "Encoding",
+ "PaintType",
+ "FontType",
+ "FontMatrix",
+ "FontBBox",
+ "UniqueID",
+ "Metrics",
+ "StrokeWidth",
+]
+
+_type1_fontinfo_order = [
+ "version",
+ "Notice",
+ "FullName",
+ "FamilyName",
+ "Weight",
+ "ItalicAngle",
+ "isFixedPitch",
+ "UnderlinePosition",
+ "UnderlineThickness",
+]
+
+_type1_post_eexec_order = ["Private", "CharStrings", "FID"]
+
+
+def _type1_item_repr(key, value):
+ psstring = ""
+ access = _accessstrings[value.access]
+ if access:
+ access = access + " "
+ if key == "CharStrings":
+ psstring = psstring + "/%s %s def\n" % (
+ key,
+ _type1_CharString_repr(value.value),
+ )
+ elif key == "Encoding":
+ psstring = psstring + _type1_Encoding_repr(value, access)
+ else:
+ psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
+ return psstring
+
+
+def _type1_Encoding_repr(encoding, access):
+ encoding = encoding.value
+ psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
+ for i in range(256):
+ name = encoding[i].value
+ if name != ".notdef":
+ psstring = psstring + "dup %d /%s put\n" % (i, name)
+ return psstring + access + "def\n"
+
+
+def _type1_CharString_repr(charstrings):
+ items = sorted(charstrings.items())
+ return "xxx"
+
+
+class ps_font(ps_object):
+ def __str__(self):
+ psstring = "%d dict dup begin\n" % len(self.value)
+ for key in _type1_pre_eexec_order:
+ try:
+ value = self.value[key]
+ except KeyError:
+ pass
+ else:
+ psstring = psstring + _type1_item_repr(key, value)
+ items = sorted(self.value.items())
+ for key, value in items:
+ if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
+ psstring = psstring + _type1_item_repr(key, value)
+ psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
+ for key in _type1_post_eexec_order:
+ try:
+ value = self.value[key]
+ except KeyError:
+ pass
+ else:
+ psstring = psstring + _type1_item_repr(key, value)
+ return (
+ psstring
+ + "dup/FontName get exch definefont pop\nmark currentfile closefile\n"
+ + 8 * (64 * "0" + "\n")
+ + "cleartomark"
+ + "\n"
+ )
+
+ def __repr__(self):
+ return ""
+
+
+class ps_file(ps_object):
+ pass
+
+
+class ps_dict(ps_object):
+ def __str__(self):
+ psstring = "%d dict dup begin\n" % len(self.value)
+ items = sorted(self.value.items())
+ for key, value in items:
+ access = _accessstrings[value.access]
+ if access:
+ access = access + " "
+ psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
+ return psstring + "end "
+
+ def __repr__(self):
+ return ""
+
+
+class ps_mark(ps_object):
+ def __init__(self):
+ self.value = "mark"
+ self.type = self.__class__.__name__[3:] + "type"
+
+
+class ps_procmark(ps_object):
+ def __init__(self):
+ self.value = "procmark"
+ self.type = self.__class__.__name__[3:] + "type"
+
+
+class ps_null(ps_object):
+ def __init__(self):
+ self.type = self.__class__.__name__[3:] + "type"
+
+
+class ps_boolean(ps_object):
+ def __str__(self):
+ if self.value:
+ return "true"
+ else:
+ return "false"
+
+
+class ps_string(ps_object):
+ def __str__(self):
+ return "(%s)" % repr(self.value)[1:-1]
+
+
+class ps_integer(ps_object):
+ def __str__(self):
+ return repr(self.value)
+
+
+class ps_real(ps_object):
+ def __str__(self):
+ return repr(self.value)
+
+
+class PSOperators(object):
+ def ps_def(self):
+ obj = self.pop()
+ name = self.pop()
+ self.dictstack[-1][name.value] = obj
+
+ def ps_bind(self):
+ proc = self.pop("proceduretype")
+ self.proc_bind(proc)
+ self.push(proc)
+
+ def proc_bind(self, proc):
+ for i in range(len(proc.value)):
+ item = proc.value[i]
+ if item.type == "proceduretype":
+ self.proc_bind(item)
+ else:
+ if not item.literal:
+ try:
+ obj = self.resolve_name(item.value)
+ except:
+ pass
+ else:
+ if obj.type == "operatortype":
+ proc.value[i] = obj
+
+ def ps_exch(self):
+ if len(self.stack) < 2:
+ raise RuntimeError("stack underflow")
+ obj1 = self.pop()
+ obj2 = self.pop()
+ self.push(obj1)
+ self.push(obj2)
+
+ def ps_dup(self):
+ if not self.stack:
+ raise RuntimeError("stack underflow")
+ self.push(self.stack[-1])
+
+ def ps_exec(self):
+ obj = self.pop()
+ if obj.type == "proceduretype":
+ self.call_procedure(obj)
+ else:
+ self.handle_object(obj)
+
+ def ps_count(self):
+ self.push(ps_integer(len(self.stack)))
+
+ def ps_eq(self):
+ any1 = self.pop()
+ any2 = self.pop()
+ self.push(ps_boolean(any1.value == any2.value))
+
+ def ps_ne(self):
+ any1 = self.pop()
+ any2 = self.pop()
+ self.push(ps_boolean(any1.value != any2.value))
+
+ def ps_cvx(self):
+ obj = self.pop()
+ obj.literal = 0
+ self.push(obj)
+
+ def ps_matrix(self):
+ matrix = [
+ ps_real(1.0),
+ ps_integer(0),
+ ps_integer(0),
+ ps_real(1.0),
+ ps_integer(0),
+ ps_integer(0),
+ ]
+ self.push(ps_array(matrix))
+
+ def ps_string(self):
+ num = self.pop("integertype").value
+ self.push(ps_string("\0" * num))
+
+ def ps_type(self):
+ obj = self.pop()
+ self.push(ps_string(obj.type))
+
+ def ps_store(self):
+ value = self.pop()
+ key = self.pop()
+ name = key.value
+ for i in range(len(self.dictstack) - 1, -1, -1):
+ if name in self.dictstack[i]:
+ self.dictstack[i][name] = value
+ break
+ self.dictstack[-1][name] = value
+
+ def ps_where(self):
+ name = self.pop()
+ # XXX
+ self.push(ps_boolean(0))
+
+ def ps_systemdict(self):
+ self.push(ps_dict(self.dictstack[0]))
+
+ def ps_userdict(self):
+ self.push(ps_dict(self.dictstack[1]))
+
+ def ps_currentdict(self):
+ self.push(ps_dict(self.dictstack[-1]))
+
+ def ps_currentfile(self):
+ self.push(ps_file(self.tokenizer))
+
+ def ps_eexec(self):
+ f = self.pop("filetype").value
+ f.starteexec()
+
+ def ps_closefile(self):
+ f = self.pop("filetype").value
+ f.skipwhite()
+ f.stopeexec()
+
+ def ps_cleartomark(self):
+ obj = self.pop()
+ while obj != self.mark:
+ obj = self.pop()
+
+ def ps_readstring(self, ps_boolean=ps_boolean, len=len):
+ s = self.pop("stringtype")
+ oldstr = s.value
+ f = self.pop("filetype")
+ # pad = file.value.read(1)
+ # for StringIO, this is faster
+ f.value.pos = f.value.pos + 1
+ newstr = f.value.read(len(oldstr))
+ s.value = newstr
+ self.push(s)
+ self.push(ps_boolean(len(oldstr) == len(newstr)))
+
+ def ps_known(self):
+ key = self.pop()
+ d = self.pop("dicttype", "fonttype")
+ self.push(ps_boolean(key.value in d.value))
+
+ def ps_if(self):
+ proc = self.pop("proceduretype")
+ if self.pop("booleantype").value:
+ self.call_procedure(proc)
+
+ def ps_ifelse(self):
+ proc2 = self.pop("proceduretype")
+ proc1 = self.pop("proceduretype")
+ if self.pop("booleantype").value:
+ self.call_procedure(proc1)
+ else:
+ self.call_procedure(proc2)
+
+ def ps_readonly(self):
+ obj = self.pop()
+ if obj.access < 1:
+ obj.access = 1
+ self.push(obj)
+
+ def ps_executeonly(self):
+ obj = self.pop()
+ if obj.access < 2:
+ obj.access = 2
+ self.push(obj)
+
+ def ps_noaccess(self):
+ obj = self.pop()
+ if obj.access < 3:
+ obj.access = 3
+ self.push(obj)
+
+ def ps_not(self):
+ obj = self.pop("booleantype", "integertype")
+ if obj.type == "booleantype":
+ self.push(ps_boolean(not obj.value))
+ else:
+ self.push(ps_integer(~obj.value))
+
+ def ps_print(self):
+ str = self.pop("stringtype")
+ print("PS output --->", str.value)
+
+ def ps_anchorsearch(self):
+ seek = self.pop("stringtype")
+ s = self.pop("stringtype")
+ seeklen = len(seek.value)
+ if s.value[:seeklen] == seek.value:
+ self.push(ps_string(s.value[seeklen:]))
+ self.push(seek)
+ self.push(ps_boolean(1))
+ else:
+ self.push(s)
+ self.push(ps_boolean(0))
+
+ def ps_array(self):
+ num = self.pop("integertype")
+ array = ps_array([None] * num.value)
+ self.push(array)
+
+ def ps_astore(self):
+ array = self.pop("arraytype")
+ for i in range(len(array.value) - 1, -1, -1):
+ array.value[i] = self.pop()
+ self.push(array)
+
+ def ps_load(self):
+ name = self.pop()
+ self.push(self.resolve_name(name.value))
+
+ def ps_put(self):
+ obj1 = self.pop()
+ obj2 = self.pop()
+ obj3 = self.pop("arraytype", "dicttype", "stringtype", "proceduretype")
+ tp = obj3.type
+ if tp == "arraytype" or tp == "proceduretype":
+ obj3.value[obj2.value] = obj1
+ elif tp == "dicttype":
+ obj3.value[obj2.value] = obj1
+ elif tp == "stringtype":
+ index = obj2.value
+ obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index + 1 :]
+
+ def ps_get(self):
+ obj1 = self.pop()
+ if obj1.value == "Encoding":
+ pass
+ obj2 = self.pop(
+ "arraytype", "dicttype", "stringtype", "proceduretype", "fonttype"
+ )
+ tp = obj2.type
+ if tp in ("arraytype", "proceduretype"):
+ self.push(obj2.value[obj1.value])
+ elif tp in ("dicttype", "fonttype"):
+ self.push(obj2.value[obj1.value])
+ elif tp == "stringtype":
+ self.push(ps_integer(ord(obj2.value[obj1.value])))
+ else:
+ assert False, "shouldn't get here"
+
+ def ps_getinterval(self):
+ obj1 = self.pop("integertype")
+ obj2 = self.pop("integertype")
+ obj3 = self.pop("arraytype", "stringtype")
+ tp = obj3.type
+ if tp == "arraytype":
+ self.push(ps_array(obj3.value[obj2.value : obj2.value + obj1.value]))
+ elif tp == "stringtype":
+ self.push(ps_string(obj3.value[obj2.value : obj2.value + obj1.value]))
+
+ def ps_putinterval(self):
+ obj1 = self.pop("arraytype", "stringtype")
+ obj2 = self.pop("integertype")
+ obj3 = self.pop("arraytype", "stringtype")
+ tp = obj3.type
+ if tp == "arraytype":
+ obj3.value[obj2.value : obj2.value + len(obj1.value)] = obj1.value
+ elif tp == "stringtype":
+ newstr = obj3.value[: obj2.value]
+ newstr = newstr + obj1.value
+ newstr = newstr + obj3.value[obj2.value + len(obj1.value) :]
+ obj3.value = newstr
+
+ def ps_cvn(self):
+ self.push(ps_name(self.pop("stringtype").value))
+
+ def ps_index(self):
+ n = self.pop("integertype").value
+ if n < 0:
+ raise RuntimeError("index may not be negative")
+ self.push(self.stack[-1 - n])
+
+ def ps_for(self):
+ proc = self.pop("proceduretype")
+ limit = self.pop("integertype", "realtype").value
+ increment = self.pop("integertype", "realtype").value
+ i = self.pop("integertype", "realtype").value
+ while 1:
+ if increment > 0:
+ if i > limit:
+ break
+ else:
+ if i < limit:
+ break
+ if type(i) == type(0.0):
+ self.push(ps_real(i))
+ else:
+ self.push(ps_integer(i))
+ self.call_procedure(proc)
+ i = i + increment
+
+ def ps_forall(self):
+ proc = self.pop("proceduretype")
+ obj = self.pop("arraytype", "stringtype", "dicttype")
+ tp = obj.type
+ if tp == "arraytype":
+ for item in obj.value:
+ self.push(item)
+ self.call_procedure(proc)
+ elif tp == "stringtype":
+ for item in obj.value:
+ self.push(ps_integer(ord(item)))
+ self.call_procedure(proc)
+ elif tp == "dicttype":
+ for key, value in obj.value.items():
+ self.push(ps_name(key))
+ self.push(value)
+ self.call_procedure(proc)
+
+ def ps_definefont(self):
+ font = self.pop("dicttype")
+ name = self.pop()
+ font = ps_font(font.value)
+ self.dictstack[0]["FontDirectory"].value[name.value] = font
+ self.push(font)
+
+ def ps_findfont(self):
+ name = self.pop()
+ font = self.dictstack[0]["FontDirectory"].value[name.value]
+ self.push(font)
+
+ def ps_pop(self):
+ self.pop()
+
+ def ps_dict(self):
+ self.pop("integertype")
+ self.push(ps_dict({}))
+
+ def ps_begin(self):
+ self.dictstack.append(self.pop("dicttype").value)
+
+ def ps_end(self):
+ if len(self.dictstack) > 2:
+ del self.dictstack[-1]
+ else:
+ raise RuntimeError("dictstack underflow")
+
+
+notdef = ".notdef"
+from fontTools.encodings.StandardEncoding import StandardEncoding
+
+ps_StandardEncoding = list(map(ps_name, StandardEncoding))
diff --git a/lib/python3.12/site-packages/fontTools/misc/py23.py b/lib/python3.12/site-packages/fontTools/misc/py23.py
new file mode 100644
index 0000000000000000000000000000000000000000..29f634d624b7df125722c3bae594c1d39a835aec
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/py23.py
@@ -0,0 +1,96 @@
+"""Python 2/3 compat layer leftovers."""
+
+import decimal as _decimal
+import math as _math
+import warnings
+from contextlib import redirect_stderr, redirect_stdout
+from io import BytesIO
+from io import StringIO as UnicodeIO
+from types import SimpleNamespace
+
+from .textTools import Tag, bytechr, byteord, bytesjoin, strjoin, tobytes, tostr
+
+warnings.warn(
+ "The py23 module has been deprecated and will be removed in a future release. "
+ "Please update your code.",
+ DeprecationWarning,
+)
+
+__all__ = [
+ "basestring",
+ "bytechr",
+ "byteord",
+ "BytesIO",
+ "bytesjoin",
+ "open",
+ "Py23Error",
+ "range",
+ "RecursionError",
+ "round",
+ "SimpleNamespace",
+ "StringIO",
+ "strjoin",
+ "Tag",
+ "tobytes",
+ "tostr",
+ "tounicode",
+ "unichr",
+ "unicode",
+ "UnicodeIO",
+ "xrange",
+ "zip",
+]
+
+
+class Py23Error(NotImplementedError):
+ pass
+
+
+RecursionError = RecursionError
+StringIO = UnicodeIO
+
+basestring = str
+isclose = _math.isclose
+isfinite = _math.isfinite
+open = open
+range = range
+round = round3 = round
+unichr = chr
+unicode = str
+zip = zip
+
+tounicode = tostr
+
+
+def xrange(*args, **kwargs):
+ raise Py23Error("'xrange' is not defined. Use 'range' instead.")
+
+
+def round2(number, ndigits=None):
+ """
+ Implementation of Python 2 built-in round() function.
+ Rounds a number to a given precision in decimal digits (default
+ 0 digits). The result is a floating point number. Values are rounded
+ to the closest multiple of 10 to the power minus ndigits; if two
+ multiples are equally close, rounding is done away from 0.
+ ndigits may be negative.
+ See Python 2 documentation:
+ https://docs.python.org/2/library/functions.html?highlight=round#round
+ """
+ if ndigits is None:
+ ndigits = 0
+
+ if ndigits < 0:
+ exponent = 10 ** (-ndigits)
+ quotient, remainder = divmod(number, exponent)
+ if remainder >= exponent // 2 and number >= 0:
+ quotient += 1
+ return float(quotient * exponent)
+ else:
+ exponent = _decimal.Decimal("10") ** (-ndigits)
+
+ d = _decimal.Decimal.from_float(number).quantize(
+ exponent, rounding=_decimal.ROUND_HALF_UP
+ )
+
+ return float(d)
diff --git a/lib/python3.12/site-packages/fontTools/misc/roundTools.py b/lib/python3.12/site-packages/fontTools/misc/roundTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4d45c31b2265cc5f705c39f41e952cc69514517
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/roundTools.py
@@ -0,0 +1,110 @@
+"""
+Various round-to-integer helpers.
+"""
+
+import math
+import functools
+import logging
+
+log = logging.getLogger(__name__)
+
+__all__ = [
+ "noRound",
+ "otRound",
+ "maybeRound",
+ "roundFunc",
+ "nearestMultipleShortestRepr",
+]
+
+
+def noRound(value):
+ return value
+
+
+def otRound(value):
+ """Round float value to nearest integer towards ``+Infinity``.
+
+ The OpenType spec (in the section on `"normalization" of OpenType Font Variations `_)
+ defines the required method for converting floating point values to
+ fixed-point. In particular it specifies the following rounding strategy:
+
+ for fractional values of 0.5 and higher, take the next higher integer;
+ for other fractional values, truncate.
+
+ This function rounds the floating-point value according to this strategy
+ in preparation for conversion to fixed-point.
+
+ Args:
+ value (float): The input floating-point value.
+
+ Returns
+ float: The rounded value.
+ """
+ # See this thread for how we ended up with this implementation:
+ # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
+ return int(math.floor(value + 0.5))
+
+
+def maybeRound(v, tolerance, round=otRound):
+ rounded = round(v)
+ return rounded if abs(rounded - v) <= tolerance else v
+
+
+def roundFunc(tolerance, round=otRound):
+ if tolerance < 0:
+ raise ValueError("Rounding tolerance must be positive")
+
+ if tolerance == 0:
+ return noRound
+
+ if tolerance >= 0.5:
+ return round
+
+ return functools.partial(maybeRound, tolerance=tolerance, round=round)
+
+
+def nearestMultipleShortestRepr(value: float, factor: float) -> str:
+ """Round to nearest multiple of factor and return shortest decimal representation.
+
+ This chooses the float that is closer to a multiple of the given factor while
+ having the shortest decimal representation (the least number of fractional decimal
+ digits).
+
+ For example, given the following:
+
+ >>> nearestMultipleShortestRepr(-0.61883544921875, 1.0/(1<<14))
+ '-0.61884'
+
+ Useful when you need to serialize or print a fixed-point number (or multiples
+ thereof, such as F2Dot14 fractions of 180 degrees in COLRv1 PaintRotate) in
+ a human-readable form.
+
+ Args:
+ value (value): The value to be rounded and serialized.
+ factor (float): The value which the result is a close multiple of.
+
+ Returns:
+ str: A compact string representation of the value.
+ """
+ if not value:
+ return "0.0"
+
+ value = otRound(value / factor) * factor
+ eps = 0.5 * factor
+ lo = value - eps
+ hi = value + eps
+ # If the range of valid choices spans an integer, return the integer.
+ if int(lo) != int(hi):
+ return str(float(round(value)))
+
+ fmt = "%.8f"
+ lo = fmt % lo
+ hi = fmt % hi
+ assert len(lo) == len(hi) and lo != hi
+ for i in range(len(lo)):
+ if lo[i] != hi[i]:
+ break
+ period = lo.find(".")
+ assert period < i
+ fmt = "%%.%df" % (i - period)
+ return fmt % value
diff --git a/lib/python3.12/site-packages/fontTools/misc/sstruct.py b/lib/python3.12/site-packages/fontTools/misc/sstruct.py
new file mode 100644
index 0000000000000000000000000000000000000000..23227d8a6839cf276045fabae4b6b1c182665a51
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/sstruct.py
@@ -0,0 +1,227 @@
+"""sstruct.py -- SuperStruct
+
+Higher level layer on top of the struct module, enabling to
+bind names to struct elements. The interface is similar to
+struct, except the objects passed and returned are not tuples
+(or argument lists), but dictionaries or instances.
+
+Just like struct, we use fmt strings to describe a data
+structure, except we use one line per element. Lines are
+separated by newlines or semi-colons. Each line contains
+either one of the special struct characters ('@', '=', '<',
+'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f').
+Repetitions, like the struct module offers them are not useful
+in this context, except for fixed length strings (eg. 'myInt:5h'
+is not allowed but 'myString:5s' is). The 'x' fmt character
+(pad byte) is treated as 'special', since it is by definition
+anonymous. Extra whitespace is allowed everywhere.
+
+The sstruct module offers one feature that the "normal" struct
+module doesn't: support for fixed point numbers. These are spelled
+as "n.mF", where n is the number of bits before the point, and m
+the number of bits after the point. Fixed point numbers get
+converted to floats.
+
+pack(fmt, object):
+ 'object' is either a dictionary or an instance (or actually
+ anything that has a __dict__ attribute). If it is a dictionary,
+ its keys are used for names. If it is an instance, it's
+ attributes are used to grab struct elements from. Returns
+ a string containing the data.
+
+unpack(fmt, data, object=None)
+ If 'object' is omitted (or None), a new dictionary will be
+ returned. If 'object' is a dictionary, it will be used to add
+ struct elements to. If it is an instance (or in fact anything
+ that has a __dict__ attribute), an attribute will be added for
+ each struct element. In the latter two cases, 'object' itself
+ is returned.
+
+unpack2(fmt, data, object=None)
+ Convenience function. Same as unpack, except data may be longer
+ than needed. The returned value is a tuple: (object, leftoverdata).
+
+calcsize(fmt)
+ like struct.calcsize(), but uses our own fmt strings:
+ it returns the size of the data in bytes.
+"""
+
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from fontTools.misc.textTools import tobytes, tostr
+import struct
+import re
+
+__version__ = "1.2"
+__copyright__ = "Copyright 1998, Just van Rossum "
+
+
+class Error(Exception):
+ pass
+
+
+def pack(fmt, obj):
+ formatstring, names, fixes = getformat(fmt, keep_pad_byte=True)
+ elements = []
+ if not isinstance(obj, dict):
+ obj = obj.__dict__
+ for name in names.keys():
+ value = obj[name]
+ if name in fixes:
+ # fixed point conversion
+ value = fl2fi(value, fixes[name])
+ elif isinstance(value, str):
+ value = tobytes(value)
+ elements.append(value)
+ # Check it fits
+ try:
+ struct.pack(names[name], value)
+ except Exception as e:
+ raise ValueError(
+ "Value %s does not fit in format %s for %s" % (value, names[name], name)
+ ) from e
+ data = struct.pack(*(formatstring,) + tuple(elements))
+ return data
+
+
+def unpack(fmt, data, obj=None):
+ if obj is None:
+ obj = {}
+ data = tobytes(data)
+ formatstring, names, fixes = getformat(fmt)
+ if isinstance(obj, dict):
+ d = obj
+ else:
+ d = obj.__dict__
+ elements = struct.unpack(formatstring, data)
+ for i, name in enumerate(names.keys()):
+ value = elements[i]
+ if name in fixes:
+ # fixed point conversion
+ value = fi2fl(value, fixes[name])
+ elif isinstance(value, bytes):
+ try:
+ value = tostr(value)
+ except UnicodeDecodeError:
+ pass
+ d[name] = value
+ return obj
+
+
+def unpack2(fmt, data, obj=None):
+ length = calcsize(fmt)
+ return unpack(fmt, data[:length], obj), data[length:]
+
+
+def calcsize(fmt):
+ formatstring, names, fixes = getformat(fmt)
+ return struct.calcsize(formatstring)
+
+
+# matches "name:formatchar" (whitespace is allowed)
+_elementRE = re.compile(
+ r"\s*" # whitespace
+ r"([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier)
+ r"\s*:\s*" # whitespace : whitespace
+ r"([xcbB?hHiIlLqQfd]|" # formatchar...
+ r"[0-9]+[ps]|" # ...formatchar...
+ r"([0-9]+)\.([0-9]+)(F))" # ...formatchar
+ r"\s*" # whitespace
+ r"(#.*)?$" # [comment] + end of string
+)
+
+# matches the special struct fmt chars and 'x' (pad byte)
+_extraRE = re.compile(r"\s*([x@=<>!])\s*(#.*)?$")
+
+# matches an "empty" string, possibly containing whitespace and/or a comment
+_emptyRE = re.compile(r"\s*(#.*)?$")
+
+_fixedpointmappings = {8: "b", 16: "h", 32: "l"}
+
+_formatcache = {}
+
+
+def getformat(fmt, keep_pad_byte=False):
+ fmt = tostr(fmt, encoding="ascii")
+ try:
+ formatstring, names, fixes = _formatcache[fmt]
+ except KeyError:
+ lines = re.split("[\n;]", fmt)
+ formatstring = ""
+ names = {}
+ fixes = {}
+ for line in lines:
+ if _emptyRE.match(line):
+ continue
+ m = _extraRE.match(line)
+ if m:
+ formatchar = m.group(1)
+ if formatchar != "x" and formatstring:
+ raise Error("a special fmt char must be first")
+ else:
+ m = _elementRE.match(line)
+ if not m:
+ raise Error("syntax error in fmt: '%s'" % line)
+ name = m.group(1)
+ formatchar = m.group(2)
+ if keep_pad_byte or formatchar != "x":
+ names[name] = formatchar
+ if m.group(3):
+ # fixed point
+ before = int(m.group(3))
+ after = int(m.group(4))
+ bits = before + after
+ if bits not in [8, 16, 32]:
+ raise Error("fixed point must be 8, 16 or 32 bits long")
+ formatchar = _fixedpointmappings[bits]
+ names[name] = formatchar
+ assert m.group(5) == "F"
+ fixes[name] = after
+ formatstring += formatchar
+ _formatcache[fmt] = formatstring, names, fixes
+ return formatstring, names, fixes
+
+
+def _test():
+ fmt = """
+ # comments are allowed
+ > # big endian (see documentation for struct)
+ # empty lines are allowed:
+
+ ashort: h
+ along: l
+ abyte: b # a byte
+ achar: c
+ astr: 5s
+ afloat: f; adouble: d # multiple "statements" are allowed
+ afixed: 16.16F
+ abool: ?
+ apad: x
+ """
+
+ print("size:", calcsize(fmt))
+
+ class foo(object):
+ pass
+
+ i = foo()
+
+ i.ashort = 0x7FFF
+ i.along = 0x7FFFFFFF
+ i.abyte = 0x7F
+ i.achar = "a"
+ i.astr = "12345"
+ i.afloat = 0.5
+ i.adouble = 0.5
+ i.afixed = 1.5
+ i.abool = True
+
+ data = pack(fmt, i)
+ print("data:", repr(data))
+ print(unpack(fmt, data))
+ i2 = foo()
+ unpack(fmt, data, i2)
+ print(vars(i2))
+
+
+if __name__ == "__main__":
+ _test()
diff --git a/lib/python3.12/site-packages/fontTools/misc/symfont.py b/lib/python3.12/site-packages/fontTools/misc/symfont.py
new file mode 100644
index 0000000000000000000000000000000000000000..9bba2d2d5672a2acdbb0763397288b49a2b7a8de
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/symfont.py
@@ -0,0 +1,242 @@
+from fontTools.pens.basePen import BasePen
+from functools import partial
+from itertools import count
+import sympy as sp
+import sys
+
+n = 3 # Max Bezier degree; 3 for cubic, 2 for quadratic
+
+t, x, y = sp.symbols("t x y", real=True)
+c = sp.symbols("c", real=False) # Complex representation instead of x/y
+
+X = tuple(sp.symbols("x:%d" % (n + 1), real=True))
+Y = tuple(sp.symbols("y:%d" % (n + 1), real=True))
+P = tuple(zip(*(sp.symbols("p:%d[%s]" % (n + 1, w), real=True) for w in "01")))
+C = tuple(sp.symbols("c:%d" % (n + 1), real=False))
+
+# Cubic Bernstein basis functions
+BinomialCoefficient = [(1, 0)]
+for i in range(1, n + 1):
+ last = BinomialCoefficient[-1]
+ this = tuple(last[j - 1] + last[j] for j in range(len(last))) + (0,)
+ BinomialCoefficient.append(this)
+BinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient)
+del last, this
+
+BernsteinPolynomial = tuple(
+ tuple(c * t**i * (1 - t) ** (n - i) for i, c in enumerate(coeffs))
+ for n, coeffs in enumerate(BinomialCoefficient)
+)
+
+BezierCurve = tuple(
+ tuple(
+ sum(P[i][j] * bernstein for i, bernstein in enumerate(bernsteins))
+ for j in range(2)
+ )
+ for n, bernsteins in enumerate(BernsteinPolynomial)
+)
+BezierCurveC = tuple(
+ sum(C[i] * bernstein for i, bernstein in enumerate(bernsteins))
+ for n, bernsteins in enumerate(BernsteinPolynomial)
+)
+
+
+def green(f, curveXY):
+ f = -sp.integrate(sp.sympify(f), y)
+ f = f.subs({x: curveXY[0], y: curveXY[1]})
+ f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1))
+ return f
+
+
+class _BezierFuncsLazy(dict):
+ def __init__(self, symfunc):
+ self._symfunc = symfunc
+ self._bezfuncs = {}
+
+ def __missing__(self, i):
+ args = ["p%d" % d for d in range(i + 1)]
+ f = green(self._symfunc, BezierCurve[i])
+ f = sp.gcd_terms(f.collect(sum(P, ()))) # Optimize
+ return sp.lambdify(args, f)
+
+
+class GreenPen(BasePen):
+ _BezierFuncs = {}
+
+ @classmethod
+ def _getGreenBezierFuncs(celf, func):
+ funcstr = str(func)
+ if not funcstr in celf._BezierFuncs:
+ celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func)
+ return celf._BezierFuncs[funcstr]
+
+ def __init__(self, func, glyphset=None):
+ BasePen.__init__(self, glyphset)
+ self._funcs = self._getGreenBezierFuncs(func)
+ self.value = 0
+
+ def _moveTo(self, p0):
+ self._startPoint = p0
+
+ def _closePath(self):
+ p0 = self._getCurrentPoint()
+ if p0 != self._startPoint:
+ self._lineTo(self._startPoint)
+
+ def _endPath(self):
+ p0 = self._getCurrentPoint()
+ if p0 != self._startPoint:
+ # Green theorem is not defined on open contours.
+ raise NotImplementedError
+
+ def _lineTo(self, p1):
+ p0 = self._getCurrentPoint()
+ self.value += self._funcs[1](p0, p1)
+
+ def _qCurveToOne(self, p1, p2):
+ p0 = self._getCurrentPoint()
+ self.value += self._funcs[2](p0, p1, p2)
+
+ def _curveToOne(self, p1, p2, p3):
+ p0 = self._getCurrentPoint()
+ self.value += self._funcs[3](p0, p1, p2, p3)
+
+
+# Sample pens.
+# Do not use this in real code.
+# Use fontTools.pens.momentsPen.MomentsPen instead.
+AreaPen = partial(GreenPen, func=1)
+MomentXPen = partial(GreenPen, func=x)
+MomentYPen = partial(GreenPen, func=y)
+MomentXXPen = partial(GreenPen, func=x * x)
+MomentYYPen = partial(GreenPen, func=y * y)
+MomentXYPen = partial(GreenPen, func=x * y)
+
+
+def printGreenPen(penName, funcs, file=sys.stdout, docstring=None):
+ if docstring is not None:
+ print('"""%s"""' % docstring)
+
+ print(
+ """from fontTools.pens.basePen import BasePen, OpenContourError
+try:
+ import cython
+except (AttributeError, ImportError):
+ # if cython not installed, use mock module with no-op decorators and types
+ from fontTools.misc import cython
+COMPILED = cython.compiled
+
+
+__all__ = ["%s"]
+
+class %s(BasePen):
+
+ def __init__(self, glyphset=None):
+ BasePen.__init__(self, glyphset)
+"""
+ % (penName, penName),
+ file=file,
+ )
+ for name, f in funcs:
+ print(" self.%s = 0" % name, file=file)
+ print(
+ """
+ def _moveTo(self, p0):
+ self._startPoint = p0
+
+ def _closePath(self):
+ p0 = self._getCurrentPoint()
+ if p0 != self._startPoint:
+ self._lineTo(self._startPoint)
+
+ def _endPath(self):
+ p0 = self._getCurrentPoint()
+ if p0 != self._startPoint:
+ raise OpenContourError(
+ "Glyph statistics is not defined on open contours."
+ )
+""",
+ end="",
+ file=file,
+ )
+
+ for n in (1, 2, 3):
+ subs = {P[i][j]: [X, Y][j][i] for i in range(n + 1) for j in range(2)}
+ greens = [green(f, BezierCurve[n]) for name, f in funcs]
+ greens = [sp.gcd_terms(f.collect(sum(P, ()))) for f in greens] # Optimize
+ greens = [f.subs(subs) for f in greens] # Convert to p to x/y
+ defs, exprs = sp.cse(
+ greens,
+ optimizations="basic",
+ symbols=(sp.Symbol("r%d" % i) for i in count()),
+ )
+
+ print()
+ for name, value in defs:
+ print(" @cython.locals(%s=cython.double)" % name, file=file)
+ if n == 1:
+ print(
+ """\
+ @cython.locals(x0=cython.double, y0=cython.double)
+ @cython.locals(x1=cython.double, y1=cython.double)
+ def _lineTo(self, p1):
+ x0,y0 = self._getCurrentPoint()
+ x1,y1 = p1
+""",
+ file=file,
+ )
+ elif n == 2:
+ print(
+ """\
+ @cython.locals(x0=cython.double, y0=cython.double)
+ @cython.locals(x1=cython.double, y1=cython.double)
+ @cython.locals(x2=cython.double, y2=cython.double)
+ def _qCurveToOne(self, p1, p2):
+ x0,y0 = self._getCurrentPoint()
+ x1,y1 = p1
+ x2,y2 = p2
+""",
+ file=file,
+ )
+ elif n == 3:
+ print(
+ """\
+ @cython.locals(x0=cython.double, y0=cython.double)
+ @cython.locals(x1=cython.double, y1=cython.double)
+ @cython.locals(x2=cython.double, y2=cython.double)
+ @cython.locals(x3=cython.double, y3=cython.double)
+ def _curveToOne(self, p1, p2, p3):
+ x0,y0 = self._getCurrentPoint()
+ x1,y1 = p1
+ x2,y2 = p2
+ x3,y3 = p3
+""",
+ file=file,
+ )
+ for name, value in defs:
+ print(" %s = %s" % (name, value), file=file)
+
+ print(file=file)
+ for name, value in zip([f[0] for f in funcs], exprs):
+ print(" self.%s += %s" % (name, value), file=file)
+
+ print(
+ """
+if __name__ == '__main__':
+ from fontTools.misc.symfont import x, y, printGreenPen
+ printGreenPen('%s', ["""
+ % penName,
+ file=file,
+ )
+ for name, f in funcs:
+ print(" ('%s', %s)," % (name, str(f)), file=file)
+ print(" ])", file=file)
+
+
+if __name__ == "__main__":
+ import sys
+
+ if sys.argv[1:]:
+ penName = sys.argv[1]
+ funcs = [(name, eval(f)) for name, f in zip(sys.argv[2::2], sys.argv[3::2])]
+ printGreenPen(penName, funcs, file=sys.stdout)
diff --git a/lib/python3.12/site-packages/fontTools/misc/testTools.py b/lib/python3.12/site-packages/fontTools/misc/testTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2eaf3833216a996103cfe682009057668768c45
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/testTools.py
@@ -0,0 +1,233 @@
+"""Helpers for writing unit tests."""
+
+from collections.abc import Iterable
+from io import BytesIO
+import os
+import re
+import shutil
+import sys
+import tempfile
+from unittest import TestCase as _TestCase
+from fontTools.config import Config
+from fontTools.misc.textTools import tobytes
+from fontTools.misc.xmlWriter import XMLWriter
+
+
+def parseXML(xmlSnippet):
+ """Parses a snippet of XML.
+
+ Input can be either a single string (unicode or UTF-8 bytes), or a
+ a sequence of strings.
+
+ The result is in the same format that would be returned by
+ XMLReader, but the parser imposes no constraints on the root
+ element so it can be called on small snippets of TTX files.
+ """
+ # To support snippets with multiple elements, we add a fake root.
+ reader = TestXMLReader_()
+ xml = b""
+ if isinstance(xmlSnippet, bytes):
+ xml += xmlSnippet
+ elif isinstance(xmlSnippet, str):
+ xml += tobytes(xmlSnippet, "utf-8")
+ elif isinstance(xmlSnippet, Iterable):
+ xml += b"".join(tobytes(s, "utf-8") for s in xmlSnippet)
+ else:
+ raise TypeError(
+ "expected string or sequence of strings; found %r"
+ % type(xmlSnippet).__name__
+ )
+ xml += b""
+ reader.parser.Parse(xml, 1)
+ return reader.root[2]
+
+
+def parseXmlInto(font, parseInto, xmlSnippet):
+ parsed_xml = [e for e in parseXML(xmlSnippet.strip()) if not isinstance(e, str)]
+ for name, attrs, content in parsed_xml:
+ parseInto.fromXML(name, attrs, content, font)
+ if hasattr(parseInto, "populateDefaults"):
+ parseInto.populateDefaults()
+ return parseInto
+
+
+class FakeFont:
+ def __init__(self, glyphs):
+ self.glyphOrder_ = glyphs
+ self.reverseGlyphOrderDict_ = {g: i for i, g in enumerate(glyphs)}
+ self.lazy = False
+ self.tables = {}
+ self.cfg = Config()
+
+ def __contains__(self, tag):
+ return tag in self.tables
+
+ def __getitem__(self, tag):
+ return self.tables[tag]
+
+ def __setitem__(self, tag, table):
+ self.tables[tag] = table
+
+ def get(self, tag, default=None):
+ return self.tables.get(tag, default)
+
+ def getGlyphID(self, name):
+ return self.reverseGlyphOrderDict_[name]
+
+ def getGlyphIDMany(self, lst):
+ return [self.getGlyphID(gid) for gid in lst]
+
+ def getGlyphName(self, glyphID):
+ if glyphID < len(self.glyphOrder_):
+ return self.glyphOrder_[glyphID]
+ else:
+ return "glyph%.5d" % glyphID
+
+ def getGlyphNameMany(self, lst):
+ return [self.getGlyphName(gid) for gid in lst]
+
+ def getGlyphOrder(self):
+ return self.glyphOrder_
+
+ def getReverseGlyphMap(self):
+ return self.reverseGlyphOrderDict_
+
+ def getGlyphNames(self):
+ return sorted(self.getGlyphOrder())
+
+
+class TestXMLReader_(object):
+ def __init__(self):
+ from xml.parsers.expat import ParserCreate
+
+ self.parser = ParserCreate()
+ self.parser.StartElementHandler = self.startElement_
+ self.parser.EndElementHandler = self.endElement_
+ self.parser.CharacterDataHandler = self.addCharacterData_
+ self.root = None
+ self.stack = []
+
+ def startElement_(self, name, attrs):
+ element = (name, attrs, [])
+ if self.stack:
+ self.stack[-1][2].append(element)
+ else:
+ self.root = element
+ self.stack.append(element)
+
+ def endElement_(self, name):
+ self.stack.pop()
+
+ def addCharacterData_(self, data):
+ self.stack[-1][2].append(data)
+
+
+def makeXMLWriter(newlinestr="\n"):
+ # don't write OS-specific new lines
+ writer = XMLWriter(BytesIO(), newlinestr=newlinestr)
+ # erase XML declaration
+ writer.file.seek(0)
+ writer.file.truncate()
+ return writer
+
+
+def getXML(func, ttFont=None):
+ """Call the passed toXML function and return the written content as a
+ list of lines (unicode strings).
+ Result is stripped of XML declaration and OS-specific newline characters.
+ """
+ writer = makeXMLWriter()
+ func(writer, ttFont)
+ xml = writer.file.getvalue().decode("utf-8")
+ # toXML methods must always end with a writer.newline()
+ assert xml.endswith("\n")
+ return xml.splitlines()
+
+
+def stripVariableItemsFromTTX(
+ string: str,
+ ttLibVersion: bool = True,
+ checkSumAdjustment: bool = True,
+ modified: bool = True,
+ created: bool = True,
+ sfntVersion: bool = False, # opt-in only
+) -> str:
+ """Strip stuff like ttLibVersion, checksums, timestamps, etc. from TTX dumps."""
+ # ttlib changes with the fontTools version
+ if ttLibVersion:
+ string = re.sub(' ttLibVersion="[^"]+"', "", string)
+ # sometimes (e.g. some subsetter tests) we don't care whether it's OTF or TTF
+ if sfntVersion:
+ string = re.sub(' sfntVersion="[^"]+"', "", string)
+ # head table checksum and creation and mod date changes with each save.
+ if checkSumAdjustment:
+ string = re.sub('', "", string)
+ if modified:
+ string = re.sub('', "", string)
+ if created:
+ string = re.sub('', "", string)
+ return string
+
+
+class MockFont(object):
+ """A font-like object that automatically adds any looked up glyphname
+ to its glyphOrder."""
+
+ def __init__(self):
+ self._glyphOrder = [".notdef"]
+
+ class AllocatingDict(dict):
+ def __missing__(reverseDict, key):
+ self._glyphOrder.append(key)
+ gid = len(reverseDict)
+ reverseDict[key] = gid
+ return gid
+
+ self._reverseGlyphOrder = AllocatingDict({".notdef": 0})
+ self.lazy = False
+
+ def getGlyphID(self, glyph):
+ gid = self._reverseGlyphOrder[glyph]
+ return gid
+
+ def getReverseGlyphMap(self):
+ return self._reverseGlyphOrder
+
+ def getGlyphName(self, gid):
+ return self._glyphOrder[gid]
+
+ def getGlyphOrder(self):
+ return self._glyphOrder
+
+
+class TestCase(_TestCase):
+ def __init__(self, methodName):
+ _TestCase.__init__(self, methodName)
+ # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+ # and fires deprecation warnings if a program uses the old name.
+ if not hasattr(self, "assertRaisesRegex"):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
+
+class DataFilesHandler(TestCase):
+ def setUp(self):
+ self.tempdir = None
+ self.num_tempfiles = 0
+
+ def tearDown(self):
+ if self.tempdir:
+ shutil.rmtree(self.tempdir)
+
+ def getpath(self, testfile):
+ folder = os.path.dirname(sys.modules[self.__module__].__file__)
+ return os.path.join(folder, "data", testfile)
+
+ def temp_dir(self):
+ if not self.tempdir:
+ self.tempdir = tempfile.mkdtemp()
+
+ def temp_font(self, font_path, file_name):
+ self.temp_dir()
+ temppath = os.path.join(self.tempdir, file_name)
+ shutil.copy2(font_path, temppath)
+ return temppath
diff --git a/lib/python3.12/site-packages/fontTools/misc/textTools.py b/lib/python3.12/site-packages/fontTools/misc/textTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..f38cfe4ede5b1176cf14c131059f213e40966478
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/textTools.py
@@ -0,0 +1,156 @@
+"""fontTools.misc.textTools.py -- miscellaneous routines."""
+
+from __future__ import annotations
+
+import ast
+import string
+
+
+# alias kept for backward compatibility
+safeEval = ast.literal_eval
+
+
+class Tag(str):
+ @staticmethod
+ def transcode(blob):
+ if isinstance(blob, bytes):
+ blob = blob.decode("latin-1")
+ return blob
+
+ def __new__(self, content):
+ return str.__new__(self, self.transcode(content))
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ return str.__eq__(self, self.transcode(other))
+
+ def __hash__(self):
+ return str.__hash__(self)
+
+ def tobytes(self):
+ return self.encode("latin-1")
+
+
+def readHex(content):
+ """Convert a list of hex strings to binary data."""
+ return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str)))
+
+
+def deHexStr(hexdata):
+ """Convert a hex string to binary data."""
+ hexdata = strjoin(hexdata.split())
+ if len(hexdata) % 2:
+ hexdata = hexdata + "0"
+ data = []
+ for i in range(0, len(hexdata), 2):
+ data.append(bytechr(int(hexdata[i : i + 2], 16)))
+ return bytesjoin(data)
+
+
+def hexStr(data):
+ """Convert binary data to a hex string."""
+ h = string.hexdigits
+ r = ""
+ for c in data:
+ i = byteord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
+
+
+def num2binary(l, bits=32):
+ items = []
+ binary = ""
+ for i in range(bits):
+ if l & 0x1:
+ binary = "1" + binary
+ else:
+ binary = "0" + binary
+ l = l >> 1
+ if not ((i + 1) % 8):
+ items.append(binary)
+ binary = ""
+ if binary:
+ items.append(binary)
+ items.reverse()
+ assert l in (0, -1), "number doesn't fit in number of bits"
+ return " ".join(items)
+
+
+def binary2num(bin):
+ bin = strjoin(bin.split())
+ l = 0
+ for digit in bin:
+ l = l << 1
+ if digit != "0":
+ l = l | 0x1
+ return l
+
+
+def caselessSort(alist):
+ """Return a sorted copy of a list. If there are only strings
+ in the list, it will not consider case.
+ """
+
+ try:
+ return sorted(alist, key=lambda a: (a.lower(), a))
+ except TypeError:
+ return sorted(alist)
+
+
+def pad(data, size):
+ r"""Pad byte string 'data' with null bytes until its length is a
+ multiple of 'size'.
+
+ >>> len(pad(b'abcd', 4))
+ 4
+ >>> len(pad(b'abcde', 2))
+ 6
+ >>> len(pad(b'abcde', 4))
+ 8
+ >>> pad(b'abcdef', 4) == b'abcdef\x00\x00'
+ True
+ """
+ data = tobytes(data)
+ if size > 1:
+ remainder = len(data) % size
+ if remainder:
+ data += b"\0" * (size - remainder)
+ return data
+
+
+def tostr(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> str:
+ if not isinstance(s, str):
+ return s.decode(encoding, errors)
+ else:
+ return s
+
+
+def tobytes(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> bytes:
+ if isinstance(s, str):
+ return s.encode(encoding, errors)
+ else:
+ return bytes(s)
+
+
+def bytechr(n):
+ return bytes([n])
+
+
+def byteord(c):
+ return c if isinstance(c, int) else ord(c)
+
+
+def strjoin(iterable, joiner=""):
+ return tostr(joiner).join(iterable)
+
+
+def bytesjoin(iterable, joiner=b""):
+ return tobytes(joiner).join(tobytes(item) for item in iterable)
+
+
+if __name__ == "__main__":
+ import doctest, sys
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/timeTools.py b/lib/python3.12/site-packages/fontTools/misc/timeTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..175ce81563daf3e9a924701dd2c9d4b71084c286
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/timeTools.py
@@ -0,0 +1,88 @@
+"""fontTools.misc.timeTools.py -- tools for working with OpenType timestamps.
+"""
+
+import os
+import time
+from datetime import datetime, timezone
+import calendar
+
+
+epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
+
+DAYNAMES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+MONTHNAMES = [
+ None,
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+]
+
+
+def asctime(t=None):
+ """
+ Convert a tuple or struct_time representing a time as returned by gmtime()
+ or localtime() to a 24-character string of the following form:
+
+ >>> asctime(time.gmtime(0))
+ 'Thu Jan 1 00:00:00 1970'
+
+ If t is not provided, the current time as returned by localtime() is used.
+ Locale information is not used by asctime().
+
+ This is meant to normalise the output of the built-in time.asctime() across
+ different platforms and Python versions.
+ In Python 3.x, the day of the month is right-justified, whereas on Windows
+ Python 2.7 it is padded with zeros.
+
+ See https://github.com/fonttools/fonttools/issues/455
+ """
+ if t is None:
+ t = time.localtime()
+ s = "%s %s %2s %s" % (
+ DAYNAMES[t.tm_wday],
+ MONTHNAMES[t.tm_mon],
+ t.tm_mday,
+ time.strftime("%H:%M:%S %Y", t),
+ )
+ return s
+
+
+def timestampToString(value):
+ return asctime(time.gmtime(max(0, value + epoch_diff)))
+
+
+def timestampFromString(value):
+ wkday, mnth = value[:7].split()
+ t = datetime.strptime(value[7:], " %d %H:%M:%S %Y")
+ t = t.replace(month=MONTHNAMES.index(mnth), tzinfo=timezone.utc)
+ wkday_idx = DAYNAMES.index(wkday)
+ assert t.weekday() == wkday_idx, '"' + value + '" has inconsistent weekday'
+ return int(t.timestamp()) - epoch_diff
+
+
+def timestampNow():
+ # https://reproducible-builds.org/specs/source-date-epoch/
+ source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH")
+ if source_date_epoch is not None:
+ return int(source_date_epoch) - epoch_diff
+ return int(time.time() - epoch_diff)
+
+
+def timestampSinceEpoch(value):
+ return int(value - epoch_diff)
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/transform.py b/lib/python3.12/site-packages/fontTools/misc/transform.py
new file mode 100644
index 0000000000000000000000000000000000000000..aeacc30fcb183cee3f47310ebf4606d69184cdc1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/transform.py
@@ -0,0 +1,516 @@
+"""Affine 2D transformation matrix class.
+
+The Transform class implements various transformation matrix operations,
+both on the matrix itself, as well as on 2D coordinates.
+
+Transform instances are effectively immutable: all methods that operate on the
+transformation itself always return a new instance. This has as the
+interesting side effect that Transform instances are hashable, ie. they can be
+used as dictionary keys.
+
+This module exports the following symbols:
+
+Transform
+ this is the main class
+Identity
+ Transform instance set to the identity transformation
+Offset
+ Convenience function that returns a translating transformation
+Scale
+ Convenience function that returns a scaling transformation
+
+The DecomposedTransform class implements a transformation with separate
+translate, rotation, scale, skew, and transformation-center components.
+
+:Example:
+
+ >>> t = Transform(2, 0, 0, 3, 0, 0)
+ >>> t.transformPoint((100, 100))
+ (200, 300)
+ >>> t = Scale(2, 3)
+ >>> t.transformPoint((100, 100))
+ (200, 300)
+ >>> t.transformPoint((0, 0))
+ (0, 0)
+ >>> t = Offset(2, 3)
+ >>> t.transformPoint((100, 100))
+ (102, 103)
+ >>> t.transformPoint((0, 0))
+ (2, 3)
+ >>> t2 = t.scale(0.5)
+ >>> t2.transformPoint((100, 100))
+ (52.0, 53.0)
+ >>> import math
+ >>> t3 = t2.rotate(math.pi / 2)
+ >>> t3.transformPoint((0, 0))
+ (2.0, 3.0)
+ >>> t3.transformPoint((100, 100))
+ (-48.0, 53.0)
+ >>> t = Identity.scale(0.5).translate(100, 200).skew(0.1, 0.2)
+ >>> t.transformPoints([(0, 0), (1, 1), (100, 100)])
+ [(50.0, 100.0), (50.550167336042726, 100.60135501775433), (105.01673360427253, 160.13550177543362)]
+ >>>
+"""
+
+from __future__ import annotations
+
+import math
+from typing import NamedTuple
+from dataclasses import dataclass
+
+
+__all__ = ["Transform", "Identity", "Offset", "Scale", "DecomposedTransform"]
+
+
+_EPSILON = 1e-15
+_ONE_EPSILON = 1 - _EPSILON
+_MINUS_ONE_EPSILON = -1 + _EPSILON
+
+
+def _normSinCos(v: float) -> float:
+ if abs(v) < _EPSILON:
+ v = 0
+ elif v > _ONE_EPSILON:
+ v = 1
+ elif v < _MINUS_ONE_EPSILON:
+ v = -1
+ return v
+
+
+class Transform(NamedTuple):
+ """2x2 transformation matrix plus offset, a.k.a. Affine transform.
+ Transform instances are immutable: all transforming methods, eg.
+ rotate(), return a new Transform instance.
+
+ :Example:
+
+ >>> t = Transform()
+ >>> t
+
+ >>> t.scale(2)
+
+ >>> t.scale(2.5, 5.5)
+
+ >>>
+ >>> t.scale(2, 3).transformPoint((100, 100))
+ (200, 300)
+
+ Transform's constructor takes six arguments, all of which are
+ optional, and can be used as keyword arguments::
+
+ >>> Transform(12)
+
+ >>> Transform(dx=12)
+
+ >>> Transform(yx=12)
+
+
+ Transform instances also behave like sequences of length 6::
+
+ >>> len(Identity)
+ 6
+ >>> list(Identity)
+ [1, 0, 0, 1, 0, 0]
+ >>> tuple(Identity)
+ (1, 0, 0, 1, 0, 0)
+
+ Transform instances are comparable::
+
+ >>> t1 = Identity.scale(2, 3).translate(4, 6)
+ >>> t2 = Identity.translate(8, 18).scale(2, 3)
+ >>> t1 == t2
+ 1
+
+ But beware of floating point rounding errors::
+
+ >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
+ >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
+ >>> t1
+
+ >>> t2
+
+ >>> t1 == t2
+ 0
+
+ Transform instances are hashable, meaning you can use them as
+ keys in dictionaries::
+
+ >>> d = {Scale(12, 13): None}
+ >>> d
+ {: None}
+
+ But again, beware of floating point rounding errors::
+
+ >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
+ >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
+ >>> t1
+
+ >>> t2
+
+ >>> d = {t1: None}
+ >>> d
+ {: None}
+ >>> d[t2]
+ Traceback (most recent call last):
+ File "", line 1, in ?
+ KeyError:
+ """
+
+ xx: float = 1
+ xy: float = 0
+ yx: float = 0
+ yy: float = 1
+ dx: float = 0
+ dy: float = 0
+
+ def transformPoint(self, p):
+ """Transform a point.
+
+ :Example:
+
+ >>> t = Transform()
+ >>> t = t.scale(2.5, 5.5)
+ >>> t.transformPoint((100, 100))
+ (250.0, 550.0)
+ """
+ (x, y) = p
+ xx, xy, yx, yy, dx, dy = self
+ return (xx * x + yx * y + dx, xy * x + yy * y + dy)
+
+ def transformPoints(self, points):
+ """Transform a list of points.
+
+ :Example:
+
+ >>> t = Scale(2, 3)
+ >>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)])
+ [(0, 0), (0, 300), (200, 300), (200, 0)]
+ >>>
+ """
+ xx, xy, yx, yy, dx, dy = self
+ return [(xx * x + yx * y + dx, xy * x + yy * y + dy) for x, y in points]
+
+ def transformVector(self, v):
+ """Transform an (dx, dy) vector, treating translation as zero.
+
+ :Example:
+
+ >>> t = Transform(2, 0, 0, 2, 10, 20)
+ >>> t.transformVector((3, -4))
+ (6, -8)
+ >>>
+ """
+ (dx, dy) = v
+ xx, xy, yx, yy = self[:4]
+ return (xx * dx + yx * dy, xy * dx + yy * dy)
+
+ def transformVectors(self, vectors):
+ """Transform a list of (dx, dy) vector, treating translation as zero.
+
+ :Example:
+ >>> t = Transform(2, 0, 0, 2, 10, 20)
+ >>> t.transformVectors([(3, -4), (5, -6)])
+ [(6, -8), (10, -12)]
+ >>>
+ """
+ xx, xy, yx, yy = self[:4]
+ return [(xx * dx + yx * dy, xy * dx + yy * dy) for dx, dy in vectors]
+
+ def translate(self, x: float = 0, y: float = 0):
+ """Return a new transformation, translated (offset) by x, y.
+
+ :Example:
+ >>> t = Transform()
+ >>> t.translate(20, 30)
+
+ >>>
+ """
+ return self.transform((1, 0, 0, 1, x, y))
+
+ def scale(self, x: float = 1, y: float | None = None):
+ """Return a new transformation, scaled by x, y. The 'y' argument
+ may be None, which implies to use the x value for y as well.
+
+ :Example:
+ >>> t = Transform()
+ >>> t.scale(5)
+
+ >>> t.scale(5, 6)
+
+ >>>
+ """
+ if y is None:
+ y = x
+ return self.transform((x, 0, 0, y, 0, 0))
+
+ def rotate(self, angle: float):
+ """Return a new transformation, rotated by 'angle' (radians).
+
+ :Example:
+ >>> import math
+ >>> t = Transform()
+ >>> t.rotate(math.pi / 2)
+
+ >>>
+ """
+ c = _normSinCos(math.cos(angle))
+ s = _normSinCos(math.sin(angle))
+ return self.transform((c, s, -s, c, 0, 0))
+
+ def skew(self, x: float = 0, y: float = 0):
+ """Return a new transformation, skewed by x and y.
+
+ :Example:
+ >>> import math
+ >>> t = Transform()
+ >>> t.skew(math.pi / 4)
+
+ >>>
+ """
+ return self.transform((1, math.tan(y), math.tan(x), 1, 0, 0))
+
+ def transform(self, other):
+ """Return a new transformation, transformed by another
+ transformation.
+
+ :Example:
+ >>> t = Transform(2, 0, 0, 3, 1, 6)
+ >>> t.transform((4, 3, 2, 1, 5, 6))
+
+ >>>
+ """
+ xx1, xy1, yx1, yy1, dx1, dy1 = other
+ xx2, xy2, yx2, yy2, dx2, dy2 = self
+ return self.__class__(
+ xx1 * xx2 + xy1 * yx2,
+ xx1 * xy2 + xy1 * yy2,
+ yx1 * xx2 + yy1 * yx2,
+ yx1 * xy2 + yy1 * yy2,
+ xx2 * dx1 + yx2 * dy1 + dx2,
+ xy2 * dx1 + yy2 * dy1 + dy2,
+ )
+
+ def reverseTransform(self, other):
+ """Return a new transformation, which is the other transformation
+ transformed by self. self.reverseTransform(other) is equivalent to
+ other.transform(self).
+
+ :Example:
+ >>> t = Transform(2, 0, 0, 3, 1, 6)
+ >>> t.reverseTransform((4, 3, 2, 1, 5, 6))
+
+ >>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6))
+
+ >>>
+ """
+ xx1, xy1, yx1, yy1, dx1, dy1 = self
+ xx2, xy2, yx2, yy2, dx2, dy2 = other
+ return self.__class__(
+ xx1 * xx2 + xy1 * yx2,
+ xx1 * xy2 + xy1 * yy2,
+ yx1 * xx2 + yy1 * yx2,
+ yx1 * xy2 + yy1 * yy2,
+ xx2 * dx1 + yx2 * dy1 + dx2,
+ xy2 * dx1 + yy2 * dy1 + dy2,
+ )
+
+ def inverse(self):
+ """Return the inverse transformation.
+
+ :Example:
+ >>> t = Identity.translate(2, 3).scale(4, 5)
+ >>> t.transformPoint((10, 20))
+ (42, 103)
+ >>> it = t.inverse()
+ >>> it.transformPoint((42, 103))
+ (10.0, 20.0)
+ >>>
+ """
+ if self == Identity:
+ return self
+ xx, xy, yx, yy, dx, dy = self
+ det = xx * yy - yx * xy
+ xx, xy, yx, yy = yy / det, -xy / det, -yx / det, xx / det
+ dx, dy = -xx * dx - yx * dy, -xy * dx - yy * dy
+ return self.__class__(xx, xy, yx, yy, dx, dy)
+
+ def toPS(self) -> str:
+ """Return a PostScript representation
+
+ :Example:
+
+ >>> t = Identity.scale(2, 3).translate(4, 5)
+ >>> t.toPS()
+ '[2 0 0 3 8 15]'
+ >>>
+ """
+ return "[%s %s %s %s %s %s]" % self
+
+ def toDecomposed(self) -> "DecomposedTransform":
+ """Decompose into a DecomposedTransform."""
+ return DecomposedTransform.fromTransform(self)
+
+ def __bool__(self) -> bool:
+ """Returns True if transform is not identity, False otherwise.
+
+ :Example:
+
+ >>> bool(Identity)
+ False
+ >>> bool(Transform())
+ False
+ >>> bool(Scale(1.))
+ False
+ >>> bool(Scale(2))
+ True
+ >>> bool(Offset())
+ False
+ >>> bool(Offset(0))
+ False
+ >>> bool(Offset(2))
+ True
+ """
+ return self != Identity
+
+ def __repr__(self) -> str:
+ return "<%s [%g %g %g %g %g %g]>" % ((self.__class__.__name__,) + self)
+
+
+Identity = Transform()
+
+
+def Offset(x: float = 0, y: float = 0) -> Transform:
+ """Return the identity transformation offset by x, y.
+
+ :Example:
+ >>> Offset(2, 3)
+
+ >>>
+ """
+ return Transform(1, 0, 0, 1, x, y)
+
+
+def Scale(x: float, y: float | None = None) -> Transform:
+ """Return the identity transformation scaled by x, y. The 'y' argument
+ may be None, which implies to use the x value for y as well.
+
+ :Example:
+ >>> Scale(2, 3)
+
+ >>>
+ """
+ if y is None:
+ y = x
+ return Transform(x, 0, 0, y, 0, 0)
+
+
+@dataclass
+class DecomposedTransform:
+ """The DecomposedTransform class implements a transformation with separate
+ translate, rotation, scale, skew, and transformation-center components.
+ """
+
+ translateX: float = 0
+ translateY: float = 0
+ rotation: float = 0 # in degrees, counter-clockwise
+ scaleX: float = 1
+ scaleY: float = 1
+ skewX: float = 0 # in degrees, clockwise
+ skewY: float = 0 # in degrees, counter-clockwise
+ tCenterX: float = 0
+ tCenterY: float = 0
+
+ def __bool__(self):
+ return (
+ self.translateX != 0
+ or self.translateY != 0
+ or self.rotation != 0
+ or self.scaleX != 1
+ or self.scaleY != 1
+ or self.skewX != 0
+ or self.skewY != 0
+ or self.tCenterX != 0
+ or self.tCenterY != 0
+ )
+
+ @classmethod
+ def fromTransform(self, transform):
+ """Return a DecomposedTransform() equivalent of this transformation.
+ The returned solution always has skewY = 0, and angle in the (-180, 180].
+
+ :Example:
+ >>> DecomposedTransform.fromTransform(Transform(3, 0, 0, 2, 0, 0))
+ DecomposedTransform(translateX=0, translateY=0, rotation=0.0, scaleX=3.0, scaleY=2.0, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)
+ >>> DecomposedTransform.fromTransform(Transform(0, 0, 0, 1, 0, 0))
+ DecomposedTransform(translateX=0, translateY=0, rotation=0.0, scaleX=0.0, scaleY=1.0, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)
+ >>> DecomposedTransform.fromTransform(Transform(0, 0, 1, 1, 0, 0))
+ DecomposedTransform(translateX=0, translateY=0, rotation=-45.0, scaleX=0.0, scaleY=1.4142135623730951, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)
+ """
+ # Adapted from an answer on
+ # https://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
+
+ a, b, c, d, x, y = transform
+
+ sx = math.copysign(1, a)
+ if sx < 0:
+ a *= sx
+ b *= sx
+
+ delta = a * d - b * c
+
+ rotation = 0
+ scaleX = scaleY = 0
+ skewX = 0
+
+ # Apply the QR-like decomposition.
+ if a != 0 or b != 0:
+ r = math.sqrt(a * a + b * b)
+ rotation = math.acos(a / r) if b >= 0 else -math.acos(a / r)
+ scaleX, scaleY = (r, delta / r)
+ skewX = math.atan((a * c + b * d) / (r * r))
+ elif c != 0 or d != 0:
+ s = math.sqrt(c * c + d * d)
+ rotation = math.pi / 2 - (
+ math.acos(-c / s) if d >= 0 else -math.acos(c / s)
+ )
+ scaleX, scaleY = (delta / s, s)
+ else:
+ # a = b = c = d = 0
+ pass
+
+ return DecomposedTransform(
+ x,
+ y,
+ math.degrees(rotation),
+ scaleX * sx,
+ scaleY,
+ math.degrees(skewX) * sx,
+ 0.0,
+ 0,
+ 0,
+ )
+
+ def toTransform(self) -> Transform:
+ """Return the Transform() equivalent of this transformation.
+
+ :Example:
+ >>> DecomposedTransform(scaleX=2, scaleY=2).toTransform()
+
+ >>>
+ """
+ t = Transform()
+ t = t.translate(
+ self.translateX + self.tCenterX, self.translateY + self.tCenterY
+ )
+ t = t.rotate(math.radians(self.rotation))
+ t = t.scale(self.scaleX, self.scaleY)
+ t = t.skew(math.radians(self.skewX), math.radians(self.skewY))
+ t = t.translate(-self.tCenterX, -self.tCenterY)
+ return t
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/misc/treeTools.py b/lib/python3.12/site-packages/fontTools/misc/treeTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..24e10ba5b19ef41d56a552527680a4c73503cc3c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/treeTools.py
@@ -0,0 +1,45 @@
+"""Generic tools for working with trees."""
+
+from math import ceil, log
+
+
+def build_n_ary_tree(leaves, n):
+ """Build N-ary tree from sequence of leaf nodes.
+
+ Return a list of lists where each non-leaf node is a list containing
+ max n nodes.
+ """
+ if not leaves:
+ return []
+
+ assert n > 1
+
+ depth = ceil(log(len(leaves), n))
+
+ if depth <= 1:
+ return list(leaves)
+
+ # Fully populate complete subtrees of root until we have enough leaves left
+ root = []
+ unassigned = None
+ full_step = n ** (depth - 1)
+ for i in range(0, len(leaves), full_step):
+ subtree = leaves[i : i + full_step]
+ if len(subtree) < full_step:
+ unassigned = subtree
+ break
+ while len(subtree) > n:
+ subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)]
+ root.append(subtree)
+
+ if unassigned:
+ # Recurse to fill the last subtree, which is the only partially populated one
+ subtree = build_n_ary_tree(unassigned, n)
+ if len(subtree) <= n - len(root):
+ # replace last subtree with its children if they can still fit
+ root.extend(subtree)
+ else:
+ root.append(subtree)
+ assert len(root) <= n
+
+ return root
diff --git a/lib/python3.12/site-packages/fontTools/misc/vector.py b/lib/python3.12/site-packages/fontTools/misc/vector.py
new file mode 100644
index 0000000000000000000000000000000000000000..02c62e6512a04f3497f7c9987a1f414b30cf6b05
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/vector.py
@@ -0,0 +1,147 @@
+from numbers import Number
+import math
+import operator
+import warnings
+
+
+__all__ = ["Vector"]
+
+
+class Vector(tuple):
+ """A math-like vector.
+
+ Represents an n-dimensional numeric vector. ``Vector`` objects support
+ vector addition and subtraction, scalar multiplication and division,
+ negation, rounding, and comparison tests.
+ """
+
+ __slots__ = ()
+
+ def __new__(cls, values, keep=False):
+ if keep is not False:
+ warnings.warn(
+ "the 'keep' argument has been deprecated",
+ DeprecationWarning,
+ )
+ if type(values) == Vector:
+ # No need to create a new object
+ return values
+ return super().__new__(cls, values)
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({super().__repr__()})"
+
+ def _vectorOp(self, other, op):
+ if isinstance(other, Vector):
+ assert len(self) == len(other)
+ return self.__class__(op(a, b) for a, b in zip(self, other))
+ if isinstance(other, Number):
+ return self.__class__(op(v, other) for v in self)
+ raise NotImplementedError()
+
+ def _scalarOp(self, other, op):
+ if isinstance(other, Number):
+ return self.__class__(op(v, other) for v in self)
+ raise NotImplementedError()
+
+ def _unaryOp(self, op):
+ return self.__class__(op(v) for v in self)
+
+ def __add__(self, other):
+ return self._vectorOp(other, operator.add)
+
+ __radd__ = __add__
+
+ def __sub__(self, other):
+ return self._vectorOp(other, operator.sub)
+
+ def __rsub__(self, other):
+ return self._vectorOp(other, _operator_rsub)
+
+ def __mul__(self, other):
+ return self._scalarOp(other, operator.mul)
+
+ __rmul__ = __mul__
+
+ def __truediv__(self, other):
+ return self._scalarOp(other, operator.truediv)
+
+ def __rtruediv__(self, other):
+ return self._scalarOp(other, _operator_rtruediv)
+
+ def __pos__(self):
+ return self._unaryOp(operator.pos)
+
+ def __neg__(self):
+ return self._unaryOp(operator.neg)
+
+ def __round__(self, *, round=round):
+ return self._unaryOp(round)
+
+ def __eq__(self, other):
+ if isinstance(other, list):
+ # bw compat Vector([1, 2, 3]) == [1, 2, 3]
+ other = tuple(other)
+ return super().__eq__(other)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __bool__(self):
+ return any(self)
+
+ __nonzero__ = __bool__
+
+ def __abs__(self):
+ return math.sqrt(sum(x * x for x in self))
+
+ def length(self):
+ """Return the length of the vector. Equivalent to abs(vector)."""
+ return abs(self)
+
+ def normalized(self):
+ """Return the normalized vector of the vector."""
+ return self / abs(self)
+
+ def dot(self, other):
+ """Performs vector dot product, returning the sum of
+ ``a[0] * b[0], a[1] * b[1], ...``"""
+ assert len(self) == len(other)
+ return sum(a * b for a, b in zip(self, other))
+
+ # Deprecated methods/properties
+
+ def toInt(self):
+ warnings.warn(
+ "the 'toInt' method has been deprecated, use round(vector) instead",
+ DeprecationWarning,
+ )
+ return self.__round__()
+
+ @property
+ def values(self):
+ warnings.warn(
+ "the 'values' attribute has been deprecated, use "
+ "the vector object itself instead",
+ DeprecationWarning,
+ )
+ return list(self)
+
+ @values.setter
+ def values(self, values):
+ raise AttributeError(
+ "can't set attribute, the 'values' attribute has been deprecated",
+ )
+
+ def isclose(self, other: "Vector", **kwargs) -> bool:
+ """Return True if the vector is close to another Vector."""
+ assert len(self) == len(other)
+ return all(math.isclose(a, b, **kwargs) for a, b in zip(self, other))
+
+
+def _operator_rsub(a, b):
+ return operator.sub(b, a)
+
+
+def _operator_rtruediv(a, b):
+ return operator.truediv(b, a)
diff --git a/lib/python3.12/site-packages/fontTools/misc/visitor.py b/lib/python3.12/site-packages/fontTools/misc/visitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..20381f91cb6b7136985dd6d88f759477f8df65b4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/visitor.py
@@ -0,0 +1,158 @@
+"""Generic visitor pattern implementation for Python objects."""
+
+import enum
+import weakref
+
+
+class Visitor(object):
+ defaultStop = False
+
+ _visitors = {
+ # By default we skip visiting weak references to avoid recursion
+ # issues. Users can override this by registering a visit
+ # function for weakref.ProxyType.
+ weakref.ProxyType: {None: lambda self, obj, *args, **kwargs: False}
+ }
+
+ @classmethod
+ def _register(celf, clazzes_attrs):
+ assert celf != Visitor, "Subclass Visitor instead."
+ if "_visitors" not in celf.__dict__:
+ celf._visitors = {}
+
+ def wrapper(method):
+ assert method.__name__ == "visit"
+ for clazzes, attrs in clazzes_attrs:
+ if type(clazzes) != tuple:
+ clazzes = (clazzes,)
+ if type(attrs) == str:
+ attrs = (attrs,)
+ for clazz in clazzes:
+ _visitors = celf._visitors.setdefault(clazz, {})
+ for attr in attrs:
+ assert attr not in _visitors, (
+ "Oops, class '%s' has visitor function for '%s' defined already."
+ % (clazz.__name__, attr)
+ )
+ _visitors[attr] = method
+ return None
+
+ return wrapper
+
+ @classmethod
+ def register(celf, clazzes):
+ if type(clazzes) != tuple:
+ clazzes = (clazzes,)
+ return celf._register([(clazzes, (None,))])
+
+ @classmethod
+ def register_attr(celf, clazzes, attrs):
+ clazzes_attrs = []
+ if type(clazzes) != tuple:
+ clazzes = (clazzes,)
+ if type(attrs) == str:
+ attrs = (attrs,)
+ for clazz in clazzes:
+ clazzes_attrs.append((clazz, attrs))
+ return celf._register(clazzes_attrs)
+
+ @classmethod
+ def register_attrs(celf, clazzes_attrs):
+ return celf._register(clazzes_attrs)
+
+ @classmethod
+ def _visitorsFor(celf, thing, _default={}):
+ typ = type(thing)
+
+ for celf in celf.mro():
+ _visitors = getattr(celf, "_visitors", None)
+ if _visitors is None:
+ break
+
+ for base in typ.mro():
+ m = celf._visitors.get(base, None)
+ if m is not None:
+ return m
+
+ return _default
+
+ def visitObject(self, obj, *args, **kwargs):
+ """Called to visit an object. This function loops over all non-private
+ attributes of the objects and calls any user-registered (via
+ ``@register_attr()`` or ``@register_attrs()``) ``visit()`` functions.
+
+ The visitor will proceed to call ``self.visitAttr()``, unless there is a
+ user-registered visit function and:
+
+ * It returns ``False``; or
+ * It returns ``None`` (or doesn't return anything) and
+ ``visitor.defaultStop`` is ``True`` (non-default).
+ """
+
+ keys = sorted(vars(obj).keys())
+ _visitors = self._visitorsFor(obj)
+ defaultVisitor = _visitors.get("*", None)
+ for key in keys:
+ if key[0] == "_":
+ continue
+ value = getattr(obj, key)
+ visitorFunc = _visitors.get(key, defaultVisitor)
+ if visitorFunc is not None:
+ ret = visitorFunc(self, obj, key, value, *args, **kwargs)
+ if ret == False or (ret is None and self.defaultStop):
+ continue
+ self.visitAttr(obj, key, value, *args, **kwargs)
+
+ def visitAttr(self, obj, attr, value, *args, **kwargs):
+ """Called to visit an attribute of an object."""
+ self.visit(value, *args, **kwargs)
+
+ def visitList(self, obj, *args, **kwargs):
+ """Called to visit any value that is a list."""
+ for value in obj:
+ self.visit(value, *args, **kwargs)
+
+ def visitDict(self, obj, *args, **kwargs):
+ """Called to visit any value that is a dictionary."""
+ for value in obj.values():
+ self.visit(value, *args, **kwargs)
+
+ def visitLeaf(self, obj, *args, **kwargs):
+ """Called to visit any value that is not an object, list,
+ or dictionary."""
+ pass
+
+ def visit(self, obj, *args, **kwargs):
+ """This is the main entry to the visitor. The visitor will visit object
+ ``obj``.
+
+ The visitor will first determine if there is a registered (via
+ ``@register()``) visit function for the type of object. If there is, it
+ will be called, and ``(visitor, obj, *args, **kwargs)`` will be passed
+ to the user visit function.
+
+ The visitor will not recurse if there is a user-registered visit
+ function and:
+
+ * It returns ``False``; or
+ * It returns ``None`` (or doesn't return anything) and
+ ``visitor.defaultStop`` is ``True`` (non-default)
+
+ Otherwise, the visitor will proceed to dispatch to one of
+ ``self.visitObject()``, ``self.visitList()``, ``self.visitDict()``, or
+ ``self.visitLeaf()`` (any of which can be overriden in a subclass).
+ """
+
+ visitorFunc = self._visitorsFor(obj).get(None, None)
+ if visitorFunc is not None:
+ ret = visitorFunc(self, obj, *args, **kwargs)
+ if ret == False or (ret is None and self.defaultStop):
+ return
+ if hasattr(obj, "__dict__") and not isinstance(obj, enum.Enum):
+ self.visitObject(obj, *args, **kwargs)
+ elif isinstance(obj, list):
+ self.visitList(obj, *args, **kwargs)
+ elif isinstance(obj, dict):
+ self.visitDict(obj, *args, **kwargs)
+ else:
+ self.visitLeaf(obj, *args, **kwargs)
diff --git a/lib/python3.12/site-packages/fontTools/misc/xmlReader.py b/lib/python3.12/site-packages/fontTools/misc/xmlReader.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8e502f141e9cb5df6ea11352b565c9a9cd4aa3d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/xmlReader.py
@@ -0,0 +1,188 @@
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+import sys
+import os
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+class TTXParseError(Exception):
+ pass
+
+
+BUFSIZE = 0x4000
+
+
+class XMLReader(object):
+ def __init__(
+ self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False
+ ):
+ if fileOrPath == "-":
+ fileOrPath = sys.stdin
+ if not hasattr(fileOrPath, "read"):
+ self.file = open(fileOrPath, "rb")
+ self._closeStream = True
+ else:
+ # assume readable file object
+ self.file = fileOrPath
+ self._closeStream = False
+ self.ttFont = ttFont
+ self.progress = progress
+ if quiet is not None:
+ from fontTools.misc.loggingTools import deprecateArgument
+
+ deprecateArgument("quiet", "configure logging instead")
+ self.quiet = quiet
+ self.root = None
+ self.contentStack = []
+ self.contentOnly = contentOnly
+ self.stackSize = 0
+
+ def read(self, rootless=False):
+ if rootless:
+ self.stackSize += 1
+ if self.progress:
+ self.file.seek(0, 2)
+ fileSize = self.file.tell()
+ self.progress.set(0, fileSize // 100 or 1)
+ self.file.seek(0)
+ self._parseFile(self.file)
+ if self._closeStream:
+ self.close()
+ if rootless:
+ self.stackSize -= 1
+
+ def close(self):
+ self.file.close()
+
+ def _parseFile(self, file):
+ from xml.parsers.expat import ParserCreate
+
+ parser = ParserCreate()
+ parser.StartElementHandler = self._startElementHandler
+ parser.EndElementHandler = self._endElementHandler
+ parser.CharacterDataHandler = self._characterDataHandler
+
+ pos = 0
+ while True:
+ chunk = file.read(BUFSIZE)
+ if not chunk:
+ parser.Parse(chunk, 1)
+ break
+ pos = pos + len(chunk)
+ if self.progress:
+ self.progress.set(pos // 100)
+ parser.Parse(chunk, 0)
+
+ def _startElementHandler(self, name, attrs):
+ if self.stackSize == 1 and self.contentOnly:
+ # We already know the table we're parsing, skip
+ # parsing the table tag and continue to
+ # stack '2' which begins parsing content
+ self.contentStack.append([])
+ self.stackSize = 2
+ return
+ stackSize = self.stackSize
+ self.stackSize = stackSize + 1
+ subFile = attrs.get("src")
+ if subFile is not None:
+ if hasattr(self.file, "name"):
+ # if file has a name, get its parent directory
+ dirname = os.path.dirname(self.file.name)
+ else:
+ # else fall back to using the current working directory
+ dirname = os.getcwd()
+ subFile = os.path.join(dirname, subFile)
+ if not stackSize:
+ if name != "ttFont":
+ raise TTXParseError("illegal root tag: %s" % name)
+ if self.ttFont.reader is None and not self.ttFont.tables:
+ sfntVersion = attrs.get("sfntVersion")
+ if sfntVersion is not None:
+ if len(sfntVersion) != 4:
+ sfntVersion = safeEval('"' + sfntVersion + '"')
+ self.ttFont.sfntVersion = sfntVersion
+ self.contentStack.append([])
+ elif stackSize == 1:
+ if subFile is not None:
+ subReader = XMLReader(subFile, self.ttFont, self.progress)
+ subReader.read()
+ self.contentStack.append([])
+ return
+ tag = ttLib.xmlToTag(name)
+ msg = "Parsing '%s' table..." % tag
+ if self.progress:
+ self.progress.setLabel(msg)
+ log.info(msg)
+ if tag == "GlyphOrder":
+ tableClass = ttLib.GlyphOrder
+ elif "ERROR" in attrs or ("raw" in attrs and safeEval(attrs["raw"])):
+ tableClass = DefaultTable
+ else:
+ tableClass = ttLib.getTableClass(tag)
+ if tableClass is None:
+ tableClass = DefaultTable
+ if tag == "loca" and tag in self.ttFont:
+ # Special-case the 'loca' table as we need the
+ # original if the 'glyf' table isn't recompiled.
+ self.currentTable = self.ttFont[tag]
+ else:
+ self.currentTable = tableClass(tag)
+ self.ttFont[tag] = self.currentTable
+ self.contentStack.append([])
+ elif stackSize == 2 and subFile is not None:
+ subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
+ subReader.read()
+ self.contentStack.append([])
+ self.root = subReader.root
+ elif stackSize == 2:
+ self.contentStack.append([])
+ self.root = (name, attrs, self.contentStack[-1])
+ else:
+ l = []
+ self.contentStack[-1].append((name, attrs, l))
+ self.contentStack.append(l)
+
+ def _characterDataHandler(self, data):
+ if self.stackSize > 1:
+ # parser parses in chunks, so we may get multiple calls
+ # for the same text node; thus we need to append the data
+ # to the last item in the content stack:
+ # https://github.com/fonttools/fonttools/issues/2614
+ if (
+ data != "\n"
+ and self.contentStack[-1]
+ and isinstance(self.contentStack[-1][-1], str)
+ and self.contentStack[-1][-1] != "\n"
+ ):
+ self.contentStack[-1][-1] += data
+ else:
+ self.contentStack[-1].append(data)
+
+ def _endElementHandler(self, name):
+ self.stackSize = self.stackSize - 1
+ del self.contentStack[-1]
+ if not self.contentOnly:
+ if self.stackSize == 1:
+ self.root = None
+ elif self.stackSize == 2:
+ name, attrs, content = self.root
+ self.currentTable.fromXML(name, attrs, content, self.ttFont)
+ self.root = None
+
+
+class ProgressPrinter(object):
+ def __init__(self, title, maxval=100):
+ print(title)
+
+ def set(self, val, maxval=None):
+ pass
+
+ def increment(self, val=1):
+ pass
+
+ def setLabel(self, text):
+ print(text)
diff --git a/lib/python3.12/site-packages/fontTools/misc/xmlWriter.py b/lib/python3.12/site-packages/fontTools/misc/xmlWriter.py
new file mode 100644
index 0000000000000000000000000000000000000000..0553e885e7450a2c422b5fd5d362a03f4d7d3120
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/misc/xmlWriter.py
@@ -0,0 +1,240 @@
+"""xmlWriter.py -- Simple XML authoring class"""
+
+from __future__ import annotations
+
+from typing import BinaryIO, Callable, TextIO
+from fontTools.misc.textTools import byteord, strjoin, tobytes, tostr
+import sys
+import os
+import string
+import logging
+import itertools
+
+INDENT = " "
+TTX_LOG = logging.getLogger("fontTools.ttx")
+REPLACEMENT = "?"
+ILLEGAL_XML_CHARS = dict.fromkeys(
+ itertools.chain(
+ range(0x00, 0x09),
+ (0x0B, 0x0C),
+ range(0x0E, 0x20),
+ range(0xD800, 0xE000),
+ (0xFFFE, 0xFFFF),
+ ),
+ REPLACEMENT,
+)
+
+
+class XMLWriter(object):
+ def __init__(
+ self,
+ fileOrPath: str | os.PathLike[str] | BinaryIO | TextIO,
+ indentwhite: str = INDENT,
+ idlefunc: Callable[[], None] | None = None,
+ encoding: str = "utf_8",
+ newlinestr: str | bytes = "\n",
+ ) -> None:
+ if encoding.lower().replace("-", "").replace("_", "") != "utf8":
+ raise Exception("Only UTF-8 encoding is supported.")
+ if fileOrPath == "-":
+ fileOrPath = sys.stdout
+ self.filename: str | os.PathLike[str] | None
+ if not hasattr(fileOrPath, "write"):
+ if not isinstance(fileOrPath, (str, os.PathLike)):
+ raise TypeError(
+ "fileOrPath must be a file path (str or PathLike) if it isn't an object with a `write` method."
+ )
+ self.filename = fileOrPath
+ self.file = open(fileOrPath, "wb")
+ self._closeStream = True
+ else:
+ self.filename = None
+ # assume writable file object
+ self.file = fileOrPath
+ self._closeStream = False
+
+ # Figure out if writer expects bytes or unicodes
+ try:
+ # The bytes check should be first. See:
+ # https://github.com/fonttools/fonttools/pull/233
+ self.file.write(b"")
+ self.totype = tobytes
+ except TypeError:
+ # This better not fail.
+ self.file.write("")
+ self.totype = tostr
+ self.indentwhite = self.totype(indentwhite)
+ if newlinestr is None:
+ self.newlinestr = self.totype(os.linesep)
+ else:
+ self.newlinestr = self.totype(newlinestr)
+ self.indentlevel = 0
+ self.stack = []
+ self.needindent = 1
+ self.idlefunc = idlefunc
+ self.idlecounter = 0
+ self._writeraw('')
+ self.newline()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.close()
+
+ def close(self) -> None:
+ if self._closeStream:
+ assert not isinstance(self.file, (str, os.PathLike))
+ self.file.close()
+
+ def write(self, string, indent=True):
+ """Writes text."""
+ self._writeraw(escape(string), indent=indent)
+
+ def writecdata(self, string):
+ """Writes text in a CDATA section."""
+ self._writeraw("")
+
+ def write8bit(self, data, strip=False):
+ """Writes a bytes() sequence into the XML, escaping
+ non-ASCII bytes. When this is read in xmlReader,
+ the original bytes can be recovered by encoding to
+ 'latin-1'."""
+ self._writeraw(escape8bit(data), strip=strip)
+
+ def write_noindent(self, string):
+ """Writes text without indentation."""
+ self._writeraw(escape(string), indent=False)
+
+ def _writeraw(self, data, indent=True, strip=False):
+ """Writes bytes, possibly indented."""
+ if indent and self.needindent:
+ self.file.write(self.indentlevel * self.indentwhite)
+ self.needindent = 0
+ s = self.totype(data, encoding="utf_8")
+ if strip:
+ s = s.strip()
+ self.file.write(s)
+
+ def newline(self):
+ self.file.write(self.newlinestr)
+ self.needindent = 1
+ idlecounter = self.idlecounter
+ if not idlecounter % 100 and self.idlefunc is not None:
+ self.idlefunc()
+ self.idlecounter = idlecounter + 1
+
+ def comment(self, data):
+ data = escape(data)
+ lines = data.split("\n")
+ self._writeraw("")
+
+ def simpletag(self, _TAG_, *args, **kwargs):
+ attrdata = self.stringifyattrs(*args, **kwargs)
+ data = "<%s%s/>" % (_TAG_, attrdata)
+ self._writeraw(data)
+
+ def begintag(self, _TAG_, *args, **kwargs):
+ attrdata = self.stringifyattrs(*args, **kwargs)
+ data = "<%s%s>" % (_TAG_, attrdata)
+ self._writeraw(data)
+ self.stack.append(_TAG_)
+ self.indent()
+
+ def endtag(self, _TAG_):
+ assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
+ del self.stack[-1]
+ self.dedent()
+ data = "%s>" % _TAG_
+ self._writeraw(data)
+
+ def dumphex(self, data):
+ linelength = 16
+ hexlinelength = linelength * 2
+ chunksize = 8
+ for i in range(0, len(data), linelength):
+ hexline = hexStr(data[i : i + linelength])
+ line = ""
+ white = ""
+ for j in range(0, hexlinelength, chunksize):
+ line = line + white + hexline[j : j + chunksize]
+ white = " "
+ self._writeraw(line)
+ self.newline()
+
+ def indent(self):
+ self.indentlevel = self.indentlevel + 1
+
+ def dedent(self):
+ assert self.indentlevel > 0
+ self.indentlevel = self.indentlevel - 1
+
+ def stringifyattrs(self, *args, **kwargs):
+ if kwargs:
+ assert not args
+ attributes = sorted(kwargs.items())
+ elif args:
+ assert len(args) == 1
+ attributes = args[0]
+ else:
+ return ""
+ data = ""
+ for attr, value in attributes:
+ if not isinstance(value, (bytes, str)):
+ value = str(value)
+ data = data + ' %s="%s"' % (attr, escapeattr(value))
+ return data
+
+
+def escape(data):
+ """Escape characters not allowed in `XML 1.0 `_."""
+ data = tostr(data, "utf_8")
+ data = data.replace("&", "&")
+ data = data.replace("<", "<")
+ data = data.replace(">", ">")
+ data = data.replace("\r", "
")
+
+ newData = data.translate(ILLEGAL_XML_CHARS)
+ if newData != data:
+ maxLen = 10
+ preview = repr(data)
+ if len(data) > maxLen:
+ preview = repr(data[:maxLen])[1:-1] + "..."
+ TTX_LOG.warning(
+ "Illegal XML character(s) found; replacing offending string %r with %r",
+ preview,
+ REPLACEMENT,
+ )
+ return newData
+
+
+def escapeattr(data):
+ data = escape(data)
+ data = data.replace('"', """)
+ return data
+
+
+def escape8bit(data):
+ """Input is Unicode string."""
+
+ def escapechar(c):
+ n = ord(c)
+ if 32 <= n <= 127 and c not in "<&>":
+ return c
+ else:
+ return "" + repr(n) + ";"
+
+ return strjoin(map(escapechar, data.decode("latin-1")))
+
+
+def hexStr(s):
+ h = string.hexdigits
+ r = ""
+ for c in s:
+ i = byteord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__init__.py b/lib/python3.12/site-packages/fontTools/qu2cu/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce357417c7139664a194a6826220889f5ed59894
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/__init__.py
@@ -0,0 +1,15 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .qu2cu import *
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__main__.py b/lib/python3.12/site-packages/fontTools/qu2cu/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c85f61b419b66d523ca0b0089a327fb4e0278aa
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/__main__.py
@@ -0,0 +1,7 @@
+import sys
+
+from .cli import _main as main
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d090c299db6d8f0ec4c76d9b18a65378019d0784
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..032b7e9b46ede9a68c16311d4ebb0ea238d043ef
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/__main__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/benchmark.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/benchmark.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1cbfa8d15b81c9686a3213edd29da1c180ad316f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/benchmark.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/cli.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/cli.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f3b057ee40d9b39d4768bd00542d248fe7686f3b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/cli.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/qu2cu.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/qu2cu.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dd3ced002e15a32662a66bf11a67944508eefe1f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/qu2cu/__pycache__/qu2cu.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/benchmark.py b/lib/python3.12/site-packages/fontTools/qu2cu/benchmark.py
new file mode 100644
index 0000000000000000000000000000000000000000..f28ad88cf93efffd4d49ccac4fb8248a63c70def
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/benchmark.py
@@ -0,0 +1,56 @@
+"""Benchmark the qu2cu algorithm performance."""
+
+from .qu2cu import *
+from fontTools.cu2qu import curve_to_quadratic
+import random
+import timeit
+
+MAX_ERR = 0.5
+NUM_CURVES = 5
+
+
+def generate_curves(n):
+ points = [
+ tuple(float(random.randint(0, 2048)) for coord in range(2))
+ for point in range(1 + 3 * n)
+ ]
+ curves = []
+ for i in range(n):
+ curves.append(tuple(points[i * 3 : i * 3 + 4]))
+ return curves
+
+
+def setup_quadratic_to_curves():
+ curves = generate_curves(NUM_CURVES)
+ quadratics = [curve_to_quadratic(curve, MAX_ERR) for curve in curves]
+ return quadratics, MAX_ERR
+
+
+def run_benchmark(module, function, setup_suffix="", repeat=25, number=1):
+ setup_func = "setup_" + function
+ if setup_suffix:
+ print("%s with %s:" % (function, setup_suffix), end="")
+ setup_func += "_" + setup_suffix
+ else:
+ print("%s:" % function, end="")
+
+ def wrapper(function, setup_func):
+ function = globals()[function]
+ setup_func = globals()[setup_func]
+
+ def wrapped():
+ return function(*setup_func())
+
+ return wrapped
+
+ results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number)
+ print("\t%5.1fus" % (min(results) * 1000000.0 / number))
+
+
+def main():
+ run_benchmark("qu2cu", "quadratic_to_curves")
+
+
+if __name__ == "__main__":
+ random.seed(1)
+ main()
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/cli.py b/lib/python3.12/site-packages/fontTools/qu2cu/cli.py
new file mode 100644
index 0000000000000000000000000000000000000000..101e938a6fc0ae4f04408074e579ffe33897236d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/cli.py
@@ -0,0 +1,125 @@
+import os
+import argparse
+import logging
+from fontTools.misc.cliTools import makeOutputFileName
+from fontTools.ttLib import TTFont
+from fontTools.pens.qu2cuPen import Qu2CuPen
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+import fontTools
+
+
+logger = logging.getLogger("fontTools.qu2cu")
+
+
+def _font_to_cubic(input_path, output_path=None, **kwargs):
+ font = TTFont(input_path)
+ logger.info("Converting curves for %s", input_path)
+
+ stats = {} if kwargs["dump_stats"] else None
+ qu2cu_kwargs = {
+ "stats": stats,
+ "max_err": kwargs["max_err_em"] * font["head"].unitsPerEm,
+ "all_cubic": kwargs["all_cubic"],
+ }
+
+ assert "gvar" not in font, "Cannot convert variable font"
+ glyphSet = font.getGlyphSet()
+ glyphOrder = font.getGlyphOrder()
+ glyf = font["glyf"]
+ for glyphName in glyphOrder:
+ glyph = glyphSet[glyphName]
+ ttpen = TTGlyphPen(glyphSet)
+ pen = Qu2CuPen(ttpen, **qu2cu_kwargs)
+ glyph.draw(pen)
+ glyf[glyphName] = ttpen.glyph(dropImpliedOnCurves=True)
+
+ font["head"].glyphDataFormat = 1
+
+ if kwargs["dump_stats"]:
+ logger.info("Stats: %s", stats)
+
+ logger.info("Saving %s", output_path)
+ font.save(output_path)
+
+
+def _main(args=None):
+ """Convert an OpenType font from quadratic to cubic curves"""
+ parser = argparse.ArgumentParser(prog="qu2cu")
+ parser.add_argument("--version", action="version", version=fontTools.__version__)
+ parser.add_argument(
+ "infiles",
+ nargs="+",
+ metavar="INPUT",
+ help="one or more input TTF source file(s).",
+ )
+ parser.add_argument("-v", "--verbose", action="count", default=0)
+ parser.add_argument(
+ "-e",
+ "--conversion-error",
+ type=float,
+ metavar="ERROR",
+ default=0.001,
+ help="maxiumum approximation error measured in EM (default: 0.001)",
+ )
+ parser.add_argument(
+ "-c",
+ "--all-cubic",
+ default=False,
+ action="store_true",
+ help="whether to only use cubic curves",
+ )
+
+ output_parser = parser.add_mutually_exclusive_group()
+ output_parser.add_argument(
+ "-o",
+ "--output-file",
+ default=None,
+ metavar="OUTPUT",
+ help=("output filename for the converted TTF."),
+ )
+ output_parser.add_argument(
+ "-d",
+ "--output-dir",
+ default=None,
+ metavar="DIRECTORY",
+ help="output directory where to save converted TTFs",
+ )
+
+ options = parser.parse_args(args)
+
+ if not options.verbose:
+ level = "WARNING"
+ elif options.verbose == 1:
+ level = "INFO"
+ else:
+ level = "DEBUG"
+ logging.basicConfig(level=level)
+
+ if len(options.infiles) > 1 and options.output_file:
+ parser.error("-o/--output-file can't be used with multile inputs")
+
+ if options.output_dir:
+ output_dir = options.output_dir
+ if not os.path.exists(output_dir):
+ os.mkdir(output_dir)
+ elif not os.path.isdir(output_dir):
+ parser.error("'%s' is not a directory" % output_dir)
+ output_paths = [
+ os.path.join(output_dir, os.path.basename(p)) for p in options.infiles
+ ]
+ elif options.output_file:
+ output_paths = [options.output_file]
+ else:
+ output_paths = [
+ makeOutputFileName(p, overWrite=True, suffix=".cubic")
+ for p in options.infiles
+ ]
+
+ kwargs = dict(
+ dump_stats=options.verbose > 0,
+ max_err_em=options.conversion_error,
+ all_cubic=options.all_cubic,
+ )
+
+ for input_path, output_path in zip(options.infiles, output_paths):
+ _font_to_cubic(input_path, output_path, **kwargs)
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.c b/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.c
new file mode 100644
index 0000000000000000000000000000000000000000..a586c46bd044e2589496714a652919ec03786025
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.c
@@ -0,0 +1,16682 @@
+/* Generated by Cython 3.2.2 */
+
+/* BEGIN: Cython Metadata
+{
+ "distutils": {
+ "define_macros": [
+ [
+ "CYTHON_TRACE_NOGIL",
+ "1"
+ ]
+ ],
+ "name": "fontTools.qu2cu.qu2cu",
+ "sources": [
+ "Lib/fontTools/qu2cu/qu2cu.py"
+ ]
+ },
+ "module_name": "fontTools.qu2cu.qu2cu"
+}
+END: Cython Metadata */
+
+#ifndef PY_SSIZE_T_CLEAN
+#define PY_SSIZE_T_CLEAN
+#endif /* PY_SSIZE_T_CLEAN */
+/* InitLimitedAPI */
+#if defined(Py_LIMITED_API)
+ #if !defined(CYTHON_LIMITED_API)
+ #define CYTHON_LIMITED_API 1
+ #endif
+#elif defined(CYTHON_LIMITED_API)
+ #ifdef _MSC_VER
+ #pragma message ("Limited API usage is enabled with 'CYTHON_LIMITED_API' but 'Py_LIMITED_API' does not define a Python target version. Consider setting 'Py_LIMITED_API' instead.")
+ #else
+ #warning Limited API usage is enabled with 'CYTHON_LIMITED_API' but 'Py_LIMITED_API' does not define a Python target version. Consider setting 'Py_LIMITED_API' instead.
+ #endif
+#endif
+
+#include "Python.h"
+#ifndef Py_PYTHON_H
+ #error Python headers needed to compile C extensions, please install development version of Python.
+#elif PY_VERSION_HEX < 0x03080000
+ #error Cython requires Python 3.8+.
+#else
+#define __PYX_ABI_VERSION "3_2_2"
+#define CYTHON_HEX_VERSION 0x030202F0
+#define CYTHON_FUTURE_DIVISION 1
+/* CModulePreamble */
+#include
+#ifndef offsetof
+ #define offsetof(type, member) ( (size_t) & ((type*)0) -> member )
+#endif
+#if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS)
+ #ifndef __stdcall
+ #define __stdcall
+ #endif
+ #ifndef __cdecl
+ #define __cdecl
+ #endif
+ #ifndef __fastcall
+ #define __fastcall
+ #endif
+#endif
+#ifndef DL_IMPORT
+ #define DL_IMPORT(t) t
+#endif
+#ifndef DL_EXPORT
+ #define DL_EXPORT(t) t
+#endif
+#define __PYX_COMMA ,
+#ifndef PY_LONG_LONG
+ #define PY_LONG_LONG LONG_LONG
+#endif
+#ifndef Py_HUGE_VAL
+ #define Py_HUGE_VAL HUGE_VAL
+#endif
+#define __PYX_LIMITED_VERSION_HEX PY_VERSION_HEX
+#if defined(GRAALVM_PYTHON)
+ /* For very preliminary testing purposes. Most variables are set the same as PyPy.
+ The existence of this section does not imply that anything works or is even tested */
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 1
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #undef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 1
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #undef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #undef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 1
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
+ #endif
+ #undef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 0
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#elif defined(PYPY_VERSION)
+ #define CYTHON_COMPILING_IN_PYPY 1
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #ifndef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #endif
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #undef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 1
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #ifndef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 1
+ #endif
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 0
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #if PY_VERSION_HEX < 0x03090000
+ #undef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+ #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT)
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #undef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE (PYPY_VERSION_NUM >= 0x07030C00)
+ #endif
+ #undef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 0
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_NUM >= 0x07031100)
+ #endif
+ #undef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 0
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#elif defined(CYTHON_LIMITED_API)
+ #ifdef Py_LIMITED_API
+ #undef __PYX_LIMITED_VERSION_HEX
+ #define __PYX_LIMITED_VERSION_HEX Py_LIMITED_API
+ #endif
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+ #define CYTHON_COMPILING_IN_LIMITED_API 1
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 0
+ #undef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 1
+ #undef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 0
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #undef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 0
+ #ifndef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #endif
+ #undef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 0
+ #ifndef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 0
+ #endif
+ #ifndef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #endif
+ #undef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 0
+ #undef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 0
+ #undef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 0
+ #undef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 0
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #undef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000)
+ #undef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 0
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #ifndef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #endif
+ #undef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING 0
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 0
+ #endif
+ #ifndef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND (__PYX_LIMITED_VERSION_HEX >= 0x030A0000)
+ #endif
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #undef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 0
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
+ #endif
+ #ifndef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS 1
+ #endif
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0
+#else
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 1
+ #define CYTHON_COMPILING_IN_LIMITED_API 0
+ #define CYTHON_COMPILING_IN_GRAAL 0
+ #ifdef Py_GIL_DISABLED
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 1
+ #else
+ #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0
+ #endif
+ #if PY_VERSION_HEX < 0x030A0000
+ #undef CYTHON_USE_TYPE_SLOTS
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #elif !defined(CYTHON_USE_TYPE_SLOTS)
+ #define CYTHON_USE_TYPE_SLOTS 1
+ #endif
+ #ifndef CYTHON_USE_TYPE_SPECS
+ #define CYTHON_USE_TYPE_SPECS 0
+ #endif
+ #ifndef CYTHON_USE_PYTYPE_LOOKUP
+ #define CYTHON_USE_PYTYPE_LOOKUP 1
+ #endif
+ #ifndef CYTHON_USE_PYLONG_INTERNALS
+ #define CYTHON_USE_PYLONG_INTERNALS 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_USE_PYLIST_INTERNALS
+ #define CYTHON_USE_PYLIST_INTERNALS 0
+ #elif !defined(CYTHON_USE_PYLIST_INTERNALS)
+ #define CYTHON_USE_PYLIST_INTERNALS 1
+ #endif
+ #ifndef CYTHON_USE_UNICODE_INTERNALS
+ #define CYTHON_USE_UNICODE_INTERNALS 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING || PY_VERSION_HEX >= 0x030B00A2
+ #undef CYTHON_USE_UNICODE_WRITER
+ #define CYTHON_USE_UNICODE_WRITER 0
+ #elif !defined(CYTHON_USE_UNICODE_WRITER)
+ #define CYTHON_USE_UNICODE_WRITER 1
+ #endif
+ #ifndef CYTHON_AVOID_BORROWED_REFS
+ #define CYTHON_AVOID_BORROWED_REFS 0
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1
+ #elif !defined(CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS)
+ #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0
+ #endif
+ #ifndef CYTHON_ASSUME_SAFE_MACROS
+ #define CYTHON_ASSUME_SAFE_MACROS 1
+ #endif
+ #ifndef CYTHON_ASSUME_SAFE_SIZE
+ #define CYTHON_ASSUME_SAFE_SIZE 1
+ #endif
+ #ifndef CYTHON_UNPACK_METHODS
+ #define CYTHON_UNPACK_METHODS 1
+ #endif
+ #ifndef CYTHON_FAST_THREAD_STATE
+ #define CYTHON_FAST_THREAD_STATE 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_FAST_GIL
+ #define CYTHON_FAST_GIL 0
+ #elif !defined(CYTHON_FAST_GIL)
+ #define CYTHON_FAST_GIL (PY_VERSION_HEX < 0x030C00A6)
+ #endif
+ #ifndef CYTHON_METH_FASTCALL
+ #define CYTHON_METH_FASTCALL 1
+ #endif
+ #ifndef CYTHON_FAST_PYCALL
+ #define CYTHON_FAST_PYCALL 1
+ #endif
+ #ifndef CYTHON_PEP487_INIT_SUBCLASS
+ #define CYTHON_PEP487_INIT_SUBCLASS 1
+ #endif
+ #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
+ #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+ #endif
+ #ifndef CYTHON_USE_MODULE_STATE
+ #define CYTHON_USE_MODULE_STATE 0
+ #endif
+ #ifndef CYTHON_USE_SYS_MONITORING
+ #define CYTHON_USE_SYS_MONITORING (PY_VERSION_HEX >= 0x030d00B1)
+ #endif
+ #ifndef CYTHON_USE_TP_FINALIZE
+ #define CYTHON_USE_TP_FINALIZE 1
+ #endif
+ #ifndef CYTHON_USE_AM_SEND
+ #define CYTHON_USE_AM_SEND 1
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ #undef CYTHON_USE_DICT_VERSIONS
+ #define CYTHON_USE_DICT_VERSIONS 0
+ #elif !defined(CYTHON_USE_DICT_VERSIONS)
+ #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX < 0x030C00A5 && !CYTHON_USE_MODULE_STATE)
+ #endif
+ #ifndef CYTHON_USE_EXC_INFO_STACK
+ #define CYTHON_USE_EXC_INFO_STACK 1
+ #endif
+ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+ #define CYTHON_UPDATE_DESCRIPTOR_DOC 1
+ #endif
+ #ifndef CYTHON_USE_FREELISTS
+ #define CYTHON_USE_FREELISTS (!CYTHON_COMPILING_IN_CPYTHON_FREETHREADING)
+ #endif
+ #if defined(CYTHON_IMMORTAL_CONSTANTS) && PY_VERSION_HEX < 0x030C0000
+ #undef CYTHON_IMMORTAL_CONSTANTS
+ #define CYTHON_IMMORTAL_CONSTANTS 0 // definitely won't work
+ #elif !defined(CYTHON_IMMORTAL_CONSTANTS)
+ #define CYTHON_IMMORTAL_CONSTANTS (PY_VERSION_HEX >= 0x030C0000 && !CYTHON_USE_MODULE_STATE && CYTHON_COMPILING_IN_CPYTHON_FREETHREADING)
+ #endif
+#endif
+#ifndef CYTHON_COMPRESS_STRINGS
+ #define CYTHON_COMPRESS_STRINGS 1
+#endif
+#ifndef CYTHON_FAST_PYCCALL
+#define CYTHON_FAST_PYCCALL CYTHON_FAST_PYCALL
+#endif
+#ifndef CYTHON_VECTORCALL
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define CYTHON_VECTORCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000)
+#else
+#define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL)
+#endif
+#endif
+#if CYTHON_USE_PYLONG_INTERNALS
+ #undef SHIFT
+ #undef BASE
+ #undef MASK
+ #ifdef SIZEOF_VOID_P
+ enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
+ #endif
+#endif
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+#ifndef __has_cpp_attribute
+ #define __has_cpp_attribute(x) 0
+#endif
+#ifndef CYTHON_RESTRICT
+ #if defined(__GNUC__)
+ #define CYTHON_RESTRICT __restrict__
+ #elif defined(_MSC_VER) && _MSC_VER >= 1400
+ #define CYTHON_RESTRICT __restrict
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_RESTRICT restrict
+ #else
+ #define CYTHON_RESTRICT
+ #endif
+#endif
+#ifndef CYTHON_UNUSED
+ #if defined(__cplusplus)
+ /* for clang __has_cpp_attribute(maybe_unused) is true even before C++17
+ * but leads to warnings with -pedantic, since it is a C++17 feature */
+ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+ #if __has_cpp_attribute(maybe_unused)
+ #define CYTHON_UNUSED [[maybe_unused]]
+ #endif
+ #endif
+ #endif
+#endif
+#ifndef CYTHON_UNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+#endif
+#ifndef CYTHON_UNUSED_VAR
+# if defined(__cplusplus)
+ template void CYTHON_UNUSED_VAR( const T& ) { }
+# else
+# define CYTHON_UNUSED_VAR(x) (void)(x)
+# endif
+#endif
+#ifndef CYTHON_MAYBE_UNUSED_VAR
+ #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x)
+#endif
+#ifndef CYTHON_NCP_UNUSED
+# if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+# define CYTHON_NCP_UNUSED
+# else
+# define CYTHON_NCP_UNUSED CYTHON_UNUSED
+# endif
+#endif
+#ifndef CYTHON_USE_CPP_STD_MOVE
+ #if defined(__cplusplus) && (\
+ __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600))
+ #define CYTHON_USE_CPP_STD_MOVE 1
+ #else
+ #define CYTHON_USE_CPP_STD_MOVE 0
+ #endif
+#endif
+#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None)
+#include
+typedef uintptr_t __pyx_uintptr_t;
+#ifndef CYTHON_FALLTHROUGH
+ #if defined(__cplusplus)
+ /* for clang __has_cpp_attribute(fallthrough) is true even before C++17
+ * but leads to warnings with -pedantic, since it is a C++17 feature */
+ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+ #if __has_cpp_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH [[fallthrough]]
+ #endif
+ #endif
+ #ifndef CYTHON_FALLTHROUGH
+ #if __has_cpp_attribute(clang::fallthrough)
+ #define CYTHON_FALLTHROUGH [[clang::fallthrough]]
+ #elif __has_cpp_attribute(gnu::fallthrough)
+ #define CYTHON_FALLTHROUGH [[gnu::fallthrough]]
+ #endif
+ #endif
+ #endif
+ #ifndef CYTHON_FALLTHROUGH
+ #if __has_attribute(fallthrough)
+ #define CYTHON_FALLTHROUGH __attribute__((fallthrough))
+ #else
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+ #if defined(__clang__) && defined(__apple_build_version__)
+ #if __apple_build_version__ < 7000000
+ #undef CYTHON_FALLTHROUGH
+ #define CYTHON_FALLTHROUGH
+ #endif
+ #endif
+#endif
+#ifndef Py_UNREACHABLE
+ #define Py_UNREACHABLE() assert(0); abort()
+#endif
+#ifdef __cplusplus
+ template
+ struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);};
+ #define __PYX_IS_UNSIGNED(type) (__PYX_IS_UNSIGNED_IMPL::value)
+#else
+ #define __PYX_IS_UNSIGNED(type) (((type)-1) > 0)
+#endif
+#if CYTHON_COMPILING_IN_PYPY == 1
+ #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX < 0x030A0000)
+#else
+ #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX < 0x03090000)
+#endif
+#define __PYX_REINTERPRET_FUNCION(func_pointer, other_pointer) ((func_pointer)(void(*)(void))(other_pointer))
+
+/* CInitCode */
+#ifndef CYTHON_INLINE
+ #if defined(__clang__)
+ #define CYTHON_INLINE __inline__ __attribute__ ((__unused__))
+ #elif defined(__GNUC__)
+ #define CYTHON_INLINE __inline__
+ #elif defined(_MSC_VER)
+ #define CYTHON_INLINE __inline
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_INLINE inline
+ #else
+ #define CYTHON_INLINE
+ #endif
+#endif
+
+/* PythonCompatibility */
+#define __PYX_BUILD_PY_SSIZE_T "n"
+#define CYTHON_FORMAT_SSIZE_T "z"
+#define __Pyx_BUILTIN_MODULE_NAME "builtins"
+#define __Pyx_DefaultClassType PyType_Type
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #ifndef CO_OPTIMIZED
+ static int CO_OPTIMIZED;
+ #endif
+ #ifndef CO_NEWLOCALS
+ static int CO_NEWLOCALS;
+ #endif
+ #ifndef CO_VARARGS
+ static int CO_VARARGS;
+ #endif
+ #ifndef CO_VARKEYWORDS
+ static int CO_VARKEYWORDS;
+ #endif
+ #ifndef CO_ASYNC_GENERATOR
+ static int CO_ASYNC_GENERATOR;
+ #endif
+ #ifndef CO_GENERATOR
+ static int CO_GENERATOR;
+ #endif
+ #ifndef CO_COROUTINE
+ static int CO_COROUTINE;
+ #endif
+#else
+ #ifndef CO_COROUTINE
+ #define CO_COROUTINE 0x80
+ #endif
+ #ifndef CO_ASYNC_GENERATOR
+ #define CO_ASYNC_GENERATOR 0x200
+ #endif
+#endif
+static int __Pyx_init_co_variables(void);
+#if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE)
+ #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type)
+#else
+ #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type))
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_Is)
+ #define __Pyx_Py_Is(x, y) Py_Is(x, y)
+#else
+ #define __Pyx_Py_Is(x, y) ((x) == (y))
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsNone)
+ #define __Pyx_Py_IsNone(ob) Py_IsNone(ob)
+#else
+ #define __Pyx_Py_IsNone(ob) __Pyx_Py_Is((ob), Py_None)
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsTrue)
+ #define __Pyx_Py_IsTrue(ob) Py_IsTrue(ob)
+#else
+ #define __Pyx_Py_IsTrue(ob) __Pyx_Py_Is((ob), Py_True)
+#endif
+#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsFalse)
+ #define __Pyx_Py_IsFalse(ob) Py_IsFalse(ob)
+#else
+ #define __Pyx_Py_IsFalse(ob) __Pyx_Py_Is((ob), Py_False)
+#endif
+#define __Pyx_NoneAsNull(obj) (__Pyx_Py_IsNone(obj) ? NULL : (obj))
+#if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY
+ #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o)
+#else
+ #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o)
+#endif
+#ifndef Py_TPFLAGS_CHECKTYPES
+ #define Py_TPFLAGS_CHECKTYPES 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_INDEX
+ #define Py_TPFLAGS_HAVE_INDEX 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
+ #define Py_TPFLAGS_HAVE_NEWBUFFER 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_FINALIZE
+ #define Py_TPFLAGS_HAVE_FINALIZE 0
+#endif
+#ifndef Py_TPFLAGS_SEQUENCE
+ #define Py_TPFLAGS_SEQUENCE 0
+#endif
+#ifndef Py_TPFLAGS_MAPPING
+ #define Py_TPFLAGS_MAPPING 0
+#endif
+#ifndef Py_TPFLAGS_IMMUTABLETYPE
+ #define Py_TPFLAGS_IMMUTABLETYPE (1UL << 8)
+#endif
+#ifndef Py_TPFLAGS_DISALLOW_INSTANTIATION
+ #define Py_TPFLAGS_DISALLOW_INSTANTIATION (1UL << 7)
+#endif
+#ifndef METH_STACKLESS
+ #define METH_STACKLESS 0
+#endif
+#ifndef METH_FASTCALL
+ #ifndef METH_FASTCALL
+ #define METH_FASTCALL 0x80
+ #endif
+ typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs);
+ typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args,
+ Py_ssize_t nargs, PyObject *kwnames);
+#else
+ #if PY_VERSION_HEX >= 0x030d00A4
+ # define __Pyx_PyCFunctionFast PyCFunctionFast
+ # define __Pyx_PyCFunctionFastWithKeywords PyCFunctionFastWithKeywords
+ #else
+ # define __Pyx_PyCFunctionFast _PyCFunctionFast
+ # define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords
+ #endif
+#endif
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_METH_FASTCALL METH_FASTCALL
+ #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast
+ #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords
+#else
+ #define __Pyx_METH_FASTCALL METH_VARARGS
+ #define __Pyx_PyCFunction_FastCall PyCFunction
+ #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords
+#endif
+#if CYTHON_VECTORCALL
+ #define __pyx_vectorcallfunc vectorcallfunc
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET
+ #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n))
+#else
+ #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0
+ #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n))
+#endif
+#if PY_VERSION_HEX >= 0x030900B1
+#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func)
+#else
+#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func)
+#endif
+#define __Pyx_CyOrPyCFunction_Check(func) PyCFunction_Check(func)
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth)
+#elif !CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func)
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_CyOrPyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags)
+static CYTHON_INLINE PyObject* __Pyx_CyOrPyCFunction_GET_SELF(PyObject *func) {
+ return (__Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_STATIC) ? NULL : ((PyCFunctionObject*)func)->m_self;
+}
+#endif
+static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void (*cfunc)(void)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ return PyCFunction_Check(func) && PyCFunction_GetFunction(func) == (PyCFunction) cfunc;
+#else
+ return PyCFunction_Check(func) && PyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc;
+#endif
+}
+#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCFunction(func, cfunc)
+#if PY_VERSION_HEX < 0x03090000 || (CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000)
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b))
+ typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *);
+#else
+ #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b)
+ #define __Pyx_PyCMethod PyCMethod
+#endif
+#ifndef METH_METHOD
+ #define METH_METHOD 0x200
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc)
+ #define PyObject_Malloc(s) PyMem_Malloc(s)
+ #define PyObject_Free(p) PyMem_Free(p)
+ #define PyObject_Realloc(p) PyMem_Realloc(p)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno)
+#elif CYTHON_COMPILING_IN_GRAAL && defined(GRAALPY_VERSION_NUM) && GRAALPY_VERSION_NUM > 0x19000000
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) GraalPyFrame_SetLineNumber((frame), (lineno))
+#elif CYTHON_COMPILING_IN_GRAAL
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) _PyFrame_SetLineNumber((frame), (lineno))
+#else
+ #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0)
+ #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyThreadState_Current PyThreadState_Get()
+#elif !CYTHON_FAST_THREAD_STATE
+ #define __Pyx_PyThreadState_Current PyThreadState_GET()
+#elif PY_VERSION_HEX >= 0x030d00A1
+ #define __Pyx_PyThreadState_Current PyThreadState_GetUnchecked()
+#else
+ #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet()
+#endif
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_INLINE void *__Pyx__PyModule_GetState(PyObject *op)
+{
+ void *result;
+ result = PyModule_GetState(op);
+ if (!result)
+ Py_FatalError("Couldn't find the module state");
+ return result;
+}
+#define __Pyx_PyModule_GetState(o) (__pyx_mstatetype *)__Pyx__PyModule_GetState(o)
+#else
+#define __Pyx_PyModule_GetState(op) ((void)op,__pyx_mstate_global)
+#endif
+#define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE((PyObject *) obj), name, func_ctype)
+#define __Pyx_PyObject_TryGetSlot(obj, name, func_ctype) __Pyx_PyType_TryGetSlot(Py_TYPE(obj), name, func_ctype)
+#define __Pyx_PyObject_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(Py_TYPE(obj), sub, name, func_ctype)
+#define __Pyx_PyObject_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSubSlot(Py_TYPE(obj), sub, name, func_ctype)
+#if CYTHON_USE_TYPE_SLOTS
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name)
+ #define __Pyx_PyType_TryGetSlot(type, name, func_ctype) __Pyx_PyType_GetSlot(type, name, func_ctype)
+ #define __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype) (((type)->sub) ? ((type)->sub->name) : NULL)
+ #define __Pyx_PyType_TryGetSubSlot(type, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype)
+#else
+ #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name))
+ #define __Pyx_PyType_TryGetSlot(type, name, func_ctype)\
+ ((__PYX_LIMITED_VERSION_HEX >= 0x030A0000 ||\
+ (PyType_GetFlags(type) & Py_TPFLAGS_HEAPTYPE) || __Pyx_get_runtime_version() >= 0x030A0000) ?\
+ __Pyx_PyType_GetSlot(type, name, func_ctype) : NULL)
+ #define __Pyx_PyType_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSlot(obj, name, func_ctype)
+ #define __Pyx_PyType_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSlot(obj, name, func_ctype)
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON || defined(_PyDict_NewPresized)
+#define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n))
+#else
+#define __Pyx_PyDict_NewPresized(n) PyDict_New()
+#endif
+#define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)
+#define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)
+#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_UNICODE_INTERNALS
+#define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash)
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) {
+ PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name);
+ if (res == NULL) PyErr_Clear();
+ return res;
+}
+#elif !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000
+#define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#else
+static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) {
+#if CYTHON_COMPILING_IN_PYPY
+ return PyDict_GetItem(dict, name);
+#else
+ PyDictEntry *ep;
+ PyDictObject *mp = (PyDictObject*) dict;
+ long hash = ((PyStringObject *) name)->ob_shash;
+ assert(hash != -1);
+ ep = (mp->ma_lookup)(mp, name, hash);
+ if (ep == NULL) {
+ return NULL;
+ }
+ return ep->me_value;
+#endif
+}
+#define __Pyx_PyDict_GetItemStr PyDict_GetItem
+#endif
+#if CYTHON_USE_TYPE_SLOTS
+ #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags)
+ #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0)
+#else
+ #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp))
+ #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature)
+#endif
+#define __Pyx_PyObject_GetIterNextFunc(iterator) __Pyx_PyObject_GetSlot(iterator, tp_iternext, iternextfunc)
+#if CYTHON_USE_TYPE_SPECS
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) {\
+ PyTypeObject *type = Py_TYPE((PyObject*)obj);\
+ assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));\
+ PyObject_GC_Del(obj);\
+ Py_DECREF(type);\
+}
+#else
+#define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj)
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i)
+ #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U)
+ #define __Pyx_PyUnicode_KIND(u) ((void)u, (0))
+ #define __Pyx_PyUnicode_DATA(u) ((void*)u)
+ #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i))
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u))
+#else
+ #if PY_VERSION_HEX >= 0x030C0000
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #else
+ #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\
+ 0 : _PyUnicode_Ready((PyObject *)(op)))
+ #endif
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
+ #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u)
+ #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u))
+ #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u)
+ #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
+ #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, (Py_UCS4) ch)
+ #if PY_VERSION_HEX >= 0x030C0000
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u))
+ #else
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
+ #else
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
+ #endif
+ #endif
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+ #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b)
+#else
+ #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\
+ PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b))
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+ #if !defined(PyUnicode_DecodeUnicodeEscape)
+ #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors)
+ #endif
+ #if !defined(PyUnicode_Contains)
+ #define PyUnicode_Contains(u, s) PySequence_Contains(u, s)
+ #endif
+ #if !defined(PyByteArray_Check)
+ #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type)
+ #endif
+ #if !defined(PyObject_Format)
+ #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
+ #endif
+#endif
+#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ #define __Pyx_PySequence_ListKeepNew(obj)\
+ (likely(PyList_CheckExact(obj) && PyUnstable_Object_IsUniquelyReferenced(obj)) ? __Pyx_NewRef(obj) : PySequence_List(obj))
+#elif CYTHON_COMPILING_IN_CPYTHON
+ #define __Pyx_PySequence_ListKeepNew(obj)\
+ (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj))
+#else
+ #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj)
+#endif
+#ifndef PySet_CheckExact
+ #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type)
+#endif
+#if PY_VERSION_HEX >= 0x030900A4
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size)
+#else
+ #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
+ #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size)
+#endif
+enum __Pyx_ReferenceSharing {
+ __Pyx_ReferenceSharing_DefinitelyUnique, // We created it so we know it's unshared - no need to check
+ __Pyx_ReferenceSharing_OwnStrongReference,
+ __Pyx_ReferenceSharing_FunctionArgument,
+ __Pyx_ReferenceSharing_SharedReference, // Never trust it to be unshared because it's a global or similar
+};
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && PY_VERSION_HEX >= 0x030E0000
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing)\
+ (sharing == __Pyx_ReferenceSharing_DefinitelyUnique ? 1 :\
+ (sharing == __Pyx_ReferenceSharing_FunctionArgument ? PyUnstable_Object_IsUniqueReferencedTemporary(o) :\
+ (sharing == __Pyx_ReferenceSharing_OwnStrongReference ? PyUnstable_Object_IsUniquelyReferenced(o) : 0)))
+#elif (CYTHON_COMPILING_IN_CPYTHON && !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING) || CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing) (((void)sharing), Py_REFCNT(o) == 1)
+#else
+#define __Pyx_IS_UNIQUELY_REFERENCED(o, sharing) (((void)o), ((void)sharing), 0)
+#endif
+#if CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
+ #elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyList_GetItemRef(o, i) (likely((i) >= 0) ? PySequence_GetItem(o, i) : (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL))
+ #else
+ #define __Pyx_PyList_GetItemRef(o, i) PySequence_ITEM(o, i)
+ #endif
+#elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
+ #else
+ #define __Pyx_PyList_GetItemRef(o, i) __Pyx_XNewRef(PyList_GetItem(o, i))
+ #endif
+#else
+ #define __Pyx_PyList_GetItemRef(o, i) __Pyx_NewRef(PyList_GET_ITEM(o, i))
+#endif
+#if CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS && !CYTHON_COMPILING_IN_LIMITED_API && CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyList_GetItemRefFast(o, i, unsafe_shared) (__Pyx_IS_UNIQUELY_REFERENCED(o, unsafe_shared) ?\
+ __Pyx_NewRef(PyList_GET_ITEM(o, i)) : __Pyx_PyList_GetItemRef(o, i))
+#else
+ #define __Pyx_PyList_GetItemRefFast(o, i, unsafe_shared) __Pyx_PyList_GetItemRef(o, i)
+#endif
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_PyDict_GetItemRef(dict, key, result) PyDict_GetItemRef(dict, key, result)
+#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
+ *result = PyObject_GetItem(dict, key);
+ if (*result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ return 0;
+ }
+ return -1;
+ }
+ return 1;
+}
+#else
+static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
+ *result = PyDict_GetItemWithError(dict, key);
+ if (*result == NULL) {
+ return PyErr_Occurred() ? -1 : 0;
+ }
+ Py_INCREF(*result);
+ return 1;
+}
+#endif
+#if defined(CYTHON_DEBUG_VISIT_CONST) && CYTHON_DEBUG_VISIT_CONST
+ #define __Pyx_VISIT_CONST(obj) Py_VISIT(obj)
+#else
+ #define __Pyx_VISIT_CONST(obj)
+#endif
+#if CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i)
+ #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq)
+ #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0))
+ #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GET_ITEM(o, i)
+ #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0))
+ #define __Pyx_PyList_GET_ITEM(o, i) PyList_GET_ITEM(o, i)
+#else
+ #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i)
+ #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq)
+ #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v)
+ #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GetItem(o, i)
+ #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v)
+ #define __Pyx_PyList_GET_ITEM(o, i) PyList_GetItem(o, i)
+#endif
+#if CYTHON_ASSUME_SAFE_SIZE
+ #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o)
+ #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o)
+ #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o)
+ #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
+ #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o)
+ #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GET_LENGTH(o)
+#else
+ #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o)
+ #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o)
+ #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o)
+ #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o)
+ #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o)
+ #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GetLength(o)
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_InternFromString)
+ #define PyUnicode_InternFromString(s) PyUnicode_FromString(s)
+#endif
+#define __Pyx_PyLong_FromHash_t PyLong_FromSsize_t
+#define __Pyx_PyLong_AsHash_t __Pyx_PyIndex_AsSsize_t
+#if __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+ #define __Pyx_PySendResult PySendResult
+#else
+ typedef enum {
+ PYGEN_RETURN = 0,
+ PYGEN_ERROR = -1,
+ PYGEN_NEXT = 1,
+ } __Pyx_PySendResult;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX < 0x030A00A3
+ typedef __Pyx_PySendResult (*__Pyx_pyiter_sendfunc)(PyObject *iter, PyObject *value, PyObject **result);
+#else
+ #define __Pyx_pyiter_sendfunc sendfunc
+#endif
+#if !CYTHON_USE_AM_SEND
+#define __PYX_HAS_PY_AM_SEND 0
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030A0000
+#define __PYX_HAS_PY_AM_SEND 1
+#else
+#define __PYX_HAS_PY_AM_SEND 2 // our own backported implementation
+#endif
+#if __PYX_HAS_PY_AM_SEND < 2
+ #define __Pyx_PyAsyncMethodsStruct PyAsyncMethods
+#else
+ typedef struct {
+ unaryfunc am_await;
+ unaryfunc am_aiter;
+ unaryfunc am_anext;
+ __Pyx_pyiter_sendfunc am_send;
+ } __Pyx_PyAsyncMethodsStruct;
+ #define __Pyx_SlotTpAsAsync(s) ((PyAsyncMethods*)(s))
+#endif
+#if CYTHON_USE_AM_SEND && PY_VERSION_HEX < 0x030A00F0
+ #define __Pyx_TPFLAGS_HAVE_AM_SEND (1UL << 21)
+#else
+ #define __Pyx_TPFLAGS_HAVE_AM_SEND (0)
+#endif
+#if PY_VERSION_HEX >= 0x03090000
+#define __Pyx_PyInterpreterState_Get() PyInterpreterState_Get()
+#else
+#define __Pyx_PyInterpreterState_Get() PyThreadState_Get()->interp
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030A0000
+#ifdef __cplusplus
+extern "C"
+#endif
+PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize);
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+static int __Pyx_init_co_variable(PyObject *inspect, const char* name, int *write_to) {
+ int value;
+ PyObject *py_value = PyObject_GetAttrString(inspect, name);
+ if (!py_value) return 0;
+ value = (int) PyLong_AsLong(py_value);
+ Py_DECREF(py_value);
+ *write_to = value;
+ return value != -1 || !PyErr_Occurred();
+}
+static int __Pyx_init_co_variables(void) {
+ PyObject *inspect;
+ int result;
+ inspect = PyImport_ImportModule("inspect");
+ result =
+#if !defined(CO_OPTIMIZED)
+ __Pyx_init_co_variable(inspect, "CO_OPTIMIZED", &CO_OPTIMIZED) &&
+#endif
+#if !defined(CO_NEWLOCALS)
+ __Pyx_init_co_variable(inspect, "CO_NEWLOCALS", &CO_NEWLOCALS) &&
+#endif
+#if !defined(CO_VARARGS)
+ __Pyx_init_co_variable(inspect, "CO_VARARGS", &CO_VARARGS) &&
+#endif
+#if !defined(CO_VARKEYWORDS)
+ __Pyx_init_co_variable(inspect, "CO_VARKEYWORDS", &CO_VARKEYWORDS) &&
+#endif
+#if !defined(CO_ASYNC_GENERATOR)
+ __Pyx_init_co_variable(inspect, "CO_ASYNC_GENERATOR", &CO_ASYNC_GENERATOR) &&
+#endif
+#if !defined(CO_GENERATOR)
+ __Pyx_init_co_variable(inspect, "CO_GENERATOR", &CO_GENERATOR) &&
+#endif
+#if !defined(CO_COROUTINE)
+ __Pyx_init_co_variable(inspect, "CO_COROUTINE", &CO_COROUTINE) &&
+#endif
+ 1;
+ Py_DECREF(inspect);
+ return result ? 0 : -1;
+}
+#else
+static int __Pyx_init_co_variables(void) {
+ return 0; // It's a limited API-only feature
+}
+#endif
+
+/* MathInitCode */
+#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
+ #ifndef _USE_MATH_DEFINES
+ #define _USE_MATH_DEFINES
+ #endif
+#endif
+#include
+#if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL)
+#define __Pyx_truncl trunc
+#else
+#define __Pyx_truncl truncl
+#endif
+
+#ifndef CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME 0
+#endif
+#ifndef CYTHON_CLINE_IN_TRACEBACK
+#define CYTHON_CLINE_IN_TRACEBACK CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+#endif
+#if CYTHON_CLINE_IN_TRACEBACK
+#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; __pyx_clineno = __LINE__; (void) __pyx_clineno; }
+#else
+#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; (void) __pyx_clineno; }
+#endif
+#define __PYX_ERR(f_index, lineno, Ln_error) \
+ { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; }
+
+#ifdef CYTHON_EXTERN_C
+ #undef __PYX_EXTERN_C
+ #define __PYX_EXTERN_C CYTHON_EXTERN_C
+#elif defined(__PYX_EXTERN_C)
+ #ifdef _MSC_VER
+ #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
+ #else
+ #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
+ #endif
+#else
+ #ifdef __cplusplus
+ #define __PYX_EXTERN_C extern "C"
+ #else
+ #define __PYX_EXTERN_C extern
+ #endif
+#endif
+
+#define __PYX_HAVE__fontTools__qu2cu__qu2cu
+#define __PYX_HAVE_API__fontTools__qu2cu__qu2cu
+/* Early includes */
+#ifdef _OPENMP
+#include
+#endif /* _OPENMP */
+
+#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS)
+#define CYTHON_WITHOUT_ASSERTIONS
+#endif
+
+#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0
+#define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 0
+#define __PYX_DEFAULT_STRING_ENCODING ""
+#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString
+#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize
+#define __Pyx_uchar_cast(c) ((unsigned char)c)
+#define __Pyx_long_cast(x) ((long)x)
+#define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\
+ (sizeof(type) < sizeof(Py_ssize_t)) ||\
+ (sizeof(type) > sizeof(Py_ssize_t) &&\
+ likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX) &&\
+ (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\
+ v == (type)PY_SSIZE_T_MIN))) ||\
+ (sizeof(type) == sizeof(Py_ssize_t) &&\
+ (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX))) )
+static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) {
+ return (size_t) i < (size_t) limit;
+}
+#if defined (__cplusplus) && __cplusplus >= 201103L
+ #include
+ #define __Pyx_sst_abs(value) std::abs(value)
+#elif SIZEOF_INT >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) abs(value)
+#elif SIZEOF_LONG >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) labs(value)
+#elif defined (_MSC_VER)
+ #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value))
+#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define __Pyx_sst_abs(value) llabs(value)
+#elif defined (__GNUC__)
+ #define __Pyx_sst_abs(value) __builtin_llabs(value)
+#else
+ #define __Pyx_sst_abs(value) ((value<0) ? -value : value)
+#endif
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*);
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
+static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char*);
+#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l)
+#define __Pyx_PyBytes_FromString PyBytes_FromString
+#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*);
+#if CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s))
+ #define __Pyx_PyByteArray_AsString(s) PyByteArray_AS_STRING(s)
+#else
+ #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AsString(s))
+ #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AsString(s))
+ #define __Pyx_PyByteArray_AsString(s) PyByteArray_AsString(s)
+#endif
+#define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
+#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
+#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
+#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s)
+#define __Pyx_PyUnicode_FromOrdinal(o) PyUnicode_FromOrdinal((int)o)
+#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
+static CYTHON_INLINE PyObject *__Pyx_NewRef(PyObject *obj) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_NewRef)
+ return Py_NewRef(obj);
+#else
+ Py_INCREF(obj);
+ return obj;
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_XNewRef(PyObject *obj) {
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_XNewRef)
+ return Py_XNewRef(obj);
+#else
+ Py_XINCREF(obj);
+ return obj;
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b);
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*);
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x);
+#define __Pyx_PySequence_Tuple(obj)\
+ (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj))
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
+static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t);
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*);
+#if CYTHON_ASSUME_SAFE_MACROS
+#define __Pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
+#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AS_DOUBLE(x)
+#else
+#define __Pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x)
+#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AsDouble(x)
+#endif
+#define __Pyx_PyFloat_AsFloat(x) ((float) __Pyx_PyFloat_AsDouble(x))
+#define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x))
+#if CYTHON_USE_PYLONG_INTERNALS
+ #if PY_VERSION_HEX >= 0x030C00A7
+ #ifndef _PyLong_SIGN_MASK
+ #define _PyLong_SIGN_MASK 3
+ #endif
+ #ifndef _PyLong_NON_SIZE_BITS
+ #define _PyLong_NON_SIZE_BITS 3
+ #endif
+ #define __Pyx_PyLong_Sign(x) (((PyLongObject*)x)->long_value.lv_tag & _PyLong_SIGN_MASK)
+ #define __Pyx_PyLong_IsNeg(x) ((__Pyx_PyLong_Sign(x) & 2) != 0)
+ #define __Pyx_PyLong_IsNonNeg(x) (!__Pyx_PyLong_IsNeg(x))
+ #define __Pyx_PyLong_IsZero(x) (__Pyx_PyLong_Sign(x) & 1)
+ #define __Pyx_PyLong_IsPos(x) (__Pyx_PyLong_Sign(x) == 0)
+ #define __Pyx_PyLong_CompactValueUnsigned(x) (__Pyx_PyLong_Digits(x)[0])
+ #define __Pyx_PyLong_DigitCount(x) ((Py_ssize_t) (((PyLongObject*)x)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS))
+ #define __Pyx_PyLong_SignedDigitCount(x)\
+ ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * __Pyx_PyLong_DigitCount(x))
+ #if defined(PyUnstable_Long_IsCompact) && defined(PyUnstable_Long_CompactValue)
+ #define __Pyx_PyLong_IsCompact(x) PyUnstable_Long_IsCompact((PyLongObject*) x)
+ #define __Pyx_PyLong_CompactValue(x) PyUnstable_Long_CompactValue((PyLongObject*) x)
+ #else
+ #define __Pyx_PyLong_IsCompact(x) (((PyLongObject*)x)->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS))
+ #define __Pyx_PyLong_CompactValue(x) ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * (Py_ssize_t) __Pyx_PyLong_Digits(x)[0])
+ #endif
+ typedef Py_ssize_t __Pyx_compact_pylong;
+ typedef size_t __Pyx_compact_upylong;
+ #else
+ #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0)
+ #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0)
+ #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0)
+ #define __Pyx_PyLong_IsPos(x) (Py_SIZE(x) > 0)
+ #define __Pyx_PyLong_CompactValueUnsigned(x) ((Py_SIZE(x) == 0) ? 0 : __Pyx_PyLong_Digits(x)[0])
+ #define __Pyx_PyLong_DigitCount(x) __Pyx_sst_abs(Py_SIZE(x))
+ #define __Pyx_PyLong_SignedDigitCount(x) Py_SIZE(x)
+ #define __Pyx_PyLong_IsCompact(x) (Py_SIZE(x) == 0 || Py_SIZE(x) == 1 || Py_SIZE(x) == -1)
+ #define __Pyx_PyLong_CompactValue(x)\
+ ((Py_SIZE(x) == 0) ? (sdigit) 0 : ((Py_SIZE(x) < 0) ? -(sdigit)__Pyx_PyLong_Digits(x)[0] : (sdigit)__Pyx_PyLong_Digits(x)[0]))
+ typedef sdigit __Pyx_compact_pylong;
+ typedef digit __Pyx_compact_upylong;
+ #endif
+ #if PY_VERSION_HEX >= 0x030C00A5
+ #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->long_value.ob_digit)
+ #else
+ #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->ob_digit)
+ #endif
+#endif
+#if __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL)
+#elif __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeASCII(c_str, size, NULL)
+#else
+ #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL)
+#endif
+
+
+/* Test for GCC > 2.95 */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)))
+ #define likely(x) __builtin_expect(!!(x), 1)
+ #define unlikely(x) __builtin_expect(!!(x), 0)
+#else /* !__GNUC__ or GCC < 2.95 */
+ #define likely(x) (x)
+ #define unlikely(x) (x)
+#endif /* __GNUC__ */
+/* PretendToInitialize */
+#ifdef __cplusplus
+#if __cplusplus > 201103L
+#include
+#endif
+template
+static void __Pyx_pretend_to_initialize(T* ptr) {
+#if __cplusplus > 201103L
+ if ((std::is_trivially_default_constructible::value))
+#endif
+ *ptr = T();
+ (void)ptr;
+}
+#else
+static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }
+#endif
+
+
+#if !CYTHON_USE_MODULE_STATE
+static PyObject *__pyx_m = NULL;
+#endif
+static int __pyx_lineno;
+static int __pyx_clineno = 0;
+static const char * const __pyx_cfilenm = __FILE__;
+static const char *__pyx_filename;
+
+/* Header.proto */
+#if !defined(CYTHON_CCOMPLEX)
+ #if defined(__cplusplus)
+ #define CYTHON_CCOMPLEX 1
+ #elif (defined(_Complex_I) && !defined(_MSC_VER)) || ((defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_COMPLEX__) && !defined(_MSC_VER))
+ #define CYTHON_CCOMPLEX 1
+ #else
+ #define CYTHON_CCOMPLEX 0
+ #endif
+#endif
+#if CYTHON_CCOMPLEX
+ #ifdef __cplusplus
+ #include
+ #else
+ #include
+ #endif
+#endif
+#if CYTHON_CCOMPLEX && !defined(__cplusplus) && defined(__sun__) && defined(__GNUC__)
+ #undef _Complex_I
+ #define _Complex_I 1.0fj
+#endif
+
+/* #### Code section: filename_table ### */
+
+static const char* const __pyx_f[] = {
+ "Lib/fontTools/qu2cu/qu2cu.py",
+};
+/* #### Code section: utility_code_proto_before_types ### */
+/* Atomics.proto (used by UnpackUnboundCMethod) */
+#include
+#ifndef CYTHON_ATOMICS
+ #define CYTHON_ATOMICS 1
+#endif
+#define __PYX_CYTHON_ATOMICS_ENABLED() CYTHON_ATOMICS
+#define __PYX_GET_CYTHON_COMPILING_IN_CPYTHON_FREETHREADING() CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __pyx_atomic_int_type int
+#define __pyx_nonatomic_int_type int
+#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\
+ (__STDC_VERSION__ >= 201112L) &&\
+ !defined(__STDC_NO_ATOMICS__))
+ #include
+#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\
+ (__cplusplus >= 201103L) ||\
+ (defined(_MSC_VER) && _MSC_VER >= 1700)))
+ #include
+#endif
+#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\
+ (__STDC_VERSION__ >= 201112L) &&\
+ !defined(__STDC_NO_ATOMICS__) &&\
+ ATOMIC_INT_LOCK_FREE == 2)
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type atomic_int
+ #define __pyx_atomic_ptr_type atomic_uintptr_t
+ #define __pyx_nonatomic_ptr_type uintptr_t
+ #define __pyx_atomic_incr_relaxed(value) atomic_fetch_add_explicit(value, 1, memory_order_relaxed)
+ #define __pyx_atomic_incr_acq_rel(value) atomic_fetch_add_explicit(value, 1, memory_order_acq_rel)
+ #define __pyx_atomic_decr_acq_rel(value) atomic_fetch_sub_explicit(value, 1, memory_order_acq_rel)
+ #define __pyx_atomic_sub(value, arg) atomic_fetch_sub(value, arg)
+ #define __pyx_atomic_int_cmp_exchange(value, expected, desired) atomic_compare_exchange_strong(value, expected, desired)
+ #define __pyx_atomic_load(value) atomic_load(value)
+ #define __pyx_atomic_store(value, new_value) atomic_store(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) atomic_load_explicit(value, memory_order_relaxed)
+ #define __pyx_atomic_pointer_load_acquire(value) atomic_load_explicit(value, memory_order_acquire)
+ #define __pyx_atomic_pointer_exchange(value, new_value) atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value)
+ #define __pyx_atomic_pointer_cmp_exchange(value, expected, desired) atomic_compare_exchange_strong(value, expected, desired)
+ #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER)
+ #pragma message ("Using standard C atomics")
+ #elif defined(__PYX_DEBUG_ATOMICS)
+ #warning "Using standard C atomics"
+ #endif
+#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\
+ (__cplusplus >= 201103L) ||\
+\
+ (defined(_MSC_VER) && _MSC_VER >= 1700)) &&\
+ ATOMIC_INT_LOCK_FREE == 2)
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type std::atomic_int
+ #define __pyx_atomic_ptr_type std::atomic_uintptr_t
+ #define __pyx_nonatomic_ptr_type uintptr_t
+ #define __pyx_atomic_incr_relaxed(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_relaxed)
+ #define __pyx_atomic_incr_acq_rel(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_acq_rel)
+ #define __pyx_atomic_decr_acq_rel(value) std::atomic_fetch_sub_explicit(value, 1, std::memory_order_acq_rel)
+ #define __pyx_atomic_sub(value, arg) std::atomic_fetch_sub(value, arg)
+ #define __pyx_atomic_int_cmp_exchange(value, expected, desired) std::atomic_compare_exchange_strong(value, expected, desired)
+ #define __pyx_atomic_load(value) std::atomic_load(value)
+ #define __pyx_atomic_store(value, new_value) std::atomic_store(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) std::atomic_load_explicit(value, std::memory_order_relaxed)
+ #define __pyx_atomic_pointer_load_acquire(value) std::atomic_load_explicit(value, std::memory_order_acquire)
+ #define __pyx_atomic_pointer_exchange(value, new_value) std::atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value)
+ #define __pyx_atomic_pointer_cmp_exchange(value, expected, desired) std::atomic_compare_exchange_strong(value, expected, desired)
+ #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER)
+ #pragma message ("Using standard C++ atomics")
+ #elif defined(__PYX_DEBUG_ATOMICS)
+ #warning "Using standard C++ atomics"
+ #endif
+#elif CYTHON_ATOMICS && (__GNUC__ >= 5 || (__GNUC__ == 4 &&\
+ (__GNUC_MINOR__ > 1 ||\
+ (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 2))))
+ #define __pyx_atomic_ptr_type void*
+ #define __pyx_nonatomic_ptr_type void*
+ #define __pyx_atomic_incr_relaxed(value) __sync_fetch_and_add(value, 1)
+ #define __pyx_atomic_incr_acq_rel(value) __sync_fetch_and_add(value, 1)
+ #define __pyx_atomic_decr_acq_rel(value) __sync_fetch_and_sub(value, 1)
+ #define __pyx_atomic_sub(value, arg) __sync_fetch_and_sub(value, arg)
+ static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) {
+ __pyx_nonatomic_int_type old = __sync_val_compare_and_swap(value, *expected, desired);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #define __pyx_atomic_load(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_store(value, new_value) __sync_lock_test_and_set(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_pointer_load_acquire(value) __sync_fetch_and_add(value, 0)
+ #define __pyx_atomic_pointer_exchange(value, new_value) __sync_lock_test_and_set(value, (__pyx_atomic_ptr_type)new_value)
+ static CYTHON_INLINE int __pyx_atomic_pointer_cmp_exchange(__pyx_atomic_ptr_type* value, __pyx_nonatomic_ptr_type* expected, __pyx_nonatomic_ptr_type desired) {
+ __pyx_nonatomic_ptr_type old = __sync_val_compare_and_swap(value, *expected, desired);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #ifdef __PYX_DEBUG_ATOMICS
+ #warning "Using GNU atomics"
+ #endif
+#elif CYTHON_ATOMICS && defined(_MSC_VER)
+ #include
+ #undef __pyx_atomic_int_type
+ #define __pyx_atomic_int_type long
+ #define __pyx_atomic_ptr_type void*
+ #undef __pyx_nonatomic_int_type
+ #define __pyx_nonatomic_int_type long
+ #define __pyx_nonatomic_ptr_type void*
+ #pragma intrinsic (_InterlockedExchangeAdd, _InterlockedExchange, _InterlockedCompareExchange, _InterlockedCompareExchangePointer, _InterlockedExchangePointer)
+ #define __pyx_atomic_incr_relaxed(value) _InterlockedExchangeAdd(value, 1)
+ #define __pyx_atomic_incr_acq_rel(value) _InterlockedExchangeAdd(value, 1)
+ #define __pyx_atomic_decr_acq_rel(value) _InterlockedExchangeAdd(value, -1)
+ #define __pyx_atomic_sub(value, arg) _InterlockedExchangeAdd(value, -arg)
+ static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) {
+ __pyx_nonatomic_int_type old = _InterlockedCompareExchange(value, desired, *expected);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #define __pyx_atomic_load(value) _InterlockedExchangeAdd(value, 0)
+ #define __pyx_atomic_store(value, new_value) _InterlockedExchange(value, new_value)
+ #define __pyx_atomic_pointer_load_relaxed(value) *(void * volatile *)value
+ #define __pyx_atomic_pointer_load_acquire(value) _InterlockedCompareExchangePointer(value, 0, 0)
+ #define __pyx_atomic_pointer_exchange(value, new_value) _InterlockedExchangePointer(value, (__pyx_atomic_ptr_type)new_value)
+ static CYTHON_INLINE int __pyx_atomic_pointer_cmp_exchange(__pyx_atomic_ptr_type* value, __pyx_nonatomic_ptr_type* expected, __pyx_nonatomic_ptr_type desired) {
+ __pyx_atomic_ptr_type old = _InterlockedCompareExchangePointer(value, desired, *expected);
+ int result = old == *expected;
+ *expected = old;
+ return result;
+ }
+ #ifdef __PYX_DEBUG_ATOMICS
+ #pragma message ("Using MSVC atomics")
+ #endif
+#else
+ #undef CYTHON_ATOMICS
+ #define CYTHON_ATOMICS 0
+ #ifdef __PYX_DEBUG_ATOMICS
+ #warning "Not using atomics"
+ #endif
+#endif
+
+/* CriticalSectionsDefinition.proto (used by CriticalSections) */
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __Pyx_PyCriticalSection void*
+#define __Pyx_PyCriticalSection2 void*
+#define __Pyx_PyCriticalSection_End(cs)
+#define __Pyx_PyCriticalSection2_End(cs)
+#else
+#define __Pyx_PyCriticalSection PyCriticalSection
+#define __Pyx_PyCriticalSection2 PyCriticalSection2
+#define __Pyx_PyCriticalSection_End PyCriticalSection_End
+#define __Pyx_PyCriticalSection2_End PyCriticalSection2_End
+#endif
+
+/* CriticalSections.proto (used by ParseKeywordsImpl) */
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __Pyx_PyCriticalSection_Begin(cs, arg) (void)(cs)
+#define __Pyx_PyCriticalSection2_Begin(cs, arg1, arg2) (void)(cs)
+#else
+#define __Pyx_PyCriticalSection_Begin PyCriticalSection_Begin
+#define __Pyx_PyCriticalSection2_Begin PyCriticalSection2_Begin
+#endif
+#if PY_VERSION_HEX < 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_BEGIN_CRITICAL_SECTION(o) {
+#define __Pyx_END_CRITICAL_SECTION() }
+#else
+#define __Pyx_BEGIN_CRITICAL_SECTION Py_BEGIN_CRITICAL_SECTION
+#define __Pyx_END_CRITICAL_SECTION Py_END_CRITICAL_SECTION
+#endif
+
+/* IncludeStructmemberH.proto (used by FixUpExtensionType) */
+#include
+
+/* #### Code section: numeric_typedefs ### */
+/* #### Code section: complex_type_declarations ### */
+/* Declarations.proto */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #ifdef __cplusplus
+ typedef ::std::complex< double > __pyx_t_double_complex;
+ #else
+ typedef double _Complex __pyx_t_double_complex;
+ #endif
+#else
+ typedef struct { double real, imag; } __pyx_t_double_complex;
+#endif
+static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double, double);
+
+/* #### Code section: type_declarations ### */
+
+/*--- Type declarations ---*/
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr;
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr;
+
+/* "fontTools/qu2cu/qu2cu.py":235
+ *
+ * if not is_complex:
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves] # <<<<<<<<<<<<<<
+ * return curves
+ *
+*/
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ PyObject *__pyx_v_c;
+ int __pyx_v_cost;
+ int __pyx_v_is_complex;
+ PyObject *__pyx_t_0;
+ Py_ssize_t __pyx_t_1;
+ PyObject *(*__pyx_t_2)(PyObject *);
+};
+
+
+/* "fontTools/qu2cu/qu2cu.py":340
+ * for k, reconst in enumerate(reconstructed):
+ * orig = elevated_quadratics[j + k]
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig)) # <<<<<<<<<<<<<<
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+*/
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr {
+ PyObject_HEAD
+ PyObject *__pyx_genexpr_arg_0;
+ int __pyx_v_all_cubic;
+ int __pyx_v_count;
+ double __pyx_v_err;
+ double __pyx_v_error;
+ int __pyx_v_i;
+ int __pyx_v_i_sol_count;
+ double __pyx_v_i_sol_error;
+ int __pyx_v_is_cubic;
+ int __pyx_v_j;
+ int __pyx_v_j_sol_count;
+ double __pyx_v_j_sol_error;
+ int __pyx_v_k;
+ __pyx_t_double_complex __pyx_v_p0;
+ __pyx_t_double_complex __pyx_v_p1;
+ __pyx_t_double_complex __pyx_v_p2;
+ __pyx_t_double_complex __pyx_v_p3;
+ int __pyx_v_start;
+ int __pyx_v_this_sol_count;
+ double __pyx_v_tolerance;
+ __pyx_t_double_complex __pyx_v_u;
+ __pyx_t_double_complex __pyx_v_v;
+ PyObject *__pyx_t_0;
+ Py_ssize_t __pyx_t_1;
+ PyObject *(*__pyx_t_2)(PyObject *);
+};
+
+/* #### Code section: utility_code_proto ### */
+
+/* --- Runtime support code (head) --- */
+/* Refnanny.proto */
+#ifndef CYTHON_REFNANNY
+ #define CYTHON_REFNANNY 0
+#endif
+#if CYTHON_REFNANNY
+ typedef struct {
+ void (*INCREF)(void*, PyObject*, Py_ssize_t);
+ void (*DECREF)(void*, PyObject*, Py_ssize_t);
+ void (*GOTREF)(void*, PyObject*, Py_ssize_t);
+ void (*GIVEREF)(void*, PyObject*, Py_ssize_t);
+ void* (*SetupContext)(const char*, Py_ssize_t, const char*);
+ void (*FinishContext)(void**);
+ } __Pyx_RefNannyAPIStruct;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname);
+ #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL;
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)\
+ if (acquire_gil) {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\
+ PyGILState_Release(__pyx_gilstate_save);\
+ } else {\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\
+ }
+ #define __Pyx_RefNannyFinishContextNogil() {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __Pyx_RefNannyFinishContext();\
+ PyGILState_Release(__pyx_gilstate_save);\
+ }
+ #define __Pyx_RefNannyFinishContextNogil() {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __Pyx_RefNannyFinishContext();\
+ PyGILState_Release(__pyx_gilstate_save);\
+ }
+ #define __Pyx_RefNannyFinishContext()\
+ __Pyx_RefNanny->FinishContext(&__pyx_refnanny)
+ #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__))
+ #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0)
+ #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0)
+ #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0)
+ #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0)
+#else
+ #define __Pyx_RefNannyDeclarations
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)
+ #define __Pyx_RefNannyFinishContextNogil()
+ #define __Pyx_RefNannyFinishContext()
+ #define __Pyx_INCREF(r) Py_INCREF(r)
+ #define __Pyx_DECREF(r) Py_DECREF(r)
+ #define __Pyx_GOTREF(r)
+ #define __Pyx_GIVEREF(r)
+ #define __Pyx_XINCREF(r) Py_XINCREF(r)
+ #define __Pyx_XDECREF(r) Py_XDECREF(r)
+ #define __Pyx_XGOTREF(r)
+ #define __Pyx_XGIVEREF(r)
+#endif
+#define __Pyx_Py_XDECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; Py_XDECREF(tmp);\
+ } while (0)
+#define __Pyx_XDECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_XDECREF(tmp);\
+ } while (0)
+#define __Pyx_DECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_DECREF(tmp);\
+ } while (0)
+#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0)
+#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0)
+
+/* PyErrExceptionMatches.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyErr_ExceptionMatches(err) __Pyx_PyErr_ExceptionMatchesInState(__pyx_tstate, err)
+static CYTHON_INLINE int __Pyx_PyErr_ExceptionMatchesInState(PyThreadState* tstate, PyObject* err);
+#else
+#define __Pyx_PyErr_ExceptionMatches(err) PyErr_ExceptionMatches(err)
+#endif
+
+/* PyThreadStateGet.proto (used by PyErrFetchRestore) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate;
+#define __Pyx_PyThreadState_assign __pyx_tstate = __Pyx_PyThreadState_Current;
+#if PY_VERSION_HEX >= 0x030C00A6
+#define __Pyx_PyErr_Occurred() (__pyx_tstate->current_exception != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->current_exception ? (PyObject*) Py_TYPE(__pyx_tstate->current_exception) : (PyObject*) NULL)
+#else
+#define __Pyx_PyErr_Occurred() (__pyx_tstate->curexc_type != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->curexc_type)
+#endif
+#else
+#define __Pyx_PyThreadState_declare
+#define __Pyx_PyThreadState_assign
+#define __Pyx_PyErr_Occurred() (PyErr_Occurred() != NULL)
+#define __Pyx_PyErr_CurrentExceptionType() PyErr_Occurred()
+#endif
+
+/* PyErrFetchRestore.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL)
+#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb);
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A6
+#define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL))
+#else
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
+#endif
+#else
+#define __Pyx_PyErr_Clear() PyErr_Clear()
+#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc)
+#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb)
+#endif
+
+/* PyObjectGetAttrStr.proto (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name);
+#else
+#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n)
+#endif
+
+/* PyObjectGetAttrStrNoError.proto (used by GetBuiltinName) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name);
+
+/* GetBuiltinName.proto */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name);
+
+/* TupleAndListFromArray.proto (used by fastcall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n);
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n);
+#endif
+
+/* IncludeStringH.proto (used by BytesEquals) */
+#include
+
+/* BytesEquals.proto (used by UnicodeEquals) */
+static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* UnicodeEquals.proto (used by fastcall) */
+static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* fastcall.proto */
+#if CYTHON_AVOID_BORROWED_REFS
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_PySequence_ITEM(args, i)
+#elif CYTHON_ASSUME_SAFE_MACROS
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_NewRef(__Pyx_PyTuple_GET_ITEM(args, i))
+#else
+ #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_XNewRef(PyTuple_GetItem(args, i))
+#endif
+#define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds)
+#define __Pyx_KwValues_VARARGS(args, nargs) NULL
+#define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s)
+#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw)
+#if CYTHON_METH_FASTCALL
+ #define __Pyx_ArgRef_FASTCALL(args, i) __Pyx_NewRef(args[i])
+ #define __Pyx_NumKwargs_FASTCALL(kwds) __Pyx_PyTuple_GET_SIZE(kwds)
+ #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs))
+ static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+ CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues);
+ #else
+ #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw)
+ #endif
+#else
+ #define __Pyx_ArgRef_FASTCALL __Pyx_ArgRef_VARARGS
+ #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS
+ #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS
+ #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS
+ #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS
+#endif
+#define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#if CYTHON_METH_FASTCALL || (CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(args + start, stop - start)
+#else
+#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop)
+#endif
+
+/* py_dict_items.proto (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d);
+
+/* CallCFunction.proto (used by CallUnboundCMethod0) */
+#define __Pyx_CallCFunction(cfunc, self, args)\
+ ((PyCFunction)(void(*)(void))(cfunc)->func)(self, args)
+#define __Pyx_CallCFunctionWithKeywords(cfunc, self, args, kwargs)\
+ ((PyCFunctionWithKeywords)(void(*)(void))(cfunc)->func)(self, args, kwargs)
+#define __Pyx_CallCFunctionFast(cfunc, self, args, nargs)\
+ ((__Pyx_PyCFunctionFast)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs)
+#define __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, nargs, kwnames)\
+ ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs, kwnames)
+
+/* PyObjectCall.proto (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw);
+#else
+#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw)
+#endif
+
+/* PyObjectCallMethO.proto (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg);
+#endif
+
+/* PyObjectFastCall.proto (used by PyObjectCallOneArg) */
+#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL)
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs);
+
+/* PyObjectCallOneArg.proto (used by CallUnboundCMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg);
+
+/* UnpackUnboundCMethod.proto (used by CallUnboundCMethod0) */
+typedef struct {
+ PyObject *type;
+ PyObject **method_name;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && CYTHON_ATOMICS
+ __pyx_atomic_int_type initialized;
+#endif
+ PyCFunction func;
+ PyObject *method;
+ int flag;
+} __Pyx_CachedCFunction;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+static CYTHON_INLINE int __Pyx_CachedCFunction_GetAndSetInitializing(__Pyx_CachedCFunction *cfunc) {
+#if !CYTHON_ATOMICS
+ return 1;
+#else
+ __pyx_nonatomic_int_type expected = 0;
+ if (__pyx_atomic_int_cmp_exchange(&cfunc->initialized, &expected, 1)) {
+ return 0;
+ }
+ return expected;
+#endif
+}
+static CYTHON_INLINE void __Pyx_CachedCFunction_SetFinishedInitializing(__Pyx_CachedCFunction *cfunc) {
+#if CYTHON_ATOMICS
+ __pyx_atomic_store(&cfunc->initialized, 2);
+#endif
+}
+#else
+#define __Pyx_CachedCFunction_GetAndSetInitializing(cfunc) 2
+#define __Pyx_CachedCFunction_SetFinishedInitializing(cfunc)
+#endif
+
+/* CallUnboundCMethod0.proto */
+CYTHON_UNUSED
+static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self);
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self);
+#else
+#define __Pyx_CallUnboundCMethod0(cfunc, self) __Pyx__CallUnboundCMethod0(cfunc, self)
+#endif
+
+/* py_dict_values.proto (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d);
+
+/* OwnedDictNext.proto (used by ParseKeywordsImpl) */
+#if CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, PyObject **ppos, PyObject **pkey, PyObject **pvalue);
+#else
+CYTHON_INLINE
+static int __Pyx_PyDict_NextRef(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue);
+#endif
+
+/* RaiseDoubleKeywords.proto (used by ParseKeywordsImpl) */
+static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name);
+
+/* ParseKeywordsImpl.export */
+static int __Pyx_ParseKeywordsTuple(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+static int __Pyx_ParseKeywordDictToDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ const char* function_name
+);
+static int __Pyx_ParseKeywordDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+
+/* CallUnboundCMethod2.proto */
+CYTHON_UNUSED
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2);
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2);
+#else
+#define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2)
+#endif
+
+/* ParseKeywords.proto */
+static CYTHON_INLINE int __Pyx_ParseKeywords(
+ PyObject *kwds, PyObject *const *kwvalues, PyObject ** const argnames[],
+ PyObject *kwds2, PyObject *values[],
+ Py_ssize_t num_pos_args, Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs
+);
+
+/* RaiseArgTupleInvalid.proto */
+static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
+ Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found);
+
+/* GetItemInt.proto */
+#define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck, unsafe_shared) :\
+ (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) :\
+ __Pyx_GetItemInt_Generic(o, to_py_func(i))))
+#define __Pyx_GetItemInt_List(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_List_Fast(o, (Py_ssize_t)i, wraparound, boundscheck, unsafe_shared) :\
+ (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL))
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared);
+#define __Pyx_GetItemInt_Tuple(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil, unsafe_shared)\
+ (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\
+ __Pyx_GetItemInt_Tuple_Fast(o, (Py_ssize_t)i, wraparound, boundscheck, unsafe_shared) :\
+ (PyErr_SetString(PyExc_IndexError, "tuple index out of range"), (PyObject*)NULL))
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared);
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j);
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
+ int is_list, int wraparound, int boundscheck, int unsafe_shared);
+
+/* AssertionsEnabled.proto */
+#if CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX >= 0x030C0000
+ static int __pyx_assertions_enabled_flag;
+ #define __pyx_assertions_enabled() (__pyx_assertions_enabled_flag)
+ #if __clang__ || __GNUC__
+ __attribute__((no_sanitize("thread")))
+ #endif
+ static int __Pyx_init_assertions_enabled(void) {
+ PyObject *builtins, *debug, *debug_str;
+ int flag;
+ builtins = PyEval_GetBuiltins();
+ if (!builtins) goto bad;
+ debug_str = PyUnicode_FromStringAndSize("__debug__", 9);
+ if (!debug_str) goto bad;
+ debug = PyObject_GetItem(builtins, debug_str);
+ Py_DECREF(debug_str);
+ if (!debug) goto bad;
+ flag = PyObject_IsTrue(debug);
+ Py_DECREF(debug);
+ if (flag == -1) goto bad;
+ __pyx_assertions_enabled_flag = flag;
+ return 0;
+ bad:
+ __pyx_assertions_enabled_flag = 1;
+ return -1;
+ }
+#else
+ #define __Pyx_init_assertions_enabled() (0)
+ #define __pyx_assertions_enabled() (!Py_OptimizeFlag)
+#endif
+
+/* PyAssertionError_Check.proto */
+#define __Pyx_PyExc_AssertionError_Check(obj) __Pyx_TypeCheck(obj, PyExc_AssertionError)
+
+/* RaiseException.export */
+static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause);
+
+/* py_abs.proto */
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *num);
+#define __Pyx_PyNumber_Absolute(x)\
+ ((likely(PyLong_CheckExact(x))) ?\
+ (likely(__Pyx_PyLong_IsNonNeg(x)) ? __Pyx_NewRef(x) : __Pyx_PyLong_AbsNeg(x)) :\
+ PyNumber_Absolute(x))
+#else
+#define __Pyx_PyNumber_Absolute(x) PyNumber_Absolute(x)
+#endif
+
+/* ListAppend.proto */
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS
+static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) {
+ PyListObject* L = (PyListObject*) list;
+ Py_ssize_t len = Py_SIZE(list);
+ if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) {
+ Py_INCREF(x);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
+ L->ob_item[len] = x;
+ #else
+ PyList_SET_ITEM(list, len, x);
+ #endif
+ __Pyx_SET_SIZE(list, len + 1);
+ return 0;
+ }
+ return PyList_Append(list, x);
+}
+#else
+#define __Pyx_PyList_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* SliceTupleAndList.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop);
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop);
+#else
+#define __Pyx_PyList_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop)
+#define __Pyx_PyTuple_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop)
+#endif
+
+/* ListCompAppend.proto */
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS
+static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) {
+ PyListObject* L = (PyListObject*) list;
+ Py_ssize_t len = Py_SIZE(list);
+ if (likely(L->allocated > len)) {
+ Py_INCREF(x);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000
+ L->ob_item[len] = x;
+ #else
+ PyList_SET_ITEM(list, len, x);
+ #endif
+ __Pyx_SET_SIZE(list, len + 1);
+ return 0;
+ }
+ return PyList_Append(list, x);
+}
+#else
+#define __Pyx_ListComp_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_SubtractCObj(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceSubtract(op1, op2) : PyNumber_Subtract(op1, op2))
+#endif
+
+/* ArgTypeTestFunc.export */
+static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact);
+
+/* ArgTypeTest.proto */
+#define __Pyx_ArgTypeTest(obj, type, none_allowed, name, exact)\
+ ((likely(__Pyx_IS_TYPE(obj, type) | (none_allowed && (obj == Py_None)))) ? 1 :\
+ __Pyx__ArgTypeTest(obj, type, name, exact))
+
+/* RaiseUnboundLocalError.proto */
+static void __Pyx_RaiseUnboundLocalError(const char *varname);
+
+/* GetException.proto (used by pep479) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_GetException(type, value, tb) __Pyx__GetException(__pyx_tstate, type, value, tb)
+static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#else
+static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb);
+#endif
+
+/* pep479.proto */
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen);
+
+/* RaiseTooManyValuesToUnpack.proto */
+static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected);
+
+/* RaiseNeedMoreValuesToUnpack.proto */
+static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
+
+/* IterFinish.proto */
+static CYTHON_INLINE int __Pyx_IterFinish(void);
+
+/* UnpackItemEndCheck.proto */
+static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected);
+
+/* PyDictVersioning.proto (used by GetModuleGlobalName) */
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1)
+#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)\
+ (version_var) = __PYX_GET_DICT_VERSION(dict);\
+ (cache_var) = (value);
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) {\
+ static PY_UINT64_T __pyx_dict_version = 0;\
+ static PyObject *__pyx_dict_cached_value = NULL;\
+ if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) {\
+ (VAR) = __Pyx_XNewRef(__pyx_dict_cached_value);\
+ } else {\
+ (VAR) = __pyx_dict_cached_value = (LOOKUP);\
+ __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT);\
+ }\
+}
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj);
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj);
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version);
+#else
+#define __PYX_GET_DICT_VERSION(dict) (0)
+#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)
+#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP);
+#endif
+
+/* GetModuleGlobalName.proto */
+#if CYTHON_USE_DICT_VERSIONS
+#define __Pyx_GetModuleGlobalName(var, name) do {\
+ static PY_UINT64_T __pyx_dict_version = 0;\
+ static PyObject *__pyx_dict_cached_value = NULL;\
+ (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION(__pyx_mstate_global->__pyx_d))) ?\
+ (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) :\
+ __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\
+} while(0)
+#define __Pyx_GetModuleGlobalNameUncached(var, name) do {\
+ PY_UINT64_T __pyx_dict_version;\
+ PyObject *__pyx_dict_cached_value;\
+ (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\
+} while(0)
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value);
+#else
+#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name)
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name);
+#endif
+
+/* SliceObject.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(
+ PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop,
+ PyObject** py_start, PyObject** py_stop, PyObject** py_slice,
+ int has_cstart, int has_cstop, int wraparound);
+
+/* PyObjectCallNoArg.proto (used by PyObjectCallMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func);
+
+/* PyObjectGetMethod.proto (used by PyObjectCallMethod0) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);
+#endif
+
+/* PyObjectCallMethod0.proto (used by pop) */
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name);
+
+/* pop.proto */
+static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L);
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L);
+#define __Pyx_PyObject_Pop(L) (likely(PyList_CheckExact(L)) ?\
+ __Pyx_PyList_Pop(L) : __Pyx__PyObject_Pop(L))
+#else
+#define __Pyx_PyList_Pop(L) __Pyx__PyObject_Pop(L)
+#define __Pyx_PyObject_Pop(L) __Pyx__PyObject_Pop(L)
+#endif
+
+/* ListExtend.proto */
+static CYTHON_INLINE int __Pyx_PyList_Extend(PyObject* L, PyObject* v) {
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00a2
+ return PyList_Extend(L, v);
+#elif CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000
+ PyObject* none = _PyList_Extend((PyListObject*)L, v);
+ if (unlikely(!none))
+ return -1;
+ Py_DECREF(none);
+ return 0;
+#else
+ return PyList_SetSlice(L, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, v);
+#endif
+}
+
+/* RaiseUnexpectedTypeError.proto */
+static int __Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj);
+
+/* PyLongBinop.proto */
+#if !CYTHON_COMPILING_IN_PYPY
+static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check);
+#else
+#define __Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check)\
+ (inplace ? PyNumber_InPlaceAdd(op1, op2) : PyNumber_Add(op1, op2))
+#endif
+
+/* GetTopmostException.proto (used by SaveResetException) */
+#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE
+static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate);
+#endif
+
+/* SaveResetException.proto */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_ExceptionSave(type, value, tb) __Pyx__ExceptionSave(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#define __Pyx_ExceptionReset(type, value, tb) __Pyx__ExceptionReset(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb);
+#else
+#define __Pyx_ExceptionSave(type, value, tb) PyErr_GetExcInfo(type, value, tb)
+#define __Pyx_ExceptionReset(type, value, tb) PyErr_SetExcInfo(type, value, tb)
+#endif
+
+/* PyZeroDivisionError_Check.proto */
+#define __Pyx_PyExc_ZeroDivisionError_Check(obj) __Pyx_TypeCheck(obj, PyExc_ZeroDivisionError)
+
+/* pyfrozenset_new.proto (used by PySetContains) */
+static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it);
+
+/* PySetContains.proto */
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq);
+
+/* HasAttr.proto (used by ImportImpl) */
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_HasAttr(o, n) PyObject_HasAttrWithError(o, n)
+#else
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *, PyObject *);
+#endif
+
+/* ImportImpl.export */
+static PyObject *__Pyx__Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, PyObject *moddict, int level);
+
+/* Import.proto */
+static CYTHON_INLINE PyObject *__Pyx_Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, int level);
+
+/* ImportFrom.proto */
+static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name);
+
+/* CheckTypeForFreelists.proto */
+#if CYTHON_USE_FREELISTS
+#if CYTHON_USE_TYPE_SPECS
+#define __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, expected_tp, expected_size) ((int) ((t) == (expected_tp)))
+#define __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS Py_TPFLAGS_IS_ABSTRACT
+#else
+#define __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, expected_tp, expected_size) ((int) ((t)->tp_basicsize == (expected_size)))
+#define __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)
+#endif
+#define __PYX_CHECK_TYPE_FOR_FREELISTS(t, expected_tp, expected_size)\
+ (__PYX_CHECK_FINAL_TYPE_FOR_FREELISTS((t), (expected_tp), (expected_size)) &\
+ (int) (!__Pyx_PyType_HasFeature((t), __PYX_CHECK_TYPE_FOR_FREELIST_FLAGS)))
+#endif
+
+/* AllocateExtensionType.proto */
+static PyObject *__Pyx_AllocateExtensionType(PyTypeObject *t, int is_final);
+
+/* CallTypeTraverse.proto */
+#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000)
+#define __Pyx_call_type_traverse(o, always_call, visit, arg) 0
+#else
+static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg);
+#endif
+
+/* LimitedApiGetTypeDict.proto (used by SetItemOnTypeDict) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp);
+#endif
+
+/* SetItemOnTypeDict.proto (used by FixUpExtensionType) */
+static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v);
+#define __Pyx_SetItemOnTypeDict(tp, k, v) __Pyx__SetItemOnTypeDict((PyTypeObject*)tp, k, v)
+
+/* FixUpExtensionType.proto */
+static CYTHON_INLINE int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type);
+
+/* ValidateBasesTuple.proto (used by PyType_Ready) */
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases);
+#endif
+
+/* PyType_Ready.proto */
+CYTHON_UNUSED static int __Pyx_PyType_Ready(PyTypeObject *t);
+
+/* ListPack.proto */
+static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...);
+
+/* dict_setdefault.proto (used by FetchCommonType) */
+static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value);
+
+/* AddModuleRef.proto (used by FetchSharedCythonModule) */
+#if ((CYTHON_COMPILING_IN_CPYTHON_FREETHREADING ) ||\
+ __PYX_LIMITED_VERSION_HEX < 0x030d0000)
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name);
+#else
+ #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name)
+#endif
+
+/* FetchSharedCythonModule.proto (used by FetchCommonType) */
+static PyObject *__Pyx_FetchSharedCythonABIModule(void);
+
+/* FetchCommonType.proto (used by CommonTypesMetaclass) */
+static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases);
+
+/* CommonTypesMetaclass.proto (used by CythonFunctionShared) */
+static int __pyx_CommonTypesMetaclass_init(PyObject *module);
+#define __Pyx_CommonTypesMetaclass_USED
+
+/* PyMethodNew.proto (used by CythonFunctionShared) */
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ);
+
+/* PyVectorcallFastCallDict.proto (used by CythonFunctionShared) */
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw);
+#endif
+
+/* CythonFunctionShared.proto (used by CythonFunction) */
+#define __Pyx_CyFunction_USED
+#define __Pyx_CYFUNCTION_STATICMETHOD 0x01
+#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02
+#define __Pyx_CYFUNCTION_CCLASS 0x04
+#define __Pyx_CYFUNCTION_COROUTINE 0x08
+#define __Pyx_CyFunction_GetClosure(f)\
+ (((__pyx_CyFunctionObject *) (f))->func_closure)
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ #define __Pyx_CyFunction_GetClassObj(f)\
+ (((__pyx_CyFunctionObject *) (f))->func_classobj)
+#else
+ #define __Pyx_CyFunction_GetClassObj(f)\
+ ((PyObject*) ((PyCMethodObject *) (f))->mm_class)
+#endif
+#define __Pyx_CyFunction_SetClassObj(f, classobj)\
+ __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj))
+#define __Pyx_CyFunction_Defaults(type, f)\
+ ((type *)(((__pyx_CyFunctionObject *) (f))->defaults))
+#define __Pyx_CyFunction_SetDefaultsGetter(f, g)\
+ ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g)
+typedef struct {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject_HEAD
+ PyObject *func;
+#elif PY_VERSION_HEX < 0x030900B1
+ PyCFunctionObject func;
+#else
+ PyCMethodObject func;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API && CYTHON_METH_FASTCALL
+ __pyx_vectorcallfunc func_vectorcall;
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_weakreflist;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_dict;
+#endif
+ PyObject *func_name;
+ PyObject *func_qualname;
+ PyObject *func_doc;
+ PyObject *func_globals;
+ PyObject *func_code;
+ PyObject *func_closure;
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *func_classobj;
+#endif
+ PyObject *defaults;
+ int flags;
+ PyObject *defaults_tuple;
+ PyObject *defaults_kwdict;
+ PyObject *(*defaults_getter)(PyObject *);
+ PyObject *func_annotations;
+ PyObject *func_is_coroutine;
+} __pyx_CyFunctionObject;
+#undef __Pyx_CyOrPyCFunction_Check
+#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_mstate_global->__pyx_CyFunctionType)
+#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_mstate_global->__pyx_CyFunctionType, &PyCFunction_Type)
+#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_CyFunctionType)
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void));
+#undef __Pyx_IsSameCFunction
+#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc)
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml,
+ int flags, PyObject* qualname,
+ PyObject *closure,
+ PyObject *module, PyObject *globals,
+ PyObject* code);
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj);
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func,
+ PyTypeObject *defaults_type);
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m,
+ PyObject *tuple);
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m,
+ PyObject *dict);
+static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m,
+ PyObject *dict);
+static int __pyx_CyFunction_init(PyObject *module);
+#if CYTHON_METH_FASTCALL
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall)
+#else
+#define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall)
+#endif
+#endif
+
+/* CythonFunction.proto */
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml,
+ int flags, PyObject* qualname,
+ PyObject *closure,
+ PyObject *module, PyObject *globals,
+ PyObject* code);
+
+/* ObjectGetItem.proto */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key);
+#else
+#define __Pyx_PyObject_GetItem(obj, key) PyObject_GetItem(obj, key)
+#endif
+
+/* CLineInTraceback.proto (used by AddTraceback) */
+#if CYTHON_CLINE_IN_TRACEBACK && CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line);
+#else
+#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0)
+#endif
+
+/* CodeObjectCache.proto (used by AddTraceback) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+typedef PyObject __Pyx_CachedCodeObjectType;
+#else
+typedef PyCodeObject __Pyx_CachedCodeObjectType;
+#endif
+typedef struct {
+ __Pyx_CachedCodeObjectType* code_object;
+ int code_line;
+} __Pyx_CodeObjectCacheEntry;
+struct __Pyx_CodeObjectCache {
+ int count;
+ int max_count;
+ __Pyx_CodeObjectCacheEntry* entries;
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_int_type accessor_count;
+ #endif
+};
+static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line);
+static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line);
+static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object);
+
+/* AddTraceback.proto */
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename);
+
+/* RealImag.proto */
+#if CYTHON_CCOMPLEX
+ #ifdef __cplusplus
+ #define __Pyx_CREAL(z) ((z).real())
+ #define __Pyx_CIMAG(z) ((z).imag())
+ #else
+ #define __Pyx_CREAL(z) (__real__(z))
+ #define __Pyx_CIMAG(z) (__imag__(z))
+ #endif
+#else
+ #define __Pyx_CREAL(z) ((z).real)
+ #define __Pyx_CIMAG(z) ((z).imag)
+#endif
+#if defined(__cplusplus) && CYTHON_CCOMPLEX\
+ && (defined(_WIN32) || defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5 || __GNUC__ == 4 && __GNUC_MINOR__ >= 4 )) || __cplusplus >= 201103)
+ #define __Pyx_SET_CREAL(z,x) ((z).real(x))
+ #define __Pyx_SET_CIMAG(z,y) ((z).imag(y))
+#else
+ #define __Pyx_SET_CREAL(z,x) __Pyx_CREAL(z) = (x)
+ #define __Pyx_SET_CIMAG(z,y) __Pyx_CIMAG(z) = (y)
+#endif
+
+/* Arithmetic.proto */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #define __Pyx_c_eq_double(a, b) ((a)==(b))
+ #define __Pyx_c_sum_double(a, b) ((a)+(b))
+ #define __Pyx_c_diff_double(a, b) ((a)-(b))
+ #define __Pyx_c_prod_double(a, b) ((a)*(b))
+ #define __Pyx_c_quot_double(a, b) ((a)/(b))
+ #define __Pyx_c_neg_double(a) (-(a))
+ #ifdef __cplusplus
+ #define __Pyx_c_is_zero_double(z) ((z)==(double)0)
+ #define __Pyx_c_conj_double(z) (::std::conj(z))
+ #if 1
+ #define __Pyx_c_abs_double(z) (::std::abs(z))
+ #define __Pyx_c_pow_double(a, b) (::std::pow(a, b))
+ #endif
+ #else
+ #define __Pyx_c_is_zero_double(z) ((z)==0)
+ #define __Pyx_c_conj_double(z) (conj(z))
+ #if 1
+ #define __Pyx_c_abs_double(z) (cabs(z))
+ #define __Pyx_c_pow_double(a, b) (cpow(a, b))
+ #endif
+ #endif
+#else
+ static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex);
+ static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex);
+ #if 1
+ static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex);
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex, __pyx_t_double_complex);
+ #endif
+#endif
+
+/* FromPy.proto */
+static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject*);
+
+/* GCCDiagnostics.proto */
+#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define __Pyx_HAS_GCC_DIAGNOSTIC
+#endif
+
+/* ToPy.proto */
+#define __pyx_PyComplex_FromComplex(z)\
+ PyComplex_FromDoubles((double)__Pyx_CREAL(z),\
+ (double)__Pyx_CIMAG(z))
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *);
+
+/* PyObjectVectorCallKwBuilder.proto (used by CIntToPy) */
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+#if CYTHON_VECTORCALL
+#if PY_VERSION_HEX >= 0x03090000
+#define __Pyx_Object_Vectorcall_CallFromBuilder PyObject_Vectorcall
+#else
+#define __Pyx_Object_Vectorcall_CallFromBuilder _PyObject_Vectorcall
+#endif
+#define __Pyx_MakeVectorcallBuilderKwds(n) PyTuple_New(n)
+static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n);
+#else
+#define __Pyx_Object_Vectorcall_CallFromBuilder __Pyx_PyObject_FastCallDict
+#define __Pyx_MakeVectorcallBuilderKwds(n) __Pyx_PyDict_NewPresized(n)
+#define __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n) PyDict_SetItem(builder, key, value)
+#define __Pyx_VectorcallBuilder_AddArgStr(key, value, builder, args, n) PyDict_SetItemString(builder, key, value)
+#endif
+
+/* CIntToPy.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_int(int value);
+
+/* CIntToPy.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value);
+
+/* FormatTypeName.proto */
+#if CYTHON_COMPILING_IN_LIMITED_API
+typedef PyObject *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%U"
+#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj)
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+#define __Pyx_PyType_GetFullyQualifiedName PyType_GetFullyQualifiedName
+#else
+static __Pyx_TypeName __Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp);
+#endif
+#else // !LIMITED_API
+typedef const char *__Pyx_TypeName;
+#define __Pyx_FMT_TYPENAME "%.200s"
+#define __Pyx_PyType_GetFullyQualifiedName(tp) ((tp)->tp_name)
+#define __Pyx_DECREF_TypeName(obj)
+#endif
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *);
+
+/* FastTypeChecks.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2)
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b);
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b);
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type);
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2);
+#else
+#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type)
+#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2))
+#define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type)
+static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2) {
+ return PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2);
+}
+#endif
+#define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_CurrentExceptionType(), err1, err2)
+#define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception)
+#ifdef PyExceptionInstance_Check
+ #define __Pyx_PyBaseException_Check(obj) PyExceptionInstance_Check(obj)
+#else
+ #define __Pyx_PyBaseException_Check(obj) __Pyx_TypeCheck(obj, PyExc_BaseException)
+#endif
+
+/* GetRuntimeVersion.proto */
+#if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+static unsigned long __Pyx_cached_runtime_version = 0;
+static void __Pyx_init_runtime_version(void);
+#else
+#define __Pyx_init_runtime_version()
+#endif
+static unsigned long __Pyx_get_runtime_version(void);
+
+/* SwapException.proto (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_ExceptionSwap(type, value, tb) __Pyx__ExceptionSwap(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#else
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb);
+#endif
+
+/* IterNextPlain.proto (used by CoroutineBase) */
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator);
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void);
+#endif
+
+/* PyObjectCall2Args.proto (used by PyObjectCallMethod1) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2);
+
+/* PyObjectCallMethod1.proto (used by CoroutineBase) */
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg);
+
+/* ReturnWithStopIteration.proto (used by CoroutineBase) */
+static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext);
+
+/* CoroutineBase.proto (used by Generator) */
+struct __pyx_CoroutineObject;
+typedef PyObject *(*__pyx_coroutine_body_t)(struct __pyx_CoroutineObject *, PyThreadState *, PyObject *);
+#if CYTHON_USE_EXC_INFO_STACK
+#define __Pyx_ExcInfoStruct _PyErr_StackItem
+#else
+typedef struct {
+ PyObject *exc_type;
+ PyObject *exc_value;
+ PyObject *exc_traceback;
+} __Pyx_ExcInfoStruct;
+#endif
+typedef struct __pyx_CoroutineObject {
+ PyObject_HEAD
+ __pyx_coroutine_body_t body;
+ PyObject *closure;
+ __Pyx_ExcInfoStruct gi_exc_state;
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *gi_weakreflist;
+#endif
+ PyObject *classobj;
+ PyObject *yieldfrom;
+ __Pyx_pyiter_sendfunc yieldfrom_am_send;
+ PyObject *gi_name;
+ PyObject *gi_qualname;
+ PyObject *gi_modulename;
+ PyObject *gi_code;
+ PyObject *gi_frame;
+#if CYTHON_USE_SYS_MONITORING && (CYTHON_PROFILE || CYTHON_TRACE)
+ PyMonitoringState __pyx_pymonitoring_state[__Pyx_MonitoringEventTypes_CyGen_count];
+ uint64_t __pyx_pymonitoring_version;
+#endif
+ int resume_label;
+ char is_running;
+} __pyx_CoroutineObject;
+static __pyx_CoroutineObject *__Pyx__Coroutine_New(
+ PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name);
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name);
+static CYTHON_INLINE void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *self);
+static int __Pyx_Coroutine_clear(PyObject *self);
+static __Pyx_PySendResult __Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval);
+static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value);
+static __Pyx_PySendResult __Pyx_Coroutine_Close(PyObject *self, PyObject **retval);
+static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args);
+#if CYTHON_USE_EXC_INFO_STACK
+#define __Pyx_Coroutine_SwapException(self)
+#define __Pyx_Coroutine_ResetAndClearException(self) __Pyx_Coroutine_ExceptionClear(&(self)->gi_exc_state)
+#else
+#define __Pyx_Coroutine_SwapException(self) {\
+ __Pyx_ExceptionSwap(&(self)->gi_exc_state.exc_type, &(self)->gi_exc_state.exc_value, &(self)->gi_exc_state.exc_traceback);\
+ __Pyx_Coroutine_ResetFrameBackpointer(&(self)->gi_exc_state);\
+ }
+#define __Pyx_Coroutine_ResetAndClearException(self) {\
+ __Pyx_ExceptionReset((self)->gi_exc_state.exc_type, (self)->gi_exc_state.exc_value, (self)->gi_exc_state.exc_traceback);\
+ (self)->gi_exc_state.exc_type = (self)->gi_exc_state.exc_value = (self)->gi_exc_state.exc_traceback = NULL;\
+ }
+#endif
+#if CYTHON_FAST_THREAD_STATE
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\
+ __Pyx_PyGen__FetchStopIterationValue(__pyx_tstate, pvalue)
+#else
+#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue)
+#endif
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue);
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state);
+static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen);
+static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen);
+static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen);
+static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure);
+#if __PYX_HAS_PY_AM_SEND == 2
+static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send);
+#endif
+static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg);
+
+/* Generator.proto */
+#define __Pyx_Generator_USED
+#define __Pyx_Generator_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_GeneratorType)
+#define __Pyx_Generator_New(body, code, closure, name, qualname, module_name)\
+ __Pyx__Coroutine_New(__pyx_mstate_global->__pyx_GeneratorType, body, code, closure, name, qualname, module_name)
+static PyObject *__Pyx_Generator_Next(PyObject *self);
+static int __pyx_Generator_init(PyObject *module);
+static CYTHON_INLINE PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self);
+
+/* CheckBinaryVersion.proto */
+static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer);
+
+/* DecompressString.proto */
+static PyObject *__Pyx_DecompressString(const char *s, Py_ssize_t length, int algo);
+
+/* MultiPhaseInitModuleState.proto */
+#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE
+static PyObject *__Pyx_State_FindModule(void*);
+static int __Pyx_State_AddModule(PyObject* module, void*);
+static int __Pyx_State_RemoveModule(void*);
+#elif CYTHON_USE_MODULE_STATE
+#define __Pyx_State_FindModule PyState_FindModule
+#define __Pyx_State_AddModule PyState_AddModule
+#define __Pyx_State_RemoveModule PyState_RemoveModule
+#endif
+
+/* #### Code section: module_declarations ### */
+/* CythonABIVersion.proto */
+#if CYTHON_COMPILING_IN_LIMITED_API
+ #if CYTHON_METH_FASTCALL
+ #define __PYX_FASTCALL_ABI_SUFFIX "_fastcall"
+ #else
+ #define __PYX_FASTCALL_ABI_SUFFIX
+ #endif
+ #define __PYX_LIMITED_ABI_SUFFIX "limited" __PYX_FASTCALL_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX
+#else
+ #define __PYX_LIMITED_ABI_SUFFIX
+#endif
+#if __PYX_HAS_PY_AM_SEND == 1
+ #define __PYX_AM_SEND_ABI_SUFFIX
+#elif __PYX_HAS_PY_AM_SEND == 2
+ #define __PYX_AM_SEND_ABI_SUFFIX "amsendbackport"
+#else
+ #define __PYX_AM_SEND_ABI_SUFFIX "noamsend"
+#endif
+#ifndef __PYX_MONITORING_ABI_SUFFIX
+ #define __PYX_MONITORING_ABI_SUFFIX
+#endif
+#if CYTHON_USE_TP_FINALIZE
+ #define __PYX_TP_FINALIZE_ABI_SUFFIX
+#else
+ #define __PYX_TP_FINALIZE_ABI_SUFFIX "nofinalize"
+#endif
+#if CYTHON_USE_FREELISTS || !defined(__Pyx_AsyncGen_USED)
+ #define __PYX_FREELISTS_ABI_SUFFIX
+#else
+ #define __PYX_FREELISTS_ABI_SUFFIX "nofreelists"
+#endif
+#define CYTHON_ABI __PYX_ABI_VERSION __PYX_LIMITED_ABI_SUFFIX __PYX_MONITORING_ABI_SUFFIX __PYX_TP_FINALIZE_ABI_SUFFIX __PYX_FREELISTS_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX
+#define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI
+#define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "."
+
+
+/* Module declarations from "cython" */
+
+/* Module declarations from "fontTools.qu2cu.qu2cu" */
+static int __pyx_f_9fontTools_5qu2cu_5qu2cu_cubic_farthest_fit_inside(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, double); /*proto*/
+static PyObject *__pyx_f_9fontTools_5qu2cu_5qu2cu_merge_curves(PyObject *, int, int); /*proto*/
+/* #### Code section: typeinfo ### */
+/* #### Code section: before_global_var ### */
+#define __Pyx_MODULE_NAME "fontTools.qu2cu.qu2cu"
+extern int __pyx_module_is_main_fontTools__qu2cu__qu2cu;
+int __pyx_module_is_main_fontTools__qu2cu__qu2cu = 0;
+
+/* Implementation of "fontTools.qu2cu.qu2cu" */
+/* #### Code section: global_var ### */
+static PyObject *__pyx_builtin_enumerate;
+static PyObject *__pyx_builtin_reversed;
+static PyObject *__pyx_builtin_zip;
+static PyObject *__pyx_builtin_print;
+/* #### Code section: string_decls ### */
+/* #### Code section: decls ### */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_elevate_quadratic(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_2add_implicit_on_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_p); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_4quadratic_to_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_quads, double __pyx_v_max_err, int __pyx_v_all_cubic); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_6spline_to_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_q, PyObject *__pyx_v_costs, double __pyx_v_tolerance, int __pyx_v_all_cubic); /* proto */
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_8main(CYTHON_UNUSED PyObject *__pyx_self); /* proto */
+static PyObject *__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+/* #### Code section: late_includes ### */
+/* #### Code section: module_state ### */
+/* SmallCodeConfig */
+#ifndef CYTHON_SMALL_CODE
+#if defined(__clang__)
+ #define CYTHON_SMALL_CODE
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define CYTHON_SMALL_CODE __attribute__((cold))
+#else
+ #define CYTHON_SMALL_CODE
+#endif
+#endif
+
+typedef struct {
+ PyObject *__pyx_d;
+ PyObject *__pyx_b;
+ PyObject *__pyx_cython_runtime;
+ PyObject *__pyx_empty_tuple;
+ PyObject *__pyx_empty_bytes;
+ PyObject *__pyx_empty_unicode;
+ PyObject *__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr;
+ PyObject *__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr;
+ PyTypeObject *__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_items;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_pop;
+ __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_values;
+ __Pyx_CachedCFunction __pyx_umethod_PyList_Type_pop;
+ PyObject *__pyx_slice[1];
+ PyObject *__pyx_tuple[1];
+ PyObject *__pyx_codeobj_tab[7];
+ PyObject *__pyx_string_tab[137];
+ PyObject *__pyx_number_tab[3];
+/* #### Code section: module_state_contents ### */
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr[8];
+int __pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr;
+#endif
+
+#if CYTHON_USE_FREELISTS
+struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr[8];
+int __pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr;
+#endif
+/* CommonTypesMetaclass.module_state_decls */
+PyTypeObject *__pyx_CommonTypesMetaclassType;
+
+/* CachedMethodType.module_state_decls */
+#if CYTHON_COMPILING_IN_LIMITED_API
+PyObject *__Pyx_CachedMethodType;
+#endif
+
+/* CythonFunctionShared.module_state_decls */
+PyTypeObject *__pyx_CyFunctionType;
+
+/* CodeObjectCache.module_state_decls */
+struct __Pyx_CodeObjectCache __pyx_code_cache;
+
+/* IterNextPlain.module_state_decls */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+PyObject *__Pyx_GetBuiltinNext_LimitedAPI_cache;
+#endif
+
+/* Generator.module_state_decls */
+PyTypeObject *__pyx_GeneratorType;
+
+/* #### Code section: module_state_end ### */
+} __pyx_mstatetype;
+
+#if CYTHON_USE_MODULE_STATE
+#ifdef __cplusplus
+namespace {
+extern struct PyModuleDef __pyx_moduledef;
+} /* anonymous namespace */
+#else
+static struct PyModuleDef __pyx_moduledef;
+#endif
+
+#define __pyx_mstate_global (__Pyx_PyModule_GetState(__Pyx_State_FindModule(&__pyx_moduledef)))
+
+#define __pyx_m (__Pyx_State_FindModule(&__pyx_moduledef))
+#else
+static __pyx_mstatetype __pyx_mstate_global_static =
+#ifdef __cplusplus
+ {};
+#else
+ {0};
+#endif
+static __pyx_mstatetype * const __pyx_mstate_global = &__pyx_mstate_global_static;
+#endif
+/* #### Code section: constant_name_defines ### */
+#define __pyx_kp_u_ __pyx_string_tab[0]
+#define __pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py __pyx_string_tab[1]
+#define __pyx_kp_u_List_List_Point __pyx_string_tab[2]
+#define __pyx_kp_u_List_Tuple_Point __pyx_string_tab[3]
+#define __pyx_kp_u_Note_that_Cython_is_deliberately __pyx_string_tab[4]
+#define __pyx_kp_u_One_random_cubic_turned_into_d_q __pyx_string_tab[5]
+#define __pyx_kp_u_Original_curve __pyx_string_tab[6]
+#define __pyx_kp_u_Reconstructed_curve_s __pyx_string_tab[7]
+#define __pyx_kp_u_Those_quadratics_turned_back_int __pyx_string_tab[8]
+#define __pyx_kp_u__2 __pyx_string_tab[9]
+#define __pyx_kp_u_add_note __pyx_string_tab[10]
+#define __pyx_kp_u_cu2qu_tolerance_g_qu2cu_toleranc __pyx_string_tab[11]
+#define __pyx_kp_u_disable __pyx_string_tab[12]
+#define __pyx_kp_u_enable __pyx_string_tab[13]
+#define __pyx_kp_u_gc __pyx_string_tab[14]
+#define __pyx_kp_u_isenabled __pyx_string_tab[15]
+#define __pyx_kp_u_quadratic_spline_requires_at_lea __pyx_string_tab[16]
+#define __pyx_n_u_COMPILED __pyx_string_tab[17]
+#define __pyx_n_u_List __pyx_string_tab[18]
+#define __pyx_n_u_Point __pyx_string_tab[19]
+#define __pyx_n_u_Pyx_PyDict_NextRef __pyx_string_tab[20]
+#define __pyx_n_u_Solution __pyx_string_tab[21]
+#define __pyx_n_u_Tuple __pyx_string_tab[22]
+#define __pyx_n_u_Union __pyx_string_tab[23]
+#define __pyx_n_u_add_implicit_on_curves __pyx_string_tab[24]
+#define __pyx_n_u_all __pyx_string_tab[25]
+#define __pyx_n_u_all_cubic __pyx_string_tab[26]
+#define __pyx_n_u_asyncio_coroutines __pyx_string_tab[27]
+#define __pyx_n_u_best_sol __pyx_string_tab[28]
+#define __pyx_n_u_bool __pyx_string_tab[29]
+#define __pyx_n_u_c __pyx_string_tab[30]
+#define __pyx_n_u_class_getitem __pyx_string_tab[31]
+#define __pyx_n_u_cline_in_traceback __pyx_string_tab[32]
+#define __pyx_n_u_close __pyx_string_tab[33]
+#define __pyx_n_u_collections __pyx_string_tab[34]
+#define __pyx_n_u_cost __pyx_string_tab[35]
+#define __pyx_n_u_costs __pyx_string_tab[36]
+#define __pyx_n_u_count __pyx_string_tab[37]
+#define __pyx_n_u_cubic __pyx_string_tab[38]
+#define __pyx_n_u_curve __pyx_string_tab[39]
+#define __pyx_n_u_curve_to_quadratic __pyx_string_tab[40]
+#define __pyx_n_u_curves __pyx_string_tab[41]
+#define __pyx_n_u_elevate_quadratic __pyx_string_tab[42]
+#define __pyx_n_u_elevated_quadratics __pyx_string_tab[43]
+#define __pyx_n_u_enumerate __pyx_string_tab[44]
+#define __pyx_n_u_err __pyx_string_tab[45]
+#define __pyx_n_u_error __pyx_string_tab[46]
+#define __pyx_n_u_float __pyx_string_tab[47]
+#define __pyx_n_u_fontTools_cu2qu __pyx_string_tab[48]
+#define __pyx_n_u_fontTools_cu2qu_benchmark __pyx_string_tab[49]
+#define __pyx_n_u_fontTools_misc_bezierTools __pyx_string_tab[50]
+#define __pyx_n_u_fontTools_qu2cu_qu2cu __pyx_string_tab[51]
+#define __pyx_n_u_forced __pyx_string_tab[52]
+#define __pyx_n_u_func __pyx_string_tab[53]
+#define __pyx_n_u_generate_curve __pyx_string_tab[54]
+#define __pyx_n_u_genexpr __pyx_string_tab[55]
+#define __pyx_n_u_i __pyx_string_tab[56]
+#define __pyx_n_u_i_sol __pyx_string_tab[57]
+#define __pyx_n_u_i_sol_count __pyx_string_tab[58]
+#define __pyx_n_u_i_sol_error __pyx_string_tab[59]
+#define __pyx_n_u_imag __pyx_string_tab[60]
+#define __pyx_n_u_impossible __pyx_string_tab[61]
+#define __pyx_n_u_is_complex __pyx_string_tab[62]
+#define __pyx_n_u_is_coroutine __pyx_string_tab[63]
+#define __pyx_n_u_is_cubic __pyx_string_tab[64]
+#define __pyx_n_u_items __pyx_string_tab[65]
+#define __pyx_n_u_j __pyx_string_tab[66]
+#define __pyx_n_u_j_sol_count __pyx_string_tab[67]
+#define __pyx_n_u_j_sol_error __pyx_string_tab[68]
+#define __pyx_n_u_k __pyx_string_tab[69]
+#define __pyx_n_u_main __pyx_string_tab[70]
+#define __pyx_n_u_main_2 __pyx_string_tab[71]
+#define __pyx_n_u_math __pyx_string_tab[72]
+#define __pyx_n_u_max_err __pyx_string_tab[73]
+#define __pyx_n_u_module __pyx_string_tab[74]
+#define __pyx_n_u_name __pyx_string_tab[75]
+#define __pyx_n_u_namedtuple __pyx_string_tab[76]
+#define __pyx_n_u_next __pyx_string_tab[77]
+#define __pyx_n_u_num_offcurves __pyx_string_tab[78]
+#define __pyx_n_u_num_points __pyx_string_tab[79]
+#define __pyx_n_u_off1 __pyx_string_tab[80]
+#define __pyx_n_u_off2 __pyx_string_tab[81]
+#define __pyx_n_u_on __pyx_string_tab[82]
+#define __pyx_n_u_orig __pyx_string_tab[83]
+#define __pyx_n_u_p __pyx_string_tab[84]
+#define __pyx_n_u_p0 __pyx_string_tab[85]
+#define __pyx_n_u_p1 __pyx_string_tab[86]
+#define __pyx_n_u_p1_2_3 __pyx_string_tab[87]
+#define __pyx_n_u_p2 __pyx_string_tab[88]
+#define __pyx_n_u_p3 __pyx_string_tab[89]
+#define __pyx_n_u_pop __pyx_string_tab[90]
+#define __pyx_n_u_print __pyx_string_tab[91]
+#define __pyx_n_u_q __pyx_string_tab[92]
+#define __pyx_n_u_qq __pyx_string_tab[93]
+#define __pyx_n_u_quadratic_to_curves __pyx_string_tab[94]
+#define __pyx_n_u_quadratic_to_curves_locals_genex __pyx_string_tab[95]
+#define __pyx_n_u_quadratics __pyx_string_tab[96]
+#define __pyx_n_u_quads __pyx_string_tab[97]
+#define __pyx_n_u_qualname __pyx_string_tab[98]
+#define __pyx_n_u_real __pyx_string_tab[99]
+#define __pyx_n_u_reconst __pyx_string_tab[100]
+#define __pyx_n_u_reconstruct_tolerance __pyx_string_tab[101]
+#define __pyx_n_u_reconstructed __pyx_string_tab[102]
+#define __pyx_n_u_reconstructed_iter __pyx_string_tab[103]
+#define __pyx_n_u_return __pyx_string_tab[104]
+#define __pyx_n_u_reversed __pyx_string_tab[105]
+#define __pyx_n_u_send __pyx_string_tab[106]
+#define __pyx_n_u_set_name __pyx_string_tab[107]
+#define __pyx_n_u_setdefault __pyx_string_tab[108]
+#define __pyx_n_u_sols __pyx_string_tab[109]
+#define __pyx_n_u_spline_to_curves __pyx_string_tab[110]
+#define __pyx_n_u_spline_to_curves_locals_genexpr __pyx_string_tab[111]
+#define __pyx_n_u_splitCubicAtTC __pyx_string_tab[112]
+#define __pyx_n_u_splits __pyx_string_tab[113]
+#define __pyx_n_u_start __pyx_string_tab[114]
+#define __pyx_n_u_start_index __pyx_string_tab[115]
+#define __pyx_n_u_test __pyx_string_tab[116]
+#define __pyx_n_u_this_count __pyx_string_tab[117]
+#define __pyx_n_u_this_sol_count __pyx_string_tab[118]
+#define __pyx_n_u_throw __pyx_string_tab[119]
+#define __pyx_n_u_tolerance __pyx_string_tab[120]
+#define __pyx_n_u_ts __pyx_string_tab[121]
+#define __pyx_n_u_typing __pyx_string_tab[122]
+#define __pyx_n_u_u __pyx_string_tab[123]
+#define __pyx_n_u_v __pyx_string_tab[124]
+#define __pyx_n_u_value __pyx_string_tab[125]
+#define __pyx_n_u_values __pyx_string_tab[126]
+#define __pyx_n_u_x __pyx_string_tab[127]
+#define __pyx_n_u_y __pyx_string_tab[128]
+#define __pyx_n_u_zip __pyx_string_tab[129]
+#define __pyx_kp_b_iso88591_AQ_A_Cq_2Q_U_3a_q_q_2Q_U_U_F_A __pyx_string_tab[130]
+#define __pyx_kp_b_iso88591_Jb_N_1G1_2_8_Qa_q_Cq_Q_Q __pyx_string_tab[131]
+#define __pyx_kp_b_iso88591_Q_q_6_Qe1Bat3a_t1_S_S_c_U_ar_AQ __pyx_string_tab[132]
+#define __pyx_kp_b_iso88591_S_2Rq_Cr_3b_Cr_3b __pyx_string_tab[133]
+#define __pyx_kp_b_iso88591__3 __pyx_string_tab[134]
+#define __pyx_kp_b_iso88591__4 __pyx_string_tab[135]
+#define __pyx_kp_b_iso88591_a_3as_S_1AT_2T_U_q_3as_Cq_U_3c __pyx_string_tab[136]
+#define __pyx_int_0 __pyx_number_tab[0]
+#define __pyx_int_1 __pyx_number_tab[1]
+#define __pyx_int_3 __pyx_number_tab[2]
+/* #### Code section: module_state_clear ### */
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __pyx_m_clear(PyObject *m) {
+ __pyx_mstatetype *clear_module_state = __Pyx_PyModule_GetState(m);
+ if (!clear_module_state) return 0;
+ Py_CLEAR(clear_module_state->__pyx_d);
+ Py_CLEAR(clear_module_state->__pyx_b);
+ Py_CLEAR(clear_module_state->__pyx_cython_runtime);
+ Py_CLEAR(clear_module_state->__pyx_empty_tuple);
+ Py_CLEAR(clear_module_state->__pyx_empty_bytes);
+ Py_CLEAR(clear_module_state->__pyx_empty_unicode);
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __Pyx_State_RemoveModule(NULL);
+ #endif
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr);
+ Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr);
+ Py_CLEAR(clear_module_state->__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr);
+ for (int i=0; i<1; ++i) { Py_CLEAR(clear_module_state->__pyx_slice[i]); }
+ for (int i=0; i<1; ++i) { Py_CLEAR(clear_module_state->__pyx_tuple[i]); }
+ for (int i=0; i<7; ++i) { Py_CLEAR(clear_module_state->__pyx_codeobj_tab[i]); }
+ for (int i=0; i<137; ++i) { Py_CLEAR(clear_module_state->__pyx_string_tab[i]); }
+ for (int i=0; i<3; ++i) { Py_CLEAR(clear_module_state->__pyx_number_tab[i]); }
+/* #### Code section: module_state_clear_contents ### */
+/* CommonTypesMetaclass.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_CommonTypesMetaclassType);
+
+/* CythonFunctionShared.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_CyFunctionType);
+
+/* Generator.module_state_clear */
+Py_CLEAR(clear_module_state->__pyx_GeneratorType);
+
+/* #### Code section: module_state_clear_end ### */
+return 0;
+}
+#endif
+/* #### Code section: module_state_traverse ### */
+#if CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) {
+ __pyx_mstatetype *traverse_module_state = __Pyx_PyModule_GetState(m);
+ if (!traverse_module_state) return 0;
+ Py_VISIT(traverse_module_state->__pyx_d);
+ Py_VISIT(traverse_module_state->__pyx_b);
+ Py_VISIT(traverse_module_state->__pyx_cython_runtime);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_tuple);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_bytes);
+ __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_unicode);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr);
+ Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr);
+ Py_VISIT(traverse_module_state->__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr);
+ for (int i=0; i<1; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_slice[i]); }
+ for (int i=0; i<1; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_tuple[i]); }
+ for (int i=0; i<7; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_codeobj_tab[i]); }
+ for (int i=0; i<137; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_string_tab[i]); }
+ for (int i=0; i<3; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_number_tab[i]); }
+/* #### Code section: module_state_traverse_contents ### */
+/* CommonTypesMetaclass.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_CommonTypesMetaclassType);
+
+/* CythonFunctionShared.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_CyFunctionType);
+
+/* Generator.module_state_traverse */
+Py_VISIT(traverse_module_state->__pyx_GeneratorType);
+
+/* #### Code section: module_state_traverse_end ### */
+return 0;
+}
+#endif
+/* #### Code section: module_code ### */
+
+/* "fontTools/qu2cu/qu2cu.py":40
+ *
+ * # Copied from cu2qu
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.returns(cython.int)
+ * @cython.locals(
+*/
+
+static int __pyx_f_9fontTools_5qu2cu_5qu2cu_cubic_farthest_fit_inside(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, double __pyx_v_tolerance) {
+ __pyx_t_double_complex __pyx_v_mid;
+ __pyx_t_double_complex __pyx_v_deriv3;
+ int __pyx_r;
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ int __pyx_t_4;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":69
+ * """
+ * # First check p2 then p1, as p2 has higher error early on.
+ * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<<
+ * return True
+ *
+*/
+ __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p2) <= __pyx_v_tolerance);
+ if (__pyx_t_2) {
+ } else {
+ __pyx_t_1 = __pyx_t_2;
+ goto __pyx_L4_bool_binop_done;
+ }
+ __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p1) <= __pyx_v_tolerance);
+ __pyx_t_1 = __pyx_t_2;
+ __pyx_L4_bool_binop_done:;
+ if (__pyx_t_1) {
+
+ /* "fontTools/qu2cu/qu2cu.py":70
+ * # First check p2 then p1, as p2 has higher error early on.
+ * if abs(p2) <= tolerance and abs(p1) <= tolerance:
+ * return True # <<<<<<<<<<<<<<
+ *
+ * # Split.
+*/
+ __pyx_r = 1;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":69
+ * """
+ * # First check p2 then p1, as p2 has higher error early on.
+ * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<<
+ * return True
+ *
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":73
+ *
+ * # Split.
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<<
+ * if abs(mid) > tolerance:
+ * return False
+*/
+ __pyx_v_mid = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__pyx_v_p1, __pyx_v_p2))), __pyx_v_p3), __pyx_t_double_complex_from_parts(0.125, 0));
+
+ /* "fontTools/qu2cu/qu2cu.py":74
+ * # Split.
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * if abs(mid) > tolerance: # <<<<<<<<<<<<<<
+ * return False
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+*/
+ __pyx_t_1 = (__Pyx_c_abs_double(__pyx_v_mid) > __pyx_v_tolerance);
+ if (__pyx_t_1) {
+
+ /* "fontTools/qu2cu/qu2cu.py":75
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * if abs(mid) > tolerance:
+ * return False # <<<<<<<<<<<<<<
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return cubic_farthest_fit_inside(
+*/
+ __pyx_r = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":74
+ * # Split.
+ * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ * if abs(mid) > tolerance: # <<<<<<<<<<<<<<
+ * return False
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":76
+ * if abs(mid) > tolerance:
+ * return False
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<<
+ * return cubic_farthest_fit_inside(
+ * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
+*/
+ __pyx_v_deriv3 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __pyx_v_p2), __pyx_v_p1), __pyx_v_p0), __pyx_t_double_complex_from_parts(0.125, 0));
+
+ /* "fontTools/qu2cu/qu2cu.py":77
+ * return False
+ * deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ * return cubic_farthest_fit_inside( # <<<<<<<<<<<<<<
+ * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
+ * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance)
+*/
+ __pyx_t_4 = __pyx_f_9fontTools_5qu2cu_5qu2cu_cubic_farthest_fit_inside(__pyx_v_p0, __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p0, __pyx_v_p1), __pyx_t_double_complex_from_parts(0.5, 0)), __Pyx_c_diff_double(__pyx_v_mid, __pyx_v_deriv3), __pyx_v_mid, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 77, __pyx_L1_error)
+ if (__pyx_t_4) {
+ } else {
+ __pyx_t_3 = __pyx_t_4;
+ goto __pyx_L7_bool_binop_done;
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":79
+ * return cubic_farthest_fit_inside(
+ * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
+ * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_4 = __pyx_f_9fontTools_5qu2cu_5qu2cu_cubic_farthest_fit_inside(__pyx_v_mid, __Pyx_c_sum_double(__pyx_v_mid, __pyx_v_deriv3), __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(0.5, 0)), __pyx_v_p3, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 79, __pyx_L1_error)
+ __pyx_t_3 = __pyx_t_4;
+ __pyx_L7_bool_binop_done:;
+ __pyx_r = __pyx_t_3;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":40
+ *
+ * # Copied from cu2qu
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.returns(cython.int)
+ * @cython.locals(
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.cubic_farthest_fit_inside", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":82
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * p0=cython.complex,
+ * p1=cython.complex,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_1elevate_quadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_5qu2cu_5qu2cu_elevate_quadratic, "elevate_quadratic(double complex p0, double complex p1, double complex p2)\n\nGiven a quadratic bezier curve, return its degree-elevated cubic.");
+static PyMethodDef __pyx_mdef_9fontTools_5qu2cu_5qu2cu_1elevate_quadratic = {"elevate_quadratic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5qu2cu_5qu2cu_1elevate_quadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5qu2cu_5qu2cu_elevate_quadratic};
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_1elevate_quadratic(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ __pyx_t_double_complex __pyx_v_p0;
+ __pyx_t_double_complex __pyx_v_p1;
+ __pyx_t_double_complex __pyx_v_p2;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("elevate_quadratic (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_p0,&__pyx_mstate_global->__pyx_n_u_p1,&__pyx_mstate_global->__pyx_n_u_p2,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 82, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 82, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 82, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 82, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "elevate_quadratic", 0) < (0)) __PYX_ERR(0, 82, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 3; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("elevate_quadratic", 1, 3, 3, i); __PYX_ERR(0, 82, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 3)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 82, __pyx_L3_error)
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 82, __pyx_L3_error)
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 82, __pyx_L3_error)
+ }
+ __pyx_v_p0 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 88, __pyx_L3_error)
+ __pyx_v_p1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 88, __pyx_L3_error)
+ __pyx_v_p2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 88, __pyx_L3_error)
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("elevate_quadratic", 1, 3, 3, __pyx_nargs); __PYX_ERR(0, 82, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.elevate_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_5qu2cu_5qu2cu_elevate_quadratic(__pyx_self, __pyx_v_p0, __pyx_v_p1, __pyx_v_p2);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_elevate_quadratic(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2) {
+ __pyx_t_double_complex __pyx_v_p1_2_3;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __pyx_t_double_complex __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("elevate_quadratic", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":92
+ *
+ * # https://pomax.github.io/bezierinfo/#reordering
+ * p1_2_3 = p1 * (2 / 3) # <<<<<<<<<<<<<<
+ * return (
+ * p0,
+*/
+ __pyx_v_p1_2_3 = __Pyx_c_prod_double(__pyx_v_p1, __pyx_t_double_complex_from_parts((2.0 / 3.0), 0));
+
+ /* "fontTools/qu2cu/qu2cu.py":93
+ * # https://pomax.github.io/bezierinfo/#reordering
+ * p1_2_3 = p1 * (2 / 3)
+ * return ( # <<<<<<<<<<<<<<
+ * p0,
+ * (p0 * (1 / 3) + p1_2_3),
+*/
+ __Pyx_XDECREF(__pyx_r);
+
+ /* "fontTools/qu2cu/qu2cu.py":94
+ * p1_2_3 = p1 * (2 / 3)
+ * return (
+ * p0, # <<<<<<<<<<<<<<
+ * (p0 * (1 / 3) + p1_2_3),
+ * (p2 * (1 / 3) + p1_2_3),
+*/
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 94, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+
+ /* "fontTools/qu2cu/qu2cu.py":95
+ * return (
+ * p0,
+ * (p0 * (1 / 3) + p1_2_3), # <<<<<<<<<<<<<<
+ * (p2 * (1 / 3) + p1_2_3),
+ * p2,
+*/
+ __pyx_t_2 = __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_v_p0, __pyx_t_double_complex_from_parts((1.0 / 3.0), 0)), __pyx_v_p1_2_3);
+ __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 95, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+
+ /* "fontTools/qu2cu/qu2cu.py":96
+ * p0,
+ * (p0 * (1 / 3) + p1_2_3),
+ * (p2 * (1 / 3) + p1_2_3), # <<<<<<<<<<<<<<
+ * p2,
+ * )
+*/
+ __pyx_t_2 = __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_v_p2, __pyx_t_double_complex_from_parts((1.0 / 3.0), 0)), __pyx_v_p1_2_3);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 96, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+
+ /* "fontTools/qu2cu/qu2cu.py":97
+ * (p0 * (1 / 3) + p1_2_3),
+ * (p2 * (1 / 3) + p1_2_3),
+ * p2, # <<<<<<<<<<<<<<
+ * )
+ *
+*/
+ __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 97, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+
+ /* "fontTools/qu2cu/qu2cu.py":94
+ * p1_2_3 = p1 * (2 / 3)
+ * return (
+ * p0, # <<<<<<<<<<<<<<
+ * (p0 * (1 / 3) + p1_2_3),
+ * (p2 * (1 / 3) + p1_2_3),
+*/
+ __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 94, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 94, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 94, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_4) != (0)) __PYX_ERR(0, 94, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5) != (0)) __PYX_ERR(0, 94, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":82
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * p0=cython.complex,
+ * p1=cython.complex,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.elevate_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":101
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * start=cython.int,
+*/
+
+static PyObject *__pyx_f_9fontTools_5qu2cu_5qu2cu_merge_curves(PyObject *__pyx_v_curves, int __pyx_v_start, int __pyx_v_n) {
+ int __pyx_v_k;
+ double __pyx_v_prod_ratio;
+ double __pyx_v_sum_ratio;
+ double __pyx_v_ratio;
+ __pyx_t_double_complex __pyx_v_p0;
+ __pyx_t_double_complex __pyx_v_p1;
+ __pyx_t_double_complex __pyx_v_p2;
+ __pyx_t_double_complex __pyx_v_p3;
+ PyObject *__pyx_v_ts = NULL;
+ PyObject *__pyx_v_ck = NULL;
+ PyObject *__pyx_v_c_before = NULL;
+ PyObject *__pyx_v_curve = NULL;
+ double __pyx_7genexpr__pyx_v_t;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ long __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_t_9;
+ PyObject *__pyx_t_10 = NULL;
+ double __pyx_t_11;
+ int __pyx_t_12;
+ Py_ssize_t __pyx_t_13;
+ __pyx_t_double_complex __pyx_t_14;
+ PyObject *__pyx_t_15 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("merge_curves", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":121
+ *
+ * # Reconstruct the t values of the cut segments
+ * prod_ratio = 1.0 # <<<<<<<<<<<<<<
+ * sum_ratio = 1.0
+ * ts = [1]
+*/
+ __pyx_v_prod_ratio = 1.0;
+
+ /* "fontTools/qu2cu/qu2cu.py":122
+ * # Reconstruct the t values of the cut segments
+ * prod_ratio = 1.0
+ * sum_ratio = 1.0 # <<<<<<<<<<<<<<
+ * ts = [1]
+ * for k in range(1, n):
+*/
+ __pyx_v_sum_ratio = 1.0;
+
+ /* "fontTools/qu2cu/qu2cu.py":123
+ * prod_ratio = 1.0
+ * sum_ratio = 1.0
+ * ts = [1] # <<<<<<<<<<<<<<
+ * for k in range(1, n):
+ * ck = curves[start + k]
+*/
+ __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 123, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_1);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_int_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_1, 0, __pyx_mstate_global->__pyx_int_1) != (0)) __PYX_ERR(0, 123, __pyx_L1_error);
+ __pyx_v_ts = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":124
+ * sum_ratio = 1.0
+ * ts = [1]
+ * for k in range(1, n): # <<<<<<<<<<<<<<
+ * ck = curves[start + k]
+ * c_before = curves[start + k - 1]
+*/
+ __pyx_t_2 = __pyx_v_n;
+ __pyx_t_3 = __pyx_t_2;
+ for (__pyx_t_4 = 1; __pyx_t_4 < __pyx_t_3; __pyx_t_4+=1) {
+ __pyx_v_k = __pyx_t_4;
+
+ /* "fontTools/qu2cu/qu2cu.py":125
+ * ts = [1]
+ * for k in range(1, n):
+ * ck = curves[start + k] # <<<<<<<<<<<<<<
+ * c_before = curves[start + k - 1]
+ *
+*/
+ __pyx_t_5 = (__pyx_v_start + __pyx_v_k);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_t_5, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 125, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_ck, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":126
+ * for k in range(1, n):
+ * ck = curves[start + k]
+ * c_before = curves[start + k - 1] # <<<<<<<<<<<<<<
+ *
+ * # |t_(k+1) - t_k| / |t_k - t_(k - 1)| = ratio
+*/
+ __pyx_t_6 = ((__pyx_v_start + __pyx_v_k) - 1);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_t_6, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_c_before, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":129
+ *
+ * # |t_(k+1) - t_k| / |t_k - t_(k - 1)| = ratio
+ * assert ck[0] == c_before[3] # <<<<<<<<<<<<<<
+ * ratio = abs(ck[1] - ck[0]) / abs(c_before[3] - c_before[2])
+ *
+*/
+ #ifndef CYTHON_WITHOUT_ASSERTIONS
+ if (unlikely(__pyx_assertions_enabled())) {
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_ck, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 129, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_c_before, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 129, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = PyObject_RichCompare(__pyx_t_1, __pyx_t_7, Py_EQ); __Pyx_XGOTREF(__pyx_t_8); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 129, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_8); if (unlikely((__pyx_t_9 < 0))) __PYX_ERR(0, 129, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_9)) {
+ __Pyx_Raise(((PyObject *)(((PyTypeObject*)PyExc_AssertionError))), 0, 0, 0);
+ __PYX_ERR(0, 129, __pyx_L1_error)
+ }
+ }
+ #else
+ if ((1)); else __PYX_ERR(0, 129, __pyx_L1_error)
+ #endif
+
+ /* "fontTools/qu2cu/qu2cu.py":130
+ * # |t_(k+1) - t_k| / |t_k - t_(k - 1)| = ratio
+ * assert ck[0] == c_before[3]
+ * ratio = abs(ck[1] - ck[0]) / abs(c_before[3] - c_before[2]) # <<<<<<<<<<<<<<
+ *
+ * prod_ratio *= ratio
+*/
+ __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_ck, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_ck, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_1 = PyNumber_Subtract(__pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = __Pyx_PyNumber_Absolute(__pyx_t_1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_c_before, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_c_before, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_10 = PyNumber_Subtract(__pyx_t_1, __pyx_t_8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PyNumber_Absolute(__pyx_t_10); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_10 = __Pyx_PyNumber_Divide(__pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_11 = __Pyx_PyFloat_AsDouble(__pyx_t_10); if (unlikely((__pyx_t_11 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 130, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_v_ratio = __pyx_t_11;
+
+ /* "fontTools/qu2cu/qu2cu.py":132
+ * ratio = abs(ck[1] - ck[0]) / abs(c_before[3] - c_before[2])
+ *
+ * prod_ratio *= ratio # <<<<<<<<<<<<<<
+ * sum_ratio += prod_ratio
+ * ts.append(sum_ratio)
+*/
+ __pyx_v_prod_ratio = (__pyx_v_prod_ratio * __pyx_v_ratio);
+
+ /* "fontTools/qu2cu/qu2cu.py":133
+ *
+ * prod_ratio *= ratio
+ * sum_ratio += prod_ratio # <<<<<<<<<<<<<<
+ * ts.append(sum_ratio)
+ *
+*/
+ __pyx_v_sum_ratio = (__pyx_v_sum_ratio + __pyx_v_prod_ratio);
+
+ /* "fontTools/qu2cu/qu2cu.py":134
+ * prod_ratio *= ratio
+ * sum_ratio += prod_ratio
+ * ts.append(sum_ratio) # <<<<<<<<<<<<<<
+ *
+ * # (t(n) - t(n - 1)) / (t_(1) - t(0)) = prod_ratio
+*/
+ __pyx_t_10 = PyFloat_FromDouble(__pyx_v_sum_ratio); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 134, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_12 = __Pyx_PyList_Append(__pyx_v_ts, __pyx_t_10); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 134, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":138
+ * # (t(n) - t(n - 1)) / (t_(1) - t(0)) = prod_ratio
+ *
+ * ts = [t / sum_ratio for t in ts[:-1]] # <<<<<<<<<<<<<<
+ *
+ * p0 = curves[start][0]
+*/
+ { /* enter inner scope */
+ __pyx_t_10 = PyList_New(0); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_8 = __Pyx_PyList_GetSlice(__pyx_v_ts, 0, -1L); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_7 = __pyx_t_8; __Pyx_INCREF(__pyx_t_7);
+ __pyx_t_13 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_7);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 138, __pyx_L1_error)
+ #endif
+ if (__pyx_t_13 >= __pyx_temp) break;
+ }
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(__pyx_t_7, __pyx_t_13, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_13;
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_11 = __Pyx_PyFloat_AsDouble(__pyx_t_8); if (unlikely((__pyx_t_11 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_7genexpr__pyx_v_t = __pyx_t_11;
+ if (unlikely(__pyx_v_sum_ratio == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 138, __pyx_L1_error)
+ }
+ __pyx_t_8 = PyFloat_FromDouble((__pyx_7genexpr__pyx_v_t / __pyx_v_sum_ratio)); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_10, (PyObject*)__pyx_t_8))) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ } /* exit inner scope */
+ __Pyx_DECREF_SET(__pyx_v_ts, ((PyObject*)__pyx_t_10));
+ __pyx_t_10 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":140
+ * ts = [t / sum_ratio for t in ts[:-1]]
+ *
+ * p0 = curves[start][0] # <<<<<<<<<<<<<<
+ * p1 = curves[start][1]
+ * p2 = curves[start + n - 1][2]
+*/
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_v_start, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 140, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_10, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 140, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_7); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 140, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_p0 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":141
+ *
+ * p0 = curves[start][0]
+ * p1 = curves[start][1] # <<<<<<<<<<<<<<
+ * p2 = curves[start + n - 1][2]
+ * p3 = curves[start + n - 1][3]
+*/
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_v_start, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 141, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_t_7, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 141, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_10); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_v_p1 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":142
+ * p0 = curves[start][0]
+ * p1 = curves[start][1]
+ * p2 = curves[start + n - 1][2] # <<<<<<<<<<<<<<
+ * p3 = curves[start + n - 1][3]
+ *
+*/
+ __pyx_t_6 = ((__pyx_v_start + __pyx_v_n) - 1);
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_t_6, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_10, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 142, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_7); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 142, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_p2 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":143
+ * p1 = curves[start][1]
+ * p2 = curves[start + n - 1][2]
+ * p3 = curves[start + n - 1][3] # <<<<<<<<<<<<<<
+ *
+ * # Build the curve by scaling the control-points.
+*/
+ __pyx_t_6 = ((__pyx_v_start + __pyx_v_n) - 1);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_t_6, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 143, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_10 = __Pyx_GetItemInt(__pyx_t_7, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 143, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_10); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 143, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_v_p3 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":146
+ *
+ * # Build the curve by scaling the control-points.
+ * p1 = p0 + (p1 - p0) / (ts[0] if ts else 1) # <<<<<<<<<<<<<<
+ * p2 = p3 + (p2 - p3) / ((1 - ts[-1]) if ts else 1)
+ *
+*/
+ __pyx_t_10 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_14 = __Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0);
+ __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_t_14); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_v_ts);
+ if (unlikely(((!CYTHON_ASSUME_SAFE_SIZE) && __pyx_temp < 0))) __PYX_ERR(0, 146, __pyx_L1_error)
+ __pyx_t_9 = (__pyx_temp != 0);
+ }
+
+ if (__pyx_t_9) {
+ __pyx_t_1 = __Pyx_GetItemInt_List(__pyx_v_ts, 0, long, 1, __Pyx_PyLong_From_long, 1, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_8 = __pyx_t_1;
+ __pyx_t_1 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_1);
+ __pyx_t_8 = __pyx_mstate_global->__pyx_int_1;
+ }
+ __pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = PyNumber_Add(__pyx_t_10, __pyx_t_1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 146, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_v_p1 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":147
+ * # Build the curve by scaling the control-points.
+ * p1 = p0 + (p1 - p0) / (ts[0] if ts else 1)
+ * p2 = p3 + (p2 - p3) / ((1 - ts[-1]) if ts else 1) # <<<<<<<<<<<<<<
+ *
+ * curve = (p0, p1, p2, p3)
+*/
+ __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_14 = __Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p3);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_t_14); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_v_ts);
+ if (unlikely(((!CYTHON_ASSUME_SAFE_SIZE) && __pyx_temp < 0))) __PYX_ERR(0, 147, __pyx_L1_error)
+ __pyx_t_9 = (__pyx_temp != 0);
+ }
+
+ if (__pyx_t_9) {
+ __pyx_t_7 = __Pyx_GetItemInt_List(__pyx_v_ts, -1L, long, 1, __Pyx_PyLong_From_long, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_15 = __Pyx_PyLong_SubtractCObj(__pyx_mstate_global->__pyx_int_1, __pyx_t_7, 1, 0, 0); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_15);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_10 = __pyx_t_15;
+ __pyx_t_15 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_1);
+ __pyx_t_10 = __pyx_mstate_global->__pyx_int_1;
+ }
+ __pyx_t_15 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_10); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_15);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_t_10 = PyNumber_Add(__pyx_t_8, __pyx_t_15); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0;
+ __pyx_t_14 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_10); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 147, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
+ __pyx_v_p2 = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":149
+ * p2 = p3 + (p2 - p3) / ((1 - ts[-1]) if ts else 1)
+ *
+ * curve = (p0, p1, p2, p3) # <<<<<<<<<<<<<<
+ *
+ * return curve, ts
+*/
+ __pyx_t_10 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_15 = __pyx_PyComplex_FromComplex(__pyx_v_p1); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_15);
+ __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_7 = PyTuple_New(4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 149, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_GIVEREF(__pyx_t_10);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_10) != (0)) __PYX_ERR(0, 149, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_15);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_15) != (0)) __PYX_ERR(0, 149, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_8);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_8) != (0)) __PYX_ERR(0, 149, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 3, __pyx_t_1) != (0)) __PYX_ERR(0, 149, __pyx_L1_error);
+ __pyx_t_10 = 0;
+ __pyx_t_15 = 0;
+ __pyx_t_8 = 0;
+ __pyx_t_1 = 0;
+ __pyx_v_curve = __pyx_t_7;
+ __pyx_t_7 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":151
+ * curve = (p0, p1, p2, p3)
+ *
+ * return curve, ts # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_7 = PyTuple_New(2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 151, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx_v_curve);
+ __Pyx_GIVEREF(__pyx_v_curve);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_v_curve) != (0)) __PYX_ERR(0, 151, __pyx_L1_error);
+ __Pyx_INCREF(__pyx_v_ts);
+ __Pyx_GIVEREF(__pyx_v_ts);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_v_ts) != (0)) __PYX_ERR(0, 151, __pyx_L1_error);
+ __pyx_r = __pyx_t_7;
+ __pyx_t_7 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":101
+ *
+ *
+ * @cython.cfunc # <<<<<<<<<<<<<<
+ * @cython.locals(
+ * start=cython.int,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_15);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.merge_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_ts);
+ __Pyx_XDECREF(__pyx_v_ck);
+ __Pyx_XDECREF(__pyx_v_c_before);
+ __Pyx_XDECREF(__pyx_v_curve);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":154
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * count=cython.int,
+ * num_offcurves=cython.int,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_3add_implicit_on_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_5qu2cu_5qu2cu_2add_implicit_on_curves, "add_implicit_on_curves(p)");
+static PyMethodDef __pyx_mdef_9fontTools_5qu2cu_5qu2cu_3add_implicit_on_curves = {"add_implicit_on_curves", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5qu2cu_5qu2cu_3add_implicit_on_curves, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5qu2cu_5qu2cu_2add_implicit_on_curves};
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_3add_implicit_on_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_p = 0;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[1] = {0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("add_implicit_on_curves (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_p,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 154, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 154, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "add_implicit_on_curves", 0) < (0)) __PYX_ERR(0, 154, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("add_implicit_on_curves", 1, 1, 1, i); __PYX_ERR(0, 154, __pyx_L3_error) }
+ }
+ } else if (unlikely(__pyx_nargs != 1)) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 154, __pyx_L3_error)
+ }
+ __pyx_v_p = values[0];
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("add_implicit_on_curves", 1, 1, 1, __pyx_nargs); __PYX_ERR(0, 154, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.add_implicit_on_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_5qu2cu_5qu2cu_2add_implicit_on_curves(__pyx_self, __pyx_v_p);
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_2add_implicit_on_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_p) {
+ int __pyx_v_count;
+ int __pyx_v_num_offcurves;
+ int __pyx_v_i;
+ __pyx_t_double_complex __pyx_v_off1;
+ __pyx_t_double_complex __pyx_v_off2;
+ __pyx_t_double_complex __pyx_v_on;
+ PyObject *__pyx_v_q = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ int __pyx_t_3;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ __pyx_t_double_complex __pyx_t_6;
+ long __pyx_t_7;
+ int __pyx_t_8;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("add_implicit_on_curves", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":163
+ * )
+ * def add_implicit_on_curves(p):
+ * q = list(p) # <<<<<<<<<<<<<<
+ * count = 0
+ * num_offcurves = len(p) - 2
+*/
+ __pyx_t_1 = PySequence_List(__pyx_v_p); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 163, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_q = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":164
+ * def add_implicit_on_curves(p):
+ * q = list(p)
+ * count = 0 # <<<<<<<<<<<<<<
+ * num_offcurves = len(p) - 2
+ * for i in range(1, num_offcurves):
+*/
+ __pyx_v_count = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":165
+ * q = list(p)
+ * count = 0
+ * num_offcurves = len(p) - 2 # <<<<<<<<<<<<<<
+ * for i in range(1, num_offcurves):
+ * off1 = p[i]
+*/
+ __pyx_t_2 = PyObject_Length(__pyx_v_p); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 165, __pyx_L1_error)
+ __pyx_v_num_offcurves = (__pyx_t_2 - 2);
+
+ /* "fontTools/qu2cu/qu2cu.py":166
+ * count = 0
+ * num_offcurves = len(p) - 2
+ * for i in range(1, num_offcurves): # <<<<<<<<<<<<<<
+ * off1 = p[i]
+ * off2 = p[i + 1]
+*/
+ __pyx_t_3 = __pyx_v_num_offcurves;
+ __pyx_t_4 = __pyx_t_3;
+ for (__pyx_t_5 = 1; __pyx_t_5 < __pyx_t_4; __pyx_t_5+=1) {
+ __pyx_v_i = __pyx_t_5;
+
+ /* "fontTools/qu2cu/qu2cu.py":167
+ * num_offcurves = len(p) - 2
+ * for i in range(1, num_offcurves):
+ * off1 = p[i] # <<<<<<<<<<<<<<
+ * off2 = p[i + 1]
+ * on = off1 + (off2 - off1) * 0.5
+*/
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_p, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 167, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 167, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_off1 = __pyx_t_6;
+
+ /* "fontTools/qu2cu/qu2cu.py":168
+ * for i in range(1, num_offcurves):
+ * off1 = p[i]
+ * off2 = p[i + 1] # <<<<<<<<<<<<<<
+ * on = off1 + (off2 - off1) * 0.5
+ * q.insert(i + 1 + count, on)
+*/
+ __pyx_t_7 = (__pyx_v_i + 1);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_p, __pyx_t_7, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 168, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 168, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_v_off2 = __pyx_t_6;
+
+ /* "fontTools/qu2cu/qu2cu.py":169
+ * off1 = p[i]
+ * off2 = p[i + 1]
+ * on = off1 + (off2 - off1) * 0.5 # <<<<<<<<<<<<<<
+ * q.insert(i + 1 + count, on)
+ * count += 1
+*/
+ __pyx_v_on = __Pyx_c_sum_double(__pyx_v_off1, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_off2, __pyx_v_off1), __pyx_t_double_complex_from_parts(0.5, 0)));
+
+ /* "fontTools/qu2cu/qu2cu.py":170
+ * off2 = p[i + 1]
+ * on = off1 + (off2 - off1) * 0.5
+ * q.insert(i + 1 + count, on) # <<<<<<<<<<<<<<
+ * count += 1
+ * return q
+*/
+ __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_on); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 170, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_8 = PyList_Insert(__pyx_v_q, ((__pyx_v_i + 1) + __pyx_v_count), __pyx_t_1); if (unlikely(__pyx_t_8 == ((int)-1))) __PYX_ERR(0, 170, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":171
+ * on = off1 + (off2 - off1) * 0.5
+ * q.insert(i + 1 + count, on)
+ * count += 1 # <<<<<<<<<<<<<<
+ * return q
+ *
+*/
+ __pyx_v_count = (__pyx_v_count + 1);
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":172
+ * q.insert(i + 1 + count, on)
+ * count += 1
+ * return q # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_q);
+ __pyx_r = __pyx_v_q;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":154
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * count=cython.int,
+ * num_offcurves=cython.int,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.add_implicit_on_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_q);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":178
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * cost=cython.int,
+ * is_complex=cython.int,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_5quadratic_to_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_5qu2cu_5qu2cu_4quadratic_to_curves, "quadratic_to_curves(list quads: List[List[Point]], double max_err: float = 0.5, bool all_cubic: bool = False) -> List[Tuple[Point, ...]]\n\nConverts a connecting list of quadratic splines to a list of quadratic\nand cubic curves.\n\nA quadratic spline is specified as a list of points. Either each point is\na 2-tuple of X,Y coordinates, or each point is a complex number with\nreal/imaginary components representing X,Y coordinates.\n\nThe first and last points are on-curve points and the rest are off-curve\npoints, with an implied on-curve point in the middle between every two\nconsequtive off-curve points.\n\nReturns:\n The output is a list of tuples of points. Points are represented\n in the same format as the input, either as 2-tuples or complex numbers.\n\n Each tuple is either of length three, for a quadratic curve, or four,\n for a cubic curve. Each curve's last point is the same as the next\n curve's first point.\n\nArgs:\n quads: quadratic splines\n\n max_err: absolute error tolerance; defaults to 0.5\n\n all_cubic: if True, only cubic curves are generated; defaults to False");
+static PyMethodDef __pyx_mdef_9fontTools_5qu2cu_5qu2cu_5quadratic_to_curves = {"quadratic_to_curves", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5qu2cu_5qu2cu_5quadratic_to_curves, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5qu2cu_5qu2cu_4quadratic_to_curves};
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_5quadratic_to_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_quads = 0;
+ double __pyx_v_max_err;
+ int __pyx_v_all_cubic;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[3] = {0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("quadratic_to_curves (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_quads,&__pyx_mstate_global->__pyx_n_u_max_err,&__pyx_mstate_global->__pyx_n_u_all_cubic,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 178, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 178, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 178, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 178, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "quadratic_to_curves", 0) < (0)) __PYX_ERR(0, 178, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 1; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("quadratic_to_curves", 0, 1, 3, i); __PYX_ERR(0, 178, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 178, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 178, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 178, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_quads = ((PyObject*)values[0]);
+ if (values[1]) {
+ __pyx_v_max_err = __Pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_max_err == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 184, __pyx_L3_error)
+ } else {
+ __pyx_v_max_err = ((double)((double)0.5));
+ }
+ if (values[2]) {
+ __pyx_v_all_cubic = __Pyx_PyObject_IsTrue(values[2]); if (unlikely((__pyx_v_all_cubic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 185, __pyx_L3_error)
+ } else {
+
+ /* "fontTools/qu2cu/qu2cu.py":185
+ * quads: List[List[Point]],
+ * max_err: float = 0.5,
+ * all_cubic: bool = False, # <<<<<<<<<<<<<<
+ * ) -> List[Tuple[Point, ...]]:
+ * """Converts a connecting list of quadratic splines to a list of quadratic
+*/
+ __pyx_v_all_cubic = ((int)((int)0));
+ }
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("quadratic_to_curves", 0, 1, 3, __pyx_nargs); __PYX_ERR(0, 178, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.quadratic_to_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_quads), (&PyList_Type), 0, "quads", 2))) __PYX_ERR(0, 183, __pyx_L1_error)
+ __pyx_r = __pyx_pf_9fontTools_5qu2cu_5qu2cu_4quadratic_to_curves(__pyx_self, __pyx_v_quads, __pyx_v_max_err, __pyx_v_all_cubic);
+
+ /* "fontTools/qu2cu/qu2cu.py":178
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * cost=cython.int,
+ * is_complex=cython.int,
+*/
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __pyx_r = NULL;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ goto __pyx_L7_cleaned_up;
+ __pyx_L0:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __pyx_L7_cleaned_up:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/qu2cu/qu2cu.py":235
+ *
+ * if not is_complex:
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves] # <<<<<<<<<<<<<<
+ * return curves
+ *
+*/
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 235, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_2generator, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[0]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_quadratic_to_curves_locals_genex, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu); if (unlikely(!gen)) __PYX_ERR(0, 235, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.quadratic_to_curves.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ case 1: goto __pyx_L6_resume_from_yield;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(__pyx_sent_value != Py_None)) {
+ if (unlikely(__pyx_sent_value)) PyErr_SetString(PyExc_TypeError, "can't send non-None value to a just-started generator");
+ __PYX_ERR(0, 235, __pyx_L1_error)
+ }
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 235, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 235, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 235, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 235, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 235, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 235, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 235, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_c);
+ __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_c, __pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_cur_scope->__pyx_v_c, __pyx_mstate_global->__pyx_n_u_real); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 235, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_cur_scope->__pyx_v_c, __pyx_mstate_global->__pyx_n_u_imag); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 235, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = PyTuple_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 235, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_4);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_4) != (0)) __PYX_ERR(0, 235, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 235, __pyx_L1_error);
+ __pyx_t_4 = 0;
+ __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ __Pyx_XGIVEREF(__pyx_t_1);
+ __pyx_cur_scope->__pyx_t_0 = __pyx_t_1;
+ __pyx_cur_scope->__pyx_t_1 = __pyx_t_2;
+ __pyx_cur_scope->__pyx_t_2 = __pyx_t_3;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ /* return from generator, yielding value */
+ __pyx_generator->resume_label = 1;
+ return __pyx_r;
+ __pyx_L6_resume_from_yield:;
+ __pyx_t_1 = __pyx_cur_scope->__pyx_t_0;
+ __pyx_cur_scope->__pyx_t_0 = 0;
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __pyx_cur_scope->__pyx_t_1;
+ __pyx_t_3 = __pyx_cur_scope->__pyx_t_2;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 235, __pyx_L1_error)
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":178
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * cost=cython.int,
+ * is_complex=cython.int,
+*/
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_4quadratic_to_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_quads, double __pyx_v_max_err, int __pyx_v_all_cubic) {
+ int __pyx_v_cost;
+ int __pyx_v_is_complex;
+ PyObject *__pyx_v_q = NULL;
+ PyObject *__pyx_v_costs = NULL;
+ PyObject *__pyx_v_p = NULL;
+ CYTHON_UNUSED Py_ssize_t __pyx_v_i;
+ PyObject *__pyx_v_qq = NULL;
+ PyObject *__pyx_v_curves = NULL;
+ PyObject *__pyx_8genexpr1__pyx_v_p = NULL;
+ PyObject *__pyx_8genexpr2__pyx_v_x = NULL;
+ PyObject *__pyx_8genexpr2__pyx_v_y = NULL;
+ PyObject *__pyx_8genexpr3__pyx_v_curve = NULL;
+ PyObject *__pyx_8genexpr3__pyx_v_0 = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ int __pyx_t_3;
+ Py_ssize_t __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ Py_ssize_t __pyx_t_7;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ PyObject *__pyx_t_9 = NULL;
+ PyObject *__pyx_t_10 = NULL;
+ PyObject *__pyx_t_11 = NULL;
+ PyObject *__pyx_t_12 = NULL;
+ PyObject *(*__pyx_t_13)(PyObject *);
+ size_t __pyx_t_14;
+ Py_ssize_t __pyx_t_15;
+ Py_ssize_t __pyx_t_16;
+ int __pyx_t_17;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("quadratic_to_curves", 0);
+ __Pyx_INCREF(__pyx_v_quads);
+
+ /* "fontTools/qu2cu/qu2cu.py":213
+ * all_cubic: if True, only cubic curves are generated; defaults to False
+ * """
+ * is_complex = type(quads[0][0]) is complex # <<<<<<<<<<<<<<
+ * if not is_complex:
+ * quads = [[complex(x, y) for (x, y) in p] for p in quads]
+*/
+ __pyx_t_1 = __Pyx_GetItemInt_List(__pyx_v_quads, 0, long, 1, __Pyx_PyLong_From_long, 1, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 213, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 213, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __pyx_t_3 = (((PyObject *)Py_TYPE(__pyx_t_2)) == ((PyObject *)(&PyComplex_Type)));
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_v_is_complex = __pyx_t_3;
+
+ /* "fontTools/qu2cu/qu2cu.py":214
+ * """
+ * is_complex = type(quads[0][0]) is complex
+ * if not is_complex: # <<<<<<<<<<<<<<
+ * quads = [[complex(x, y) for (x, y) in p] for p in quads]
+ *
+*/
+ __pyx_t_3 = (!(__pyx_v_is_complex != 0));
+ if (__pyx_t_3) {
+
+ /* "fontTools/qu2cu/qu2cu.py":215
+ * is_complex = type(quads[0][0]) is complex
+ * if not is_complex:
+ * quads = [[complex(x, y) for (x, y) in p] for p in quads] # <<<<<<<<<<<<<<
+ *
+ * q = [quads[0][0]]
+*/
+ { /* enter inner scope */
+ __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 215, __pyx_L6_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __pyx_v_quads; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_4 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 215, __pyx_L6_error)
+ #endif
+ if (__pyx_t_4 >= __pyx_temp) break;
+ }
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_4, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_4;
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 215, __pyx_L6_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_XDECREF_SET(__pyx_8genexpr1__pyx_v_p, __pyx_t_5);
+ __pyx_t_5 = 0;
+ { /* enter inner scope */
+ __pyx_t_5 = PyList_New(0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ if (likely(PyList_CheckExact(__pyx_8genexpr1__pyx_v_p)) || PyTuple_CheckExact(__pyx_8genexpr1__pyx_v_p)) {
+ __pyx_t_6 = __pyx_8genexpr1__pyx_v_p; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ } else {
+ __pyx_t_7 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_8genexpr1__pyx_v_p); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 215, __pyx_L11_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_8)) {
+ if (likely(PyList_CheckExact(__pyx_t_6))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 215, __pyx_L11_error)
+ #endif
+ if (__pyx_t_7 >= __pyx_temp) break;
+ }
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_7, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_7;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 215, __pyx_L11_error)
+ #endif
+ if (__pyx_t_7 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_9 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_7));
+ #else
+ __pyx_t_9 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_7);
+ #endif
+ ++__pyx_t_7;
+ }
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 215, __pyx_L11_error)
+ } else {
+ __pyx_t_9 = __pyx_t_8(__pyx_t_6);
+ if (unlikely(!__pyx_t_9)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 215, __pyx_L11_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_9);
+ if ((likely(PyTuple_CheckExact(__pyx_t_9))) || (PyList_CheckExact(__pyx_t_9))) {
+ PyObject* sequence = __pyx_t_9;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 215, __pyx_L11_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_10 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_10);
+ __pyx_t_11 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_11);
+ } else {
+ __pyx_t_10 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_XGOTREF(__pyx_t_10);
+ __pyx_t_11 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_XGOTREF(__pyx_t_11);
+ }
+ #else
+ __pyx_t_10 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_10);
+ __pyx_t_11 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_11);
+ #endif
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_12 = PyObject_GetIter(__pyx_t_9); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_12);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __pyx_t_13 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_12);
+ index = 0; __pyx_t_10 = __pyx_t_13(__pyx_t_12); if (unlikely(!__pyx_t_10)) goto __pyx_L14_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_10);
+ index = 1; __pyx_t_11 = __pyx_t_13(__pyx_t_12); if (unlikely(!__pyx_t_11)) goto __pyx_L14_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_11);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_13(__pyx_t_12), 2) < (0)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __pyx_t_13 = NULL;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ goto __pyx_L15_unpacking_done;
+ __pyx_L14_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
+ __pyx_t_13 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 215, __pyx_L11_error)
+ __pyx_L15_unpacking_done:;
+ }
+ __Pyx_XDECREF_SET(__pyx_8genexpr2__pyx_v_x, __pyx_t_10);
+ __pyx_t_10 = 0;
+ __Pyx_XDECREF_SET(__pyx_8genexpr2__pyx_v_y, __pyx_t_11);
+ __pyx_t_11 = 0;
+ __pyx_t_11 = NULL;
+ __pyx_t_14 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_11, __pyx_8genexpr2__pyx_v_x, __pyx_8genexpr2__pyx_v_y};
+ __pyx_t_9 = __Pyx_PyObject_FastCall((PyObject*)(&PyComplex_Type), __pyx_callargs+__pyx_t_14, (3-__pyx_t_14) | (__pyx_t_14*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0;
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ }
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_5, (PyObject*)__pyx_t_9))) __PYX_ERR(0, 215, __pyx_L11_error)
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_x); __pyx_8genexpr2__pyx_v_x = 0;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_y); __pyx_8genexpr2__pyx_v_y = 0;
+ goto __pyx_L17_exit_scope;
+ __pyx_L11_error:;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_x); __pyx_8genexpr2__pyx_v_x = 0;
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_y); __pyx_8genexpr2__pyx_v_y = 0;
+ goto __pyx_L6_error;
+ __pyx_L17_exit_scope:;
+ } /* exit inner scope */
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 215, __pyx_L6_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_p); __pyx_8genexpr1__pyx_v_p = 0;
+ goto __pyx_L19_exit_scope;
+ __pyx_L6_error:;
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_p); __pyx_8genexpr1__pyx_v_p = 0;
+ goto __pyx_L1_error;
+ __pyx_L19_exit_scope:;
+ } /* exit inner scope */
+ __Pyx_DECREF_SET(__pyx_v_quads, ((PyObject*)__pyx_t_2));
+ __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":214
+ * """
+ * is_complex = type(quads[0][0]) is complex
+ * if not is_complex: # <<<<<<<<<<<<<<
+ * quads = [[complex(x, y) for (x, y) in p] for p in quads]
+ *
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":217
+ * quads = [[complex(x, y) for (x, y) in p] for p in quads]
+ *
+ * q = [quads[0][0]] # <<<<<<<<<<<<<<
+ * costs = [1]
+ * cost = 1
+*/
+ __pyx_t_2 = __Pyx_GetItemInt_List(__pyx_v_quads, 0, long, 1, __Pyx_PyLong_From_long, 1, 0, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 217, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_1 = __Pyx_GetItemInt(__pyx_t_2, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 217, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 217, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 217, __pyx_L1_error);
+ __pyx_t_1 = 0;
+ __pyx_v_q = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":218
+ *
+ * q = [quads[0][0]]
+ * costs = [1] # <<<<<<<<<<<<<<
+ * cost = 1
+ * for p in quads:
+*/
+ __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 218, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_mstate_global->__pyx_int_1);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_int_1);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_mstate_global->__pyx_int_1) != (0)) __PYX_ERR(0, 218, __pyx_L1_error);
+ __pyx_v_costs = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":219
+ * q = [quads[0][0]]
+ * costs = [1]
+ * cost = 1 # <<<<<<<<<<<<<<
+ * for p in quads:
+ * assert q[-1] == p[0]
+*/
+ __pyx_v_cost = 1;
+
+ /* "fontTools/qu2cu/qu2cu.py":220
+ * costs = [1]
+ * cost = 1
+ * for p in quads: # <<<<<<<<<<<<<<
+ * assert q[-1] == p[0]
+ * for i in range(len(p) - 2):
+*/
+ __pyx_t_2 = __pyx_v_quads; __Pyx_INCREF(__pyx_t_2);
+ __pyx_t_4 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 220, __pyx_L1_error)
+ #endif
+ if (__pyx_t_4 >= __pyx_temp) break;
+ }
+ __pyx_t_1 = __Pyx_PyList_GetItemRefFast(__pyx_t_2, __pyx_t_4, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_4;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 220, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_XDECREF_SET(__pyx_v_p, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":221
+ * cost = 1
+ * for p in quads:
+ * assert q[-1] == p[0] # <<<<<<<<<<<<<<
+ * for i in range(len(p) - 2):
+ * cost += 1
+*/
+ #ifndef CYTHON_WITHOUT_ASSERTIONS
+ if (unlikely(__pyx_assertions_enabled())) {
+ __pyx_t_1 = __Pyx_GetItemInt_List(__pyx_v_q, -1L, long, 1, __Pyx_PyLong_From_long, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 221, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_5 = __Pyx_GetItemInt(__pyx_v_p, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 221, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = PyObject_RichCompare(__pyx_t_1, __pyx_t_5, Py_EQ); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 221, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_3 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_3 < 0))) __PYX_ERR(0, 221, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_3)) {
+ __Pyx_Raise(((PyObject *)(((PyTypeObject*)PyExc_AssertionError))), 0, 0, 0);
+ __PYX_ERR(0, 221, __pyx_L1_error)
+ }
+ }
+ #else
+ if ((1)); else __PYX_ERR(0, 221, __pyx_L1_error)
+ #endif
+
+ /* "fontTools/qu2cu/qu2cu.py":222
+ * for p in quads:
+ * assert q[-1] == p[0]
+ * for i in range(len(p) - 2): # <<<<<<<<<<<<<<
+ * cost += 1
+ * costs.append(cost)
+*/
+ __pyx_t_7 = PyObject_Length(__pyx_v_p); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 222, __pyx_L1_error)
+ __pyx_t_15 = (__pyx_t_7 - 2);
+ __pyx_t_7 = __pyx_t_15;
+ for (__pyx_t_16 = 0; __pyx_t_16 < __pyx_t_7; __pyx_t_16+=1) {
+ __pyx_v_i = __pyx_t_16;
+
+ /* "fontTools/qu2cu/qu2cu.py":223
+ * assert q[-1] == p[0]
+ * for i in range(len(p) - 2):
+ * cost += 1 # <<<<<<<<<<<<<<
+ * costs.append(cost)
+ * costs.append(cost)
+*/
+ __pyx_v_cost = (__pyx_v_cost + 1);
+
+ /* "fontTools/qu2cu/qu2cu.py":224
+ * for i in range(len(p) - 2):
+ * cost += 1
+ * costs.append(cost) # <<<<<<<<<<<<<<
+ * costs.append(cost)
+ * qq = add_implicit_on_curves(p)[1:]
+*/
+ __pyx_t_6 = __Pyx_PyLong_From_int(__pyx_v_cost); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 224, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_17 = __Pyx_PyList_Append(__pyx_v_costs, __pyx_t_6); if (unlikely(__pyx_t_17 == ((int)-1))) __PYX_ERR(0, 224, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":225
+ * cost += 1
+ * costs.append(cost)
+ * costs.append(cost) # <<<<<<<<<<<<<<
+ * qq = add_implicit_on_curves(p)[1:]
+ * costs.pop()
+*/
+ __pyx_t_6 = __Pyx_PyLong_From_int(__pyx_v_cost); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 225, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_17 = __Pyx_PyList_Append(__pyx_v_costs, __pyx_t_6); if (unlikely(__pyx_t_17 == ((int)-1))) __PYX_ERR(0, 225, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":226
+ * costs.append(cost)
+ * costs.append(cost)
+ * qq = add_implicit_on_curves(p)[1:] # <<<<<<<<<<<<<<
+ * costs.pop()
+ * q.extend(qq)
+*/
+ __pyx_t_5 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_add_implicit_on_curves); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_14 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_1))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_1);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_1);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_1, __pyx__function);
+ __pyx_t_14 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_v_p};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_1, __pyx_callargs+__pyx_t_14, (2-__pyx_t_14) | (__pyx_t_14*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_1 = __Pyx_PyObject_GetSlice(__pyx_t_6, 1, 0, NULL, NULL, &__pyx_mstate_global->__pyx_slice[0], 1, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_qq, __pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":227
+ * costs.append(cost)
+ * qq = add_implicit_on_curves(p)[1:]
+ * costs.pop() # <<<<<<<<<<<<<<
+ * q.extend(qq)
+ * cost += 1
+*/
+ __pyx_t_1 = __Pyx_PyList_Pop(__pyx_v_costs); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 227, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":228
+ * qq = add_implicit_on_curves(p)[1:]
+ * costs.pop()
+ * q.extend(qq) # <<<<<<<<<<<<<<
+ * cost += 1
+ * costs.append(cost)
+*/
+ __pyx_t_17 = __Pyx_PyList_Extend(__pyx_v_q, __pyx_v_qq); if (unlikely(__pyx_t_17 == ((int)-1))) __PYX_ERR(0, 228, __pyx_L1_error)
+
+ /* "fontTools/qu2cu/qu2cu.py":229
+ * costs.pop()
+ * q.extend(qq)
+ * cost += 1 # <<<<<<<<<<<<<<
+ * costs.append(cost)
+ *
+*/
+ __pyx_v_cost = (__pyx_v_cost + 1);
+
+ /* "fontTools/qu2cu/qu2cu.py":230
+ * q.extend(qq)
+ * cost += 1
+ * costs.append(cost) # <<<<<<<<<<<<<<
+ *
+ * curves = spline_to_curves(q, costs, max_err, all_cubic)
+*/
+ __pyx_t_1 = __Pyx_PyLong_From_int(__pyx_v_cost); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 230, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_17 = __Pyx_PyList_Append(__pyx_v_costs, __pyx_t_1); if (unlikely(__pyx_t_17 == ((int)-1))) __PYX_ERR(0, 230, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":220
+ * costs = [1]
+ * cost = 1
+ * for p in quads: # <<<<<<<<<<<<<<
+ * assert q[-1] == p[0]
+ * for i in range(len(p) - 2):
+*/
+ }
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":232
+ * costs.append(cost)
+ *
+ * curves = spline_to_curves(q, costs, max_err, all_cubic) # <<<<<<<<<<<<<<
+ *
+ * if not is_complex:
+*/
+ __pyx_t_1 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_spline_to_curves); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 232, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_5 = PyFloat_FromDouble(__pyx_v_max_err); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 232, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_9 = __Pyx_PyBool_FromLong(__pyx_v_all_cubic); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 232, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_14 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_6))) {
+ __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_6);
+ assert(__pyx_t_1);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_6);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_6, __pyx__function);
+ __pyx_t_14 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_1, __pyx_v_q, __pyx_v_costs, __pyx_t_5, __pyx_t_9};
+ __pyx_t_2 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_6, __pyx_callargs+__pyx_t_14, (5-__pyx_t_14) | (__pyx_t_14*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 232, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ }
+ __pyx_v_curves = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":234
+ * curves = spline_to_curves(q, costs, max_err, all_cubic)
+ *
+ * if not is_complex: # <<<<<<<<<<<<<<
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves]
+ * return curves
+*/
+ __pyx_t_3 = (!(__pyx_v_is_complex != 0));
+ if (__pyx_t_3) {
+
+ /* "fontTools/qu2cu/qu2cu.py":235
+ *
+ * if not is_complex:
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves] # <<<<<<<<<<<<<<
+ * return curves
+ *
+*/
+ { /* enter inner scope */
+ __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 235, __pyx_L28_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ if (likely(PyList_CheckExact(__pyx_v_curves)) || PyTuple_CheckExact(__pyx_v_curves)) {
+ __pyx_t_6 = __pyx_v_curves; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_4 = 0;
+ __pyx_t_8 = NULL;
+ } else {
+ __pyx_t_4 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_v_curves); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 235, __pyx_L28_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 235, __pyx_L28_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_8)) {
+ if (likely(PyList_CheckExact(__pyx_t_6))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 235, __pyx_L28_error)
+ #endif
+ if (__pyx_t_4 >= __pyx_temp) break;
+ }
+ __pyx_t_9 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_4, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_4;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 235, __pyx_L28_error)
+ #endif
+ if (__pyx_t_4 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_9 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_4));
+ #else
+ __pyx_t_9 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_4);
+ #endif
+ ++__pyx_t_4;
+ }
+ if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 235, __pyx_L28_error)
+ } else {
+ __pyx_t_9 = __pyx_t_8(__pyx_t_6);
+ if (unlikely(!__pyx_t_9)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 235, __pyx_L28_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_9);
+ __Pyx_XDECREF_SET(__pyx_8genexpr3__pyx_v_curve, __pyx_t_9);
+ __pyx_t_9 = 0;
+ __pyx_t_9 = __pyx_pf_9fontTools_5qu2cu_5qu2cu_19quadratic_to_curves_8genexpr3_genexpr(NULL, __pyx_8genexpr3__pyx_v_curve); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 235, __pyx_L28_error)
+ __Pyx_GOTREF(__pyx_t_9);
+ __pyx_t_5 = __Pyx_PySequence_Tuple(__pyx_t_9); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 235, __pyx_L28_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 235, __pyx_L28_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_curve); __pyx_8genexpr3__pyx_v_curve = 0;
+ goto __pyx_L32_exit_scope;
+ __pyx_L28_error:;
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_curve); __pyx_8genexpr3__pyx_v_curve = 0;
+ goto __pyx_L1_error;
+ __pyx_L32_exit_scope:;
+ } /* exit inner scope */
+ __Pyx_DECREF_SET(__pyx_v_curves, __pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":234
+ * curves = spline_to_curves(q, costs, max_err, all_cubic)
+ *
+ * if not is_complex: # <<<<<<<<<<<<<<
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves]
+ * return curves
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":236
+ * if not is_complex:
+ * curves = [tuple((c.real, c.imag) for c in curve) for curve in curves]
+ * return curves # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = __pyx_v_curves;
+ __Pyx_INCREF(__pyx_t_2);
+ if (!(likely(PyList_CheckExact(__pyx_t_2))||((__pyx_t_2) == Py_None) || __Pyx_RaiseUnexpectedTypeError("list", __pyx_t_2))) __PYX_ERR(0, 236, __pyx_L1_error)
+ __pyx_r = ((PyObject*)__pyx_t_2);
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":178
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * cost=cython.int,
+ * is_complex=cython.int,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_9);
+ __Pyx_XDECREF(__pyx_t_10);
+ __Pyx_XDECREF(__pyx_t_11);
+ __Pyx_XDECREF(__pyx_t_12);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.quadratic_to_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_q);
+ __Pyx_XDECREF(__pyx_v_costs);
+ __Pyx_XDECREF(__pyx_v_p);
+ __Pyx_XDECREF(__pyx_v_qq);
+ __Pyx_XDECREF(__pyx_v_curves);
+ __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_p);
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_x);
+ __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_y);
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_curve);
+ __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_0);
+ __Pyx_XDECREF(__pyx_v_quads);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":242
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * i=cython.int,
+ * j=cython.int,
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_7spline_to_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_5qu2cu_5qu2cu_6spline_to_curves, "spline_to_curves(q, costs, double tolerance=0.5, int all_cubic=False)\n\nq: quadratic spline with alternating on-curve / off-curve points.\n\ncosts: cumulative list of encoding cost of q in terms of number of\n points that need to be encoded. Implied on-curve points do not\n contribute to the cost. If all points need to be encoded, then\n costs will be range(1, len(q)+1).");
+static PyMethodDef __pyx_mdef_9fontTools_5qu2cu_5qu2cu_7spline_to_curves = {"spline_to_curves", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5qu2cu_5qu2cu_7spline_to_curves, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5qu2cu_5qu2cu_6spline_to_curves};
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_7spline_to_curves(PyObject *__pyx_self,
+#if CYTHON_METH_FASTCALL
+PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds
+#else
+PyObject *__pyx_args, PyObject *__pyx_kwds
+#endif
+) {
+ PyObject *__pyx_v_q = 0;
+ PyObject *__pyx_v_costs = 0;
+ double __pyx_v_tolerance;
+ int __pyx_v_all_cubic;
+ #if !CYTHON_METH_FASTCALL
+ CYTHON_UNUSED Py_ssize_t __pyx_nargs;
+ #endif
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject* values[4] = {0,0,0,0};
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("spline_to_curves (wrapper)", 0);
+ #if !CYTHON_METH_FASTCALL
+ #if CYTHON_ASSUME_SAFE_SIZE
+ __pyx_nargs = PyTuple_GET_SIZE(__pyx_args);
+ #else
+ __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL;
+ #endif
+ #endif
+ __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs);
+ {
+ PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_q,&__pyx_mstate_global->__pyx_n_u_costs,&__pyx_mstate_global->__pyx_n_u_tolerance,&__pyx_mstate_global->__pyx_n_u_all_cubic,0};
+ const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
+ if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 242, __pyx_L3_error)
+ if (__pyx_kwds_len > 0) {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 1:
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ const Py_ssize_t kwd_pos_args = __pyx_nargs;
+ if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "spline_to_curves", 0) < (0)) __PYX_ERR(0, 242, __pyx_L3_error)
+ for (Py_ssize_t i = __pyx_nargs; i < 2; i++) {
+ if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("spline_to_curves", 0, 2, 4, i); __PYX_ERR(0, 242, __pyx_L3_error) }
+ }
+ } else {
+ switch (__pyx_nargs) {
+ case 4:
+ values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 3:
+ values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 242, __pyx_L3_error)
+ CYTHON_FALLTHROUGH;
+ case 2:
+ values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 242, __pyx_L3_error)
+ values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0);
+ if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 242, __pyx_L3_error)
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_q = values[0];
+ __pyx_v_costs = values[1];
+ if (values[2]) {
+ __pyx_v_tolerance = __Pyx_PyFloat_AsDouble(values[2]); if (unlikely((__pyx_v_tolerance == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 265, __pyx_L3_error)
+ } else {
+ __pyx_v_tolerance = ((double)((double)0.5));
+ }
+ if (values[3]) {
+ __pyx_v_all_cubic = __Pyx_PyLong_As_int(values[3]); if (unlikely((__pyx_v_all_cubic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 265, __pyx_L3_error)
+ } else {
+
+ /* "fontTools/qu2cu/qu2cu.py":265
+ * u=cython.complex,
+ * )
+ * def spline_to_curves(q, costs, tolerance=0.5, all_cubic=False): # <<<<<<<<<<<<<<
+ * """
+ * q: quadratic spline with alternating on-curve / off-curve points.
+*/
+ __pyx_v_all_cubic = ((int)((int)0));
+ }
+ }
+ goto __pyx_L6_skip;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("spline_to_curves", 0, 2, 4, __pyx_nargs); __PYX_ERR(0, 242, __pyx_L3_error)
+ __pyx_L6_skip:;
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.spline_to_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_9fontTools_5qu2cu_5qu2cu_6spline_to_curves(__pyx_self, __pyx_v_q, __pyx_v_costs, __pyx_v_tolerance, __pyx_v_all_cubic);
+
+ /* "fontTools/qu2cu/qu2cu.py":242
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * i=cython.int,
+ * j=cython.int,
+*/
+
+ /* function exit code */
+ for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) {
+ Py_XDECREF(values[__pyx_temp]);
+ }
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static PyObject *__pyx_gb_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_2generator1(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */
+
+/* "fontTools/qu2cu/qu2cu.py":340
+ * for k, reconst in enumerate(reconstructed):
+ * orig = elevated_quadratics[j + k]
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig)) # <<<<<<<<<<<<<<
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+*/
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_genexpr(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_genexpr_arg_0) {
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *__pyx_cur_scope;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ __pyx_cur_scope = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr(__pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (unlikely(!__pyx_cur_scope)) {
+ __pyx_cur_scope = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)Py_None);
+ __Pyx_INCREF(Py_None);
+ __PYX_ERR(0, 340, __pyx_L1_error)
+ } else {
+ __Pyx_GOTREF((PyObject *)__pyx_cur_scope);
+ }
+ __pyx_cur_scope->__pyx_genexpr_arg_0 = __pyx_genexpr_arg_0;
+ __Pyx_INCREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ __Pyx_GIVEREF(__pyx_cur_scope->__pyx_genexpr_arg_0);
+ {
+ __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_2generator1, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[1]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_genexpr, __pyx_mstate_global->__pyx_n_u_spline_to_curves_locals_genexpr, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu); if (unlikely(!gen)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_cur_scope);
+ __Pyx_RefNannyFinishContext();
+ return (PyObject *) gen;
+ }
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.spline_to_curves.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __Pyx_DECREF((PyObject *)__pyx_cur_scope);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_gb_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_2generator1(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */
+{
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)__pyx_generator->closure);
+ PyObject *__pyx_r = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ Py_ssize_t __pyx_t_2;
+ PyObject *(*__pyx_t_3)(PyObject *);
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *(*__pyx_t_8)(PyObject *);
+ __pyx_t_double_complex __pyx_t_9;
+ __pyx_t_double_complex __pyx_t_10;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("genexpr", 0);
+ switch (__pyx_generator->resume_label) {
+ case 0: goto __pyx_L3_first_run;
+ case 1: goto __pyx_L8_resume_from_yield;
+ default: /* CPython raises the right error here */
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ }
+ __pyx_L3_first_run:;
+ if (unlikely(__pyx_sent_value != Py_None)) {
+ if (unlikely(__pyx_sent_value)) PyErr_SetString(PyExc_TypeError, "can't send non-None value to a just-started generator");
+ __PYX_ERR(0, 340, __pyx_L1_error)
+ }
+ if (unlikely(!__pyx_cur_scope->__pyx_genexpr_arg_0)) { __Pyx_RaiseUnboundLocalError(".0"); __PYX_ERR(0, 340, __pyx_L1_error) }
+ if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_genexpr_arg_0)) {
+ __pyx_t_1 = __pyx_cur_scope->__pyx_genexpr_arg_0; __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_2 = 0;
+ __pyx_t_3 = NULL;
+ } else {
+ __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_genexpr_arg_0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 340, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_3)) {
+ if (likely(PyList_CheckExact(__pyx_t_1))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 340, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ __pyx_t_4 = __Pyx_PyList_GetItemRefFast(__pyx_t_1, __pyx_t_2, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_2;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 340, __pyx_L1_error)
+ #endif
+ if (__pyx_t_2 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_4 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2));
+ #else
+ __pyx_t_4 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_2);
+ #endif
+ ++__pyx_t_2;
+ }
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 340, __pyx_L1_error)
+ } else {
+ __pyx_t_4 = __pyx_t_3(__pyx_t_1);
+ if (unlikely(!__pyx_t_4)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 340, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_4);
+ if ((likely(PyTuple_CheckExact(__pyx_t_4))) || (PyList_CheckExact(__pyx_t_4))) {
+ PyObject* sequence = __pyx_t_4;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 340, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_5 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_6 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_6);
+ } else {
+ __pyx_t_5 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ }
+ #else
+ __pyx_t_5 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ #endif
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_7 = PyObject_GetIter(__pyx_t_4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_7);
+ index = 0; __pyx_t_5 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_5)) goto __pyx_L6_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_5);
+ index = 1; __pyx_t_6 = __pyx_t_8(__pyx_t_7); if (unlikely(!__pyx_t_6)) goto __pyx_L6_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_6);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_8(__pyx_t_7), 2) < (0)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __pyx_t_8 = NULL;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L7_unpacking_done;
+ __pyx_L6_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_8 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 340, __pyx_L1_error)
+ __pyx_L7_unpacking_done:;
+ }
+ __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_6); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_cur_scope->__pyx_v_v = __pyx_t_9;
+ __pyx_cur_scope->__pyx_v_u = __pyx_t_10;
+ __pyx_t_10 = __Pyx_c_diff_double(__pyx_cur_scope->__pyx_v_v, __pyx_cur_scope->__pyx_v_u);
+ __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_10); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ __Pyx_XGIVEREF(__pyx_t_1);
+ __pyx_cur_scope->__pyx_t_0 = __pyx_t_1;
+ __pyx_cur_scope->__pyx_t_1 = __pyx_t_2;
+ __pyx_cur_scope->__pyx_t_2 = __pyx_t_3;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ /* return from generator, yielding value */
+ __pyx_generator->resume_label = 1;
+ return __pyx_r;
+ __pyx_L8_resume_from_yield:;
+ __pyx_t_1 = __pyx_cur_scope->__pyx_t_0;
+ __pyx_cur_scope->__pyx_t_0 = 0;
+ __Pyx_XGOTREF(__pyx_t_1);
+ __pyx_t_2 = __pyx_cur_scope->__pyx_t_1;
+ __pyx_t_3 = __pyx_cur_scope->__pyx_t_2;
+ if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 340, __pyx_L1_error)
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope);
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ if (__Pyx_PyErr_Occurred()) {
+ __Pyx_Generator_Replace_StopIteration(0);
+ __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ #if !CYTHON_USE_EXC_INFO_STACK
+ __Pyx_Coroutine_ResetAndClearException(__pyx_generator);
+ #endif
+ __pyx_generator->resume_label = -1;
+ __Pyx_Coroutine_clear((PyObject*)__pyx_generator);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":242
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * i=cython.int,
+ * j=cython.int,
+*/
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_6spline_to_curves(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_q, PyObject *__pyx_v_costs, double __pyx_v_tolerance, int __pyx_v_all_cubic) {
+ int __pyx_v_i;
+ int __pyx_v_j;
+ int __pyx_v_k;
+ int __pyx_v_start;
+ int __pyx_v_i_sol_count;
+ int __pyx_v_j_sol_count;
+ double __pyx_v_err;
+ double __pyx_v_error;
+ double __pyx_v_i_sol_error;
+ double __pyx_v_j_sol_error;
+ int __pyx_v_is_cubic;
+ int __pyx_v_count;
+ __pyx_t_double_complex __pyx_v_p0;
+ __pyx_t_double_complex __pyx_v_p1;
+ __pyx_t_double_complex __pyx_v_p2;
+ __pyx_t_double_complex __pyx_v_p3;
+ PyObject *__pyx_v_elevated_quadratics = NULL;
+ PyObject *__pyx_v_forced = NULL;
+ PyObject *__pyx_v_sols = NULL;
+ PyObject *__pyx_v_impossible = NULL;
+ PyObject *__pyx_v_best_sol = NULL;
+ PyObject *__pyx_v_this_count = NULL;
+ PyObject *__pyx_v_i_sol = NULL;
+ PyObject *__pyx_v_curve = NULL;
+ PyObject *__pyx_v_ts = NULL;
+ PyObject *__pyx_v_reconstructed_iter = NULL;
+ PyObject *__pyx_v_reconstructed = NULL;
+ PyObject *__pyx_v_reconst = NULL;
+ PyObject *__pyx_v_orig = NULL;
+ PyObject *__pyx_v_splits = NULL;
+ PyObject *__pyx_v_cubic = NULL;
+ PyObject *__pyx_v_curves = NULL;
+ int __pyx_8genexpr5__pyx_v_i;
+ PyObject *__pyx_gb_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_2generator1 = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ Py_ssize_t __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ Py_ssize_t __pyx_t_4;
+ int __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ long __pyx_t_9;
+ __pyx_t_double_complex __pyx_t_10;
+ int __pyx_t_11;
+ size_t __pyx_t_12;
+ int __pyx_t_13;
+ int __pyx_t_14;
+ int __pyx_t_15;
+ int __pyx_t_16;
+ double __pyx_t_17;
+ PyObject *__pyx_t_18 = NULL;
+ PyObject *__pyx_t_19 = NULL;
+ PyObject *__pyx_t_20 = NULL;
+ PyObject *__pyx_t_21 = NULL;
+ PyObject *__pyx_t_22 = NULL;
+ PyObject *(*__pyx_t_23)(PyObject *);
+ Py_ssize_t __pyx_t_24;
+ PyObject *(*__pyx_t_25)(PyObject *);
+ int __pyx_t_26;
+ double __pyx_t_27;
+ double __pyx_t_28;
+ __pyx_t_double_complex __pyx_t_29;
+ __pyx_t_double_complex __pyx_t_30;
+ __pyx_t_double_complex __pyx_t_31;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("spline_to_curves", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":275
+ * """
+ *
+ * assert len(q) >= 3, "quadratic spline requires at least 3 points" # <<<<<<<<<<<<<<
+ *
+ * # Elevate quadratic segments to cubic
+*/
+ #ifndef CYTHON_WITHOUT_ASSERTIONS
+ if (unlikely(__pyx_assertions_enabled())) {
+ __pyx_t_1 = PyObject_Length(__pyx_v_q); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 275, __pyx_L1_error)
+ __pyx_t_2 = (__pyx_t_1 >= 3);
+ if (unlikely(!__pyx_t_2)) {
+ __Pyx_Raise(((PyObject *)(((PyTypeObject*)PyExc_AssertionError))), __pyx_mstate_global->__pyx_kp_u_quadratic_spline_requires_at_lea, 0, 0);
+ __PYX_ERR(0, 275, __pyx_L1_error)
+ }
+ }
+ #else
+ if ((1)); else __PYX_ERR(0, 275, __pyx_L1_error)
+ #endif
+
+ /* "fontTools/qu2cu/qu2cu.py":278
+ *
+ * # Elevate quadratic segments to cubic
+ * elevated_quadratics = [ # <<<<<<<<<<<<<<
+ * elevate_quadratic(*q[i : i + 3]) for i in range(0, len(q) - 2, 2)
+ * ]
+*/
+ { /* enter inner scope */
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+
+ /* "fontTools/qu2cu/qu2cu.py":279
+ * # Elevate quadratic segments to cubic
+ * elevated_quadratics = [
+ * elevate_quadratic(*q[i : i + 3]) for i in range(0, len(q) - 2, 2) # <<<<<<<<<<<<<<
+ * ]
+ *
+*/
+ __pyx_t_1 = PyObject_Length(__pyx_v_q); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 279, __pyx_L1_error)
+ __pyx_t_4 = (__pyx_t_1 - 2);
+ __pyx_t_1 = __pyx_t_4;
+ for (__pyx_t_5 = 0; __pyx_t_5 < __pyx_t_1; __pyx_t_5+=2) {
+ __pyx_8genexpr5__pyx_v_i = __pyx_t_5;
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_elevate_quadratic); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 279, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = __Pyx_PyObject_GetSlice(__pyx_v_q, __pyx_8genexpr5__pyx_v_i, (__pyx_8genexpr5__pyx_v_i + 3), NULL, NULL, NULL, 1, 1, 1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 279, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __Pyx_PySequence_Tuple(__pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 279, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = __Pyx_PyObject_Call(__pyx_t_6, __pyx_t_8, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 279, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(__Pyx_ListComp_Append(__pyx_t_3, (PyObject*)__pyx_t_7))) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ }
+ } /* exit inner scope */
+ __pyx_v_elevated_quadratics = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":283
+ *
+ * # Find sharp corners; they have to be oncurves for sure.
+ * forced = set() # <<<<<<<<<<<<<<
+ * for i in range(1, len(elevated_quadratics)):
+ * p0 = elevated_quadratics[i - 1][2]
+*/
+ __pyx_t_3 = PySet_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 283, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_v_forced = ((PyObject*)__pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":284
+ * # Find sharp corners; they have to be oncurves for sure.
+ * forced = set()
+ * for i in range(1, len(elevated_quadratics)): # <<<<<<<<<<<<<<
+ * p0 = elevated_quadratics[i - 1][2]
+ * p1 = elevated_quadratics[i][0]
+*/
+ __pyx_t_4 = __Pyx_PyList_GET_SIZE(__pyx_v_elevated_quadratics); if (unlikely(__pyx_t_4 == ((Py_ssize_t)-1))) __PYX_ERR(0, 284, __pyx_L1_error)
+ __pyx_t_1 = __pyx_t_4;
+ for (__pyx_t_5 = 1; __pyx_t_5 < __pyx_t_1; __pyx_t_5+=1) {
+ __pyx_v_i = __pyx_t_5;
+
+ /* "fontTools/qu2cu/qu2cu.py":285
+ * forced = set()
+ * for i in range(1, len(elevated_quadratics)):
+ * p0 = elevated_quadratics[i - 1][2] # <<<<<<<<<<<<<<
+ * p1 = elevated_quadratics[i][0]
+ * p2 = elevated_quadratics[i][1]
+*/
+ __pyx_t_9 = (__pyx_v_i - 1);
+ __pyx_t_3 = __Pyx_GetItemInt_List(__pyx_v_elevated_quadratics, __pyx_t_9, long, 1, __Pyx_PyLong_From_long, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 285, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_3, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 285, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_7); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 285, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_p0 = __pyx_t_10;
+
+ /* "fontTools/qu2cu/qu2cu.py":286
+ * for i in range(1, len(elevated_quadratics)):
+ * p0 = elevated_quadratics[i - 1][2]
+ * p1 = elevated_quadratics[i][0] # <<<<<<<<<<<<<<
+ * p2 = elevated_quadratics[i][1]
+ * if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0):
+*/
+ __pyx_t_7 = __Pyx_GetItemInt_List(__pyx_v_elevated_quadratics, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 286, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_3 = __Pyx_GetItemInt(__pyx_t_7, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 286, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 286, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_p1 = __pyx_t_10;
+
+ /* "fontTools/qu2cu/qu2cu.py":287
+ * p0 = elevated_quadratics[i - 1][2]
+ * p1 = elevated_quadratics[i][0]
+ * p2 = elevated_quadratics[i][1] # <<<<<<<<<<<<<<
+ * if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0):
+ * forced.add(i)
+*/
+ __pyx_t_3 = __Pyx_GetItemInt_List(__pyx_v_elevated_quadratics, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_t_3, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_7); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 287, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_p2 = __pyx_t_10;
+
+ /* "fontTools/qu2cu/qu2cu.py":288
+ * p1 = elevated_quadratics[i][0]
+ * p2 = elevated_quadratics[i][1]
+ * if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0): # <<<<<<<<<<<<<<
+ * forced.add(i)
+ *
+*/
+ __pyx_t_2 = ((__Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0)) + __Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p1))) > (__pyx_v_tolerance + __Pyx_c_abs_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p0))));
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":289
+ * p2 = elevated_quadratics[i][1]
+ * if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0):
+ * forced.add(i) # <<<<<<<<<<<<<<
+ *
+ * # Dynamic-Programming to find the solution with fewest number of
+*/
+ __pyx_t_7 = __Pyx_PyLong_From_int(__pyx_v_i); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 289, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_11 = PySet_Add(__pyx_v_forced, __pyx_t_7); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 289, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":288
+ * p1 = elevated_quadratics[i][0]
+ * p2 = elevated_quadratics[i][1]
+ * if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0): # <<<<<<<<<<<<<<
+ * forced.add(i)
+ *
+*/
+ }
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":293
+ * # Dynamic-Programming to find the solution with fewest number of
+ * # cubic curves, and within those the one with smallest error.
+ * sols = [Solution(0, 0, 0, False)] # <<<<<<<<<<<<<<
+ * impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False)
+ * start = 0
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_Solution); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 293, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_7, __pyx_mstate_global->__pyx_tuple[0], NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 293, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = PyList_New(1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 293, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_GIVEREF(__pyx_t_3);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_7, 0, __pyx_t_3) != (0)) __PYX_ERR(0, 293, __pyx_L1_error);
+ __pyx_t_3 = 0;
+ __pyx_v_sols = ((PyObject*)__pyx_t_7);
+ __pyx_t_7 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":294
+ * # cubic curves, and within those the one with smallest error.
+ * sols = [Solution(0, 0, 0, False)]
+ * impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False) # <<<<<<<<<<<<<<
+ * start = 0
+ * for i in range(1, len(elevated_quadratics) + 1):
+*/
+ __pyx_t_3 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_Solution); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 294, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_4 = __Pyx_PyList_GET_SIZE(__pyx_v_elevated_quadratics); if (unlikely(__pyx_t_4 == ((Py_ssize_t)-1))) __PYX_ERR(0, 294, __pyx_L1_error)
+ __pyx_t_6 = PyLong_FromSsize_t(((__pyx_t_4 * 3) + 1)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 294, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_12 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_8))) {
+ __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_8);
+ assert(__pyx_t_3);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_8);
+ __Pyx_INCREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_8, __pyx__function);
+ __pyx_t_12 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_3, __pyx_t_6, __pyx_mstate_global->__pyx_int_0, __pyx_mstate_global->__pyx_int_1, Py_False};
+ __pyx_t_7 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_8, __pyx_callargs+__pyx_t_12, (5-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 294, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ }
+ __pyx_v_impossible = __pyx_t_7;
+ __pyx_t_7 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":295
+ * sols = [Solution(0, 0, 0, False)]
+ * impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False)
+ * start = 0 # <<<<<<<<<<<<<<
+ * for i in range(1, len(elevated_quadratics) + 1):
+ * best_sol = impossible
+*/
+ __pyx_v_start = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":296
+ * impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False)
+ * start = 0
+ * for i in range(1, len(elevated_quadratics) + 1): # <<<<<<<<<<<<<<
+ * best_sol = impossible
+ * for j in range(start, i):
+*/
+ __pyx_t_4 = __Pyx_PyList_GET_SIZE(__pyx_v_elevated_quadratics); if (unlikely(__pyx_t_4 == ((Py_ssize_t)-1))) __PYX_ERR(0, 296, __pyx_L1_error)
+ __pyx_t_1 = (__pyx_t_4 + 1);
+ __pyx_t_4 = __pyx_t_1;
+ for (__pyx_t_5 = 1; __pyx_t_5 < __pyx_t_4; __pyx_t_5+=1) {
+ __pyx_v_i = __pyx_t_5;
+
+ /* "fontTools/qu2cu/qu2cu.py":297
+ * start = 0
+ * for i in range(1, len(elevated_quadratics) + 1):
+ * best_sol = impossible # <<<<<<<<<<<<<<
+ * for j in range(start, i):
+ * j_sol_count, j_sol_error = sols[j].num_points, sols[j].error
+*/
+ __Pyx_INCREF(__pyx_v_impossible);
+ __Pyx_XDECREF_SET(__pyx_v_best_sol, __pyx_v_impossible);
+
+ /* "fontTools/qu2cu/qu2cu.py":298
+ * for i in range(1, len(elevated_quadratics) + 1):
+ * best_sol = impossible
+ * for j in range(start, i): # <<<<<<<<<<<<<<
+ * j_sol_count, j_sol_error = sols[j].num_points, sols[j].error
+ *
+*/
+ __pyx_t_13 = __pyx_v_i;
+ __pyx_t_14 = __pyx_t_13;
+ for (__pyx_t_15 = __pyx_v_start; __pyx_t_15 < __pyx_t_14; __pyx_t_15+=1) {
+ __pyx_v_j = __pyx_t_15;
+
+ /* "fontTools/qu2cu/qu2cu.py":299
+ * best_sol = impossible
+ * for j in range(start, i):
+ * j_sol_count, j_sol_error = sols[j].num_points, sols[j].error # <<<<<<<<<<<<<<
+ *
+ * if not all_cubic:
+*/
+ __pyx_t_7 = __Pyx_GetItemInt_List(__pyx_v_sols, __pyx_v_j, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_num_points); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_16 = __Pyx_PyLong_As_int(__pyx_t_8); if (unlikely((__pyx_t_16 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_GetItemInt_List(__pyx_v_sols, __pyx_v_j, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_error); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_17 = __Pyx_PyFloat_AsDouble(__pyx_t_7); if (unlikely((__pyx_t_17 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 299, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_j_sol_count = __pyx_t_16;
+ __pyx_v_j_sol_error = __pyx_t_17;
+
+ /* "fontTools/qu2cu/qu2cu.py":301
+ * j_sol_count, j_sol_error = sols[j].num_points, sols[j].error
+ *
+ * if not all_cubic: # <<<<<<<<<<<<<<
+ * # Solution with quadratics between j:i
+ * this_count = costs[2 * i - 1] - costs[2 * j] + 1
+*/
+ __pyx_t_2 = (!(__pyx_v_all_cubic != 0));
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":303
+ * if not all_cubic:
+ * # Solution with quadratics between j:i
+ * this_count = costs[2 * i - 1] - costs[2 * j] + 1 # <<<<<<<<<<<<<<
+ * i_sol_count = j_sol_count + this_count
+ * i_sol_error = j_sol_error
+*/
+ __pyx_t_9 = ((2 * __pyx_v_i) - 1);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_costs, __pyx_t_9, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_9 = (2 * __pyx_v_j);
+ __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_costs, __pyx_t_9, long, 1, __Pyx_PyLong_From_long, 0, 1, 1, 1, __Pyx_ReferenceSharing_FunctionArgument); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_6 = PyNumber_Subtract(__pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PyLong_AddObjC(__pyx_t_6, __pyx_mstate_global->__pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 303, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_this_count, __pyx_t_8);
+ __pyx_t_8 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":304
+ * # Solution with quadratics between j:i
+ * this_count = costs[2 * i - 1] - costs[2 * j] + 1
+ * i_sol_count = j_sol_count + this_count # <<<<<<<<<<<<<<
+ * i_sol_error = j_sol_error
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+*/
+ __pyx_t_8 = __Pyx_PyLong_From_int(__pyx_v_j_sol_count); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 304, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_6 = PyNumber_Add(__pyx_t_8, __pyx_v_this_count); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 304, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_16 = __Pyx_PyLong_As_int(__pyx_t_6); if (unlikely((__pyx_t_16 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 304, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_i_sol_count = __pyx_t_16;
+
+ /* "fontTools/qu2cu/qu2cu.py":305
+ * this_count = costs[2 * i - 1] - costs[2 * j] + 1
+ * i_sol_count = j_sol_count + this_count
+ * i_sol_error = j_sol_error # <<<<<<<<<<<<<<
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+ * if i_sol < best_sol:
+*/
+ __pyx_v_i_sol_error = __pyx_v_j_sol_error;
+
+ /* "fontTools/qu2cu/qu2cu.py":306
+ * i_sol_count = j_sol_count + this_count
+ * i_sol_error = j_sol_error
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False) # <<<<<<<<<<<<<<
+ * if i_sol < best_sol:
+ * best_sol = i_sol
+*/
+ __pyx_t_8 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_Solution); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 306, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_3 = __Pyx_PyLong_From_int(__pyx_v_i_sol_count); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 306, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_18 = PyFloat_FromDouble(__pyx_v_i_sol_error); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 306, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __pyx_t_19 = __Pyx_PyLong_From_int((__pyx_v_i - __pyx_v_j)); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 306, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __pyx_t_12 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_7))) {
+ __pyx_t_8 = PyMethod_GET_SELF(__pyx_t_7);
+ assert(__pyx_t_8);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_7);
+ __Pyx_INCREF(__pyx_t_8);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_7, __pyx__function);
+ __pyx_t_12 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_8, __pyx_t_3, __pyx_t_18, __pyx_t_19, Py_False};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_7, __pyx_callargs+__pyx_t_12, (5-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 306, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_XDECREF_SET(__pyx_v_i_sol, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":307
+ * i_sol_error = j_sol_error
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+ * if i_sol < best_sol: # <<<<<<<<<<<<<<
+ * best_sol = i_sol
+ *
+*/
+ __pyx_t_6 = PyObject_RichCompare(__pyx_v_i_sol, __pyx_v_best_sol, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 307, __pyx_L1_error)
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 307, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":308
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+ * if i_sol < best_sol:
+ * best_sol = i_sol # <<<<<<<<<<<<<<
+ *
+ * if this_count <= 3:
+*/
+ __Pyx_INCREF(__pyx_v_i_sol);
+ __Pyx_DECREF_SET(__pyx_v_best_sol, __pyx_v_i_sol);
+
+ /* "fontTools/qu2cu/qu2cu.py":307
+ * i_sol_error = j_sol_error
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+ * if i_sol < best_sol: # <<<<<<<<<<<<<<
+ * best_sol = i_sol
+ *
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":310
+ * best_sol = i_sol
+ *
+ * if this_count <= 3: # <<<<<<<<<<<<<<
+ * # Can't get any better than this in the path below
+ * continue
+*/
+ __pyx_t_6 = PyObject_RichCompare(__pyx_v_this_count, __pyx_mstate_global->__pyx_int_3, Py_LE); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 310, __pyx_L1_error)
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 310, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":312
+ * if this_count <= 3:
+ * # Can't get any better than this in the path below
+ * continue # <<<<<<<<<<<<<<
+ *
+ * # Fit elevated_quadratics[j:i] into one cubic
+*/
+ goto __pyx_L10_continue;
+
+ /* "fontTools/qu2cu/qu2cu.py":310
+ * best_sol = i_sol
+ *
+ * if this_count <= 3: # <<<<<<<<<<<<<<
+ * # Can't get any better than this in the path below
+ * continue
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":301
+ * j_sol_count, j_sol_error = sols[j].num_points, sols[j].error
+ *
+ * if not all_cubic: # <<<<<<<<<<<<<<
+ * # Solution with quadratics between j:i
+ * this_count = costs[2 * i - 1] - costs[2 * j] + 1
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":315
+ *
+ * # Fit elevated_quadratics[j:i] into one cubic
+ * try: # <<<<<<<<<<<<<<
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ * except ZeroDivisionError:
+*/
+ {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ __Pyx_ExceptionSave(&__pyx_t_20, &__pyx_t_21, &__pyx_t_22);
+ __Pyx_XGOTREF(__pyx_t_20);
+ __Pyx_XGOTREF(__pyx_t_21);
+ __Pyx_XGOTREF(__pyx_t_22);
+ /*try:*/ {
+
+ /* "fontTools/qu2cu/qu2cu.py":316
+ * # Fit elevated_quadratics[j:i] into one cubic
+ * try:
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j) # <<<<<<<<<<<<<<
+ * except ZeroDivisionError:
+ * continue
+*/
+ __pyx_t_6 = __pyx_f_9fontTools_5qu2cu_5qu2cu_merge_curves(__pyx_v_elevated_quadratics, __pyx_v_j, (__pyx_v_i - __pyx_v_j)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ if ((likely(PyTuple_CheckExact(__pyx_t_6))) || (PyList_CheckExact(__pyx_t_6))) {
+ PyObject* sequence = __pyx_t_6;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 316, __pyx_L15_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_7 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_7);
+ __pyx_t_19 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_19);
+ } else {
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_XGOTREF(__pyx_t_7);
+ __pyx_t_19 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_XGOTREF(__pyx_t_19);
+ }
+ #else
+ __pyx_t_7 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_19 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ #endif
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_18 = PyObject_GetIter(__pyx_t_6); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_23 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_18);
+ index = 0; __pyx_t_7 = __pyx_t_23(__pyx_t_18); if (unlikely(!__pyx_t_7)) goto __pyx_L23_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_7);
+ index = 1; __pyx_t_19 = __pyx_t_23(__pyx_t_18); if (unlikely(!__pyx_t_19)) goto __pyx_L23_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_19);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_23(__pyx_t_18), 2) < (0)) __PYX_ERR(0, 316, __pyx_L15_error)
+ __pyx_t_23 = NULL;
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ goto __pyx_L24_unpacking_done;
+ __pyx_L23_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __pyx_t_23 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 316, __pyx_L15_error)
+ __pyx_L24_unpacking_done:;
+ }
+ __Pyx_XDECREF_SET(__pyx_v_curve, __pyx_t_7);
+ __pyx_t_7 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_ts, __pyx_t_19);
+ __pyx_t_19 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":315
+ *
+ * # Fit elevated_quadratics[j:i] into one cubic
+ * try: # <<<<<<<<<<<<<<
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ * except ZeroDivisionError:
+*/
+ }
+ __Pyx_XDECREF(__pyx_t_20); __pyx_t_20 = 0;
+ __Pyx_XDECREF(__pyx_t_21); __pyx_t_21 = 0;
+ __Pyx_XDECREF(__pyx_t_22); __pyx_t_22 = 0;
+ goto __pyx_L22_try_end;
+ __pyx_L15_error:;
+ __Pyx_XDECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __Pyx_XDECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":317
+ * try:
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ * except ZeroDivisionError: # <<<<<<<<<<<<<<
+ * continue
+ *
+*/
+ __pyx_t_16 = __Pyx_PyErr_ExceptionMatches(((PyObject *)(((PyTypeObject*)PyExc_ZeroDivisionError))));
+ if (__pyx_t_16) {
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.spline_to_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ if (__Pyx_GetException(&__pyx_t_6, &__pyx_t_19, &__pyx_t_7) < 0) __PYX_ERR(0, 317, __pyx_L17_except_error)
+ __Pyx_XGOTREF(__pyx_t_6);
+ __Pyx_XGOTREF(__pyx_t_19);
+ __Pyx_XGOTREF(__pyx_t_7);
+
+ /* "fontTools/qu2cu/qu2cu.py":318
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ * except ZeroDivisionError:
+ * continue # <<<<<<<<<<<<<<
+ *
+ * # Now reconstruct the segments from the fitted curve
+*/
+ goto __pyx_L25_except_continue;
+ __pyx_L25_except_continue:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ goto __pyx_L21_try_continue;
+ }
+ goto __pyx_L17_except_error;
+
+ /* "fontTools/qu2cu/qu2cu.py":315
+ *
+ * # Fit elevated_quadratics[j:i] into one cubic
+ * try: # <<<<<<<<<<<<<<
+ * curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ * except ZeroDivisionError:
+*/
+ __pyx_L17_except_error:;
+ __Pyx_XGIVEREF(__pyx_t_20);
+ __Pyx_XGIVEREF(__pyx_t_21);
+ __Pyx_XGIVEREF(__pyx_t_22);
+ __Pyx_ExceptionReset(__pyx_t_20, __pyx_t_21, __pyx_t_22);
+ goto __pyx_L1_error;
+ __pyx_L21_try_continue:;
+ __Pyx_XGIVEREF(__pyx_t_20);
+ __Pyx_XGIVEREF(__pyx_t_21);
+ __Pyx_XGIVEREF(__pyx_t_22);
+ __Pyx_ExceptionReset(__pyx_t_20, __pyx_t_21, __pyx_t_22);
+ goto __pyx_L10_continue;
+ __pyx_L22_try_end:;
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":321
+ *
+ * # Now reconstruct the segments from the fitted curve
+ * reconstructed_iter = splitCubicAtTC(*curve, *ts) # <<<<<<<<<<<<<<
+ * reconstructed = []
+ *
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_splitCubicAtTC); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_19 = __Pyx_PySequence_Tuple(__pyx_v_curve); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __pyx_t_6 = __Pyx_PySequence_Tuple(__pyx_v_ts); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_18 = PyNumber_Add(__pyx_t_19, __pyx_t_6); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = __Pyx_PyObject_Call(__pyx_t_7, __pyx_t_18, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 321, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __Pyx_XDECREF_SET(__pyx_v_reconstructed_iter, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":322
+ * # Now reconstruct the segments from the fitted curve
+ * reconstructed_iter = splitCubicAtTC(*curve, *ts)
+ * reconstructed = [] # <<<<<<<<<<<<<<
+ *
+ * # Knot errors
+*/
+ __pyx_t_6 = PyList_New(0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 322, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_XDECREF_SET(__pyx_v_reconstructed, ((PyObject*)__pyx_t_6));
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":325
+ *
+ * # Knot errors
+ * error = 0 # <<<<<<<<<<<<<<
+ * for k, reconst in enumerate(reconstructed_iter):
+ * orig = elevated_quadratics[j + k]
+*/
+ __pyx_v_error = 0.0;
+
+ /* "fontTools/qu2cu/qu2cu.py":326
+ * # Knot errors
+ * error = 0
+ * for k, reconst in enumerate(reconstructed_iter): # <<<<<<<<<<<<<<
+ * orig = elevated_quadratics[j + k]
+ * err = abs(reconst[3] - orig[3])
+*/
+ __pyx_t_16 = 0;
+ if (likely(PyList_CheckExact(__pyx_v_reconstructed_iter)) || PyTuple_CheckExact(__pyx_v_reconstructed_iter)) {
+ __pyx_t_6 = __pyx_v_reconstructed_iter; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_24 = 0;
+ __pyx_t_25 = NULL;
+ } else {
+ __pyx_t_24 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_v_reconstructed_iter); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 326, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_25 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_25)) __PYX_ERR(0, 326, __pyx_L1_error)
+ }
+ for (;;) {
+ if (likely(!__pyx_t_25)) {
+ if (likely(PyList_CheckExact(__pyx_t_6))) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 326, __pyx_L1_error)
+ #endif
+ if (__pyx_t_24 >= __pyx_temp) break;
+ }
+ __pyx_t_18 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_24, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_24;
+ } else {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 326, __pyx_L1_error)
+ #endif
+ if (__pyx_t_24 >= __pyx_temp) break;
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_18 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_24));
+ #else
+ __pyx_t_18 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_24);
+ #endif
+ ++__pyx_t_24;
+ }
+ if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 326, __pyx_L1_error)
+ } else {
+ __pyx_t_18 = __pyx_t_25(__pyx_t_6);
+ if (unlikely(!__pyx_t_18)) {
+ PyObject* exc_type = PyErr_Occurred();
+ if (exc_type) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 326, __pyx_L1_error)
+ PyErr_Clear();
+ }
+ break;
+ }
+ }
+ __Pyx_GOTREF(__pyx_t_18);
+ __Pyx_XDECREF_SET(__pyx_v_reconst, __pyx_t_18);
+ __pyx_t_18 = 0;
+ __pyx_v_k = __pyx_t_16;
+ __pyx_t_16 = (__pyx_t_16 + 1);
+
+ /* "fontTools/qu2cu/qu2cu.py":327
+ * error = 0
+ * for k, reconst in enumerate(reconstructed_iter):
+ * orig = elevated_quadratics[j + k] # <<<<<<<<<<<<<<
+ * err = abs(reconst[3] - orig[3])
+ * error = max(error, err)
+*/
+ __pyx_t_26 = (__pyx_v_j + __pyx_v_k);
+ __pyx_t_18 = __Pyx_GetItemInt_List(__pyx_v_elevated_quadratics, __pyx_t_26, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 327, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __Pyx_XDECREF_SET(__pyx_v_orig, __pyx_t_18);
+ __pyx_t_18 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":328
+ * for k, reconst in enumerate(reconstructed_iter):
+ * orig = elevated_quadratics[j + k]
+ * err = abs(reconst[3] - orig[3]) # <<<<<<<<<<<<<<
+ * error = max(error, err)
+ * if error > tolerance:
+*/
+ __pyx_t_18 = __Pyx_GetItemInt(__pyx_v_reconst, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __pyx_t_7 = __Pyx_GetItemInt(__pyx_v_orig, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_19 = PyNumber_Subtract(__pyx_t_18, __pyx_t_7); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = __Pyx_PyNumber_Absolute(__pyx_t_19); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __pyx_t_17 = __Pyx_PyFloat_AsDouble(__pyx_t_7); if (unlikely((__pyx_t_17 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 328, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_v_err = __pyx_t_17;
+
+ /* "fontTools/qu2cu/qu2cu.py":329
+ * orig = elevated_quadratics[j + k]
+ * err = abs(reconst[3] - orig[3])
+ * error = max(error, err) # <<<<<<<<<<<<<<
+ * if error > tolerance:
+ * break
+*/
+ __pyx_t_17 = __pyx_v_err;
+ __pyx_t_27 = __pyx_v_error;
+ __pyx_t_2 = (__pyx_t_17 > __pyx_t_27);
+ if (__pyx_t_2) {
+ __pyx_t_28 = __pyx_t_17;
+ } else {
+ __pyx_t_28 = __pyx_t_27;
+ }
+ __pyx_v_error = __pyx_t_28;
+
+ /* "fontTools/qu2cu/qu2cu.py":330
+ * err = abs(reconst[3] - orig[3])
+ * error = max(error, err)
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * break
+ * reconstructed.append(reconst)
+*/
+ __pyx_t_2 = (__pyx_v_error > __pyx_v_tolerance);
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":331
+ * error = max(error, err)
+ * if error > tolerance:
+ * break # <<<<<<<<<<<<<<
+ * reconstructed.append(reconst)
+ * if error > tolerance:
+*/
+ goto __pyx_L28_break;
+
+ /* "fontTools/qu2cu/qu2cu.py":330
+ * err = abs(reconst[3] - orig[3])
+ * error = max(error, err)
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * break
+ * reconstructed.append(reconst)
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":332
+ * if error > tolerance:
+ * break
+ * reconstructed.append(reconst) # <<<<<<<<<<<<<<
+ * if error > tolerance:
+ * # Not feasible
+*/
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_reconstructed, __pyx_v_reconst); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 332, __pyx_L1_error)
+
+ /* "fontTools/qu2cu/qu2cu.py":326
+ * # Knot errors
+ * error = 0
+ * for k, reconst in enumerate(reconstructed_iter): # <<<<<<<<<<<<<<
+ * orig = elevated_quadratics[j + k]
+ * err = abs(reconst[3] - orig[3])
+*/
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L30_for_end;
+ __pyx_L28_break:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L30_for_end;
+ __pyx_L30_for_end:;
+
+ /* "fontTools/qu2cu/qu2cu.py":333
+ * break
+ * reconstructed.append(reconst)
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * # Not feasible
+ * continue
+*/
+ __pyx_t_2 = (__pyx_v_error > __pyx_v_tolerance);
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":335
+ * if error > tolerance:
+ * # Not feasible
+ * continue # <<<<<<<<<<<<<<
+ *
+ * # Interior errors
+*/
+ goto __pyx_L10_continue;
+
+ /* "fontTools/qu2cu/qu2cu.py":333
+ * break
+ * reconstructed.append(reconst)
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * # Not feasible
+ * continue
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":338
+ *
+ * # Interior errors
+ * for k, reconst in enumerate(reconstructed): # <<<<<<<<<<<<<<
+ * orig = elevated_quadratics[j + k]
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+*/
+ __pyx_t_16 = 0;
+ __pyx_t_6 = __pyx_v_reconstructed; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_24 = 0;
+ for (;;) {
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 338, __pyx_L1_error)
+ #endif
+ if (__pyx_t_24 >= __pyx_temp) break;
+ }
+ __pyx_t_7 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_24, __Pyx_ReferenceSharing_OwnStrongReference);
+ ++__pyx_t_24;
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 338, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_XDECREF_SET(__pyx_v_reconst, __pyx_t_7);
+ __pyx_t_7 = 0;
+ __pyx_v_k = __pyx_t_16;
+ __pyx_t_16 = (__pyx_t_16 + 1);
+
+ /* "fontTools/qu2cu/qu2cu.py":339
+ * # Interior errors
+ * for k, reconst in enumerate(reconstructed):
+ * orig = elevated_quadratics[j + k] # <<<<<<<<<<<<<<
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+ *
+*/
+ __pyx_t_26 = (__pyx_v_j + __pyx_v_k);
+ __pyx_t_7 = __Pyx_GetItemInt_List(__pyx_v_elevated_quadratics, __pyx_t_26, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 339, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_XDECREF_SET(__pyx_v_orig, __pyx_t_7);
+ __pyx_t_7 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":340
+ * for k, reconst in enumerate(reconstructed):
+ * orig = elevated_quadratics[j + k]
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig)) # <<<<<<<<<<<<<<
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+*/
+ __pyx_t_19 = NULL;
+ __pyx_t_12 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_19, __pyx_v_reconst, __pyx_v_orig};
+ __pyx_t_7 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_zip, __pyx_callargs+__pyx_t_12, (3-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_19); __pyx_t_19 = 0;
+ if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ }
+ __pyx_t_19 = __pyx_pf_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_genexpr(NULL, __pyx_t_7); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = __Pyx_PySequence_Tuple(__pyx_t_19); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ if (1) {
+ PyObject* sequence = __pyx_t_7;
+ Py_ssize_t size = __Pyx_PyTuple_GET_SIZE(sequence);
+ if (unlikely(size != 4)) {
+ if (size > 4) __Pyx_RaiseTooManyValuesError(4);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 340, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ __pyx_t_19 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_19);
+ __pyx_t_18 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_18);
+ __pyx_t_3 = PyTuple_GET_ITEM(sequence, 2);
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_8 = PyTuple_GET_ITEM(sequence, 3);
+ __Pyx_INCREF(__pyx_t_8);
+ #else
+ {
+ Py_ssize_t i;
+ PyObject** temps[4] = {&__pyx_t_19,&__pyx_t_18,&__pyx_t_3,&__pyx_t_8};
+ for (i=0; i < 4; i++) {
+ PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(item);
+ *(temps[i]) = item;
+ }
+ }
+ #endif
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ }
+ __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_19); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __pyx_t_29 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_18); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __pyx_t_30 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_31 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_v_p0 = __pyx_t_10;
+ __pyx_v_p1 = __pyx_t_29;
+ __pyx_v_p2 = __pyx_t_30;
+ __pyx_v_p3 = __pyx_t_31;
+
+ /* "fontTools/qu2cu/qu2cu.py":342
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance): # <<<<<<<<<<<<<<
+ * error = tolerance + 1
+ * break
+*/
+ __pyx_t_26 = __pyx_f_9fontTools_5qu2cu_5qu2cu_cubic_farthest_fit_inside(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3, __pyx_v_tolerance); if (unlikely(__pyx_t_26 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 342, __pyx_L1_error)
+ __pyx_t_2 = (!(__pyx_t_26 != 0));
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":343
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+ * error = tolerance + 1 # <<<<<<<<<<<<<<
+ * break
+ * if error > tolerance:
+*/
+ __pyx_v_error = (__pyx_v_tolerance + 1.0);
+
+ /* "fontTools/qu2cu/qu2cu.py":344
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+ * error = tolerance + 1
+ * break # <<<<<<<<<<<<<<
+ * if error > tolerance:
+ * # Not feasible
+*/
+ goto __pyx_L33_break;
+
+ /* "fontTools/qu2cu/qu2cu.py":342
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+ *
+ * if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance): # <<<<<<<<<<<<<<
+ * error = tolerance + 1
+ * break
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":338
+ *
+ * # Interior errors
+ * for k, reconst in enumerate(reconstructed): # <<<<<<<<<<<<<<
+ * orig = elevated_quadratics[j + k]
+ * p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+*/
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L35_for_end;
+ __pyx_L33_break:;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ goto __pyx_L35_for_end;
+ __pyx_L35_for_end:;
+
+ /* "fontTools/qu2cu/qu2cu.py":345
+ * error = tolerance + 1
+ * break
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * # Not feasible
+ * continue
+*/
+ __pyx_t_2 = (__pyx_v_error > __pyx_v_tolerance);
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":347
+ * if error > tolerance:
+ * # Not feasible
+ * continue # <<<<<<<<<<<<<<
+ *
+ * # Save best solution
+*/
+ goto __pyx_L10_continue;
+
+ /* "fontTools/qu2cu/qu2cu.py":345
+ * error = tolerance + 1
+ * break
+ * if error > tolerance: # <<<<<<<<<<<<<<
+ * # Not feasible
+ * continue
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":350
+ *
+ * # Save best solution
+ * i_sol_count = j_sol_count + 3 # <<<<<<<<<<<<<<
+ * i_sol_error = max(j_sol_error, error)
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+*/
+ __pyx_v_i_sol_count = (__pyx_v_j_sol_count + 3);
+
+ /* "fontTools/qu2cu/qu2cu.py":351
+ * # Save best solution
+ * i_sol_count = j_sol_count + 3
+ * i_sol_error = max(j_sol_error, error) # <<<<<<<<<<<<<<
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+ * if i_sol < best_sol:
+*/
+ __pyx_t_28 = __pyx_v_error;
+ __pyx_t_17 = __pyx_v_j_sol_error;
+ __pyx_t_2 = (__pyx_t_28 > __pyx_t_17);
+ if (__pyx_t_2) {
+ __pyx_t_27 = __pyx_t_28;
+ } else {
+ __pyx_t_27 = __pyx_t_17;
+ }
+ __pyx_v_i_sol_error = __pyx_t_27;
+
+ /* "fontTools/qu2cu/qu2cu.py":352
+ * i_sol_count = j_sol_count + 3
+ * i_sol_error = max(j_sol_error, error)
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True) # <<<<<<<<<<<<<<
+ * if i_sol < best_sol:
+ * best_sol = i_sol
+*/
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_Solution); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_3 = __Pyx_PyLong_From_int(__pyx_v_i_sol_count); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_18 = PyFloat_FromDouble(__pyx_v_i_sol_error); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __pyx_t_19 = __Pyx_PyLong_From_int((__pyx_v_i - __pyx_v_j)); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __pyx_t_12 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_8))) {
+ __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_8);
+ assert(__pyx_t_7);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_8);
+ __Pyx_INCREF(__pyx_t_7);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_8, __pyx__function);
+ __pyx_t_12 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[5] = {__pyx_t_7, __pyx_t_3, __pyx_t_18, __pyx_t_19, Py_True};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_8, __pyx_callargs+__pyx_t_12, (5-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __Pyx_XDECREF_SET(__pyx_v_i_sol, __pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":353
+ * i_sol_error = max(j_sol_error, error)
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+ * if i_sol < best_sol: # <<<<<<<<<<<<<<
+ * best_sol = i_sol
+ *
+*/
+ __pyx_t_6 = PyObject_RichCompare(__pyx_v_i_sol, __pyx_v_best_sol, Py_LT); __Pyx_XGOTREF(__pyx_t_6); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 353, __pyx_L1_error)
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_6); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 353, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":354
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+ * if i_sol < best_sol:
+ * best_sol = i_sol # <<<<<<<<<<<<<<
+ *
+ * if i_sol_count == 3:
+*/
+ __Pyx_INCREF(__pyx_v_i_sol);
+ __Pyx_DECREF_SET(__pyx_v_best_sol, __pyx_v_i_sol);
+
+ /* "fontTools/qu2cu/qu2cu.py":353
+ * i_sol_error = max(j_sol_error, error)
+ * i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+ * if i_sol < best_sol: # <<<<<<<<<<<<<<
+ * best_sol = i_sol
+ *
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":356
+ * best_sol = i_sol
+ *
+ * if i_sol_count == 3: # <<<<<<<<<<<<<<
+ * # Can't get any better than this
+ * break
+*/
+ __pyx_t_2 = (__pyx_v_i_sol_count == 3);
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":358
+ * if i_sol_count == 3:
+ * # Can't get any better than this
+ * break # <<<<<<<<<<<<<<
+ *
+ * sols.append(best_sol)
+*/
+ goto __pyx_L11_break;
+
+ /* "fontTools/qu2cu/qu2cu.py":356
+ * best_sol = i_sol
+ *
+ * if i_sol_count == 3: # <<<<<<<<<<<<<<
+ * # Can't get any better than this
+ * break
+*/
+ }
+ __pyx_L10_continue:;
+ }
+ __pyx_L11_break:;
+
+ /* "fontTools/qu2cu/qu2cu.py":360
+ * break
+ *
+ * sols.append(best_sol) # <<<<<<<<<<<<<<
+ * if i in forced:
+ * start = i
+*/
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_sols, __pyx_v_best_sol); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 360, __pyx_L1_error)
+
+ /* "fontTools/qu2cu/qu2cu.py":361
+ *
+ * sols.append(best_sol)
+ * if i in forced: # <<<<<<<<<<<<<<
+ * start = i
+ *
+*/
+ __pyx_t_6 = __Pyx_PyLong_From_int(__pyx_v_i); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 361, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_2 = (__Pyx_PySet_ContainsTF(__pyx_t_6, __pyx_v_forced, Py_EQ)); if (unlikely((__pyx_t_2 < 0))) __PYX_ERR(0, 361, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":362
+ * sols.append(best_sol)
+ * if i in forced:
+ * start = i # <<<<<<<<<<<<<<
+ *
+ * # Reconstruct solution
+*/
+ __pyx_v_start = __pyx_v_i;
+
+ /* "fontTools/qu2cu/qu2cu.py":361
+ *
+ * sols.append(best_sol)
+ * if i in forced: # <<<<<<<<<<<<<<
+ * start = i
+ *
+*/
+ }
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":365
+ *
+ * # Reconstruct solution
+ * splits = [] # <<<<<<<<<<<<<<
+ * cubic = []
+ * i = len(sols) - 1
+*/
+ __pyx_t_6 = PyList_New(0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 365, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_v_splits = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":366
+ * # Reconstruct solution
+ * splits = []
+ * cubic = [] # <<<<<<<<<<<<<<
+ * i = len(sols) - 1
+ * while i:
+*/
+ __pyx_t_6 = PyList_New(0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 366, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_v_cubic = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":367
+ * splits = []
+ * cubic = []
+ * i = len(sols) - 1 # <<<<<<<<<<<<<<
+ * while i:
+ * count, is_cubic = sols[i].start_index, sols[i].is_cubic
+*/
+ __pyx_t_1 = __Pyx_PyList_GET_SIZE(__pyx_v_sols); if (unlikely(__pyx_t_1 == ((Py_ssize_t)-1))) __PYX_ERR(0, 367, __pyx_L1_error)
+ __pyx_v_i = (__pyx_t_1 - 1);
+
+ /* "fontTools/qu2cu/qu2cu.py":368
+ * cubic = []
+ * i = len(sols) - 1
+ * while i: # <<<<<<<<<<<<<<
+ * count, is_cubic = sols[i].start_index, sols[i].is_cubic
+ * splits.append(i)
+*/
+ while (1) {
+ __pyx_t_2 = (__pyx_v_i != 0);
+ if (!__pyx_t_2) break;
+
+ /* "fontTools/qu2cu/qu2cu.py":369
+ * i = len(sols) - 1
+ * while i:
+ * count, is_cubic = sols[i].start_index, sols[i].is_cubic # <<<<<<<<<<<<<<
+ * splits.append(i)
+ * cubic.append(is_cubic)
+*/
+ __pyx_t_6 = __Pyx_GetItemInt_List(__pyx_v_sols, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_start_index); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_5 = __Pyx_PyLong_As_int(__pyx_t_8); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_GetItemInt_List(__pyx_v_sols, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_is_cubic); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_13 = __Pyx_PyLong_As_int(__pyx_t_6); if (unlikely((__pyx_t_13 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 369, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_v_count = __pyx_t_5;
+ __pyx_v_is_cubic = __pyx_t_13;
+
+ /* "fontTools/qu2cu/qu2cu.py":370
+ * while i:
+ * count, is_cubic = sols[i].start_index, sols[i].is_cubic
+ * splits.append(i) # <<<<<<<<<<<<<<
+ * cubic.append(is_cubic)
+ * i -= count
+*/
+ __pyx_t_6 = __Pyx_PyLong_From_int(__pyx_v_i); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 370, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_splits, __pyx_t_6); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 370, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":371
+ * count, is_cubic = sols[i].start_index, sols[i].is_cubic
+ * splits.append(i)
+ * cubic.append(is_cubic) # <<<<<<<<<<<<<<
+ * i -= count
+ * curves = []
+*/
+ __pyx_t_6 = __Pyx_PyLong_From_int(__pyx_v_is_cubic); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 371, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_cubic, __pyx_t_6); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 371, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":372
+ * splits.append(i)
+ * cubic.append(is_cubic)
+ * i -= count # <<<<<<<<<<<<<<
+ * curves = []
+ * j = 0
+*/
+ __pyx_v_i = (__pyx_v_i - __pyx_v_count);
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":373
+ * cubic.append(is_cubic)
+ * i -= count
+ * curves = [] # <<<<<<<<<<<<<<
+ * j = 0
+ * for i, is_cubic in reversed(list(zip(splits, cubic))):
+*/
+ __pyx_t_6 = PyList_New(0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 373, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_v_curves = ((PyObject*)__pyx_t_6);
+ __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":374
+ * i -= count
+ * curves = []
+ * j = 0 # <<<<<<<<<<<<<<
+ * for i, is_cubic in reversed(list(zip(splits, cubic))):
+ * if is_cubic:
+*/
+ __pyx_v_j = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":375
+ * curves = []
+ * j = 0
+ * for i, is_cubic in reversed(list(zip(splits, cubic))): # <<<<<<<<<<<<<<
+ * if is_cubic:
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+*/
+ __pyx_t_8 = NULL;
+ __pyx_t_12 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_8, __pyx_v_splits, __pyx_v_cubic};
+ __pyx_t_6 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_zip, __pyx_callargs+__pyx_t_12, (3-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ }
+ __pyx_t_8 = __Pyx_PySequence_ListKeepNew(__pyx_t_6); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __pyx_t_6 = __pyx_t_8; __Pyx_INCREF(__pyx_t_6);
+ __pyx_t_1 = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 375, __pyx_L1_error)
+ #endif
+ --__pyx_t_1;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ for (;;) {
+ if (__pyx_t_1 < 0) break;
+ {
+ Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 375, __pyx_L1_error)
+ #endif
+ if (__pyx_t_1 >= __pyx_temp) break;
+ }
+ __pyx_t_8 = __Pyx_PyList_GetItemRefFast(__pyx_t_6, __pyx_t_1, __Pyx_ReferenceSharing_OwnStrongReference);
+ --__pyx_t_1;
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if ((likely(PyTuple_CheckExact(__pyx_t_8))) || (PyList_CheckExact(__pyx_t_8))) {
+ PyObject* sequence = __pyx_t_8;
+ Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
+ if (unlikely(size != 2)) {
+ if (size > 2) __Pyx_RaiseTooManyValuesError(2);
+ else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
+ __PYX_ERR(0, 375, __pyx_L1_error)
+ }
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ if (likely(PyTuple_CheckExact(sequence))) {
+ __pyx_t_19 = PyTuple_GET_ITEM(sequence, 0);
+ __Pyx_INCREF(__pyx_t_19);
+ __pyx_t_18 = PyTuple_GET_ITEM(sequence, 1);
+ __Pyx_INCREF(__pyx_t_18);
+ } else {
+ __pyx_t_19 = __Pyx_PyList_GetItemRefFast(sequence, 0, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_19);
+ __pyx_t_18 = __Pyx_PyList_GetItemRefFast(sequence, 1, __Pyx_ReferenceSharing_SharedReference);
+ if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_XGOTREF(__pyx_t_18);
+ }
+ #else
+ __pyx_t_19 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_19)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_19);
+ __pyx_t_18 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ #endif
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ } else {
+ Py_ssize_t index = -1;
+ __pyx_t_3 = PyObject_GetIter(__pyx_t_8); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_23 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_3);
+ index = 0; __pyx_t_19 = __pyx_t_23(__pyx_t_3); if (unlikely(!__pyx_t_19)) goto __pyx_L44_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_19);
+ index = 1; __pyx_t_18 = __pyx_t_23(__pyx_t_3); if (unlikely(!__pyx_t_18)) goto __pyx_L44_unpacking_failed;
+ __Pyx_GOTREF(__pyx_t_18);
+ if (__Pyx_IternextUnpackEndCheck(__pyx_t_23(__pyx_t_3), 2) < (0)) __PYX_ERR(0, 375, __pyx_L1_error)
+ __pyx_t_23 = NULL;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L45_unpacking_done;
+ __pyx_L44_unpacking_failed:;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_23 = NULL;
+ if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
+ __PYX_ERR(0, 375, __pyx_L1_error)
+ __pyx_L45_unpacking_done:;
+ }
+ __pyx_t_13 = __Pyx_PyLong_As_int(__pyx_t_19); if (unlikely((__pyx_t_13 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_19); __pyx_t_19 = 0;
+ __pyx_t_5 = __Pyx_PyLong_As_int(__pyx_t_18); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 375, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ __pyx_v_i = __pyx_t_13;
+ __pyx_v_is_cubic = __pyx_t_5;
+
+ /* "fontTools/qu2cu/qu2cu.py":376
+ * j = 0
+ * for i, is_cubic in reversed(list(zip(splits, cubic))):
+ * if is_cubic: # <<<<<<<<<<<<<<
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+ * else:
+*/
+ __pyx_t_2 = (__pyx_v_is_cubic != 0);
+ if (__pyx_t_2) {
+
+ /* "fontTools/qu2cu/qu2cu.py":377
+ * for i, is_cubic in reversed(list(zip(splits, cubic))):
+ * if is_cubic:
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0]) # <<<<<<<<<<<<<<
+ * else:
+ * for k in range(j, i):
+*/
+ __pyx_t_8 = __pyx_f_9fontTools_5qu2cu_5qu2cu_merge_curves(__pyx_v_elevated_quadratics, __pyx_v_j, (__pyx_v_i - __pyx_v_j)); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 377, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_18 = __Pyx_GetItemInt(__pyx_t_8, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1, __Pyx_ReferenceSharing_OwnStrongReference); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 377, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_curves, __pyx_t_18); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 377, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":376
+ * j = 0
+ * for i, is_cubic in reversed(list(zip(splits, cubic))):
+ * if is_cubic: # <<<<<<<<<<<<<<
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+ * else:
+*/
+ goto __pyx_L46;
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":379
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+ * else:
+ * for k in range(j, i): # <<<<<<<<<<<<<<
+ * curves.append(q[k * 2 : k * 2 + 3])
+ * j = i
+*/
+ /*else*/ {
+ __pyx_t_5 = __pyx_v_i;
+ __pyx_t_13 = __pyx_t_5;
+ for (__pyx_t_14 = __pyx_v_j; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) {
+ __pyx_v_k = __pyx_t_14;
+
+ /* "fontTools/qu2cu/qu2cu.py":380
+ * else:
+ * for k in range(j, i):
+ * curves.append(q[k * 2 : k * 2 + 3]) # <<<<<<<<<<<<<<
+ * j = i
+ *
+*/
+ __pyx_t_18 = __Pyx_PyObject_GetSlice(__pyx_v_q, (__pyx_v_k * 2), ((__pyx_v_k * 2) + 3), NULL, NULL, NULL, 1, 1, 1); if (unlikely(!__pyx_t_18)) __PYX_ERR(0, 380, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_18);
+ __pyx_t_11 = __Pyx_PyList_Append(__pyx_v_curves, __pyx_t_18); if (unlikely(__pyx_t_11 == ((int)-1))) __PYX_ERR(0, 380, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_18); __pyx_t_18 = 0;
+ }
+ }
+ __pyx_L46:;
+
+ /* "fontTools/qu2cu/qu2cu.py":381
+ * for k in range(j, i):
+ * curves.append(q[k * 2 : k * 2 + 3])
+ * j = i # <<<<<<<<<<<<<<
+ *
+ * return curves
+*/
+ __pyx_v_j = __pyx_v_i;
+
+ /* "fontTools/qu2cu/qu2cu.py":375
+ * curves = []
+ * j = 0
+ * for i, is_cubic in reversed(list(zip(splits, cubic))): # <<<<<<<<<<<<<<
+ * if is_cubic:
+ * curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+*/
+ }
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":383
+ * j = i
+ *
+ * return curves # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_curves);
+ __pyx_r = __pyx_v_curves;
+ goto __pyx_L0;
+
+ /* "fontTools/qu2cu/qu2cu.py":242
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * i=cython.int,
+ * j=cython.int,
+*/
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_XDECREF(__pyx_t_18);
+ __Pyx_XDECREF(__pyx_t_19);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.spline_to_curves", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_elevated_quadratics);
+ __Pyx_XDECREF(__pyx_v_forced);
+ __Pyx_XDECREF(__pyx_v_sols);
+ __Pyx_XDECREF(__pyx_v_impossible);
+ __Pyx_XDECREF(__pyx_v_best_sol);
+ __Pyx_XDECREF(__pyx_v_this_count);
+ __Pyx_XDECREF(__pyx_v_i_sol);
+ __Pyx_XDECREF(__pyx_v_curve);
+ __Pyx_XDECREF(__pyx_v_ts);
+ __Pyx_XDECREF(__pyx_v_reconstructed_iter);
+ __Pyx_XDECREF(__pyx_v_reconstructed);
+ __Pyx_XDECREF(__pyx_v_reconst);
+ __Pyx_XDECREF(__pyx_v_orig);
+ __Pyx_XDECREF(__pyx_v_splits);
+ __Pyx_XDECREF(__pyx_v_cubic);
+ __Pyx_XDECREF(__pyx_v_curves);
+ __Pyx_XDECREF(__pyx_gb_9fontTools_5qu2cu_5qu2cu_16spline_to_curves_2generator1);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "fontTools/qu2cu/qu2cu.py":386
+ *
+ *
+ * def main(): # <<<<<<<<<<<<<<
+ * from fontTools.cu2qu.benchmark import generate_curve
+ * from fontTools.cu2qu import curve_to_quadratic
+*/
+
+/* Python wrapper */
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_9main(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused); /*proto*/
+PyDoc_STRVAR(__pyx_doc_9fontTools_5qu2cu_5qu2cu_8main, "main()");
+static PyMethodDef __pyx_mdef_9fontTools_5qu2cu_5qu2cu_9main = {"main", (PyCFunction)__pyx_pw_9fontTools_5qu2cu_5qu2cu_9main, METH_NOARGS, __pyx_doc_9fontTools_5qu2cu_5qu2cu_8main};
+static PyObject *__pyx_pw_9fontTools_5qu2cu_5qu2cu_9main(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused) {
+ CYTHON_UNUSED PyObject *const *__pyx_kwvalues;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("main (wrapper)", 0);
+ __pyx_kwvalues = __Pyx_KwValues_VARARGS(__pyx_args, __pyx_nargs);
+ __pyx_r = __pyx_pf_9fontTools_5qu2cu_5qu2cu_8main(__pyx_self);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_9fontTools_5qu2cu_5qu2cu_8main(CYTHON_UNUSED PyObject *__pyx_self) {
+ PyObject *__pyx_v_generate_curve = NULL;
+ PyObject *__pyx_v_curve_to_quadratic = NULL;
+ double __pyx_v_tolerance;
+ double __pyx_v_reconstruct_tolerance;
+ PyObject *__pyx_v_curve = NULL;
+ PyObject *__pyx_v_quadratics = NULL;
+ PyObject *__pyx_v_curves = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ Py_ssize_t __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ size_t __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("main", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":387
+ *
+ * def main():
+ * from fontTools.cu2qu.benchmark import generate_curve # <<<<<<<<<<<<<<
+ * from fontTools.cu2qu import curve_to_quadratic
+ *
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_generate_curve};
+ __pyx_t_2 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_fontTools_cu2qu_benchmark, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 387, __pyx_L1_error)
+ }
+ __pyx_t_1 = __pyx_t_2;
+ __Pyx_GOTREF(__pyx_t_1);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_generate_curve};
+ __pyx_t_3 = 0; {
+ __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_1, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 387, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ switch (__pyx_t_3) {
+ case 0:
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_v_generate_curve = __pyx_t_4;
+ break;
+ default:;
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":388
+ * def main():
+ * from fontTools.cu2qu.benchmark import generate_curve
+ * from fontTools.cu2qu import curve_to_quadratic # <<<<<<<<<<<<<<
+ *
+ * tolerance = 0.05
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_curve_to_quadratic};
+ __pyx_t_2 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_fontTools_cu2qu, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 388, __pyx_L1_error)
+ }
+ __pyx_t_1 = __pyx_t_2;
+ __Pyx_GOTREF(__pyx_t_1);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_curve_to_quadratic};
+ __pyx_t_3 = 0; {
+ __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_1, __pyx_imported_names[__pyx_t_3]); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 388, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ switch (__pyx_t_3) {
+ case 0:
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_v_curve_to_quadratic = __pyx_t_4;
+ break;
+ default:;
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":390
+ * from fontTools.cu2qu import curve_to_quadratic
+ *
+ * tolerance = 0.05 # <<<<<<<<<<<<<<
+ * reconstruct_tolerance = tolerance * 1
+ * curve = generate_curve()
+*/
+ __pyx_v_tolerance = 0.05;
+
+ /* "fontTools/qu2cu/qu2cu.py":391
+ *
+ * tolerance = 0.05
+ * reconstruct_tolerance = tolerance * 1 # <<<<<<<<<<<<<<
+ * curve = generate_curve()
+ * quadratics = curve_to_quadratic(curve, tolerance)
+*/
+ __pyx_v_reconstruct_tolerance = (__pyx_v_tolerance * 1.0);
+
+ /* "fontTools/qu2cu/qu2cu.py":392
+ * tolerance = 0.05
+ * reconstruct_tolerance = tolerance * 1
+ * curve = generate_curve() # <<<<<<<<<<<<<<
+ * quadratics = curve_to_quadratic(curve, tolerance)
+ * print(
+*/
+ __pyx_t_4 = NULL;
+ __Pyx_INCREF(__pyx_v_generate_curve);
+ __pyx_t_5 = __pyx_v_generate_curve;
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_4);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, NULL};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_6, (1-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 392, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_curve = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":393
+ * reconstruct_tolerance = tolerance * 1
+ * curve = generate_curve()
+ * quadratics = curve_to_quadratic(curve, tolerance) # <<<<<<<<<<<<<<
+ * print(
+ * "cu2qu tolerance %g. qu2cu tolerance %g." % (tolerance, reconstruct_tolerance)
+*/
+ __pyx_t_5 = NULL;
+ __Pyx_INCREF(__pyx_v_curve_to_quadratic);
+ __pyx_t_4 = __pyx_v_curve_to_quadratic;
+ __pyx_t_7 = PyFloat_FromDouble(__pyx_v_tolerance); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 393, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_4);
+ assert(__pyx_t_5);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_4, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_5, __pyx_v_curve, __pyx_t_7};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_4, __pyx_callargs+__pyx_t_6, (3-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 393, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_quadratics = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":394
+ * curve = generate_curve()
+ * quadratics = curve_to_quadratic(curve, tolerance)
+ * print( # <<<<<<<<<<<<<<
+ * "cu2qu tolerance %g. qu2cu tolerance %g." % (tolerance, reconstruct_tolerance)
+ * )
+*/
+ __pyx_t_4 = NULL;
+
+ /* "fontTools/qu2cu/qu2cu.py":395
+ * quadratics = curve_to_quadratic(curve, tolerance)
+ * print(
+ * "cu2qu tolerance %g. qu2cu tolerance %g." % (tolerance, reconstruct_tolerance) # <<<<<<<<<<<<<<
+ * )
+ * print("One random cubic turned into %d quadratics." % len(quadratics))
+*/
+ __pyx_t_7 = PyFloat_FromDouble(__pyx_v_tolerance); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 395, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_5 = PyFloat_FromDouble(__pyx_v_reconstruct_tolerance); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 395, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 395, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_GIVEREF(__pyx_t_7);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_7) != (0)) __PYX_ERR(0, 395, __pyx_L1_error);
+ __Pyx_GIVEREF(__pyx_t_5);
+ if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 395, __pyx_L1_error);
+ __pyx_t_7 = 0;
+ __pyx_t_5 = 0;
+ __pyx_t_5 = PyUnicode_Format(__pyx_mstate_global->__pyx_kp_u_cu2qu_tolerance_g_qu2cu_toleranc, __pyx_t_8); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 395, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_4, __pyx_t_5};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 394, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":397
+ * "cu2qu tolerance %g. qu2cu tolerance %g." % (tolerance, reconstruct_tolerance)
+ * )
+ * print("One random cubic turned into %d quadratics." % len(quadratics)) # <<<<<<<<<<<<<<
+ * curves = quadratic_to_curves([quadratics], reconstruct_tolerance)
+ * print("Those quadratics turned back into %d cubics. " % len(curves))
+*/
+ __pyx_t_5 = NULL;
+ __pyx_t_3 = PyObject_Length(__pyx_v_quadratics); if (unlikely(__pyx_t_3 == ((Py_ssize_t)-1))) __PYX_ERR(0, 397, __pyx_L1_error)
+ __pyx_t_4 = PyLong_FromSsize_t(__pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 397, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_8 = PyUnicode_Format(__pyx_mstate_global->__pyx_kp_u_One_random_cubic_turned_into_d_q, __pyx_t_4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 397, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_t_8};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 397, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":398
+ * )
+ * print("One random cubic turned into %d quadratics." % len(quadratics))
+ * curves = quadratic_to_curves([quadratics], reconstruct_tolerance) # <<<<<<<<<<<<<<
+ * print("Those quadratics turned back into %d cubics. " % len(curves))
+ * print("Original curve:", curve)
+*/
+ __pyx_t_8 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_quadratic_to_curves); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 398, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = PyList_New(1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 398, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_v_quadratics);
+ __Pyx_GIVEREF(__pyx_v_quadratics);
+ if (__Pyx_PyList_SET_ITEM(__pyx_t_4, 0, __pyx_v_quadratics) != (0)) __PYX_ERR(0, 398, __pyx_L1_error);
+ __pyx_t_7 = PyFloat_FromDouble(__pyx_v_reconstruct_tolerance); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 398, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_6 = 1;
+ #if CYTHON_UNPACK_METHODS
+ if (unlikely(PyMethod_Check(__pyx_t_5))) {
+ __pyx_t_8 = PyMethod_GET_SELF(__pyx_t_5);
+ assert(__pyx_t_8);
+ PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5);
+ __Pyx_INCREF(__pyx_t_8);
+ __Pyx_INCREF(__pyx__function);
+ __Pyx_DECREF_SET(__pyx_t_5, __pyx__function);
+ __pyx_t_6 = 0;
+ }
+ #endif
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_8, __pyx_t_4, __pyx_t_7};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_5, __pyx_callargs+__pyx_t_6, (3-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 398, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __pyx_v_curves = __pyx_t_1;
+ __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":399
+ * print("One random cubic turned into %d quadratics." % len(quadratics))
+ * curves = quadratic_to_curves([quadratics], reconstruct_tolerance)
+ * print("Those quadratics turned back into %d cubics. " % len(curves)) # <<<<<<<<<<<<<<
+ * print("Original curve:", curve)
+ * print("Reconstructed curve(s):", curves)
+*/
+ __pyx_t_5 = NULL;
+ __pyx_t_3 = PyObject_Length(__pyx_v_curves); if (unlikely(__pyx_t_3 == ((Py_ssize_t)-1))) __PYX_ERR(0, 399, __pyx_L1_error)
+ __pyx_t_7 = PyLong_FromSsize_t(__pyx_t_3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 399, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_4 = PyUnicode_Format(__pyx_mstate_global->__pyx_kp_u_Those_quadratics_turned_back_int, __pyx_t_7); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 399, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_t_4};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_6, (2-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 399, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":400
+ * curves = quadratic_to_curves([quadratics], reconstruct_tolerance)
+ * print("Those quadratics turned back into %d cubics. " % len(curves))
+ * print("Original curve:", curve) # <<<<<<<<<<<<<<
+ * print("Reconstructed curve(s):", curves)
+ *
+*/
+ __pyx_t_4 = NULL;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_4, __pyx_mstate_global->__pyx_kp_u_Original_curve, __pyx_v_curve};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_6, (3-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 400, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":401
+ * print("Those quadratics turned back into %d cubics. " % len(curves))
+ * print("Original curve:", curve)
+ * print("Reconstructed curve(s):", curves) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_4 = NULL;
+ __pyx_t_6 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_4, __pyx_mstate_global->__pyx_kp_u_Reconstructed_curve_s, __pyx_v_curves};
+ __pyx_t_1 = __Pyx_PyObject_FastCall((PyObject*)__pyx_builtin_print, __pyx_callargs+__pyx_t_6, (3-__pyx_t_6) | (__pyx_t_6*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 401, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":386
+ *
+ *
+ * def main(): # <<<<<<<<<<<<<<
+ * from fontTools.cu2qu.benchmark import generate_curve
+ * from fontTools.cu2qu import curve_to_quadratic
+*/
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("fontTools.qu2cu.qu2cu.main", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_generate_curve);
+ __Pyx_XDECREF(__pyx_v_curve_to_quadratic);
+ __Pyx_XDECREF(__pyx_v_curve);
+ __Pyx_XDECREF(__pyx_v_quadratics);
+ __Pyx_XDECREF(__pyx_v_curves);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+/* #### Code section: module_exttypes ### */
+
+static PyObject *__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *p = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_v_c);
+ Py_CLEAR(p->__pyx_t_0);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr++] = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *p = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_v_c) {
+ e = (*v)(p->__pyx_v_c, a); if (e) return e;
+ }
+ if (p->__pyx_t_0) {
+ e = (*v)(p->__pyx_t_0, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr_spec = {
+ "fontTools.qu2cu.qu2cu.__pyx_scope_struct__genexpr",
+ sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.qu2cu.qu2cu.""__pyx_scope_struct__genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyObject *__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ PyObject *o;
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr > 0) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(t, __pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr))))
+ {
+ o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr[--__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr];
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(Py_TYPE(o));
+ #endif
+ memset(o, 0, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr));
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ (void) PyObject_Init(o, t);
+ #else
+ (void) PyObject_INIT(o, t);
+ #endif
+ PyObject_GC_Track(o);
+ } else
+ #endif
+ {
+ o = __Pyx_AllocateExtensionType(t, 1);
+ if (unlikely(!o)) return 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr(PyObject *o) {
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *p = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)o;
+ #if CYTHON_USE_TP_FINALIZE
+ if (unlikely(__Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && !__Pyx_PyObject_GC_IsFinalized(o)) {
+ if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->__pyx_genexpr_arg_0);
+ Py_CLEAR(p->__pyx_t_0);
+ #if CYTHON_USE_FREELISTS
+ if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr < 8) & __PYX_CHECK_FINAL_TYPE_FOR_FREELISTS(Py_TYPE(o), __pyx_mstate_global->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr))))
+ {
+ __pyx_mstate_global->__pyx_freelist_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr[__pyx_mstate_global->__pyx_freecount_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr++] = ((struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)o);
+ } else
+ #endif
+ {
+ PyTypeObject *tp = Py_TYPE(o);
+ #if CYTHON_USE_TYPE_SLOTS
+ (*tp->tp_free)(o);
+ #else
+ {
+ freefunc tp_free = (freefunc)PyType_GetSlot(tp, Py_tp_free);
+ if (tp_free) tp_free(o);
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ Py_DECREF(tp);
+ #endif
+ }
+}
+
+static int __pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *p = (struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr *)o;
+ {
+ e = __Pyx_call_type_traverse(o, 1, v, a);
+ if (e) return e;
+ }
+ if (p->__pyx_genexpr_arg_0) {
+ e = (*v)(p->__pyx_genexpr_arg_0, a); if (e) return e;
+ }
+ if (p->__pyx_t_0) {
+ e = (*v)(p->__pyx_t_0, a); if (e) return e;
+ }
+ return 0;
+}
+#if CYTHON_USE_TYPE_SPECS
+static PyType_Slot __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr_slots[] = {
+ {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr},
+ {Py_tp_traverse, (void *)__pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr},
+ {Py_tp_new, (void *)__pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr},
+ {0, 0},
+};
+static PyType_Spec __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr_spec = {
+ "fontTools.qu2cu.qu2cu.__pyx_scope_struct_1_genexpr",
+ sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC,
+ __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr_slots,
+};
+#else
+
+static PyTypeObject __pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "fontTools.qu2cu.qu2cu.""__pyx_scope_struct_1_genexpr", /*tp_name*/
+ sizeof(struct __pyx_obj_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ __pyx_tp_traverse_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ #if !CYTHON_USE_TYPE_SPECS
+ 0, /*tp_dictoffset*/
+ #endif
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if CYTHON_USE_TP_FINALIZE
+ 0, /*tp_finalize*/
+ #else
+ NULL, /*tp_finalize*/
+ #endif
+ #if !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800
+ 0, /*tp_vectorcall*/
+ #endif
+ #if __PYX_NEED_TP_PRINT_SLOT == 1
+ 0, /*tp_print*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000
+ 0, /*tp_watched*/
+ #endif
+ #if PY_VERSION_HEX >= 0x030d00A4
+ 0, /*tp_versions_used*/
+ #endif
+ #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000
+ 0, /*tp_pypy_flags*/
+ #endif
+};
+#endif
+
+static PyMethodDef __pyx_methods[] = {
+ {0, 0, 0, 0}
+};
+/* #### Code section: initfunc_declarations ### */
+static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/
+static CYTHON_SMALL_CODE int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate); /*proto*/
+/* #### Code section: init_module ### */
+
+static int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_global_init_code", 0);
+ /*--- Global init code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_variable_export_code", 0);
+ /*--- Variable export code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_function_export_code", 0);
+ /*--- Function export code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannySetupContext("__Pyx_modinit_type_init_code", 0);
+ /*--- Type init code ---*/
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr)) __PYX_ERR(0, 235, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr) < (0)) __PYX_ERR(0, 235, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr = &__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr) < (0)) __PYX_ERR(0, 235, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct__genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ #if CYTHON_USE_TYPE_SPECS
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr)) __PYX_ERR(0, 340, __pyx_L1_error)
+ if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr_spec, __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr) < (0)) __PYX_ERR(0, 340, __pyx_L1_error)
+ #else
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr = &__pyx_type_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr;
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ #endif
+ #if !CYTHON_USE_TYPE_SPECS
+ if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr) < (0)) __PYX_ERR(0, 340, __pyx_L1_error)
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount((PyObject*)__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr);
+ #endif
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr->tp_getattro == PyObject_GenericGetAttr)) {
+ __pyx_mstate->__pyx_ptype_9fontTools_5qu2cu_5qu2cu___pyx_scope_struct_1_genexpr->tp_getattro = PyObject_GenericGetAttr;
+ }
+ #endif
+ __Pyx_RefNannyFinishContext();
+ return 0;
+ __pyx_L1_error:;
+ __Pyx_RefNannyFinishContext();
+ return -1;
+}
+
+static int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_type_import_code", 0);
+ /*--- Type import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_variable_import_code", 0);
+ /*--- Variable import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+static int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_modinit_function_import_code", 0);
+ /*--- Function import code ---*/
+ __Pyx_RefNannyFinishContext();
+ return 0;
+}
+
+#if CYTHON_PEP489_MULTI_PHASE_INIT
+static PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def); /*proto*/
+static int __pyx_pymod_exec_qu2cu(PyObject* module); /*proto*/
+static PyModuleDef_Slot __pyx_moduledef_slots[] = {
+ {Py_mod_create, (void*)__pyx_pymod_create},
+ {Py_mod_exec, (void*)__pyx_pymod_exec_qu2cu},
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ {Py_mod_gil, Py_MOD_GIL_USED},
+ #endif
+ #if PY_VERSION_HEX >= 0x030C0000 && CYTHON_USE_MODULE_STATE
+ {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
+ #endif
+ {0, NULL}
+};
+#endif
+
+#ifdef __cplusplus
+namespace {
+ struct PyModuleDef __pyx_moduledef =
+ #else
+ static struct PyModuleDef __pyx_moduledef =
+ #endif
+ {
+ PyModuleDef_HEAD_INIT,
+ "qu2cu",
+ 0, /* m_doc */
+ #if CYTHON_USE_MODULE_STATE
+ sizeof(__pyx_mstatetype), /* m_size */
+ #else
+ (CYTHON_PEP489_MULTI_PHASE_INIT) ? 0 : -1, /* m_size */
+ #endif
+ __pyx_methods /* m_methods */,
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __pyx_moduledef_slots, /* m_slots */
+ #else
+ NULL, /* m_reload */
+ #endif
+ #if CYTHON_USE_MODULE_STATE
+ __pyx_m_traverse, /* m_traverse */
+ __pyx_m_clear, /* m_clear */
+ NULL /* m_free */
+ #else
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+ #endif
+ };
+ #ifdef __cplusplus
+} /* anonymous namespace */
+#endif
+
+/* PyModInitFuncType */
+#ifndef CYTHON_NO_PYINIT_EXPORT
+ #define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC
+#else
+ #ifdef __cplusplus
+ #define __Pyx_PyMODINIT_FUNC extern "C" PyObject *
+ #else
+ #define __Pyx_PyMODINIT_FUNC PyObject *
+ #endif
+#endif
+
+__Pyx_PyMODINIT_FUNC PyInit_qu2cu(void) CYTHON_SMALL_CODE; /*proto*/
+__Pyx_PyMODINIT_FUNC PyInit_qu2cu(void)
+#if CYTHON_PEP489_MULTI_PHASE_INIT
+{
+ return PyModuleDef_Init(&__pyx_moduledef);
+}
+/* ModuleCreationPEP489 */
+#if CYTHON_COMPILING_IN_LIMITED_API && (__PYX_LIMITED_VERSION_HEX < 0x03090000\
+ || ((defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)) && __PYX_LIMITED_VERSION_HEX < 0x030A0000))
+static PY_INT64_T __Pyx_GetCurrentInterpreterId(void) {
+ {
+ PyObject *module = PyImport_ImportModule("_interpreters"); // 3.13+ I think
+ if (!module) {
+ PyErr_Clear(); // just try the 3.8-3.12 version
+ module = PyImport_ImportModule("_xxsubinterpreters");
+ if (!module) goto bad;
+ }
+ PyObject *current = PyObject_CallMethod(module, "get_current", NULL);
+ Py_DECREF(module);
+ if (!current) goto bad;
+ if (PyTuple_Check(current)) {
+ PyObject *new_current = PySequence_GetItem(current, 0);
+ Py_DECREF(current);
+ current = new_current;
+ if (!new_current) goto bad;
+ }
+ long long as_c_int = PyLong_AsLongLong(current);
+ Py_DECREF(current);
+ return as_c_int;
+ }
+ bad:
+ PySys_WriteStderr("__Pyx_GetCurrentInterpreterId failed. Try setting the C define CYTHON_PEP489_MULTI_PHASE_INIT=0\n");
+ return -1;
+}
+#endif
+#if !CYTHON_USE_MODULE_STATE
+static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) {
+ static PY_INT64_T main_interpreter_id = -1;
+#if CYTHON_COMPILING_IN_GRAAL && defined(GRAALPY_VERSION_NUM) && GRAALPY_VERSION_NUM > 0x19000000
+ PY_INT64_T current_id = GraalPyInterpreterState_GetIDFromThreadState(PyThreadState_Get());
+#elif CYTHON_COMPILING_IN_GRAAL
+ PY_INT64_T current_id = PyInterpreterState_GetIDFromThreadState(PyThreadState_Get());
+#elif CYTHON_COMPILING_IN_LIMITED_API && (__PYX_LIMITED_VERSION_HEX < 0x03090000\
+ || ((defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)) && __PYX_LIMITED_VERSION_HEX < 0x030A0000))
+ PY_INT64_T current_id = __Pyx_GetCurrentInterpreterId();
+#elif CYTHON_COMPILING_IN_LIMITED_API
+ PY_INT64_T current_id = PyInterpreterState_GetID(PyInterpreterState_Get());
+#else
+ PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp);
+#endif
+ if (unlikely(current_id == -1)) {
+ return -1;
+ }
+ if (main_interpreter_id == -1) {
+ main_interpreter_id = current_id;
+ return 0;
+ } else if (unlikely(main_interpreter_id != current_id)) {
+ PyErr_SetString(
+ PyExc_ImportError,
+ "Interpreter change detected - this module can only be loaded into one interpreter per process.");
+ return -1;
+ }
+ return 0;
+}
+#endif
+static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none)
+{
+ PyObject *value = PyObject_GetAttrString(spec, from_name);
+ int result = 0;
+ if (likely(value)) {
+ if (allow_none || value != Py_None) {
+ result = PyDict_SetItemString(moddict, to_name, value);
+ }
+ Py_DECREF(value);
+ } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ } else {
+ result = -1;
+ }
+ return result;
+}
+static CYTHON_SMALL_CODE PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def) {
+ PyObject *module = NULL, *moddict, *modname;
+ CYTHON_UNUSED_VAR(def);
+ #if !CYTHON_USE_MODULE_STATE
+ if (__Pyx_check_single_interpreter())
+ return NULL;
+ #endif
+ if (__pyx_m)
+ return __Pyx_NewRef(__pyx_m);
+ modname = PyObject_GetAttrString(spec, "name");
+ if (unlikely(!modname)) goto bad;
+ module = PyModule_NewObject(modname);
+ Py_DECREF(modname);
+ if (unlikely(!module)) goto bad;
+ moddict = PyModule_GetDict(module);
+ if (unlikely(!moddict)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad;
+ if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad;
+ return module;
+bad:
+ Py_XDECREF(module);
+ return NULL;
+}
+
+
+static CYTHON_SMALL_CODE int __pyx_pymod_exec_qu2cu(PyObject *__pyx_pyinit_module)
+#endif
+{
+ int stringtab_initialized = 0;
+ #if CYTHON_USE_MODULE_STATE
+ int pystate_addmodule_run = 0;
+ #endif
+ __pyx_mstatetype *__pyx_mstate = NULL;
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ Py_ssize_t __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ PyObject *__pyx_t_7 = NULL;
+ PyObject *__pyx_t_8 = NULL;
+ size_t __pyx_t_9;
+ int __pyx_t_10;
+ int __pyx_lineno = 0;
+ const char *__pyx_filename = NULL;
+ int __pyx_clineno = 0;
+ __Pyx_RefNannyDeclarations
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ if (__pyx_m) {
+ if (__pyx_m == __pyx_pyinit_module) return 0;
+ PyErr_SetString(PyExc_RuntimeError, "Module 'qu2cu' has already been imported. Re-initialisation is not supported.");
+ return -1;
+ }
+ #else
+ if (__pyx_m) return __Pyx_NewRef(__pyx_m);
+ #endif
+ /*--- Module creation code ---*/
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ __pyx_t_1 = __pyx_pyinit_module;
+ Py_INCREF(__pyx_t_1);
+ #else
+ __pyx_t_1 = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #if CYTHON_USE_MODULE_STATE
+ {
+ int add_module_result = __Pyx_State_AddModule(__pyx_t_1, &__pyx_moduledef);
+ __pyx_t_1 = 0; /* transfer ownership from __pyx_t_1 to "qu2cu" pseudovariable */
+ if (unlikely((add_module_result < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ pystate_addmodule_run = 1;
+ }
+ #else
+ __pyx_m = __pyx_t_1;
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ PyUnstable_Module_SetGIL(__pyx_m, Py_MOD_GIL_USED);
+ #endif
+ __pyx_mstate = __pyx_mstate_global;
+ CYTHON_UNUSED_VAR(__pyx_t_1);
+ __pyx_mstate->__pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_mstate->__pyx_d)) __PYX_ERR(0, 1, __pyx_L1_error)
+ Py_INCREF(__pyx_mstate->__pyx_d);
+ __pyx_mstate->__pyx_b = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_mstate->__pyx_b)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_cython_runtime = __Pyx_PyImport_AddModuleRef("cython_runtime"); if (unlikely(!__pyx_mstate->__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_mstate->__pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ /* ImportRefnannyAPI */
+ #if CYTHON_REFNANNY
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny");
+ if (!__Pyx_RefNanny) {
+ PyErr_Clear();
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny");
+ if (!__Pyx_RefNanny)
+ Py_FatalError("failed to import 'refnanny' module");
+ }
+ #endif
+
+__Pyx_RefNannySetupContext("PyInit_qu2cu", 0);
+ __Pyx_init_runtime_version();
+ if (__Pyx_check_binary_version(__PYX_LIMITED_VERSION_HEX, __Pyx_get_runtime_version(), CYTHON_COMPILING_IN_LIMITED_API) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_mstate->__pyx_empty_tuple)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_bytes)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_mstate->__pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_unicode)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Library function declarations ---*/
+ /*--- Initialize various global constants etc. ---*/
+ if (__Pyx_InitConstants(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ stringtab_initialized = 1;
+ if (__Pyx_InitGlobals() < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (__pyx_module_is_main_fontTools__qu2cu__qu2cu) {
+ if (PyObject_SetAttr(__pyx_m, __pyx_mstate_global->__pyx_n_u_name, __pyx_mstate_global->__pyx_n_u_main) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ {
+ PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (!PyDict_GetItemString(modules, "fontTools.qu2cu.qu2cu")) {
+ if (unlikely((PyDict_SetItemString(modules, "fontTools.qu2cu.qu2cu", __pyx_m) < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ /*--- Builtin init code ---*/
+ if (__Pyx_InitCachedBuiltins(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Constants init code ---*/
+ if (__Pyx_InitCachedConstants(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (__Pyx_CreateCodeObjects(__pyx_mstate) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Global type/function init code ---*/
+ (void)__Pyx_modinit_global_init_code(__pyx_mstate);
+ (void)__Pyx_modinit_variable_export_code(__pyx_mstate);
+ (void)__Pyx_modinit_function_export_code(__pyx_mstate);
+ if (unlikely((__Pyx_modinit_type_init_code(__pyx_mstate) < 0))) __PYX_ERR(0, 1, __pyx_L1_error)
+ (void)__Pyx_modinit_type_import_code(__pyx_mstate);
+ (void)__Pyx_modinit_variable_import_code(__pyx_mstate);
+ (void)__Pyx_modinit_function_import_code(__pyx_mstate);
+ /*--- Execution code ---*/
+
+ /* "fontTools/qu2cu/qu2cu.py":19
+ * # limitations under the License.
+ *
+ * try: # <<<<<<<<<<<<<<
+ * import cython
+ * except (AttributeError, ImportError):
+*/
+ {
+ (void)__pyx_t_1; (void)__pyx_t_2; (void)__pyx_t_3; /* mark used */
+ /*try:*/ {
+
+ /* "fontTools/qu2cu/qu2cu.py":20
+ *
+ * try:
+ * import cython # <<<<<<<<<<<<<<
+ * except (AttributeError, ImportError):
+ * # if cython not installed, use mock module with no-op decorators and types
+*/
+ }
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":24
+ * # if cython not installed, use mock module with no-op decorators and types
+ * from fontTools.misc import cython
+ * COMPILED = cython.compiled # <<<<<<<<<<<<<<
+ *
+ * from fontTools.misc.bezierTools import splitCubicAtTC
+*/
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_COMPILED, Py_True) < (0)) __PYX_ERR(0, 24, __pyx_L1_error)
+
+ /* "fontTools/qu2cu/qu2cu.py":26
+ * COMPILED = cython.compiled
+ *
+ * from fontTools.misc.bezierTools import splitCubicAtTC # <<<<<<<<<<<<<<
+ * from collections import namedtuple
+ * import math
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_splitCubicAtTC};
+ __pyx_t_3 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_fontTools_misc_bezierTools, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 26, __pyx_L1_error)
+ }
+ __pyx_t_4 = __pyx_t_3;
+ __Pyx_GOTREF(__pyx_t_4);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_splitCubicAtTC};
+ __pyx_t_5 = 0; {
+ __pyx_t_6 = __Pyx_ImportFrom(__pyx_t_4, __pyx_imported_names[__pyx_t_5]); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 26, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_5], __pyx_t_6) < (0)) __PYX_ERR(0, 26, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":27
+ *
+ * from fontTools.misc.bezierTools import splitCubicAtTC
+ * from collections import namedtuple # <<<<<<<<<<<<<<
+ * import math
+ * from typing import (
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_namedtuple};
+ __pyx_t_3 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_collections, __pyx_imported_names, 1, NULL, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 27, __pyx_L1_error)
+ }
+ __pyx_t_4 = __pyx_t_3;
+ __Pyx_GOTREF(__pyx_t_4);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_namedtuple};
+ __pyx_t_5 = 0; {
+ __pyx_t_6 = __Pyx_ImportFrom(__pyx_t_4, __pyx_imported_names[__pyx_t_5]); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 27, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_5], __pyx_t_6) < (0)) __PYX_ERR(0, 27, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":28
+ * from fontTools.misc.bezierTools import splitCubicAtTC
+ * from collections import namedtuple
+ * import math # <<<<<<<<<<<<<<
+ * from typing import (
+ * List,
+*/
+ __pyx_t_3 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_math, 0, 0, NULL, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 28, __pyx_L1_error)
+ __pyx_t_4 = __pyx_t_3;
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_math, __pyx_t_4) < (0)) __PYX_ERR(0, 28, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":29
+ * from collections import namedtuple
+ * import math
+ * from typing import ( # <<<<<<<<<<<<<<
+ * List,
+ * Tuple,
+*/
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_List,__pyx_mstate_global->__pyx_n_u_Tuple,__pyx_mstate_global->__pyx_n_u_Union};
+ __pyx_t_3 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_typing, __pyx_imported_names, 3, NULL, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 29, __pyx_L1_error)
+ }
+ __pyx_t_4 = __pyx_t_3;
+ __Pyx_GOTREF(__pyx_t_4);
+ {
+ PyObject* const __pyx_imported_names[] = {__pyx_mstate_global->__pyx_n_u_List,__pyx_mstate_global->__pyx_n_u_Tuple,__pyx_mstate_global->__pyx_n_u_Union};
+ for (__pyx_t_5=0; __pyx_t_5 < 3; __pyx_t_5++) {
+ __pyx_t_6 = __Pyx_ImportFrom(__pyx_t_4, __pyx_imported_names[__pyx_t_5]); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 29, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_imported_names[__pyx_t_5], __pyx_t_6) < (0)) __PYX_ERR(0, 29, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":36
+ *
+ *
+ * __all__ = ["quadratic_to_curves"] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_4 = __Pyx_PyList_Pack(1, __pyx_mstate_global->__pyx_n_u_quadratic_to_curves); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 36, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_all, __pyx_t_4) < (0)) __PYX_ERR(0, 36, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":82
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * p0=cython.complex,
+ * p1=cython.complex,
+*/
+ __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5qu2cu_5qu2cu_1elevate_quadratic, 0, __pyx_mstate_global->__pyx_n_u_elevate_quadratic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[2])); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 82, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_4);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_elevate_quadratic, __pyx_t_4) < (0)) __PYX_ERR(0, 82, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":154
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * count=cython.int,
+ * num_offcurves=cython.int,
+*/
+ __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5qu2cu_5qu2cu_3add_implicit_on_curves, 0, __pyx_mstate_global->__pyx_n_u_add_implicit_on_curves, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[3])); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 154, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_4);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_add_implicit_on_curves, __pyx_t_4) < (0)) __PYX_ERR(0, 154, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":175
+ *
+ *
+ * Point = Union[Tuple[float, float], complex] # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_Union); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_GetModuleGlobalName(__pyx_t_6, __pyx_mstate_global->__pyx_n_u_Tuple); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_7 = PyTuple_Pack(2, ((PyObject *)(&PyFloat_Type)), ((PyObject *)(&PyFloat_Type))); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __pyx_t_8 = __Pyx_PyObject_GetItem(__pyx_t_6, __pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = PyTuple_Pack(2, __pyx_t_8, ((PyObject *)(&PyComplex_Type))); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __pyx_t_8 = __Pyx_PyObject_GetItem(__pyx_t_4, __pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_Point, __pyx_t_8) < (0)) __PYX_ERR(0, 175, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":184
+ * def quadratic_to_curves(
+ * quads: List[List[Point]],
+ * max_err: float = 0.5, # <<<<<<<<<<<<<<
+ * all_cubic: bool = False,
+ * ) -> List[Tuple[Point, ...]]:
+*/
+ __pyx_t_8 = PyFloat_FromDouble(((double)0.5)); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 184, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+
+ /* "fontTools/qu2cu/qu2cu.py":185
+ * quads: List[List[Point]],
+ * max_err: float = 0.5,
+ * all_cubic: bool = False, # <<<<<<<<<<<<<<
+ * ) -> List[Tuple[Point, ...]]:
+ * """Converts a connecting list of quadratic splines to a list of quadratic
+*/
+ __pyx_t_7 = __Pyx_PyBool_FromLong(((int)0)); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 185, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+
+ /* "fontTools/qu2cu/qu2cu.py":178
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * cost=cython.int,
+ * is_complex=cython.int,
+*/
+ __pyx_t_4 = PyTuple_Pack(2, __pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __pyx_t_7 = __Pyx_PyDict_NewPresized(4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (PyDict_SetItem(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_quads, __pyx_mstate_global->__pyx_kp_u_List_List_Point) < (0)) __PYX_ERR(0, 178, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_max_err, __pyx_mstate_global->__pyx_n_u_float) < (0)) __PYX_ERR(0, 178, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_all_cubic, __pyx_mstate_global->__pyx_n_u_bool) < (0)) __PYX_ERR(0, 178, __pyx_L1_error)
+ if (PyDict_SetItem(__pyx_t_7, __pyx_mstate_global->__pyx_n_u_return, __pyx_mstate_global->__pyx_kp_u_List_Tuple_Point) < (0)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __pyx_t_8 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5qu2cu_5qu2cu_5quadratic_to_curves, 0, __pyx_mstate_global->__pyx_n_u_quadratic_to_curves, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[4])); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_8);
+ #endif
+ __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_8, __pyx_t_4);
+ __Pyx_CyFunction_SetAnnotationsDict(__pyx_t_8, __pyx_t_7);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_quadratic_to_curves, __pyx_t_8) < (0)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":239
+ *
+ *
+ * Solution = namedtuple("Solution", ["num_points", "error", "start_index", "is_cubic"]) # <<<<<<<<<<<<<<
+ *
+ *
+*/
+ __pyx_t_7 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_namedtuple); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_6 = __Pyx_PyList_Pack(4, __pyx_mstate_global->__pyx_n_u_num_points, __pyx_mstate_global->__pyx_n_u_error, __pyx_mstate_global->__pyx_n_u_start_index, __pyx_mstate_global->__pyx_n_u_is_cubic); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[3] = {__pyx_t_7, __pyx_mstate_global->__pyx_n_u_Solution, __pyx_t_6};
+ __pyx_t_8 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_4, __pyx_callargs+__pyx_t_9, (3-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 239, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ }
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_Solution, __pyx_t_8) < (0)) __PYX_ERR(0, 239, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":265
+ * u=cython.complex,
+ * )
+ * def spline_to_curves(q, costs, tolerance=0.5, all_cubic=False): # <<<<<<<<<<<<<<
+ * """
+ * q: quadratic spline with alternating on-curve / off-curve points.
+*/
+ __pyx_t_8 = PyFloat_FromDouble(((double)0.5)); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 265, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_4 = __Pyx_PyBool_FromLong(((int)0)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 265, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+
+ /* "fontTools/qu2cu/qu2cu.py":242
+ *
+ *
+ * @cython.locals( # <<<<<<<<<<<<<<
+ * i=cython.int,
+ * j=cython.int,
+*/
+ __pyx_t_6 = PyTuple_Pack(2, __pyx_t_8, __pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 242, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5qu2cu_5qu2cu_7spline_to_curves, 0, __pyx_mstate_global->__pyx_n_u_spline_to_curves, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[5])); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 242, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_4);
+ #endif
+ __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_4, __pyx_t_6);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_spline_to_curves, __pyx_t_4) < (0)) __PYX_ERR(0, 242, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":386
+ *
+ *
+ * def main(): # <<<<<<<<<<<<<<
+ * from fontTools.cu2qu.benchmark import generate_curve
+ * from fontTools.cu2qu import curve_to_quadratic
+*/
+ __pyx_t_4 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5qu2cu_5qu2cu_9main, 0, __pyx_mstate_global->__pyx_n_u_main_2, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_qu2cu_qu2cu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[6])); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 386, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030E0000
+ PyUnstable_Object_EnableDeferredRefcount(__pyx_t_4);
+ #endif
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_main_2, __pyx_t_4) < (0)) __PYX_ERR(0, 386, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":404
+ *
+ *
+ * if __name__ == "__main__": # <<<<<<<<<<<<<<
+ * main()
+*/
+ __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_name); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 404, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_10 = (__Pyx_PyUnicode_Equals(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_main, Py_EQ)); if (unlikely((__pyx_t_10 < 0))) __PYX_ERR(0, 404, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (__pyx_t_10) {
+
+ /* "fontTools/qu2cu/qu2cu.py":405
+ *
+ * if __name__ == "__main__":
+ * main() # <<<<<<<<<<<<<<
+*/
+ __pyx_t_6 = NULL;
+ __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_main_2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 405, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __pyx_t_9 = 1;
+ {
+ PyObject *__pyx_callargs[2] = {__pyx_t_6, NULL};
+ __pyx_t_4 = __Pyx_PyObject_FastCall((PyObject*)__pyx_t_8, __pyx_callargs+__pyx_t_9, (1-__pyx_t_9) | (__pyx_t_9*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET));
+ __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 405, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "fontTools/qu2cu/qu2cu.py":404
+ *
+ *
+ * if __name__ == "__main__": # <<<<<<<<<<<<<<
+ * main()
+*/
+ }
+
+ /* "fontTools/qu2cu/qu2cu.py":1
+ * # cython: language_level=3 # <<<<<<<<<<<<<<
+ * # distutils: define_macros=CYTHON_TRACE_NOGIL=1
+ *
+*/
+ __pyx_t_4 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_test, __pyx_t_4) < (0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /*--- Wrapped vars code ---*/
+
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_XDECREF(__pyx_t_8);
+ if (__pyx_m) {
+ if (__pyx_mstate->__pyx_d && stringtab_initialized) {
+ __Pyx_AddTraceback("init fontTools.qu2cu.qu2cu", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ #if !CYTHON_USE_MODULE_STATE
+ Py_CLEAR(__pyx_m);
+ #else
+ Py_DECREF(__pyx_m);
+ if (pystate_addmodule_run) {
+ PyObject *tp, *value, *tb;
+ PyErr_Fetch(&tp, &value, &tb);
+ PyState_RemoveModule(&__pyx_moduledef);
+ PyErr_Restore(tp, value, tb);
+ }
+ #endif
+ } else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError, "init fontTools.qu2cu.qu2cu");
+ }
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ #if CYTHON_PEP489_MULTI_PHASE_INIT
+ return (__pyx_m != NULL) ? 0 : -1;
+ #else
+ return __pyx_m;
+ #endif
+}
+/* #### Code section: pystring_table ### */
+/* #### Code section: cached_builtins ### */
+
+static int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate) {
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __pyx_builtin_enumerate = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_enumerate); if (!__pyx_builtin_enumerate) __PYX_ERR(0, 326, __pyx_L1_error)
+ __pyx_builtin_reversed = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_reversed); if (!__pyx_builtin_reversed) __PYX_ERR(0, 375, __pyx_L1_error)
+ __pyx_builtin_zip = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_zip); if (!__pyx_builtin_zip) __PYX_ERR(0, 375, __pyx_L1_error)
+ __pyx_builtin_print = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_print); if (!__pyx_builtin_print) __PYX_ERR(0, 394, __pyx_L1_error)
+
+ /* Cached unbound methods */
+ __pyx_mstate->__pyx_umethod_PyDict_Type_items.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_items.method_name = &__pyx_mstate->__pyx_n_u_items;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_pop.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_pop.method_name = &__pyx_mstate->__pyx_n_u_pop;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_values.type = (PyObject*)&PyDict_Type;
+ __pyx_mstate->__pyx_umethod_PyDict_Type_values.method_name = &__pyx_mstate->__pyx_n_u_values;
+ __pyx_mstate->__pyx_umethod_PyList_Type_pop.type = (PyObject*)&PyList_Type;
+ __pyx_mstate->__pyx_umethod_PyList_Type_pop.method_name = &__pyx_mstate->__pyx_n_u_pop;
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: cached_constants ### */
+
+static int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate) {
+ __Pyx_RefNannyDeclarations
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0);
+
+ /* "fontTools/qu2cu/qu2cu.py":226
+ * costs.append(cost)
+ * costs.append(cost)
+ * qq = add_implicit_on_curves(p)[1:] # <<<<<<<<<<<<<<
+ * costs.pop()
+ * q.extend(qq)
+*/
+ __pyx_mstate_global->__pyx_slice[0] = PySlice_New(__pyx_mstate_global->__pyx_int_1, Py_None, Py_None); if (unlikely(!__pyx_mstate_global->__pyx_slice[0])) __PYX_ERR(0, 226, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_slice[0]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_slice[0]);
+
+ /* "fontTools/qu2cu/qu2cu.py":293
+ * # Dynamic-Programming to find the solution with fewest number of
+ * # cubic curves, and within those the one with smallest error.
+ * sols = [Solution(0, 0, 0, False)] # <<<<<<<<<<<<<<
+ * impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False)
+ * start = 0
+*/
+ __pyx_mstate_global->__pyx_tuple[0] = PyTuple_Pack(4, __pyx_mstate_global->__pyx_int_0, __pyx_mstate_global->__pyx_int_0, __pyx_mstate_global->__pyx_int_0, Py_False); if (unlikely(!__pyx_mstate_global->__pyx_tuple[0])) __PYX_ERR(0, 293, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_mstate_global->__pyx_tuple[0]);
+ __Pyx_GIVEREF(__pyx_mstate_global->__pyx_tuple[0]);
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_tuple;
+ for (Py_ssize_t i=0; i<1; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_slice;
+ for (Py_ssize_t i=0; i<1; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ __Pyx_RefNannyFinishContext();
+ return 0;
+ __pyx_L1_error:;
+ __Pyx_RefNannyFinishContext();
+ return -1;
+}
+/* #### Code section: init_constants ### */
+
+static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) {
+ CYTHON_UNUSED_VAR(__pyx_mstate);
+ {
+ const struct { const unsigned int length: 10; } index[] = {{1},{28},{17},{23},{179},{43},{15},{23},{45},{1},{8},{39},{7},{6},{2},{9},{43},{8},{4},{5},{20},{8},{5},{5},{22},{7},{9},{18},{8},{4},{1},{17},{18},{5},{11},{4},{5},{5},{5},{5},{18},{6},{17},{19},{9},{3},{5},{5},{15},{25},{26},{21},{6},{8},{14},{7},{1},{5},{11},{11},{4},{10},{10},{13},{8},{5},{1},{11},{11},{1},{8},{4},{4},{7},{10},{8},{10},{4},{13},{10},{4},{4},{2},{4},{1},{2},{2},{6},{2},{2},{3},{5},{1},{2},{19},{36},{10},{5},{12},{4},{7},{21},{13},{18},{6},{8},{4},{12},{10},{4},{16},{33},{14},{6},{5},{11},{8},{10},{14},{5},{9},{2},{6},{1},{1},{5},{6},{1},{1},{3},{112},{131},{262},{56},{2},{2},{914}};
+ #if (CYTHON_COMPRESS_STRINGS) == 2 /* compression: bz2 (1847 bytes) */
+const char* const cstring = "BZh91AY&SY\307\325n\034\000\001'\377\377\377\377\377\377\376\367\375}\277\367\376\252\277\377\377\374@@@@@@@@@\000@@@\000@\000`\006\233\272L\215j\230\225k(+\355\340;\300\325?I)\250\304\3002\233I\261\0364\247\246M5M\014\320\232{T\006\320\t\221\211\241\220b=F\206\310\236\232\236\231L%\010#\020M&\t\243&\247\221#CF \321\210\000\000\000\000\001\240\320\036\240h`\324\300%4I\351\r\017P\320\007\250\320\003F@\000\000\000\000\000\000\r\251\232 \tM \232\202S\366\223OBS\366\245?Rg\251=M\037\2254\300hM\000\032\0310@\323L\230\214\321\032hbdd\034\000\000\000\000\000\000\000\r\000\000\000\000\000\000\000\000\021ORFC\324\300LL\232`L\004b`\230L\t\2011\030\000F\021\246\021\21010\032:\351jG\222\227\n\203s\334\366\346`\240\375S6\203;\016y\273\350h*L\032J\222 \016\212\340\223\302T'Bm2>s\320&\004\230Ha! \224i\r\200\"\025>\232;H\226ip\342B\024T\020p\024c\264E\350fJ\330j\340\016A\315\255r\214\222C\024$\022\332\023<\302\246\022x$\010A\236c\357\023F \352\343\\HI\201&\204TJ\257R\222$O\003k\r\200U\204^%n\303=\330c~\353d\311\275u\205\002i\024*\234\314WP\036Py\202\224/\213Y;\211\034\230\260\211\177\nL\336H\215\003\034\\\216\245*\302\326~\365\225\220\276\327\273\202\005\222b\324\021\371\037\nq\265\031\272\302c\314\347\346\374\376\230\014`^^\005\2333\236\337Z\345\331\")\374~\344ak\020\342\300\2217u@dl)x^\327Q2\026\233\";\344\241&\004\007w\243\321\362\342\374\007~`\033t9x\341\0029w\241\3260\317\023\356\273\037{\"\333x\\{73\264\351\342y_\3004\025yU\247\345\377y56\265`\321\2252\006C~\r\006F|\021\376\025\250JL\225\301#RVtfj\330_m\224(\205\002\224\364&{\336\334\035W\n\234\375\276~\277i\320N\"\376\177\001\034\233 T\233\305\344\010't#\021\007\211\030\2310\335\375\364^\272]\233l\366\265D\277\273\267C\005\327t\244\363\224 u\362}s\375\371c\371mu\235y\030\254\354L\311#\014\013C>\\\256\271EB\002\305\356\372\244\324\013K\302\265\033Z\207\245\250\0175*\014\256\006%ff\315{\001`{\216\241\036\310 \330\303d\216\035\037\246\037\353\306Yl\352\355\353u\033\255\233=u\254V\224\240@\240\252,:d\224\252\003\207\230\227F""\242\021\333B\227V\003\016}L\344\324bA\020[t\202\271\226\017\343\224(.GLt\246v;$C\263@\344^\205/<<\311e\222\224\250)\226l\201>GlM\251\2517mS\232\316~\345p\344z\361\360\337\217A\271\372\231\302vQ\371\352\260\235\311B\352\212-z\\\260L\345\353!\000\367\\\211\265\234[\354N\230X\257\354\250\005\006\330\0056\320\n\242\311\321<\221-\250\202\314\346F\310\3312l\323\270\205c\213C\245a\203\322\222\255\345&\004\221\226\263=\223.g \2636v\214\222#l\251T\017\"\"\267\364\252\216\257\334\201R\312q{\347\211\322XR\271F\264\261\364\324\332\302m*\270^\024\272\2734\322\2718\271\250mrMB2\327\024\272\222}\243bi\247\360\262\325\227\245\304c\025\311:_\314\305\370\277%\272\2129xG\267d\236\377\016\260\330\321\307X\325\\xW\034b\020\210;\244\334m\217:lJ\274\323\303\010)\003\315D4&\322\2660\211\221l\325my\355\330\313\r\223\310\030\232\213[\235\31574\363U\202Z\364\327d\210\2621\030p\313\030FAtww*AB+?\360J\021a1\013\326 I\272f\241\261F6\234\244\006\307i\2015\276%\225\214\210\210\301\037O\026E\252\202\255\307z\237q\302\302\307\222@\2172\235\3747\251\001\244\230E\346L#.[\254\017T.N\013c\337\323\250\232G\211d\201\2648F\3778\237\246\351\251\337+\320\324\206\2761-v\\\312D\236H\270\207v\274\016B\244\206O\327\325\277=i\2457\314\310?%O\346\350\351\332\246\355\037{[\200\277\252i[\203\001!lA\352\317\245\264\355\234F\342\361H\373\023\223\220\212&\270\324/\341p\271R\364\014\327\363\"\370e\250\255\227d\252\356\233}Uy\332\322\244q\307\n\",J\302\240L\251\206<\246S1\232\024\301g\241\267~\212\310\271\321 \354\365\005J9sC%K\2261\256R3\350$\355\225\322\010\213\030\023\361\343,\341k\251'\332\017\035(\335E\006{\210U\230\303\032I\344%7\354\345z\016E\271[\025U:\n\\\261\203\244,\006U\"2\340\353\201\257w\033I\310\030iF\225\026\027\2512h(\277\363\232\341\"\346Y\333|\336f\230\345r\235b\313m-\263Ei2a2=\264\005\302\031\220V\032\344\241\251\360\343\304\332o\241\3174\204\201\034O\235\305\243\304SN$\031Q5hIU\2568\224L\257\263\241\310\375x&\210(\343(\3103\324Q4=\303\310\002\200\200\t\001tC\306\361\027\254\222x\004\202K\017\240""\200\001B#]\237\302s\261\271\030A\223n\373\204wCyh\207\033\263\253\254\226X\037\014T m\276U\245\302\007\337x]1\352`\355\275T\263\"\350\033<\263\2254\326 \206@P\353\026\272r\262?\014(\212E\246\211\244\301\376\2058\226.\305\025\201X\242/\340\003\247\201\035;\256\250y\302pOD\256\367\2318P\370\221\221\311\\B\223\337\367\300:\031\205B\341\014\320\215\020\030\004?\363\005\013\t\243E\0245\"\177\250\032(\224P\027\341\267\016\361\254E\271\000\205%\rKH5\300\323\212%D\"\206\n\005\210\3222\251\220\277\034\017\211\342h\2152\314f\233\335\307\230&\223\216\213\324\322R\372\227q\r=-\300\342W3\362\252\255\302A\246\316\\\306\332[\211m\217c\237\306\302\376\216\207[p\034\221R\253M[\316U\211\302D\341\3154T\306\251\217\220\254\036\3766\025\234X$\t\"\034\\\370\245J]\231/J\304\222WrU\205\206\375-\246\333\233)\335\022\345\211\222\316=\257^gy\205\351o\377K\237G\037\254g\\\306\\gk\035\335\301\322\017|E\360\r\353\235V=\230\331\335W\363\234\316Ve\336\253DC\021\023\025\212d\256\323\010\233d@\215Y\202\226rxPL\220\204\306\204w\035\370R\344\263\000\215S\035kh?\201\301\312\324\316\323M\242*\243\261\252\264\210\nHp\362\210A\202\207!\223\377\027rE8P\220\307\325n\034";
+ PyObject *data = __Pyx_DecompressString(cstring, 1847, 2);
+ if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
+ const char* const bytes = __Pyx_PyBytes_AsString(data);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
+ #endif
+ #elif (CYTHON_COMPRESS_STRINGS) != 0 /* compression: zlib (1783 bytes) */
+const char* const cstring = "x\332uTMs\032G\032\026\262p\220\203\034\300he\307\261w\220\242\322f\313\306\001T\251\254\343\312\026\222?\222-\307\026\372\250\324VJ5\325\014\2154\3660\003\323=\212\224\335\203\217s\234c\037\3478G\216s\344'p\234#?A?a\237\356A {w)h\372\363\355\367\371x\273\372\332l?\351:6?t\034\213=\031xu\303K\333j\377\342\265\311\370o\252\331sL\233\037\037\253\376\241\327\267h:\363H\253V\253\307\307o\034N5~J\270\266{\301O\035[3\231\326\241\226\331\246.\341\324\272\320\030wM\203SWn\262\265\275\027{\217\267\277\337\326\210\335\321\\\372\216\032\234i\314k\033\026a\2142\315\351jm\317\264\270ik\374\242OYU\373\271\253]8\236fS\332\321\270\243\365\261\357\372\001~Jm\215Q.;\332\026\261m\207\023n:\266\216\343\246}\262\245uL\027\227\230gT\236~I,F\253om\252\271H\300\351i\206\3276\r\215{\256\215\360\000\345h\233\035m\340\221\016\2227\rV}\353\232'\246M,lt\317\350\323}j86\000y\000\324I\347\376\302\276yzx\3520z\355\330U\30061\336\317\242\252\253\000\350\357\244\323\321\221&5\274\372\300CV\026\230\262\r\252m\236T5\305\376\307s\035\223\221\266E\251-\333\023\303di\2573\273Nc}\313\224\220\350\300\003X\246A\013\213\022\306\265\206\326\227J\261\335\267\277\354\375\374\372\305s)\241\322N\327\367.\316\361{\016e\3647\364\234\357\323\356\201cy\2229%\361\221\215\236L\324\354!\270ar\035\224*\274L\327\211e\245\215\202D\330\205m\230N\325p\\\007\001l\312\332\224q\2359V\033\2662t]I\245\237Pnr\332\223C\354\321M(\344\022\203J\212\014\013\364\031\216eI\241\300\257\3410.\177\350x6W\227\250\253U\243sG\237AO3\242\026=\203\327\346\323\323\211\316|\006\244y=\345H\352\272\370:n\327r\010\237\231\277\252\304\370dXmS\3338\355\021\367\375|\241g2\003\363\177\230\324U\023\363\225\264pT\333u\\\003\267\353]\317\006\376\023j\253\233S\376\344\350\274\357\232\246dH5\272\202\231vUjf\217\234\200w\2071\023B\233\014\033\240\002=\327Uw\312\262\354Kf$\251\354\335\273y\240w\363@\357u\275G\300\264j{\204\003\313\271\\\301\204\323\361,\252\343c\223\036M\333\016\227\312#;\016\256t\247\333M\331\225\203\324F\230\252\341Wwl\344x\322\357\177\333""\257\365kz]o\364\353\375F\337\351\003\225\315\007\370\\\261.\265J\203\374\217\251\3523\3131P\217?V\247\224\314\305\222=\370\014\177V\232\236K\211\345Nko^\202\372\254N\334\353u\371\321@\007?P\\\026\244K\317\250\313h\007^\2006x3\246\330\321\353\320.\361, \264XZL\3634?\035\377W\332r\003\337\225R4\371\341\256\0321\306\211\313U\003\243w\240\234\316eM\350\374TI\010\225To\246\031?u\235\337ghpR=^\336\331\031\261<\252\032v~\361\207\331\377\220\271,-d\227\375%\277\351\267\222\245\274\337L\226\312\301n0\0207D]`&\347g\375\243\240\0224\002\222\344\276\360\007A&(^uJA=h%\271\025l\330\010\216\304\272x\031\256\207\315$\267\354\257MW\367\003W\254\205\231$W\304\251\245\317\375\332\207L\262\264<^\376kTQ\377Z\230\231,\025\002L~%\376\021\266\243\214L\341\215\300bq\\\334\010k\341\253\250\206\215>\256\254\0177\206\277\215\260\035\243\361\255\357\343\305x#n\305$YZ\031\257 LX\014\037G\203$]}\032\257\307\273\361`\224\231\216\037H$\252\267\031\266\000\371\326B~\005\007%b I\2627/\277[\310\226\202r\320\n\250\250\211\035A\004\017\033!\202\177\366\201\373\265$W\220\260\203-Q\024\033\342 \314\206\007\321\315\310\030\226\207Gqe\002\2122\376\252O|\027[*\212C\031\030`S\366Z)]\213\340\350@d\020\242\222\344\362\376\213`5 \001\023\025\321\020m0\224/\007\315$_\014\326\344\226y'\007t\222\207\235\220\204\003I\365\241\244?%\270\250\362\222\223\277\006\315\2405\221L\000\2510\302\265hyX\234\\%\017\356\203\315\300\035\337\373[\\\216\3779\"S!.\313\013\331\333\376ApC\351\004\352\262\010\234,\347\375] )A\360\266\310|:\314-\213bT\304\331\352\202\366$\"2D\336o\000;K\321]\336\\\310\336\227\000\357\211\022\210l\212\303\260\024\326\303\303\250\034\035\r7\207\203\370F\334\210I\314F\353\243\335\321\3402\267\220-J\361\347&3R~\246\232\312\263\373\341 \312\\\233\251I\207}<\372|\232\302z\360\\,B!IA)\334\016\335\350\313a}x\020g\342;\361\016\254\222/\005_\203tuo\336\257\371?\201\266\003\030\275!\300I1\270\253\344m\215\0376pj\177\350\342\324.R\255L\313\342\243$\307_>\211\334!\024\270\023\324\346r\376\216@\371{\342a\310\243Z""\264\023\365\342m\230pqT\231\344\277\360yP\233\024\036\210\243\260\242@\271Q)jD\355av\330\032\266\225\231\367\343ARx(\254h=j\312\016I\nwE\016(\037 P\035\3132\225\302\235\340;ImR\376\263\030L0\372\001X\213\223\362*\230_\271\235\024\326\002O\274\2162\343o~\214\215QiT\037\265\222;\345\244P\224\313\353\341C\344|7\206\275\276\022M9\263*=W\200\003~\020\246\242\271\2606^CmF\365h\037\305T\370\223\322c\013\265\265\201\224%(dv\037\302\276\nk\327RYM\000\354\327\260\031\266\022\000=\223n\232\244W\026\377\177\3542\304Z\022/\302\325\320\210\212\022\311\366x\355\333a\006Eu\010\233\036\216\212I\371\352M\300\005\237\304\275/\036\251\225\274\364{\017o\004\312\347'\330\3558z\014\335J\322d\263#*c\251\300\277\245\320\0100\201^\257\202Z\240\234SG\005\244\325'\275{{\366\010\346\374\033p\310Kx\n\203[~E\026\361\277\304\327x\031\\\320\310\207\265\341\316\020/\342m\377\004\217\006\231\025\241\354\264\246O\013\336\2054\316\263\340\\\014\302%\020t\020e\242\273\3226\362u\200\035\345\213\362($\343\255g\260\232\233\n6\"\023h\222\305\203ZQ\316\204\n'@I\020b\021\357\353\363h\021\016\331\201u2\322x\315\211\254\346\377\000\010\324o#";
+ PyObject *data = __Pyx_DecompressString(cstring, 1783, 1);
+ if (unlikely(!data)) __PYX_ERR(0, 1, __pyx_L1_error)
+ const char* const bytes = __Pyx_PyBytes_AsString(data);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (likely(bytes)); else { Py_DECREF(data); __PYX_ERR(0, 1, __pyx_L1_error) }
+ #endif
+ #else /* compression: none (2937 bytes) */
+const char* const bytes = ".Lib/fontTools/qu2cu/qu2cu.pyList[List[Point]]List[Tuple[Point, ...]]Note that Cython is deliberately stricter than PEP-484 and rejects subclasses of builtin types. If you need to pass subclasses then set the 'annotation_typing' directive to False.One random cubic turned into %d quadratics.Original curve:Reconstructed curve(s):Those quadratics turned back into %d cubics. ?add_notecu2qu tolerance %g. qu2cu tolerance %g.disableenablegcisenabledquadratic spline requires at least 3 pointsCOMPILEDListPoint__Pyx_PyDict_NextRefSolutionTupleUnionadd_implicit_on_curves__all__all_cubicasyncio.coroutinesbest_solboolc__class_getitem__cline_in_tracebackclosecollectionscostcostscountcubiccurvecurve_to_quadraticcurveselevate_quadraticelevated_quadraticsenumerateerrerrorfloatfontTools.cu2qufontTools.cu2qu.benchmarkfontTools.misc.bezierToolsfontTools.qu2cu.qu2cuforced__func__generate_curvegenexprii_soli_sol_counti_sol_errorimagimpossibleis_complex_is_coroutineis_cubicitemsjj_sol_countj_sol_errork__main__mainmathmax_err__module____name__namedtuplenextnum_offcurvesnum_pointsoff1off2onorigpp0p1p1_2_3p2p3popprintqqqquadratic_to_curvesquadratic_to_curves..genexprquadraticsquads__qualname__realreconstreconstruct_tolerancereconstructedreconstructed_iterreturnreversedsend__set_name__setdefaultsolsspline_to_curvesspline_to_curves..genexprsplitCubicAtTCsplitsstartstart_index__test__this_countthis_sol_countthrowtolerancetstypinguvvaluevaluesxyzip\200\001\360\022\000\005\t\210\004\210A\210Q\330\004\014\210A\330\004\024\220C\220q\230\003\2302\230Q\330\004\010\210\005\210U\220!\2203\220a\330\010\017\210q\220\001\220\021\330\010\017\210q\220\001\220\022\2202\220Q\330\010\r\210U\220#\220U\230\"\230F\240\"\240A\330\010\t\210\027\220\001\220\022\2202\220R\220r\230\027\240\001\330\010\021\220\021\330\004\013\2101\200\001\330\004\t\320\t*\250!\330\004\t\320\t \240\001\340\004\020\220\001\330\004\034\230J\240b\250\001\330\004\014\210N\230!\330\004\021\320\021#\2401\240G\2501\330\004\t""\210\021\330\0102\260#\260[\300\001\340\004\t\210\021\320\n8\270\002\270#\270Q\270a\330\004\r\320\r \240\001\240\021\240-\250q\330\004\t\210\021\320\n:\270\"\270C\270q\300\001\330\004\t\210\021\320\n\035\230Q\330\004\t\210\021\320\n%\240Q\200\001\360\n\000\014\r\330\004\r\210Q\330\004\017\210q\330\005\006\3606\000\005\022\220\024\220Q\220e\2301\230B\230a\230t\2403\240a\330\004\007\200t\2101\330\010\020\220\001\220\021\220'\230\021\230#\230S\240\005\240S\250\006\250c\260\024\260U\270!\340\004\010\210\001\210\025\210a\210r\220\021\220!\330\004\014\210A\210Q\330\004\013\2101\330\004\010\210\005\210Q\330\010\017\210q\220\002\220#\220S\230\001\230\021\230!\330\010\014\210E\220\025\220a\220s\230!\2303\230b\240\001\330\014\024\220A\330\014\021\220\027\230\001\230\021\330\014\021\220\027\230\001\230\021\330\010\r\320\r#\2401\240B\240a\240q\330\010\r\210T\220\021\330\010\t\210\027\220\001\220\021\330\010\020\220\001\330\010\r\210W\220A\220Q\340\004\r\320\r\035\230Q\230c\240\027\250\t\260\021\340\004\007\200t\2101\330\010\021\220\021\220%\220r\320\0319\270\024\270Y\300a\330\004\013\2101\200\001\360\024\000\005\016\210S\220\003\2202\220R\220q\330\004\005\330\010\t\330\t\014\210C\210r\220\022\2203\220b\230\001\330\t\014\210C\210r\220\022\2203\220b\230\001\330\010\t\230\021\250\021\200\001\360.\000 /\250a\360\024\000\005\014\2103\210a\210s\220#\220S\230\001\360\006\000\005\033\230!\330\010\031\230\022\2301\230A\230T\240\022\2402\240T\250\024\250U\260%\260q\270\003\2703\270a\270s\300\"\300C\300q\360\010\000\005\021\220\001\330\004\010\210\005\210U\220!\2203\220c\230\021\230!\330\010\r\320\r \240\001\240\022\2402\240R\240q\250\001\330\010\r\320\r \240\001\240\022\2401\240A\330\010\r\320\r \240\001\240\022\2401\240A\330\010\013\2103\210a\210s\220\"\220D\230\002\230#\230Q\230c\240\022\2404\240r\250\032\2602\260S\270\001\270\023\270B\270a\330\014\022\220$\220a\220q\360\010\000\005\014\2101\210H\220A\220S\230\003\2303\230a\330\004\021\220\030\230\021\230#\230Q\320\0363\2602\260R\260r""\270\023\270C\270s\300!\330\004\014\210A\330\004\010\210\005\210U\220!\2203\220c\230\021\320\032/\250r\260\021\330\010\023\2201\330\010\014\210E\220\025\220a\220w\230a\330\014\031\230\036\240t\2501\250B\250m\2704\270q\300\002\300!\340\014\017\210t\2201\340\020\035\230U\240!\2402\240R\240r\250\022\2503\250b\260\005\260Q\260b\270\002\270#\270R\270q\330\020\036\230l\250\"\250A\330\020\036\230a\330\020\030\230\010\240\001\240\035\250m\2702\270R\270s\300!\330\020\023\2206\230\022\2301\330\024\037\230q\340\020\023\220;\230c\240\021\340\024\025\360\006\000\r\016\330\020\027\220u\230L\250\001\320)>\270c\300\022\3002\300Q\330\023\024\330\020\021\360\006\000\r\"\240\036\250r\260\030\270\021\330\014\034\230A\360\006\000\r\025\220A\330\014\020\220\003\220;\230i\240q\250\001\330\020\027\320\027*\250!\2502\250R\250q\330\020\026\220c\230\021\230'\240\021\240#\240R\240t\2501\250A\330\020\033\2301\230G\2401\330\020\023\2206\230\022\2301\330\024\025\330\020\035\230W\240A\240Q\330\014\017\210v\220R\220q\340\020\021\360\006\000\r\021\220\003\220;\230i\240q\250\001\330\020\027\320\027*\250!\2502\250R\250q\330\020\024\220D\230\004\230E\240\025\240c\250\021\340\020\023\2204\320\0270\260\001\260\024\260T\270\024\270T\300\021\330\024\034\230J\240b\250\001\330\024\025\330\014\017\210v\220R\220q\340\020\021\360\006\000\r\033\230,\240b\250\001\330\014\035\230Q\230m\2501\330\014\024\220H\230A\230]\250-\260r\270\022\2703\270a\330\014\017\210v\220R\220q\330\020\033\2301\340\014\017\210|\2303\230a\340\020\021\340\010\014\210G\2201\220A\330\010\013\2102\210S\220\001\330\014\024\220A\360\006\000\005\016\210Q\330\004\014\210A\330\004\010\210\003\2101\210F\220\"\220A\330\004\n\210!\330\010\017\210{\230$\230a\230r\240\036\250t\2601\260B\260a\330\010\016\210g\220Q\220a\330\010\r\210W\220A\220Q\330\010\r\210Q\330\004\r\210Q\330\004\010\210\001\330\004\010\210\003\210<\220x\230q\240\004\240A\240S\250\001\250\030\260\021\330\010\013\2101\330\014\022\220'\230\021\230,\240a\320'<\270C\270r\300\022\3002\300Q""\300a\340\014\020\220\005\220U\230!\2303\230a\330\020\026\220g\230Q\230a\230q\240\002\240\"\240D\250\002\250\"\250B\250b\260\001\330\010\014\210A\340\004\013\2101";
+ PyObject *data = NULL;
+ CYTHON_UNUSED_VAR(__Pyx_DecompressString);
+ #endif
+ PyObject **stringtab = __pyx_mstate->__pyx_string_tab;
+ Py_ssize_t pos = 0;
+ for (int i = 0; i < 130; i++) {
+ Py_ssize_t bytes_length = index[i].length;
+ PyObject *string = PyUnicode_DecodeUTF8(bytes + pos, bytes_length, NULL);
+ if (likely(string) && i >= 17) PyUnicode_InternInPlace(&string);
+ if (unlikely(!string)) {
+ Py_XDECREF(data);
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ stringtab[i] = string;
+ pos += bytes_length;
+ }
+ for (int i = 130; i < 137; i++) {
+ Py_ssize_t bytes_length = index[i].length;
+ PyObject *string = PyBytes_FromStringAndSize(bytes + pos, bytes_length);
+ stringtab[i] = string;
+ pos += bytes_length;
+ if (unlikely(!string)) {
+ Py_XDECREF(data);
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ Py_XDECREF(data);
+ for (Py_ssize_t i = 0; i < 137; i++) {
+ if (unlikely(PyObject_Hash(stringtab[i]) == -1)) {
+ __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = stringtab + 130;
+ for (Py_ssize_t i=0; i<7; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ }
+ {
+ PyObject **numbertab = __pyx_mstate->__pyx_number_tab + 0;
+ int8_t const cint_constants_1[] = {0,1,3};
+ for (int i = 0; i < 3; i++) {
+ numbertab[i] = PyLong_FromLong(cint_constants_1[i - 0]);
+ if (unlikely(!numbertab[i])) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ #if CYTHON_IMMORTAL_CONSTANTS
+ {
+ PyObject **table = __pyx_mstate->__pyx_number_tab;
+ for (Py_ssize_t i=0; i<3; ++i) {
+ #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);
+ #else
+ Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);
+ #endif
+ }
+ }
+ #endif
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: init_codeobjects ### */
+typedef struct {
+ unsigned int argcount : 3;
+ unsigned int num_posonly_args : 1;
+ unsigned int num_kwonly_args : 1;
+ unsigned int nlocals : 6;
+ unsigned int flags : 10;
+ unsigned int first_line : 9;
+} __Pyx_PyCode_New_function_description;
+/* NewCodeObj.proto */
+static PyObject* __Pyx_PyCode_New(
+ const __Pyx_PyCode_New_function_description descr,
+ PyObject * const *varnames,
+ PyObject *filename,
+ PyObject *funcname,
+ PyObject *line_table,
+ PyObject *tuple_dedup_map
+);
+
+
+static int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate) {
+ PyObject* tuple_dedup_map = PyDict_New();
+ if (unlikely(!tuple_dedup_map)) return -1;
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 3, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 235};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_cost, __pyx_mstate->__pyx_n_u_is_complex, __pyx_mstate->__pyx_n_u_c};
+ __pyx_mstate_global->__pyx_codeobj_tab[0] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591__3, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[0])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 21, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 340};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_j, __pyx_mstate->__pyx_n_u_k, __pyx_mstate->__pyx_n_u_start, __pyx_mstate->__pyx_n_u_i_sol_count, __pyx_mstate->__pyx_n_u_j_sol_count, __pyx_mstate->__pyx_n_u_this_sol_count, __pyx_mstate->__pyx_n_u_tolerance, __pyx_mstate->__pyx_n_u_err, __pyx_mstate->__pyx_n_u_error, __pyx_mstate->__pyx_n_u_i_sol_error, __pyx_mstate->__pyx_n_u_j_sol_error, __pyx_mstate->__pyx_n_u_all_cubic, __pyx_mstate->__pyx_n_u_is_cubic, __pyx_mstate->__pyx_n_u_count, __pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p3, __pyx_mstate->__pyx_n_u_v, __pyx_mstate->__pyx_n_u_u};
+ __pyx_mstate_global->__pyx_codeobj_tab[1] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_kp_b_iso88591__4, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[1])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 4, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 82};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p1_2_3};
+ __pyx_mstate_global->__pyx_codeobj_tab[2] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_elevate_quadratic, __pyx_mstate->__pyx_kp_b_iso88591_S_2Rq_Cr_3b_Cr_3b, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[2])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {1, 0, 0, 8, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 154};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_p, __pyx_mstate->__pyx_n_u_count, __pyx_mstate->__pyx_n_u_num_offcurves, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_off1, __pyx_mstate->__pyx_n_u_off2, __pyx_mstate->__pyx_n_u_on, __pyx_mstate->__pyx_n_u_q};
+ __pyx_mstate_global->__pyx_codeobj_tab[3] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_add_implicit_on_curves, __pyx_mstate->__pyx_kp_b_iso88591_AQ_A_Cq_2Q_U_3a_q_q_2Q_U_U_F_A, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[3])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 17, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 178};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_quads, __pyx_mstate->__pyx_n_u_max_err, __pyx_mstate->__pyx_n_u_all_cubic, __pyx_mstate->__pyx_n_u_cost, __pyx_mstate->__pyx_n_u_is_complex, __pyx_mstate->__pyx_n_u_q, __pyx_mstate->__pyx_n_u_costs, __pyx_mstate->__pyx_n_u_p, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_qq, __pyx_mstate->__pyx_n_u_curves, __pyx_mstate->__pyx_n_u_p, __pyx_mstate->__pyx_n_u_x, __pyx_mstate->__pyx_n_u_y, __pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[4] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_quadratic_to_curves, __pyx_mstate->__pyx_kp_b_iso88591_Q_q_6_Qe1Bat3a_t1_S_S_c_U_ar_AQ, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[4])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {4, 0, 0, 42, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 242};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_q, __pyx_mstate->__pyx_n_u_costs, __pyx_mstate->__pyx_n_u_tolerance, __pyx_mstate->__pyx_n_u_all_cubic, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_j, __pyx_mstate->__pyx_n_u_k, __pyx_mstate->__pyx_n_u_start, __pyx_mstate->__pyx_n_u_i_sol_count, __pyx_mstate->__pyx_n_u_j_sol_count, __pyx_mstate->__pyx_n_u_this_sol_count, __pyx_mstate->__pyx_n_u_err, __pyx_mstate->__pyx_n_u_error, __pyx_mstate->__pyx_n_u_i_sol_error, __pyx_mstate->__pyx_n_u_j_sol_error, __pyx_mstate->__pyx_n_u_is_cubic, __pyx_mstate->__pyx_n_u_count, __pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p3, __pyx_mstate->__pyx_n_u_v, __pyx_mstate->__pyx_n_u_u, __pyx_mstate->__pyx_n_u_elevated_quadratics, __pyx_mstate->__pyx_n_u_forced, __pyx_mstate->__pyx_n_u_sols, __pyx_mstate->__pyx_n_u_impossible, __pyx_mstate->__pyx_n_u_best_sol, __pyx_mstate->__pyx_n_u_this_count, __pyx_mstate->__pyx_n_u_i_sol, __pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_ts, __pyx_mstate->__pyx_n_u_reconstructed_iter, __pyx_mstate->__pyx_n_u_reconstructed, __pyx_mstate->__pyx_n_u_reconst, __pyx_mstate->__pyx_n_u_orig, __pyx_mstate->__pyx_n_u_splits, __pyx_mstate->__pyx_n_u_cubic, __pyx_mstate->__pyx_n_u_curves, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_genexpr, __pyx_mstate->__pyx_n_u_genexpr};
+ __pyx_mstate_global->__pyx_codeobj_tab[5] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_spline_to_curves, __pyx_mstate->__pyx_kp_b_iso88591_a_3as_S_1AT_2T_U_q_3as_Cq_U_3c, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[5])) goto bad;
+ }
+ {
+ const __Pyx_PyCode_New_function_description descr = {0, 0, 0, 7, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 386};
+ PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_generate_curve, __pyx_mstate->__pyx_n_u_curve_to_quadratic, __pyx_mstate->__pyx_n_u_tolerance, __pyx_mstate->__pyx_n_u_reconstruct_tolerance, __pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_quadratics, __pyx_mstate->__pyx_n_u_curves};
+ __pyx_mstate_global->__pyx_codeobj_tab[6] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_qu2cu_qu2cu_py, __pyx_mstate->__pyx_n_u_main_2, __pyx_mstate->__pyx_kp_b_iso88591_Jb_N_1G1_2_8_Qa_q_Cq_Q_Q, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[6])) goto bad;
+ }
+ Py_DECREF(tuple_dedup_map);
+ return 0;
+ bad:
+ Py_DECREF(tuple_dedup_map);
+ return -1;
+}
+/* #### Code section: init_globals ### */
+
+static int __Pyx_InitGlobals(void) {
+ /* PythonCompatibility.init */
+ if (likely(__Pyx_init_co_variables() == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* AssertionsEnabled.init */
+ if (likely(__Pyx_init_assertions_enabled() == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CommonTypesMetaclass.init */
+ if (likely(__pyx_CommonTypesMetaclass_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CachedMethodType.init */
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ {
+ PyObject *typesModule=NULL;
+ typesModule = PyImport_ImportModule("types");
+ if (typesModule) {
+ __pyx_mstate_global->__Pyx_CachedMethodType = PyObject_GetAttrString(typesModule, "MethodType");
+ Py_DECREF(typesModule);
+ }
+ } // error handling follows
+ #endif
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* CythonFunctionShared.init */
+ if (likely(__pyx_CyFunction_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ /* Generator.init */
+ if (likely(__pyx_Generator_init(__pyx_m) == 0)); else
+
+ if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error)
+
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+/* #### Code section: cleanup_globals ### */
+/* #### Code section: cleanup_module ### */
+/* #### Code section: main_method ### */
+/* #### Code section: utility_code_pragmas ### */
+#ifdef _MSC_VER
+#pragma warning( push )
+/* Warning 4127: conditional expression is constant
+ * Cython uses constant conditional expressions to allow in inline functions to be optimized at
+ * compile-time, so this warning is not useful
+ */
+#pragma warning( disable : 4127 )
+#endif
+
+
+
+/* #### Code section: utility_code_def ### */
+
+/* --- Runtime support code --- */
+/* Refnanny */
+#if CYTHON_REFNANNY
+static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) {
+ PyObject *m = NULL, *p = NULL;
+ void *r = NULL;
+ m = PyImport_ImportModule(modname);
+ if (!m) goto end;
+ p = PyObject_GetAttrString(m, "RefNannyAPI");
+ if (!p) goto end;
+ r = PyLong_AsVoidPtr(p);
+end:
+ Py_XDECREF(p);
+ Py_XDECREF(m);
+ return (__Pyx_RefNannyAPIStruct *)r;
+}
+#endif
+
+/* PyErrExceptionMatches (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(tuple);
+ for (i=0; i= 0x030C00A6
+ PyObject *current_exception = tstate->current_exception;
+ if (unlikely(!current_exception)) return 0;
+ exc_type = (PyObject*) Py_TYPE(current_exception);
+ if (exc_type == err) return 1;
+#else
+ exc_type = tstate->curexc_type;
+ if (exc_type == err) return 1;
+ if (unlikely(!exc_type)) return 0;
+#endif
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_INCREF(exc_type);
+ #endif
+ if (unlikely(PyTuple_Check(err))) {
+ result = __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err);
+ } else {
+ result = __Pyx_PyErr_GivenExceptionMatches(exc_type, err);
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(exc_type);
+ #endif
+ return result;
+}
+#endif
+
+/* PyErrFetchRestore (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyObject *tmp_value;
+ assert(type == NULL || (value != NULL && type == (PyObject*) Py_TYPE(value)));
+ if (value) {
+ #if CYTHON_COMPILING_IN_CPYTHON
+ if (unlikely(((PyBaseExceptionObject*) value)->traceback != tb))
+ #endif
+ PyException_SetTraceback(value, tb);
+ }
+ tmp_value = tstate->current_exception;
+ tstate->current_exception = value;
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+#else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ tmp_type = tstate->curexc_type;
+ tmp_value = tstate->curexc_value;
+ tmp_tb = tstate->curexc_traceback;
+ tstate->curexc_type = type;
+ tstate->curexc_value = value;
+ tstate->curexc_traceback = tb;
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+#endif
+}
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyObject* exc_value;
+ exc_value = tstate->current_exception;
+ tstate->current_exception = 0;
+ *value = exc_value;
+ *type = NULL;
+ *tb = NULL;
+ if (exc_value) {
+ *type = (PyObject*) Py_TYPE(exc_value);
+ Py_INCREF(*type);
+ #if CYTHON_COMPILING_IN_CPYTHON
+ *tb = ((PyBaseExceptionObject*) exc_value)->traceback;
+ Py_XINCREF(*tb);
+ #else
+ *tb = PyException_GetTraceback(exc_value);
+ #endif
+ }
+#else
+ *type = tstate->curexc_type;
+ *value = tstate->curexc_value;
+ *tb = tstate->curexc_traceback;
+ tstate->curexc_type = 0;
+ tstate->curexc_value = 0;
+ tstate->curexc_traceback = 0;
+#endif
+}
+#endif
+
+/* PyObjectGetAttrStr (used by PyObjectGetAttrStrNoError) */
+#if CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) {
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro))
+ return tp->tp_getattro(obj, attr_name);
+ return PyObject_GetAttr(obj, attr_name);
+}
+#endif
+
+/* PyObjectGetAttrStrNoError (used by GetBuiltinName) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError)))
+ __Pyx_PyErr_Clear();
+}
+#endif
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) {
+ PyObject *result;
+#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ (void) PyObject_GetOptionalAttr(obj, attr_name, &result);
+ return result;
+#else
+#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) {
+ return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1);
+ }
+#endif
+ result = __Pyx_PyObject_GetAttrStr(obj, attr_name);
+ if (unlikely(!result)) {
+ __Pyx_PyObject_GetAttrStr_ClearAttributeError();
+ }
+ return result;
+#endif
+}
+
+/* GetBuiltinName */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name) {
+ PyObject* result = __Pyx_PyObject_GetAttrStrNoError(__pyx_mstate_global->__pyx_b, name);
+ if (unlikely(!result) && !PyErr_Occurred()) {
+ PyErr_Format(PyExc_NameError,
+ "name '%U' is not defined", name);
+ }
+ return result;
+}
+
+/* TupleAndListFromArray (used by fastcall) */
+#if !CYTHON_COMPILING_IN_CPYTHON && CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject *
+__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ Py_ssize_t i;
+ if (n <= 0) {
+ return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple);
+ }
+ res = PyTuple_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ for (i = 0; i < n; i++) {
+ if (unlikely(__Pyx_PyTuple_SET_ITEM(res, i, src[i]) < (0))) {
+ Py_DECREF(res);
+ return NULL;
+ }
+ Py_INCREF(src[i]);
+ }
+ return res;
+}
+#elif CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) {
+ PyObject *v;
+ Py_ssize_t i;
+ for (i = 0; i < length; i++) {
+ v = dest[i] = src[i];
+ Py_INCREF(v);
+ }
+}
+static CYTHON_INLINE PyObject *
+__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple);
+ }
+ res = PyTuple_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n);
+ return res;
+}
+static CYTHON_INLINE PyObject *
+__Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n)
+{
+ PyObject *res;
+ if (n <= 0) {
+ return PyList_New(0);
+ }
+ res = PyList_New(n);
+ if (unlikely(res == NULL)) return NULL;
+ __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n);
+ return res;
+}
+#endif
+
+/* BytesEquals (used by UnicodeEquals) */
+static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL ||\
+ !(CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS)
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+ if (s1 == s2) {
+ return (equals == Py_EQ);
+ } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) {
+ const char *ps1, *ps2;
+ Py_ssize_t length = PyBytes_GET_SIZE(s1);
+ if (length != PyBytes_GET_SIZE(s2))
+ return (equals == Py_NE);
+ ps1 = PyBytes_AS_STRING(s1);
+ ps2 = PyBytes_AS_STRING(s2);
+ if (ps1[0] != ps2[0]) {
+ return (equals == Py_NE);
+ } else if (length == 1) {
+ return (equals == Py_EQ);
+ } else {
+ int result;
+#if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000)
+ Py_hash_t hash1, hash2;
+ hash1 = ((PyBytesObject*)s1)->ob_shash;
+ hash2 = ((PyBytesObject*)s2)->ob_shash;
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ return (equals == Py_NE);
+ }
+#endif
+ result = memcmp(ps1, ps2, (size_t)length);
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) {
+ return (equals == Py_NE);
+ } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) {
+ return (equals == Py_NE);
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+#endif
+}
+
+/* UnicodeEquals (used by fastcall) */
+static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+ int s1_is_unicode, s2_is_unicode;
+ if (s1 == s2) {
+ goto return_eq;
+ }
+ s1_is_unicode = PyUnicode_CheckExact(s1);
+ s2_is_unicode = PyUnicode_CheckExact(s2);
+ if (s1_is_unicode & s2_is_unicode) {
+ Py_ssize_t length, length2;
+ int kind;
+ void *data1, *data2;
+ #if !CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0))
+ return -1;
+ #endif
+ length = __Pyx_PyUnicode_GET_LENGTH(s1);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(length < 0)) return -1;
+ #endif
+ length2 = __Pyx_PyUnicode_GET_LENGTH(s2);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(length2 < 0)) return -1;
+ #endif
+ if (length != length2) {
+ goto return_ne;
+ }
+#if CYTHON_USE_UNICODE_INTERNALS
+ {
+ Py_hash_t hash1, hash2;
+ hash1 = ((PyASCIIObject*)s1)->hash;
+ hash2 = ((PyASCIIObject*)s2)->hash;
+ if (hash1 != hash2 && hash1 != -1 && hash2 != -1) {
+ goto return_ne;
+ }
+ }
+#endif
+ kind = __Pyx_PyUnicode_KIND(s1);
+ if (kind != __Pyx_PyUnicode_KIND(s2)) {
+ goto return_ne;
+ }
+ data1 = __Pyx_PyUnicode_DATA(s1);
+ data2 = __Pyx_PyUnicode_DATA(s2);
+ if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) {
+ goto return_ne;
+ } else if (length == 1) {
+ goto return_eq;
+ } else {
+ int result = memcmp(data1, data2, (size_t)(length * kind));
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & s2_is_unicode) {
+ goto return_ne;
+ } else if ((s2 == Py_None) & s1_is_unicode) {
+ goto return_ne;
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+return_eq:
+ return (equals == Py_EQ);
+return_ne:
+ return (equals == Py_NE);
+#endif
+}
+
+/* fastcall */
+#if CYTHON_METH_FASTCALL
+static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s)
+{
+ Py_ssize_t i, n = __Pyx_PyTuple_GET_SIZE(kwnames);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(n == -1)) return NULL;
+ #endif
+ for (i = 0; i < n; i++)
+ {
+ PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!namei)) return NULL;
+ #endif
+ if (s == namei) return kwvalues[i];
+ }
+ for (i = 0; i < n; i++)
+ {
+ PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i);
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!namei)) return NULL;
+ #endif
+ int eq = __Pyx_PyUnicode_Equals(s, namei, Py_EQ);
+ if (unlikely(eq != 0)) {
+ if (unlikely(eq < 0)) return NULL;
+ return kwvalues[i];
+ }
+ }
+ return NULL;
+}
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API
+CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) {
+ Py_ssize_t i, nkwargs;
+ PyObject *dict;
+#if !CYTHON_ASSUME_SAFE_SIZE
+ nkwargs = PyTuple_Size(kwnames);
+ if (unlikely(nkwargs < 0)) return NULL;
+#else
+ nkwargs = PyTuple_GET_SIZE(kwnames);
+#endif
+ dict = PyDict_New();
+ if (unlikely(!dict))
+ return NULL;
+ for (i=0; itp_call;
+ if (unlikely(!call))
+ return PyObject_Call(func, arg, kw);
+ if (unlikely(Py_EnterRecursiveCall(" while calling a Python object")))
+ return NULL;
+ result = (*call)(func, arg, kw);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* PyObjectCallMethO (used by PyObjectFastCall) */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) {
+ PyObject *self, *result;
+ PyCFunction cfunc;
+ cfunc = __Pyx_CyOrPyCFunction_GET_FUNCTION(func);
+ self = __Pyx_CyOrPyCFunction_GET_SELF(func);
+ if (unlikely(Py_EnterRecursiveCall(" while calling a Python object")))
+ return NULL;
+ result = cfunc(self, arg);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* PyObjectFastCall (used by PyObjectCallOneArg) */
+#if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API
+static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs) {
+ PyObject *argstuple;
+ PyObject *result = 0;
+ size_t i;
+ argstuple = PyTuple_New((Py_ssize_t)nargs);
+ if (unlikely(!argstuple)) return NULL;
+ for (i = 0; i < nargs; i++) {
+ Py_INCREF(args[i]);
+ if (__Pyx_PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]) != (0)) goto bad;
+ }
+ result = __Pyx_PyObject_Call(func, argstuple, kwargs);
+ bad:
+ Py_DECREF(argstuple);
+ return result;
+}
+#endif
+#if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API
+ #if PY_VERSION_HEX < 0x03090000
+ #define __Pyx_PyVectorcall_Function(callable) _PyVectorcall_Function(callable)
+ #elif CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE vectorcallfunc __Pyx_PyVectorcall_Function(PyObject *callable) {
+ PyTypeObject *tp = Py_TYPE(callable);
+ #if defined(__Pyx_CyFunction_USED)
+ if (__Pyx_CyFunction_CheckExact(callable)) {
+ return __Pyx_CyFunction_func_vectorcall(callable);
+ }
+ #endif
+ if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
+ return NULL;
+ }
+ assert(PyCallable_Check(callable));
+ Py_ssize_t offset = tp->tp_vectorcall_offset;
+ assert(offset > 0);
+ vectorcallfunc ptr;
+ memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
+ return ptr;
+}
+ #else
+ #define __Pyx_PyVectorcall_Function(callable) PyVectorcall_Function(callable)
+ #endif
+#endif
+static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject *const *args, size_t _nargs, PyObject *kwargs) {
+ Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs);
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (nargs == 0 && kwargs == NULL) {
+ if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS))
+ return __Pyx_PyObject_CallMethO(func, NULL);
+ }
+ else if (nargs == 1 && kwargs == NULL) {
+ if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O))
+ return __Pyx_PyObject_CallMethO(func, args[0]);
+ }
+#endif
+ if (kwargs == NULL) {
+ #if CYTHON_VECTORCALL
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ return PyObject_Vectorcall(func, args, _nargs, NULL);
+ #else
+ vectorcallfunc f = __Pyx_PyVectorcall_Function(func);
+ if (f) {
+ return f(func, args, _nargs, NULL);
+ }
+ #endif
+ #endif
+ }
+ if (nargs == 0) {
+ return __Pyx_PyObject_Call(func, __pyx_mstate_global->__pyx_empty_tuple, kwargs);
+ }
+ #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API
+ return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs);
+ #else
+ return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs);
+ #endif
+}
+
+/* PyObjectCallOneArg (used by CallUnboundCMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
+ PyObject *args[2] = {NULL, arg};
+ return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* UnpackUnboundCMethod (used by CallUnboundCMethod0) */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030C0000
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *args, PyObject *kwargs) {
+ PyObject *result;
+ PyObject *selfless_args = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
+ if (unlikely(!selfless_args)) return NULL;
+ result = PyObject_Call(method, selfless_args, kwargs);
+ Py_DECREF(selfless_args);
+ return result;
+}
+#elif CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03090000
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) {
+ return _PyObject_Vectorcall
+ (method, args ? args+1 : NULL, nargs ? nargs-1 : 0, kwnames);
+}
+#else
+static PyObject *__Pyx_SelflessCall(PyObject *method, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) {
+ return
+#if PY_VERSION_HEX < 0x03090000
+ _PyObject_Vectorcall
+#else
+ PyObject_Vectorcall
+#endif
+ (method, args ? args+1 : NULL, nargs ? (size_t) nargs-1 : 0, kwnames);
+}
+#endif
+static PyMethodDef __Pyx_UnboundCMethod_Def = {
+ "CythonUnboundCMethod",
+ __PYX_REINTERPRET_FUNCION(PyCFunction, __Pyx_SelflessCall),
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030C0000
+ METH_VARARGS | METH_KEYWORDS,
+#else
+ METH_FASTCALL | METH_KEYWORDS,
+#endif
+ NULL
+};
+static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) {
+ PyObject *method, *result=NULL;
+ method = __Pyx_PyObject_GetAttrStr(target->type, *target->method_name);
+ if (unlikely(!method))
+ return -1;
+ result = method;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (likely(__Pyx_TypeCheck(method, &PyMethodDescr_Type)))
+ {
+ PyMethodDescrObject *descr = (PyMethodDescrObject*) method;
+ target->func = descr->d_method->ml_meth;
+ target->flag = descr->d_method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_STACKLESS);
+ } else
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+#else
+ if (PyCFunction_Check(method))
+#endif
+ {
+ PyObject *self;
+ int self_found;
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ self = PyObject_GetAttrString(method, "__self__");
+ if (!self) {
+ PyErr_Clear();
+ }
+#else
+ self = PyCFunction_GET_SELF(method);
+#endif
+ self_found = (self && self != Py_None);
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ Py_XDECREF(self);
+#endif
+ if (self_found) {
+ PyObject *unbound_method = PyCFunction_New(&__Pyx_UnboundCMethod_Def, method);
+ if (unlikely(!unbound_method)) return -1;
+ Py_DECREF(method);
+ result = unbound_method;
+ }
+ }
+#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ if (unlikely(target->method)) {
+ Py_DECREF(result);
+ } else
+#endif
+ target->method = result;
+ return 0;
+}
+
+/* CallUnboundCMethod0 */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self) {
+ int was_initialized = __Pyx_CachedCFunction_GetAndSetInitializing(cfunc);
+ if (likely(was_initialized == 2 && cfunc->func)) {
+ if (likely(cfunc->flag == METH_NOARGS))
+ return __Pyx_CallCFunction(cfunc, self, NULL);
+ if (likely(cfunc->flag == METH_FASTCALL))
+ return __Pyx_CallCFunctionFast(cfunc, self, NULL, 0);
+ if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS))
+ return __Pyx_CallCFunctionFastWithKeywords(cfunc, self, NULL, 0, NULL);
+ if (likely(cfunc->flag == (METH_VARARGS | METH_KEYWORDS)))
+ return __Pyx_CallCFunctionWithKeywords(cfunc, self, __pyx_mstate_global->__pyx_empty_tuple, NULL);
+ if (cfunc->flag == METH_VARARGS)
+ return __Pyx_CallCFunction(cfunc, self, __pyx_mstate_global->__pyx_empty_tuple);
+ return __Pyx__CallUnboundCMethod0(cfunc, self);
+ }
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ else if (unlikely(was_initialized == 1)) {
+ __Pyx_CachedCFunction tmp_cfunc = {
+#ifndef __cplusplus
+ 0
+#endif
+ };
+ tmp_cfunc.type = cfunc->type;
+ tmp_cfunc.method_name = cfunc->method_name;
+ return __Pyx__CallUnboundCMethod0(&tmp_cfunc, self);
+ }
+#endif
+ PyObject *result = __Pyx__CallUnboundCMethod0(cfunc, self);
+ __Pyx_CachedCFunction_SetFinishedInitializing(cfunc);
+ return result;
+}
+#endif
+static PyObject* __Pyx__CallUnboundCMethod0(__Pyx_CachedCFunction* cfunc, PyObject* self) {
+ PyObject *result;
+ if (unlikely(!cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+ result = __Pyx_PyObject_CallOneArg(cfunc->method, self);
+ return result;
+}
+
+/* py_dict_items (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Items(PyObject* d) {
+ return __Pyx_CallUnboundCMethod0(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_items, d);
+}
+
+/* py_dict_values (used by OwnedDictNext) */
+static CYTHON_INLINE PyObject* __Pyx_PyDict_Values(PyObject* d) {
+ return __Pyx_CallUnboundCMethod0(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_values, d);
+}
+
+/* OwnedDictNext (used by ParseKeywordsImpl) */
+#if CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, PyObject **ppos, PyObject **pkey, PyObject **pvalue) {
+ PyObject *next = NULL;
+ if (!*ppos) {
+ if (pvalue) {
+ PyObject *dictview = pkey ? __Pyx_PyDict_Items(p) : __Pyx_PyDict_Values(p);
+ if (unlikely(!dictview)) goto bad;
+ *ppos = PyObject_GetIter(dictview);
+ Py_DECREF(dictview);
+ } else {
+ *ppos = PyObject_GetIter(p);
+ }
+ if (unlikely(!*ppos)) goto bad;
+ }
+ next = PyIter_Next(*ppos);
+ if (!next) {
+ if (PyErr_Occurred()) goto bad;
+ return 0;
+ }
+ if (pkey && pvalue) {
+ *pkey = __Pyx_PySequence_ITEM(next, 0);
+ if (unlikely(*pkey)) goto bad;
+ *pvalue = __Pyx_PySequence_ITEM(next, 1);
+ if (unlikely(*pvalue)) goto bad;
+ Py_DECREF(next);
+ } else if (pkey) {
+ *pkey = next;
+ } else {
+ assert(pvalue);
+ *pvalue = next;
+ }
+ return 1;
+ bad:
+ Py_XDECREF(next);
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d0000
+ PyErr_FormatUnraisable("Exception ignored in __Pyx_PyDict_NextRef");
+#else
+ PyErr_WriteUnraisable(__pyx_mstate_global->__pyx_n_u_Pyx_PyDict_NextRef);
+#endif
+ if (pkey) *pkey = NULL;
+ if (pvalue) *pvalue = NULL;
+ return 0;
+}
+#else // !CYTHON_AVOID_BORROWED_REFS
+static int __Pyx_PyDict_NextRef(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) {
+ int result = PyDict_Next(p, ppos, pkey, pvalue);
+ if (likely(result == 1)) {
+ if (pkey) Py_INCREF(*pkey);
+ if (pvalue) Py_INCREF(*pvalue);
+ }
+ return result;
+}
+#endif
+
+/* RaiseDoubleKeywords (used by ParseKeywordsImpl) */
+static void __Pyx_RaiseDoubleKeywordsError(
+ const char* func_name,
+ PyObject* kw_name)
+{
+ PyErr_Format(PyExc_TypeError,
+ "%s() got multiple values for keyword argument '%U'", func_name, kw_name);
+}
+
+/* CallUnboundCMethod2 */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2) {
+ int was_initialized = __Pyx_CachedCFunction_GetAndSetInitializing(cfunc);
+ if (likely(was_initialized == 2 && cfunc->func)) {
+ PyObject *args[2] = {arg1, arg2};
+ if (cfunc->flag == METH_FASTCALL) {
+ return __Pyx_CallCFunctionFast(cfunc, self, args, 2);
+ }
+ if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS))
+ return __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, 2, NULL);
+ }
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ else if (unlikely(was_initialized == 1)) {
+ __Pyx_CachedCFunction tmp_cfunc = {
+#ifndef __cplusplus
+ 0
+#endif
+ };
+ tmp_cfunc.type = cfunc->type;
+ tmp_cfunc.method_name = cfunc->method_name;
+ return __Pyx__CallUnboundCMethod2(&tmp_cfunc, self, arg1, arg2);
+ }
+#endif
+ PyObject *result = __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2);
+ __Pyx_CachedCFunction_SetFinishedInitializing(cfunc);
+ return result;
+}
+#endif
+static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2){
+ if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (cfunc->func && (cfunc->flag & METH_VARARGS)) {
+ PyObject *result = NULL;
+ PyObject *args = PyTuple_New(2);
+ if (unlikely(!args)) return NULL;
+ Py_INCREF(arg1);
+ PyTuple_SET_ITEM(args, 0, arg1);
+ Py_INCREF(arg2);
+ PyTuple_SET_ITEM(args, 1, arg2);
+ if (cfunc->flag & METH_KEYWORDS)
+ result = __Pyx_CallCFunctionWithKeywords(cfunc, self, args, NULL);
+ else
+ result = __Pyx_CallCFunction(cfunc, self, args);
+ Py_DECREF(args);
+ return result;
+ }
+#endif
+ {
+ PyObject *args[4] = {NULL, self, arg1, arg2};
+ return __Pyx_PyObject_FastCall(cfunc->method, args+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+ }
+}
+
+/* ParseKeywordsImpl (used by ParseKeywords) */
+static int __Pyx_ValidateDuplicatePosArgs(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ const char* function_name)
+{
+ PyObject ** const *name = argnames;
+ while (name != first_kw_arg) {
+ PyObject *key = **name;
+ int found = PyDict_Contains(kwds, key);
+ if (unlikely(found)) {
+ if (found == 1) __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+ }
+ name++;
+ }
+ return 0;
+bad:
+ return -1;
+}
+#if CYTHON_USE_UNICODE_INTERNALS
+static CYTHON_INLINE int __Pyx_UnicodeKeywordsEqual(PyObject *s1, PyObject *s2) {
+ int kind;
+ Py_ssize_t len = PyUnicode_GET_LENGTH(s1);
+ if (len != PyUnicode_GET_LENGTH(s2)) return 0;
+ kind = PyUnicode_KIND(s1);
+ if (kind != PyUnicode_KIND(s2)) return 0;
+ const void *data1 = PyUnicode_DATA(s1);
+ const void *data2 = PyUnicode_DATA(s2);
+ return (memcmp(data1, data2, (size_t) len * (size_t) kind) == 0);
+}
+#endif
+static int __Pyx_MatchKeywordArg_str(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ PyObject ** const *name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ Py_hash_t key_hash = ((PyASCIIObject*)key)->hash;
+ if (unlikely(key_hash == -1)) {
+ key_hash = PyObject_Hash(key);
+ if (unlikely(key_hash == -1))
+ goto bad;
+ }
+ #endif
+ name = first_kw_arg;
+ while (*name) {
+ PyObject *name_str = **name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ if (key_hash == ((PyASCIIObject*)name_str)->hash && __Pyx_UnicodeKeywordsEqual(name_str, key)) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ #else
+ #if CYTHON_ASSUME_SAFE_SIZE
+ if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key))
+ #endif
+ {
+ int cmp = PyUnicode_Compare(name_str, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ }
+ #endif
+ name++;
+ }
+ name = argnames;
+ while (name != first_kw_arg) {
+ PyObject *name_str = **name;
+ #if CYTHON_USE_UNICODE_INTERNALS
+ if (unlikely(key_hash == ((PyASCIIObject*)name_str)->hash)) {
+ if (__Pyx_UnicodeKeywordsEqual(name_str, key))
+ goto arg_passed_twice;
+ }
+ #else
+ #if CYTHON_ASSUME_SAFE_SIZE
+ if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key))
+ #endif
+ {
+ if (unlikely(name_str == key)) goto arg_passed_twice;
+ int cmp = PyUnicode_Compare(name_str, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) goto arg_passed_twice;
+ }
+ #endif
+ name++;
+ }
+ return 0;
+arg_passed_twice:
+ __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+bad:
+ return -1;
+}
+static int __Pyx_MatchKeywordArg_nostr(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ PyObject ** const *name;
+ if (unlikely(!PyUnicode_Check(key))) goto invalid_keyword_type;
+ name = first_kw_arg;
+ while (*name) {
+ int cmp = PyObject_RichCompareBool(**name, key, Py_EQ);
+ if (cmp == 1) {
+ *index_found = (size_t) (name - argnames);
+ return 1;
+ }
+ if (unlikely(cmp == -1)) goto bad;
+ name++;
+ }
+ name = argnames;
+ while (name != first_kw_arg) {
+ int cmp = PyObject_RichCompareBool(**name, key, Py_EQ);
+ if (unlikely(cmp != 0)) {
+ if (cmp == 1) goto arg_passed_twice;
+ else goto bad;
+ }
+ name++;
+ }
+ return 0;
+arg_passed_twice:
+ __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+invalid_keyword_type:
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() keywords must be strings", function_name);
+ goto bad;
+bad:
+ return -1;
+}
+static CYTHON_INLINE int __Pyx_MatchKeywordArg(
+ PyObject *key,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ size_t *index_found,
+ const char *function_name)
+{
+ return likely(PyUnicode_CheckExact(key)) ?
+ __Pyx_MatchKeywordArg_str(key, argnames, first_kw_arg, index_found, function_name) :
+ __Pyx_MatchKeywordArg_nostr(key, argnames, first_kw_arg, index_found, function_name);
+}
+static void __Pyx_RejectUnknownKeyword(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject ** const *first_kw_arg,
+ const char *function_name)
+{
+ #if CYTHON_AVOID_BORROWED_REFS
+ PyObject *pos = NULL;
+ #else
+ Py_ssize_t pos = 0;
+ #endif
+ PyObject *key = NULL;
+ __Pyx_BEGIN_CRITICAL_SECTION(kwds);
+ while (
+ #if CYTHON_AVOID_BORROWED_REFS
+ __Pyx_PyDict_NextRef(kwds, &pos, &key, NULL)
+ #else
+ PyDict_Next(kwds, &pos, &key, NULL)
+ #endif
+ ) {
+ PyObject** const *name = first_kw_arg;
+ while (*name && (**name != key)) name++;
+ if (!*name) {
+ size_t index_found = 0;
+ int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name);
+ if (cmp != 1) {
+ if (cmp == 0) {
+ PyErr_Format(PyExc_TypeError,
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ #endif
+ break;
+ }
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ #endif
+ }
+ __Pyx_END_CRITICAL_SECTION();
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(pos);
+ #endif
+ assert(PyErr_Occurred());
+}
+static int __Pyx_ParseKeywordDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ PyObject** const *name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ Py_ssize_t extracted = 0;
+#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments)
+ if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1;
+#endif
+ name = first_kw_arg;
+ while (*name && num_kwargs > extracted) {
+ PyObject * key = **name;
+ PyObject *value;
+ int found = 0;
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ found = PyDict_GetItemRef(kwds, key, &value);
+ #else
+ value = PyDict_GetItemWithError(kwds, key);
+ if (value) {
+ Py_INCREF(value);
+ found = 1;
+ } else {
+ if (unlikely(PyErr_Occurred())) goto bad;
+ }
+ #endif
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ extracted++;
+ }
+ name++;
+ }
+ if (num_kwargs > extracted) {
+ if (ignore_unknown_kwargs) {
+ if (unlikely(__Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name) == -1))
+ goto bad;
+ } else {
+ __Pyx_RejectUnknownKeyword(kwds, argnames, first_kw_arg, function_name);
+ goto bad;
+ }
+ }
+ return 0;
+bad:
+ return -1;
+}
+static int __Pyx_ParseKeywordDictToDict(
+ PyObject *kwds,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ const char* function_name)
+{
+ PyObject** const *name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ Py_ssize_t len;
+#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments)
+ if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1;
+#endif
+ if (PyDict_Update(kwds2, kwds) < 0) goto bad;
+ name = first_kw_arg;
+ while (*name) {
+ PyObject *key = **name;
+ PyObject *value;
+#if !CYTHON_COMPILING_IN_LIMITED_API && (PY_VERSION_HEX >= 0x030d00A2 || defined(PyDict_Pop))
+ int found = PyDict_Pop(kwds2, key, &value);
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ }
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ int found = PyDict_GetItemRef(kwds2, key, &value);
+ if (found) {
+ if (unlikely(found < 0)) goto bad;
+ values[name-argnames] = value;
+ if (unlikely(PyDict_DelItem(kwds2, key) < 0)) goto bad;
+ }
+#else
+ #if CYTHON_COMPILING_IN_CPYTHON
+ value = _PyDict_Pop(kwds2, key, kwds2);
+ #else
+ value = __Pyx_CallUnboundCMethod2(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_pop, kwds2, key, kwds2);
+ #endif
+ if (value == kwds2) {
+ Py_DECREF(value);
+ } else {
+ if (unlikely(!value)) goto bad;
+ values[name-argnames] = value;
+ }
+#endif
+ name++;
+ }
+ len = PyDict_Size(kwds2);
+ if (len > 0) {
+ return __Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name);
+ } else if (unlikely(len == -1)) {
+ goto bad;
+ }
+ return 0;
+bad:
+ return -1;
+}
+static int __Pyx_ParseKeywordsTuple(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ PyObject *key = NULL;
+ PyObject** const * name;
+ PyObject** const *first_kw_arg = argnames + num_pos_args;
+ for (Py_ssize_t pos = 0; pos < num_kwargs; pos++) {
+#if CYTHON_AVOID_BORROWED_REFS
+ key = __Pyx_PySequence_ITEM(kwds, pos);
+#else
+ key = __Pyx_PyTuple_GET_ITEM(kwds, pos);
+#endif
+#if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(!key)) goto bad;
+#endif
+ name = first_kw_arg;
+ while (*name && (**name != key)) name++;
+ if (*name) {
+ PyObject *value = kwvalues[pos];
+ values[name-argnames] = __Pyx_NewRef(value);
+ } else {
+ size_t index_found = 0;
+ int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name);
+ if (cmp == 1) {
+ PyObject *value = kwvalues[pos];
+ values[index_found] = __Pyx_NewRef(value);
+ } else {
+ if (unlikely(cmp == -1)) goto bad;
+ if (kwds2) {
+ PyObject *value = kwvalues[pos];
+ if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad;
+ } else if (!ignore_unknown_kwargs) {
+ goto invalid_keyword;
+ }
+ }
+ }
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(key);
+ key = NULL;
+ #endif
+ }
+ return 0;
+invalid_keyword:
+ PyErr_Format(PyExc_TypeError,
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ goto bad;
+bad:
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(key);
+ #endif
+ return -1;
+}
+
+/* ParseKeywords */
+static int __Pyx_ParseKeywords(
+ PyObject *kwds,
+ PyObject * const *kwvalues,
+ PyObject ** const argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ Py_ssize_t num_kwargs,
+ const char* function_name,
+ int ignore_unknown_kwargs)
+{
+ if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds)))
+ return __Pyx_ParseKeywordsTuple(kwds, kwvalues, argnames, kwds2, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs);
+ else if (kwds2)
+ return __Pyx_ParseKeywordDictToDict(kwds, argnames, kwds2, values, num_pos_args, function_name);
+ else
+ return __Pyx_ParseKeywordDict(kwds, argnames, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs);
+}
+
+/* RaiseArgTupleInvalid */
+static void __Pyx_RaiseArgtupleInvalid(
+ const char* func_name,
+ int exact,
+ Py_ssize_t num_min,
+ Py_ssize_t num_max,
+ Py_ssize_t num_found)
+{
+ Py_ssize_t num_expected;
+ const char *more_or_less;
+ if (num_found < num_min) {
+ num_expected = num_min;
+ more_or_less = "at least";
+ } else {
+ num_expected = num_max;
+ more_or_less = "at most";
+ }
+ if (exact) {
+ more_or_less = "exactly";
+ }
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes %.8s %" CYTHON_FORMAT_SSIZE_T "d positional argument%.1s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ func_name, more_or_less, num_expected,
+ (num_expected == 1) ? "" : "s", num_found);
+}
+
+/* GetItemInt */
+static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
+ PyObject *r;
+ if (unlikely(!j)) return NULL;
+ r = PyObject_GetItem(o, j);
+ Py_DECREF(j);
+ return r;
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_SIZE
+ Py_ssize_t wrapped_i = i;
+ if (wraparound & unlikely(i < 0)) {
+ wrapped_i += PyList_GET_SIZE(o);
+ }
+ if ((CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS)) {
+ return __Pyx_PyList_GetItemRefFast(o, wrapped_i, unsafe_shared);
+ } else
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyList_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyList_GET_ITEM(o, wrapped_i));
+ }
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+#else
+ (void)wraparound;
+ (void)boundscheck;
+ return PySequence_GetItem(o, i);
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ Py_ssize_t wrapped_i = i;
+ if (wraparound & unlikely(i < 0)) {
+ wrapped_i += PyTuple_GET_SIZE(o);
+ }
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyTuple_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyTuple_GET_ITEM(o, wrapped_i));
+ }
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+#else
+ (void)wraparound;
+ (void)boundscheck;
+ return PySequence_GetItem(o, i);
+#endif
+}
+static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list,
+ int wraparound, int boundscheck, int unsafe_shared) {
+ CYTHON_MAYBE_UNUSED_VAR(unsafe_shared);
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ if (is_list || PyList_CheckExact(o)) {
+ Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o);
+ if ((CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS)) {
+ return __Pyx_PyList_GetItemRefFast(o, n, unsafe_shared);
+ } else if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) {
+ return __Pyx_NewRef(PyList_GET_ITEM(o, n));
+ }
+ } else
+ #if !CYTHON_AVOID_BORROWED_REFS
+ if (PyTuple_CheckExact(o)) {
+ Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o);
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) {
+ return __Pyx_NewRef(PyTuple_GET_ITEM(o, n));
+ }
+ } else
+ #endif
+#endif
+#if CYTHON_USE_TYPE_SLOTS && !CYTHON_COMPILING_IN_PYPY
+ {
+ PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping;
+ PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence;
+ if (!is_list && mm && mm->mp_subscript) {
+ PyObject *r, *key = PyLong_FromSsize_t(i);
+ if (unlikely(!key)) return NULL;
+ r = mm->mp_subscript(o, key);
+ Py_DECREF(key);
+ return r;
+ }
+ if (is_list || likely(sm && sm->sq_item)) {
+ if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) {
+ Py_ssize_t l = sm->sq_length(o);
+ if (likely(l >= 0)) {
+ i += l;
+ } else {
+ if (!PyErr_ExceptionMatches(PyExc_OverflowError))
+ return NULL;
+ PyErr_Clear();
+ }
+ }
+ return sm->sq_item(o, i);
+ }
+ }
+#else
+ if (is_list || !PyMapping_Check(o)) {
+ return PySequence_GetItem(o, i);
+ }
+#endif
+ (void)wraparound;
+ (void)boundscheck;
+ return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i));
+}
+
+/* RaiseException */
+static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) {
+ PyObject* owned_instance = NULL;
+ if (tb == Py_None) {
+ tb = 0;
+ } else if (tb && !PyTraceBack_Check(tb)) {
+ PyErr_SetString(PyExc_TypeError,
+ "raise: arg 3 must be a traceback or None");
+ goto bad;
+ }
+ if (value == Py_None)
+ value = 0;
+ if (PyExceptionInstance_Check(type)) {
+ if (value) {
+ PyErr_SetString(PyExc_TypeError,
+ "instance exception may not have a separate value");
+ goto bad;
+ }
+ value = type;
+ type = (PyObject*) Py_TYPE(value);
+ } else if (PyExceptionClass_Check(type)) {
+ PyObject *instance_class = NULL;
+ if (value && PyExceptionInstance_Check(value)) {
+ instance_class = (PyObject*) Py_TYPE(value);
+ if (instance_class != type) {
+ int is_subclass = PyObject_IsSubclass(instance_class, type);
+ if (!is_subclass) {
+ instance_class = NULL;
+ } else if (unlikely(is_subclass == -1)) {
+ goto bad;
+ } else {
+ type = instance_class;
+ }
+ }
+ }
+ if (!instance_class) {
+ PyObject *args;
+ if (!value)
+ args = PyTuple_New(0);
+ else if (PyTuple_Check(value)) {
+ Py_INCREF(value);
+ args = value;
+ } else
+ args = PyTuple_Pack(1, value);
+ if (!args)
+ goto bad;
+ owned_instance = PyObject_Call(type, args, NULL);
+ Py_DECREF(args);
+ if (!owned_instance)
+ goto bad;
+ value = owned_instance;
+ if (!PyExceptionInstance_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "calling %R should have returned an instance of "
+ "BaseException, not %R",
+ type, Py_TYPE(value));
+ goto bad;
+ }
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "raise: exception class must be a subclass of BaseException");
+ goto bad;
+ }
+ if (cause) {
+ PyObject *fixed_cause;
+ if (cause == Py_None) {
+ fixed_cause = NULL;
+ } else if (PyExceptionClass_Check(cause)) {
+ fixed_cause = PyObject_CallObject(cause, NULL);
+ if (fixed_cause == NULL)
+ goto bad;
+ } else if (PyExceptionInstance_Check(cause)) {
+ fixed_cause = cause;
+ Py_INCREF(fixed_cause);
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "exception causes must derive from "
+ "BaseException");
+ goto bad;
+ }
+ PyException_SetCause(value, fixed_cause);
+ }
+ PyErr_SetObject(type, value);
+ if (tb) {
+#if PY_VERSION_HEX >= 0x030C00A6
+ PyException_SetTraceback(value, tb);
+#elif CYTHON_FAST_THREAD_STATE
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
+ PyObject* tmp_tb = tstate->curexc_traceback;
+ if (tb != tmp_tb) {
+ Py_INCREF(tb);
+ tstate->curexc_traceback = tb;
+ Py_XDECREF(tmp_tb);
+ }
+#else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb);
+ Py_INCREF(tb);
+ PyErr_Restore(tmp_type, tmp_value, tb);
+ Py_XDECREF(tmp_tb);
+#endif
+ }
+bad:
+ Py_XDECREF(owned_instance);
+ return;
+}
+
+/* py_abs */
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
+#if PY_VERSION_HEX >= 0x030C00A7
+ if (likely(__Pyx_PyLong_IsCompact(n))) {
+ return PyLong_FromSize_t(__Pyx_PyLong_CompactValueUnsigned(n));
+ }
+#else
+ if (likely(Py_SIZE(n) == -1)) {
+ return PyLong_FromUnsignedLong(__Pyx_PyLong_Digits(n)[0]);
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+ {
+ PyObject *copy = _PyLong_Copy((PyLongObject*)n);
+ if (likely(copy)) {
+ #if PY_VERSION_HEX >= 0x030C00A7
+ ((PyLongObject*)copy)->long_value.lv_tag ^= ((PyLongObject*)copy)->long_value.lv_tag & _PyLong_SIGN_MASK;
+ #else
+ __Pyx_SET_SIZE(copy, -Py_SIZE(copy));
+ #endif
+ }
+ return copy;
+ }
+#else
+ return PyNumber_Negative(n);
+#endif
+}
+#endif
+
+/* SliceTupleAndList */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_crop_slice(Py_ssize_t* _start, Py_ssize_t* _stop, Py_ssize_t* _length) {
+ Py_ssize_t start = *_start, stop = *_stop, length = *_length;
+ if (start < 0) {
+ start += length;
+ if (start < 0)
+ start = 0;
+ }
+ if (stop < 0)
+ stop += length;
+ else if (stop > length)
+ stop = length;
+ *_length = stop - start;
+ *_start = start;
+ *_stop = stop;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(
+ PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
+ Py_ssize_t length = PyTuple_GET_SIZE(src);
+ __Pyx_crop_slice(&start, &stop, &length);
+ return __Pyx_PyTuple_FromArray(((PyTupleObject*)src)->ob_item + start, length);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice_locked(
+ PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
+ Py_ssize_t length = PyList_GET_SIZE(src);
+ __Pyx_crop_slice(&start, &stop, &length);
+ if (length <= 0) {
+ return PyList_New(0);
+ }
+ return __Pyx_PyList_FromArray(((PyListObject*)src)->ob_item + start, length);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(
+ PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
+ PyObject *result;
+ __Pyx_BEGIN_CRITICAL_SECTION(src);
+ result = __Pyx_PyList_GetSlice_locked(src, start, stop);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+#endif // CYTHON_COMPILING_IN_CPYTHON
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceSubtract : PyNumber_Subtract)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ long b;
+ const PY_LONG_LONG lla = intval;
+ PY_LONG_LONG llb;
+ if (unlikely(__Pyx_PyLong_IsZero(op2))) {
+ return __Pyx_NewRef(op1);
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op2);
+ const digit* digits = __Pyx_PyLong_Digits(op2);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op2);
+ if (likely(size == 1)) {
+ b = (long) digits[0];
+ if (!is_positive) b *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ b = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ b = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ b = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) b *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ llb = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) llb *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_subtract(op1, op2);
+ }
+ calculate_long:
+ {
+ long x;
+ x = a - b;
+ return PyLong_FromLong(x);
+ }
+ calculate_long_long:
+ {
+ PY_LONG_LONG llx;
+ llx = lla - llb;
+ return PyLong_FromLongLong(llx);
+ }
+
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_SubtractCObj(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long a = intval;
+ double b = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) - (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_SubtractCObj(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op2))) {
+ return __Pyx_Unpacked___Pyx_PyLong_SubtractCObj(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op2)) {
+ return __Pyx_Float___Pyx_PyLong_SubtractCObj(op2, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_SubtractCObj(op1, op2, inplace);
+}
+#endif
+
+/* ArgTypeTestFunc (used by ArgTypeTest) */
+static int __Pyx__ArgTypeTest(PyObject *obj, PyTypeObject *type, const char *name, int exact)
+{
+ __Pyx_TypeName type_name;
+ __Pyx_TypeName obj_type_name;
+ PyObject *extra_info = __pyx_mstate_global->__pyx_empty_unicode;
+ int from_annotation_subclass = 0;
+ if (unlikely(!type)) {
+ PyErr_SetString(PyExc_SystemError, "Missing type object");
+ return 0;
+ }
+ else if (!exact) {
+ if (likely(__Pyx_TypeCheck(obj, type))) return 1;
+ } else if (exact == 2) {
+ if (__Pyx_TypeCheck(obj, type)) {
+ from_annotation_subclass = 1;
+ extra_info = __pyx_mstate_global->__pyx_kp_u_Note_that_Cython_is_deliberately;
+ }
+ }
+ type_name = __Pyx_PyType_GetFullyQualifiedName(type);
+ obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "Argument '%.200s' has incorrect type (expected " __Pyx_FMT_TYPENAME
+ ", got " __Pyx_FMT_TYPENAME ")"
+#if __PYX_LIMITED_VERSION_HEX < 0x030C0000
+ "%s%U"
+#endif
+ , name, type_name, obj_type_name
+#if __PYX_LIMITED_VERSION_HEX < 0x030C0000
+ , (from_annotation_subclass ? ". " : ""), extra_info
+#endif
+ );
+#if __PYX_LIMITED_VERSION_HEX >= 0x030C0000
+ if (exact == 2 && from_annotation_subclass) {
+ PyObject *res;
+ PyObject *vargs[2];
+ vargs[0] = PyErr_GetRaisedException();
+ vargs[1] = extra_info;
+ res = PyObject_VectorcallMethod(__pyx_mstate_global->__pyx_kp_u_add_note, vargs, 2, NULL);
+ Py_XDECREF(res);
+ PyErr_SetRaisedException(vargs[0]);
+ }
+#endif
+ __Pyx_DECREF_TypeName(type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return 0;
+}
+
+/* RaiseUnboundLocalError */
+static void __Pyx_RaiseUnboundLocalError(const char *varname) {
+ PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
+}
+
+/* GetException (used by pep479) */
+#if CYTHON_FAST_THREAD_STATE
+static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb)
+#else
+static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb)
+#endif
+{
+ PyObject *local_type = NULL, *local_value, *local_tb = NULL;
+#if CYTHON_FAST_THREAD_STATE
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if PY_VERSION_HEX >= 0x030C0000
+ local_value = tstate->current_exception;
+ tstate->current_exception = 0;
+ #else
+ local_type = tstate->curexc_type;
+ local_value = tstate->curexc_value;
+ local_tb = tstate->curexc_traceback;
+ tstate->curexc_type = 0;
+ tstate->curexc_value = 0;
+ tstate->curexc_traceback = 0;
+ #endif
+#elif __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ local_value = PyErr_GetRaisedException();
+#else
+ PyErr_Fetch(&local_type, &local_value, &local_tb);
+#endif
+#if __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ if (likely(local_value)) {
+ local_type = (PyObject*) Py_TYPE(local_value);
+ Py_INCREF(local_type);
+ local_tb = PyException_GetTraceback(local_value);
+ }
+#else
+ PyErr_NormalizeException(&local_type, &local_value, &local_tb);
+#if CYTHON_FAST_THREAD_STATE
+ if (unlikely(tstate->curexc_type))
+#else
+ if (unlikely(PyErr_Occurred()))
+#endif
+ goto bad;
+ if (local_tb) {
+ if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0))
+ goto bad;
+ }
+#endif // __PYX_LIMITED_VERSION_HEX > 0x030C0000
+ Py_XINCREF(local_tb);
+ Py_XINCREF(local_type);
+ Py_XINCREF(local_value);
+ *type = local_type;
+ *value = local_value;
+ *tb = local_tb;
+#if CYTHON_FAST_THREAD_STATE
+ #if CYTHON_USE_EXC_INFO_STACK
+ {
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = local_value;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_tb);
+ #else
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = local_type;
+ exc_info->exc_value = local_value;
+ exc_info->exc_traceback = local_tb;
+ #endif
+ }
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = local_type;
+ tstate->exc_value = local_value;
+ tstate->exc_traceback = local_tb;
+ #endif
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ PyErr_SetHandledException(local_value);
+ Py_XDECREF(local_value);
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_tb);
+#else
+ PyErr_SetExcInfo(local_type, local_value, local_tb);
+#endif
+ return 0;
+#if __PYX_LIMITED_VERSION_HEX <= 0x030C0000
+bad:
+ *type = 0;
+ *value = 0;
+ *tb = 0;
+ Py_XDECREF(local_type);
+ Py_XDECREF(local_value);
+ Py_XDECREF(local_tb);
+ return -1;
+#endif
+}
+
+/* pep479 */
+static void __Pyx_Generator_Replace_StopIteration(int in_async_gen) {
+ PyObject *exc, *val, *tb, *cur_exc, *new_exc;
+ __Pyx_PyThreadState_declare
+ int is_async_stopiteration = 0;
+ CYTHON_MAYBE_UNUSED_VAR(in_async_gen);
+ __Pyx_PyThreadState_assign
+ cur_exc = __Pyx_PyErr_CurrentExceptionType();
+ if (likely(!__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopIteration))) {
+ if (in_async_gen && unlikely(__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopAsyncIteration))) {
+ is_async_stopiteration = 1;
+ } else {
+ return;
+ }
+ }
+ __Pyx_GetException(&exc, &val, &tb);
+ Py_XDECREF(exc);
+ Py_XDECREF(tb);
+ new_exc = PyObject_CallFunction(PyExc_RuntimeError, "s",
+ is_async_stopiteration ? "async generator raised StopAsyncIteration" :
+ in_async_gen ? "async generator raised StopIteration" :
+ "generator raised StopIteration");
+ if (!new_exc) {
+ Py_XDECREF(val);
+ return;
+ }
+ PyException_SetCause(new_exc, val); // steals ref to val
+ PyErr_SetObject(PyExc_RuntimeError, new_exc);
+}
+
+/* RaiseTooManyValuesToUnpack */
+static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) {
+ PyErr_Format(PyExc_ValueError,
+ "too many values to unpack (expected %" CYTHON_FORMAT_SSIZE_T "d)", expected);
+}
+
+/* RaiseNeedMoreValuesToUnpack */
+static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
+ PyErr_Format(PyExc_ValueError,
+ "need more than %" CYTHON_FORMAT_SSIZE_T "d value%.1s to unpack",
+ index, (index == 1) ? "" : "s");
+}
+
+/* IterFinish */
+static CYTHON_INLINE int __Pyx_IterFinish(void) {
+ PyObject* exc_type;
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ exc_type = __Pyx_PyErr_CurrentExceptionType();
+ if (unlikely(exc_type)) {
+ if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))
+ return -1;
+ __Pyx_PyErr_Clear();
+ return 0;
+ }
+ return 0;
+}
+
+/* UnpackItemEndCheck */
+static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) {
+ if (unlikely(retval)) {
+ Py_DECREF(retval);
+ __Pyx_RaiseTooManyValuesError(expected);
+ return -1;
+ }
+ return __Pyx_IterFinish();
+}
+
+/* PyDictVersioning (used by GetModuleGlobalName) */
+#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0;
+}
+static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) {
+ PyObject **dictptr = NULL;
+ Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset;
+ if (offset) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj);
+#else
+ dictptr = _PyObject_GetDictPtr(obj);
+#endif
+ }
+ return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0;
+}
+static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) {
+ PyObject *dict = Py_TYPE(obj)->tp_dict;
+ if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict)))
+ return 0;
+ return obj_dict_version == __Pyx_get_object_dict_version(obj);
+}
+#endif
+
+/* GetModuleGlobalName */
+#if CYTHON_USE_DICT_VERSIONS
+static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value)
+#else
+static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name)
+#endif
+{
+ PyObject *result;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(!__pyx_m)) {
+ if (!PyErr_Occurred())
+ PyErr_SetNone(PyExc_NameError);
+ return NULL;
+ }
+ result = PyObject_GetAttr(__pyx_m, name);
+ if (likely(result)) {
+ return result;
+ }
+ PyErr_Clear();
+#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
+ if (unlikely(__Pyx_PyDict_GetItemRef(__pyx_mstate_global->__pyx_d, name, &result) == -1)) PyErr_Clear();
+ __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return result;
+ }
+#else
+ result = _PyDict_GetItem_KnownHash(__pyx_mstate_global->__pyx_d, name, ((PyASCIIObject *) name)->hash);
+ __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version)
+ if (likely(result)) {
+ return __Pyx_NewRef(result);
+ }
+ PyErr_Clear();
+#endif
+ return __Pyx_GetBuiltinName(name);
+}
+
+/* SliceObject */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetSlice(PyObject* obj,
+ Py_ssize_t cstart, Py_ssize_t cstop,
+ PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice,
+ int has_cstart, int has_cstop, CYTHON_UNUSED int wraparound) {
+ __Pyx_TypeName obj_type_name;
+#if CYTHON_USE_TYPE_SLOTS
+ PyMappingMethods* mp = Py_TYPE(obj)->tp_as_mapping;
+ if (likely(mp && mp->mp_subscript))
+#endif
+ {
+ PyObject* result;
+ PyObject *py_slice, *py_start, *py_stop;
+ if (_py_slice) {
+ py_slice = *_py_slice;
+ } else {
+ PyObject* owned_start = NULL;
+ PyObject* owned_stop = NULL;
+ if (_py_start) {
+ py_start = *_py_start;
+ } else {
+ if (has_cstart) {
+ owned_start = py_start = PyLong_FromSsize_t(cstart);
+ if (unlikely(!py_start)) goto bad;
+ } else
+ py_start = Py_None;
+ }
+ if (_py_stop) {
+ py_stop = *_py_stop;
+ } else {
+ if (has_cstop) {
+ owned_stop = py_stop = PyLong_FromSsize_t(cstop);
+ if (unlikely(!py_stop)) {
+ Py_XDECREF(owned_start);
+ goto bad;
+ }
+ } else
+ py_stop = Py_None;
+ }
+ py_slice = PySlice_New(py_start, py_stop, Py_None);
+ Py_XDECREF(owned_start);
+ Py_XDECREF(owned_stop);
+ if (unlikely(!py_slice)) goto bad;
+ }
+#if CYTHON_USE_TYPE_SLOTS
+ result = mp->mp_subscript(obj, py_slice);
+#else
+ result = PyObject_GetItem(obj, py_slice);
+#endif
+ if (!_py_slice) {
+ Py_DECREF(py_slice);
+ }
+ return result;
+ }
+ obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is unsliceable", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+bad:
+ return NULL;
+}
+
+/* PyObjectCallNoArg (used by PyObjectCallMethod0) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) {
+ PyObject *arg[2] = {NULL, NULL};
+ return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* PyObjectGetMethod (used by PyObjectCallMethod0) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) {
+ PyObject *attr;
+#if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP
+ __Pyx_TypeName type_name;
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyObject *descr;
+ descrgetfunc f = NULL;
+ PyObject **dictptr, *dict;
+ int meth_found = 0;
+ assert (*method == NULL);
+ if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) {
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+ }
+ if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) {
+ return 0;
+ }
+ descr = _PyType_Lookup(tp, name);
+ if (likely(descr != NULL)) {
+ Py_INCREF(descr);
+#if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR
+ if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR))
+#else
+ #ifdef __Pyx_CyFunction_USED
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr)))
+ #else
+ if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type)))
+ #endif
+#endif
+ {
+ meth_found = 1;
+ } else {
+ f = Py_TYPE(descr)->tp_descr_get;
+ if (f != NULL && PyDescr_IsData(descr)) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+ }
+ }
+ dictptr = _PyObject_GetDictPtr(obj);
+ if (dictptr != NULL && (dict = *dictptr) != NULL) {
+ Py_INCREF(dict);
+ attr = __Pyx_PyDict_GetItemStr(dict, name);
+ if (attr != NULL) {
+ Py_INCREF(attr);
+ Py_DECREF(dict);
+ Py_XDECREF(descr);
+ goto try_unpack;
+ }
+ Py_DECREF(dict);
+ }
+ if (meth_found) {
+ *method = descr;
+ return 1;
+ }
+ if (f != NULL) {
+ attr = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ goto try_unpack;
+ }
+ if (likely(descr != NULL)) {
+ *method = descr;
+ return 0;
+ }
+ type_name = __Pyx_PyType_GetFullyQualifiedName(tp);
+ PyErr_Format(PyExc_AttributeError,
+ "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'",
+ type_name, name);
+ __Pyx_DECREF_TypeName(type_name);
+ return 0;
+#else
+ attr = __Pyx_PyObject_GetAttrStr(obj, name);
+ goto try_unpack;
+#endif
+try_unpack:
+#if CYTHON_UNPACK_METHODS
+ if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) {
+ PyObject *function = PyMethod_GET_FUNCTION(attr);
+ Py_INCREF(function);
+ Py_DECREF(attr);
+ *method = function;
+ return 1;
+ }
+#endif
+ *method = attr;
+ return 0;
+}
+#endif
+
+/* PyObjectCallMethod0 (used by pop) */
+static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
+#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
+ PyObject *args[1] = {obj};
+ (void) __Pyx_PyObject_CallOneArg;
+ (void) __Pyx_PyObject_CallNoArg;
+ return PyObject_VectorcallMethod(method_name, args, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#else
+ PyObject *method = NULL, *result = NULL;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_CallOneArg(method, obj);
+ Py_DECREF(method);
+ return result;
+ }
+ if (unlikely(!method)) goto bad;
+ result = __Pyx_PyObject_CallNoArg(method);
+ Py_DECREF(method);
+bad:
+ return result;
+#endif
+}
+
+/* pop */
+static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) {
+ if (__Pyx_IS_TYPE(L, &PySet_Type)) {
+ return PySet_Pop(L);
+ }
+ return __Pyx_PyObject_CallMethod0(L, __pyx_mstate_global->__pyx_n_u_pop);
+}
+#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) {
+ if (likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) {
+ __Pyx_SET_SIZE(L, Py_SIZE(L) - 1);
+ return PyList_GET_ITEM(L, PyList_GET_SIZE(L));
+ }
+ return __Pyx_CallUnboundCMethod0(&__pyx_mstate_global->__pyx_umethod_PyList_Type_pop, L);
+}
+#endif
+
+/* RaiseUnexpectedTypeError */
+static int
+__Pyx_RaiseUnexpectedTypeError(const char *expected, PyObject *obj)
+{
+ __Pyx_TypeName obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError, "Expected %s, got " __Pyx_FMT_TYPENAME,
+ expected, obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return 0;
+}
+
+/* PyLongBinop */
+#if !CYTHON_COMPILING_IN_PYPY
+static PyObject* __Pyx_Fallback___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, int inplace) {
+ return (inplace ? PyNumber_InPlaceAdd : PyNumber_Add)(op1, op2);
+}
+#if CYTHON_USE_PYLONG_INTERNALS
+static PyObject* __Pyx_Unpacked___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(inplace);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ long a;
+ const PY_LONG_LONG llb = intval;
+ PY_LONG_LONG lla;
+ if (unlikely(__Pyx_PyLong_IsZero(op1))) {
+ return __Pyx_NewRef(op2);
+ }
+ const int is_positive = __Pyx_PyLong_IsPos(op1);
+ const digit* digits = __Pyx_PyLong_Digits(op1);
+ const Py_ssize_t size = __Pyx_PyLong_DigitCount(op1);
+ if (likely(size == 1)) {
+ a = (long) digits[0];
+ if (!is_positive) a *= -1;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ if (!is_positive) a *= -1;
+ goto calculate_long;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ if (!is_positive) lla *= -1;
+ goto calculate_long_long;
+ }
+ break;
+ }
+ return PyLong_Type.tp_as_number->nb_add(op1, op2);
+ }
+ calculate_long:
+ {
+ long x;
+ x = a + b;
+ return PyLong_FromLong(x);
+ }
+ calculate_long_long:
+ {
+ PY_LONG_LONG llx;
+ llx = lla + llb;
+ return PyLong_FromLongLong(llx);
+ }
+
+}
+#endif
+static PyObject* __Pyx_Float___Pyx_PyLong_AddObjC(PyObject *float_val, long intval, int zerodivision_check) {
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ const long b = intval;
+ double a = __Pyx_PyFloat_AS_DOUBLE(float_val);
+ double result;
+
+ result = ((double)a) + (double)b;
+ return PyFloat_FromDouble(result);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) {
+ CYTHON_MAYBE_UNUSED_VAR(intval);
+ CYTHON_UNUSED_VAR(zerodivision_check);
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(PyLong_CheckExact(op1))) {
+ return __Pyx_Unpacked___Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ return __Pyx_Float___Pyx_PyLong_AddObjC(op1, intval, zerodivision_check);
+ }
+ return __Pyx_Fallback___Pyx_PyLong_AddObjC(op1, op2, inplace);
+}
+#endif
+
+/* GetTopmostException (used by SaveResetException) */
+#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE
+static _PyErr_StackItem *
+__Pyx_PyErr_GetTopmostException(PyThreadState *tstate)
+{
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
+ exc_info->previous_item != NULL)
+ {
+ exc_info = exc_info->previous_item;
+ }
+ return exc_info;
+}
+#endif
+
+/* SaveResetException */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ PyObject *exc_value = exc_info->exc_value;
+ if (exc_value == NULL || exc_value == Py_None) {
+ *value = NULL;
+ *type = NULL;
+ *tb = NULL;
+ } else {
+ *value = exc_value;
+ Py_INCREF(*value);
+ *type = (PyObject*) Py_TYPE(exc_value);
+ Py_INCREF(*type);
+ *tb = PyException_GetTraceback(exc_value);
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate);
+ *type = exc_info->exc_type;
+ *value = exc_info->exc_value;
+ *tb = exc_info->exc_traceback;
+ Py_XINCREF(*type);
+ Py_XINCREF(*value);
+ Py_XINCREF(*tb);
+ #else
+ *type = tstate->exc_type;
+ *value = tstate->exc_value;
+ *tb = tstate->exc_traceback;
+ Py_XINCREF(*type);
+ Py_XINCREF(*value);
+ Py_XINCREF(*tb);
+ #endif
+}
+static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ PyObject *tmp_value = exc_info->exc_value;
+ exc_info->exc_value = value;
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+ #else
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = type;
+ exc_info->exc_value = value;
+ exc_info->exc_traceback = tb;
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = type;
+ tstate->exc_value = value;
+ tstate->exc_traceback = tb;
+ #endif
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+ #endif
+}
+#endif
+
+/* pyfrozenset_new (used by PySetContains) */
+static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) {
+ if (it) {
+ PyObject* result;
+#if CYTHON_COMPILING_IN_PYPY
+ PyObject* args;
+ args = PyTuple_Pack(1, it);
+ if (unlikely(!args))
+ return NULL;
+ result = PyObject_Call((PyObject*)&PyFrozenSet_Type, args, NULL);
+ Py_DECREF(args);
+ return result;
+#else
+ if (PyFrozenSet_CheckExact(it)) {
+ Py_INCREF(it);
+ return it;
+ }
+ result = PyFrozenSet_New(it);
+ if (unlikely(!result))
+ return NULL;
+ if ((__PYX_LIMITED_VERSION_HEX >= 0x030A0000)
+#if CYTHON_COMPILING_IN_LIMITED_API
+ || __Pyx_get_runtime_version() >= 0x030A0000
+#endif
+ )
+ return result;
+ {
+ Py_ssize_t size = __Pyx_PySet_GET_SIZE(result);
+ if (likely(size > 0))
+ return result;
+#if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(size < 0)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+#endif
+ }
+ Py_DECREF(result);
+#endif
+ }
+ return __Pyx_PyObject_CallNoArg((PyObject*) &PyFrozenSet_Type);
+}
+
+/* PySetContains */
+static int __Pyx_PySet_ContainsUnhashable(PyObject *set, PyObject *key) {
+ int result = -1;
+ if (PySet_Check(key) && PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyObject *tmpkey;
+ PyErr_Clear();
+ tmpkey = __Pyx_PyFrozenSet_New(key);
+ if (tmpkey != NULL) {
+ result = PySet_Contains(set, tmpkey);
+ Py_DECREF(tmpkey);
+ }
+ }
+ return result;
+}
+static CYTHON_INLINE int __Pyx_PySet_ContainsTF(PyObject* key, PyObject* set, int eq) {
+ int result = PySet_Contains(set, key);
+ if (unlikely(result < 0)) {
+ result = __Pyx_PySet_ContainsUnhashable(set, key);
+ }
+ return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
+}
+
+/* HasAttr (used by ImportImpl) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *o, PyObject *n) {
+ PyObject *r;
+ if (unlikely(!PyUnicode_Check(n))) {
+ PyErr_SetString(PyExc_TypeError,
+ "hasattr(): attribute name must be string");
+ return -1;
+ }
+ r = __Pyx_PyObject_GetAttrStrNoError(o, n);
+ if (!r) {
+ return (unlikely(PyErr_Occurred())) ? -1 : 0;
+ } else {
+ Py_DECREF(r);
+ return 1;
+ }
+}
+#endif
+
+/* ImportImpl (used by Import) */
+static int __Pyx__Import_GetModule(PyObject *qualname, PyObject **module) {
+ PyObject *imported_module = PyImport_GetModule(qualname);
+ if (unlikely(!imported_module)) {
+ *module = NULL;
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+ return 0;
+ }
+ *module = imported_module;
+ return 1;
+}
+static int __Pyx__Import_Lookup(PyObject *qualname, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject **module) {
+ PyObject *imported_module;
+ PyObject *top_level_package_name;
+ Py_ssize_t i;
+ int status, module_found;
+ Py_ssize_t dot_index;
+ module_found = __Pyx__Import_GetModule(qualname, &imported_module);
+ if (unlikely(!module_found || module_found == -1)) {
+ *module = NULL;
+ return module_found;
+ }
+ if (imported_names) {
+ for (i = 0; i < len_imported_names; i++) {
+ PyObject *imported_name = imported_names[i];
+#if __PYX_LIMITED_VERSION_HEX < 0x030d0000
+ int has_imported_attribute = PyObject_HasAttr(imported_module, imported_name);
+#else
+ int has_imported_attribute = PyObject_HasAttrWithError(imported_module, imported_name);
+ if (unlikely(has_imported_attribute == -1)) goto error;
+#endif
+ if (!has_imported_attribute) {
+ goto not_found;
+ }
+ }
+ *module = imported_module;
+ return 1;
+ }
+ dot_index = PyUnicode_FindChar(qualname, '.', 0, PY_SSIZE_T_MAX, 1);
+ if (dot_index == -1) {
+ *module = imported_module;
+ return 1;
+ }
+ if (unlikely(dot_index == -2)) goto error;
+ top_level_package_name = PyUnicode_Substring(qualname, 0, dot_index);
+ if (unlikely(!top_level_package_name)) goto error;
+ Py_DECREF(imported_module);
+ status = __Pyx__Import_GetModule(top_level_package_name, module);
+ Py_DECREF(top_level_package_name);
+ return status;
+error:
+ Py_DECREF(imported_module);
+ *module = NULL;
+ return -1;
+not_found:
+ Py_DECREF(imported_module);
+ *module = NULL;
+ return 0;
+}
+static PyObject *__Pyx__Import(PyObject *name, PyObject *const *imported_names, Py_ssize_t len_imported_names, PyObject *qualname, PyObject *moddict, int level) {
+ PyObject *module = 0;
+ PyObject *empty_dict = 0;
+ PyObject *from_list = 0;
+ int module_found;
+ if (!qualname) {
+ qualname = name;
+ }
+ module_found = __Pyx__Import_Lookup(qualname, imported_names, len_imported_names, &module);
+ if (likely(module_found == 1)) {
+ return module;
+ } else if (unlikely(module_found == -1)) {
+ return NULL;
+ }
+ empty_dict = PyDict_New();
+ if (unlikely(!empty_dict))
+ goto bad;
+ if (imported_names) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ from_list = __Pyx_PyList_FromArray(imported_names, len_imported_names);
+ if (unlikely(!from_list))
+ goto bad;
+#else
+ from_list = PyList_New(len_imported_names);
+ if (unlikely(!from_list)) goto bad;
+ for (Py_ssize_t i=0; i__pyx_d, level);
+}
+
+/* ImportFrom */
+static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) {
+ PyObject* value = __Pyx_PyObject_GetAttrStr(module, name);
+ if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ const char* module_name_str = 0;
+ PyObject* module_name = 0;
+ PyObject* module_dot = 0;
+ PyObject* full_name = 0;
+ PyErr_Clear();
+ module_name_str = PyModule_GetName(module);
+ if (unlikely(!module_name_str)) { goto modbad; }
+ module_name = PyUnicode_FromString(module_name_str);
+ if (unlikely(!module_name)) { goto modbad; }
+ module_dot = PyUnicode_Concat(module_name, __pyx_mstate_global->__pyx_kp_u_);
+ if (unlikely(!module_dot)) { goto modbad; }
+ full_name = PyUnicode_Concat(module_dot, name);
+ if (unlikely(!full_name)) { goto modbad; }
+ #if (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) ||\
+ CYTHON_COMPILING_IN_GRAAL
+ {
+ PyObject *modules = PyImport_GetModuleDict();
+ if (unlikely(!modules))
+ goto modbad;
+ value = PyObject_GetItem(modules, full_name);
+ }
+ #else
+ value = PyImport_GetModule(full_name);
+ #endif
+ modbad:
+ Py_XDECREF(full_name);
+ Py_XDECREF(module_dot);
+ Py_XDECREF(module_name);
+ }
+ if (unlikely(!value)) {
+ PyErr_Format(PyExc_ImportError, "cannot import name %S", name);
+ }
+ return value;
+}
+
+/* AllocateExtensionType */
+static PyObject *__Pyx_AllocateExtensionType(PyTypeObject *t, int is_final) {
+ if (is_final || likely(!__Pyx_PyType_HasFeature(t, Py_TPFLAGS_IS_ABSTRACT))) {
+ allocfunc alloc_func = __Pyx_PyType_GetSlot(t, tp_alloc, allocfunc);
+ return alloc_func(t, 0);
+ } else {
+ newfunc tp_new = __Pyx_PyType_TryGetSlot(&PyBaseObject_Type, tp_new, newfunc);
+ #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ if (!tp_new) {
+ PyObject *new_str = PyUnicode_FromString("__new__");
+ if (likely(new_str)) {
+ PyObject *o = PyObject_CallMethodObjArgs((PyObject *)&PyBaseObject_Type, new_str, t, NULL);
+ Py_DECREF(new_str);
+ return o;
+ } else
+ return NULL;
+ } else
+ #endif
+ return tp_new(t, __pyx_mstate_global->__pyx_empty_tuple, 0);
+ }
+}
+
+/* CallTypeTraverse */
+#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000)
+#else
+static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg) {
+ #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x03090000
+ if (__Pyx_get_runtime_version() < 0x03090000) return 0;
+ #endif
+ if (!always_call) {
+ PyTypeObject *base = __Pyx_PyObject_GetSlot(o, tp_base, PyTypeObject*);
+ unsigned long flags = PyType_GetFlags(base);
+ if (flags & Py_TPFLAGS_HEAPTYPE) {
+ return 0;
+ }
+ }
+ Py_VISIT((PyObject*)Py_TYPE(o));
+ return 0;
+}
+#endif
+
+/* LimitedApiGetTypeDict (used by SetItemOnTypeDict) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static Py_ssize_t __Pyx_GetTypeDictOffset(void) {
+ PyObject *tp_dictoffset_o;
+ Py_ssize_t tp_dictoffset;
+ tp_dictoffset_o = PyObject_GetAttrString((PyObject*)(&PyType_Type), "__dictoffset__");
+ if (unlikely(!tp_dictoffset_o)) return -1;
+ tp_dictoffset = PyLong_AsSsize_t(tp_dictoffset_o);
+ Py_DECREF(tp_dictoffset_o);
+ if (unlikely(tp_dictoffset == 0)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "'type' doesn't have a dictoffset");
+ return -1;
+ } else if (unlikely(tp_dictoffset < 0)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "'type' has an unexpected negative dictoffset. "
+ "Please report this as Cython bug");
+ return -1;
+ }
+ return tp_dictoffset;
+}
+static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp) {
+ static Py_ssize_t tp_dictoffset = 0;
+ if (unlikely(tp_dictoffset == 0)) {
+ tp_dictoffset = __Pyx_GetTypeDictOffset();
+ if (unlikely(tp_dictoffset == -1 && PyErr_Occurred())) {
+ tp_dictoffset = 0; // try again next time?
+ return NULL;
+ }
+ }
+ return *(PyObject**)((char*)tp + tp_dictoffset);
+}
+#endif
+
+/* SetItemOnTypeDict (used by FixUpExtensionType) */
+static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v) {
+ int result;
+ PyObject *tp_dict;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ tp_dict = __Pyx_GetTypeDict(tp);
+ if (unlikely(!tp_dict)) return -1;
+#else
+ tp_dict = tp->tp_dict;
+#endif
+ result = PyDict_SetItem(tp_dict, k, v);
+ if (likely(!result)) {
+ PyType_Modified(tp);
+ if (unlikely(PyObject_HasAttr(v, __pyx_mstate_global->__pyx_n_u_set_name))) {
+ PyObject *setNameResult = PyObject_CallMethodObjArgs(v, __pyx_mstate_global->__pyx_n_u_set_name, (PyObject *) tp, k, NULL);
+ if (!setNameResult) return -1;
+ Py_DECREF(setNameResult);
+ }
+ }
+ return result;
+}
+
+/* FixUpExtensionType */
+static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) {
+#if __PYX_LIMITED_VERSION_HEX > 0x030900B1
+ CYTHON_UNUSED_VAR(spec);
+ CYTHON_UNUSED_VAR(type);
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+#else
+ const PyType_Slot *slot = spec->slots;
+ int changed = 0;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ while (slot && slot->slot && slot->slot != Py_tp_members)
+ slot++;
+ if (slot && slot->slot == Py_tp_members) {
+#if !CYTHON_COMPILING_IN_CPYTHON
+ const
+#endif // !CYTHON_COMPILING_IN_CPYTHON)
+ PyMemberDef *memb = (PyMemberDef*) slot->pfunc;
+ while (memb && memb->name) {
+ if (memb->name[0] == '_' && memb->name[1] == '_') {
+ if (strcmp(memb->name, "__weaklistoffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_weaklistoffset = memb->offset;
+ changed = 1;
+ }
+ else if (strcmp(memb->name, "__dictoffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_dictoffset = memb->offset;
+ changed = 1;
+ }
+#if CYTHON_METH_FASTCALL
+ else if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
+ assert(memb->type == T_PYSSIZET);
+ assert(memb->flags == READONLY);
+ type->tp_vectorcall_offset = memb->offset;
+ changed = 1;
+ }
+#endif // CYTHON_METH_FASTCALL
+#if !CYTHON_COMPILING_IN_PYPY
+ else if (strcmp(memb->name, "__module__") == 0) {
+ PyObject *descr;
+ assert(memb->type == T_OBJECT);
+ assert(memb->flags == 0 || memb->flags == READONLY);
+ descr = PyDescr_NewMember(type, memb);
+ if (unlikely(!descr))
+ return -1;
+ int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr);
+ Py_DECREF(descr);
+ if (unlikely(set_item_result < 0)) {
+ return -1;
+ }
+ changed = 1;
+ }
+#endif // !CYTHON_COMPILING_IN_PYPY
+ }
+ memb++;
+ }
+ }
+#endif // !CYTHON_COMPILING_IN_LIMITED_API
+#if !CYTHON_COMPILING_IN_PYPY
+ slot = spec->slots;
+ while (slot && slot->slot && slot->slot != Py_tp_getset)
+ slot++;
+ if (slot && slot->slot == Py_tp_getset) {
+ PyGetSetDef *getset = (PyGetSetDef*) slot->pfunc;
+ while (getset && getset->name) {
+ if (getset->name[0] == '_' && getset->name[1] == '_' && strcmp(getset->name, "__module__") == 0) {
+ PyObject *descr = PyDescr_NewGetSet(type, getset);
+ if (unlikely(!descr))
+ return -1;
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *pyname = PyUnicode_FromString(getset->name);
+ if (unlikely(!pyname)) {
+ Py_DECREF(descr);
+ return -1;
+ }
+ int set_item_result = __Pyx_SetItemOnTypeDict(type, pyname, descr);
+ Py_DECREF(pyname);
+ #else
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+ int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr);
+ #endif
+ Py_DECREF(descr);
+ if (unlikely(set_item_result < 0)) {
+ return -1;
+ }
+ changed = 1;
+ }
+ ++getset;
+ }
+ }
+#else
+ CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict);
+#endif // !CYTHON_COMPILING_IN_PYPY
+ if (changed)
+ PyType_Modified(type);
+#endif // PY_VERSION_HEX > 0x030900B1
+ return 0;
+}
+
+/* ValidateBasesTuple (used by PyType_Ready) */
+#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS
+static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) {
+ Py_ssize_t i, n;
+#if CYTHON_ASSUME_SAFE_SIZE
+ n = PyTuple_GET_SIZE(bases);
+#else
+ n = PyTuple_Size(bases);
+ if (unlikely(n < 0)) return -1;
+#endif
+ for (i = 1; i < n; i++)
+ {
+ PyTypeObject *b;
+#if CYTHON_AVOID_BORROWED_REFS
+ PyObject *b0 = PySequence_GetItem(bases, i);
+ if (!b0) return -1;
+#elif CYTHON_ASSUME_SAFE_MACROS
+ PyObject *b0 = PyTuple_GET_ITEM(bases, i);
+#else
+ PyObject *b0 = PyTuple_GetItem(bases, i);
+ if (!b0) return -1;
+#endif
+ b = (PyTypeObject*) b0;
+ if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE))
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b);
+ PyErr_Format(PyExc_TypeError,
+ "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name);
+ __Pyx_DECREF_TypeName(b_name);
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ return -1;
+ }
+ if (dictoffset == 0)
+ {
+ Py_ssize_t b_dictoffset = 0;
+#if CYTHON_USE_TYPE_SLOTS
+ b_dictoffset = b->tp_dictoffset;
+#else
+ PyObject *py_b_dictoffset = PyObject_GetAttrString((PyObject*)b, "__dictoffset__");
+ if (!py_b_dictoffset) goto dictoffset_return;
+ b_dictoffset = PyLong_AsSsize_t(py_b_dictoffset);
+ Py_DECREF(py_b_dictoffset);
+ if (b_dictoffset == -1 && PyErr_Occurred()) goto dictoffset_return;
+#endif
+ if (b_dictoffset) {
+ {
+ __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b);
+ PyErr_Format(PyExc_TypeError,
+ "extension type '%.200s' has no __dict__ slot, "
+ "but base type '" __Pyx_FMT_TYPENAME "' has: "
+ "either add 'cdef dict __dict__' to the extension type "
+ "or add '__slots__ = [...]' to the base type",
+ type_name, b_name);
+ __Pyx_DECREF_TypeName(b_name);
+ }
+#if !CYTHON_USE_TYPE_SLOTS
+ dictoffset_return:
+#endif
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ return -1;
+ }
+ }
+#if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(b0);
+#endif
+ }
+ return 0;
+}
+#endif
+
+/* PyType_Ready */
+CYTHON_UNUSED static int __Pyx_PyType_HasMultipleInheritance(PyTypeObject *t) {
+ while (t) {
+ PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*);
+ if (bases) {
+ return 1;
+ }
+ t = __Pyx_PyType_GetSlot(t, tp_base, PyTypeObject*);
+ }
+ return 0;
+}
+static int __Pyx_PyType_Ready(PyTypeObject *t) {
+#if CYTHON_USE_TYPE_SPECS || !CYTHON_COMPILING_IN_CPYTHON || defined(PYSTON_MAJOR_VERSION)
+ (void)__Pyx_PyObject_CallMethod0;
+#if CYTHON_USE_TYPE_SPECS
+ (void)__Pyx_validate_bases_tuple;
+#endif
+ return PyType_Ready(t);
+#else
+ int r;
+ if (!__Pyx_PyType_HasMultipleInheritance(t)) {
+ return PyType_Ready(t);
+ }
+ PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*);
+ if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1))
+ return -1;
+#if !defined(PYSTON_MAJOR_VERSION)
+ {
+ int gc_was_enabled;
+ #if PY_VERSION_HEX >= 0x030A00b1
+ gc_was_enabled = PyGC_Disable();
+ (void)__Pyx_PyObject_CallMethod0;
+ #else
+ PyObject *ret, *py_status;
+ PyObject *gc = NULL;
+ #if (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400) &&\
+ !CYTHON_COMPILING_IN_GRAAL
+ gc = PyImport_GetModule(__pyx_mstate_global->__pyx_kp_u_gc);
+ #endif
+ if (unlikely(!gc)) gc = PyImport_Import(__pyx_mstate_global->__pyx_kp_u_gc);
+ if (unlikely(!gc)) return -1;
+ py_status = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_isenabled);
+ if (unlikely(!py_status)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ gc_was_enabled = __Pyx_PyObject_IsTrue(py_status);
+ Py_DECREF(py_status);
+ if (gc_was_enabled > 0) {
+ ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_disable);
+ if (unlikely(!ret)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ Py_DECREF(ret);
+ } else if (unlikely(gc_was_enabled == -1)) {
+ Py_DECREF(gc);
+ return -1;
+ }
+ #endif
+ t->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+#if PY_VERSION_HEX >= 0x030A0000
+ t->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
+#endif
+#else
+ (void)__Pyx_PyObject_CallMethod0;
+#endif
+ r = PyType_Ready(t);
+#if !defined(PYSTON_MAJOR_VERSION)
+ t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE;
+ #if PY_VERSION_HEX >= 0x030A00b1
+ if (gc_was_enabled)
+ PyGC_Enable();
+ #else
+ if (gc_was_enabled) {
+ PyObject *tp, *v, *tb;
+ PyErr_Fetch(&tp, &v, &tb);
+ ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_enable);
+ if (likely(ret || r == -1)) {
+ Py_XDECREF(ret);
+ PyErr_Restore(tp, v, tb);
+ } else {
+ Py_XDECREF(tp);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+ r = -1;
+ }
+ }
+ Py_DECREF(gc);
+ #endif
+ }
+#endif
+ return r;
+#endif
+}
+
+/* ListPack */
+static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...) {
+ va_list va;
+ PyObject *l = PyList_New(n);
+ va_start(va, n);
+ if (unlikely(!l)) goto end;
+ for (Py_ssize_t i=0; i= 0x030C0000
+ PyObject *args[] = {d, key, default_value};
+ value = PyObject_VectorcallMethod(__pyx_mstate_global->__pyx_n_u_setdefault, args, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#elif CYTHON_COMPILING_IN_LIMITED_API
+ value = PyObject_CallMethodObjArgs(d, __pyx_mstate_global->__pyx_n_u_setdefault, key, default_value, NULL);
+#elif PY_VERSION_HEX >= 0x030d0000
+ PyDict_SetDefaultRef(d, key, default_value, &value);
+#else
+ value = PyDict_SetDefault(d, key, default_value);
+ if (unlikely(!value)) return NULL;
+ Py_INCREF(value);
+#endif
+ return value;
+}
+
+/* AddModuleRef (used by FetchSharedCythonModule) */
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ static PyObject *__Pyx_PyImport_AddModuleObjectRef(PyObject *name) {
+ PyObject *module_dict = PyImport_GetModuleDict();
+ PyObject *m;
+ if (PyMapping_GetOptionalItem(module_dict, name, &m) < 0) {
+ return NULL;
+ }
+ if (m != NULL && PyModule_Check(m)) {
+ return m;
+ }
+ Py_XDECREF(m);
+ m = PyModule_NewObject(name);
+ if (m == NULL)
+ return NULL;
+ if (PyDict_CheckExact(module_dict)) {
+ PyObject *new_m;
+ (void)PyDict_SetDefaultRef(module_dict, name, m, &new_m);
+ Py_DECREF(m);
+ return new_m;
+ } else {
+ if (PyObject_SetItem(module_dict, name, m) != 0) {
+ Py_DECREF(m);
+ return NULL;
+ }
+ return m;
+ }
+ }
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name) {
+ PyObject *py_name = PyUnicode_FromString(name);
+ if (!py_name) return NULL;
+ PyObject *module = __Pyx_PyImport_AddModuleObjectRef(py_name);
+ Py_DECREF(py_name);
+ return module;
+ }
+#elif __PYX_LIMITED_VERSION_HEX >= 0x030d0000
+ #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name)
+#else
+ static PyObject *__Pyx_PyImport_AddModuleRef(const char *name) {
+ PyObject *module = PyImport_AddModule(name);
+ Py_XINCREF(module);
+ return module;
+ }
+#endif
+
+/* FetchSharedCythonModule (used by FetchCommonType) */
+static PyObject *__Pyx_FetchSharedCythonABIModule(void) {
+ return __Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME);
+}
+
+/* FetchCommonType (used by CommonTypesMetaclass) */
+#if __PYX_LIMITED_VERSION_HEX < 0x030C0000
+static PyObject* __Pyx_PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) {
+ PyObject *result = __Pyx_PyType_FromModuleAndSpec(module, spec, bases);
+ if (result && metaclass) {
+ PyObject *old_tp = (PyObject*)Py_TYPE(result);
+ Py_INCREF((PyObject*)metaclass);
+#if __PYX_LIMITED_VERSION_HEX >= 0x03090000
+ Py_SET_TYPE(result, metaclass);
+#else
+ result->ob_type = metaclass;
+#endif
+ Py_DECREF(old_tp);
+ }
+ return result;
+}
+#else
+#define __Pyx_PyType_FromMetaclass(me, mo, s, b) PyType_FromMetaclass(me, mo, s, b)
+#endif
+static int __Pyx_VerifyCachedType(PyObject *cached_type,
+ const char *name,
+ Py_ssize_t expected_basicsize) {
+ Py_ssize_t basicsize;
+ if (!PyType_Check(cached_type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s is not a type object", name);
+ return -1;
+ }
+ if (expected_basicsize == 0) {
+ return 0; // size is inherited, nothing useful to check
+ }
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_basicsize;
+ py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__");
+ if (unlikely(!py_basicsize)) return -1;
+ basicsize = PyLong_AsSsize_t(py_basicsize);
+ Py_DECREF(py_basicsize);
+ py_basicsize = NULL;
+ if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) return -1;
+#else
+ basicsize = ((PyTypeObject*) cached_type)->tp_basicsize;
+#endif
+ if (basicsize != expected_basicsize) {
+ PyErr_Format(PyExc_TypeError,
+ "Shared Cython type %.200s has the wrong size, try recompiling",
+ name);
+ return -1;
+ }
+ return 0;
+}
+static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) {
+ PyObject *abi_module = NULL, *cached_type = NULL, *abi_module_dict, *new_cached_type, *py_object_name;
+ int get_item_ref_result;
+ const char* object_name = strrchr(spec->name, '.');
+ object_name = object_name ? object_name+1 : spec->name;
+ py_object_name = PyUnicode_FromString(object_name);
+ if (!py_object_name) return NULL;
+ abi_module = __Pyx_FetchSharedCythonABIModule();
+ if (!abi_module) goto done;
+ abi_module_dict = PyModule_GetDict(abi_module);
+ if (!abi_module_dict) goto done;
+ get_item_ref_result = __Pyx_PyDict_GetItemRef(abi_module_dict, py_object_name, &cached_type);
+ if (get_item_ref_result == 1) {
+ if (__Pyx_VerifyCachedType(
+ cached_type,
+ object_name,
+ spec->basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ } else if (unlikely(get_item_ref_result == -1)) {
+ goto bad;
+ }
+ cached_type = __Pyx_PyType_FromMetaclass(
+ metaclass,
+ CYTHON_USE_MODULE_STATE ? module : abi_module,
+ spec, bases);
+ if (unlikely(!cached_type)) goto bad;
+ if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad;
+ new_cached_type = __Pyx_PyDict_SetDefault(abi_module_dict, py_object_name, cached_type);
+ if (unlikely(new_cached_type != cached_type)) {
+ if (unlikely(!new_cached_type)) goto bad;
+ Py_DECREF(cached_type);
+ cached_type = new_cached_type;
+ if (__Pyx_VerifyCachedType(
+ cached_type,
+ object_name,
+ spec->basicsize) < 0) {
+ goto bad;
+ }
+ goto done;
+ } else {
+ Py_DECREF(new_cached_type);
+ }
+done:
+ Py_XDECREF(abi_module);
+ Py_DECREF(py_object_name);
+ assert(cached_type == NULL || PyType_Check(cached_type));
+ return (PyTypeObject *) cached_type;
+bad:
+ Py_XDECREF(cached_type);
+ cached_type = NULL;
+ goto done;
+}
+
+/* CommonTypesMetaclass (used by CythonFunctionShared) */
+static PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
+ return PyUnicode_FromString(__PYX_ABI_MODULE_NAME);
+}
+#if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject* __pyx_CommonTypesMetaclass_call(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED PyObject *args, CYTHON_UNUSED PyObject *kwds) {
+ PyErr_SetString(PyExc_TypeError, "Cannot instantiate Cython internal types");
+ return NULL;
+}
+static int __pyx_CommonTypesMetaclass_setattr(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED PyObject *attr, CYTHON_UNUSED PyObject *value) {
+ PyErr_SetString(PyExc_TypeError, "Cython internal types are immutable");
+ return -1;
+}
+#endif
+static PyGetSetDef __pyx_CommonTypesMetaclass_getset[] = {
+ {"__module__", __pyx_CommonTypesMetaclass_get_module, NULL, NULL, NULL},
+ {0, 0, 0, 0, 0}
+};
+static PyType_Slot __pyx_CommonTypesMetaclass_slots[] = {
+ {Py_tp_getset, (void *)__pyx_CommonTypesMetaclass_getset},
+ #if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ {Py_tp_call, (void*)__pyx_CommonTypesMetaclass_call},
+ {Py_tp_new, (void*)__pyx_CommonTypesMetaclass_call},
+ {Py_tp_setattro, (void*)__pyx_CommonTypesMetaclass_setattr},
+ #endif
+ {0, 0}
+};
+static PyType_Spec __pyx_CommonTypesMetaclass_spec = {
+ __PYX_TYPE_MODULE_PREFIX "_common_types_metatype",
+ 0,
+ 0,
+ Py_TPFLAGS_IMMUTABLETYPE |
+ Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT,
+ __pyx_CommonTypesMetaclass_slots
+};
+static int __pyx_CommonTypesMetaclass_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ PyObject *bases = PyTuple_Pack(1, &PyType_Type);
+ if (unlikely(!bases)) {
+ return -1;
+ }
+ mstate->__pyx_CommonTypesMetaclassType = __Pyx_FetchCommonTypeFromSpec(NULL, module, &__pyx_CommonTypesMetaclass_spec, bases);
+ Py_DECREF(bases);
+ if (unlikely(mstate->__pyx_CommonTypesMetaclassType == NULL)) {
+ return -1;
+ }
+ return 0;
+}
+
+/* PyMethodNew (used by CythonFunctionShared) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(typ);
+ if (!self)
+ return __Pyx_NewRef(func);
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030C0000
+ {
+ PyObject *args[] = {func, self};
+ result = PyObject_Vectorcall(__pyx_mstate_global->__Pyx_CachedMethodType, args, 2, NULL);
+ }
+ #else
+ result = PyObject_CallFunctionObjArgs(__pyx_mstate_global->__Pyx_CachedMethodType, func, self, NULL);
+ #endif
+ return result;
+}
+#else
+static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) {
+ CYTHON_UNUSED_VAR(typ);
+ if (!self)
+ return __Pyx_NewRef(func);
+ return PyMethod_New(func, self);
+}
+#endif
+
+/* PyVectorcallFastCallDict (used by CythonFunctionShared) */
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ PyObject *res = NULL;
+ PyObject *kwnames;
+ PyObject **newargs;
+ PyObject **kwvalues;
+ Py_ssize_t i;
+ #if CYTHON_AVOID_BORROWED_REFS
+ PyObject *pos;
+ #else
+ Py_ssize_t pos;
+ #endif
+ size_t j;
+ PyObject *key, *value;
+ unsigned long keys_are_strings;
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ Py_ssize_t nkw = PyDict_Size(kw);
+ if (unlikely(nkw == -1)) return NULL;
+ #else
+ Py_ssize_t nkw = PyDict_GET_SIZE(kw);
+ #endif
+ newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0]));
+ if (unlikely(newargs == NULL)) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (j = 0; j < nargs; j++) newargs[j] = args[j];
+ kwnames = PyTuple_New(nkw);
+ if (unlikely(kwnames == NULL)) {
+ PyMem_Free(newargs);
+ return NULL;
+ }
+ kwvalues = newargs + nargs;
+ pos = 0;
+ i = 0;
+ keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;
+ while (__Pyx_PyDict_NextRef(kw, &pos, &key, &value)) {
+ keys_are_strings &=
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ PyType_GetFlags(Py_TYPE(key));
+ #else
+ Py_TYPE(key)->tp_flags;
+ #endif
+ #if !CYTHON_ASSUME_SAFE_MACROS
+ if (unlikely(PyTuple_SetItem(kwnames, i, key) < 0)) goto cleanup;
+ #else
+ PyTuple_SET_ITEM(kwnames, i, key);
+ #endif
+ kwvalues[i] = value;
+ i++;
+ }
+ if (unlikely(!keys_are_strings)) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ goto cleanup;
+ }
+ res = vc(func, newargs, nargs, kwnames);
+cleanup:
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_DECREF(pos);
+ #endif
+ Py_DECREF(kwnames);
+ for (i = 0; i < nkw; i++)
+ Py_DECREF(kwvalues[i]);
+ PyMem_Free(newargs);
+ return res;
+}
+static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw)
+{
+ Py_ssize_t kw_size =
+ likely(kw == NULL) ?
+ 0 :
+#if !CYTHON_ASSUME_SAFE_SIZE
+ PyDict_Size(kw);
+#else
+ PyDict_GET_SIZE(kw);
+#endif
+ if (kw_size == 0) {
+ return vc(func, args, nargs, NULL);
+ }
+#if !CYTHON_ASSUME_SAFE_SIZE
+ else if (unlikely(kw_size == -1)) {
+ return NULL;
+ }
+#endif
+ return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw);
+}
+#endif
+
+/* CythonFunctionShared (used by CythonFunction) */
+#if CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunctionNoMethod(PyObject *func, void (*cfunc)(void)) {
+ if (__Pyx_CyFunction_Check(func)) {
+ return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc;
+ } else if (PyCFunction_Check(func)) {
+ return PyCFunction_GetFunction(func) == (PyCFunction) cfunc;
+ }
+ return 0;
+}
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) {
+ if ((PyObject*)Py_TYPE(func) == __pyx_mstate_global->__Pyx_CachedMethodType) {
+ int result;
+ PyObject *newFunc = PyObject_GetAttr(func, __pyx_mstate_global->__pyx_n_u_func);
+ if (unlikely(!newFunc)) {
+ PyErr_Clear(); // It's only an optimization, so don't throw an error
+ return 0;
+ }
+ result = __Pyx__IsSameCyOrCFunctionNoMethod(newFunc, cfunc);
+ Py_DECREF(newFunc);
+ return result;
+ }
+ return __Pyx__IsSameCyOrCFunctionNoMethod(func, cfunc);
+}
+#else
+static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) {
+ if (PyMethod_Check(func)) {
+ func = PyMethod_GET_FUNCTION(func);
+ }
+ return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc;
+}
+#endif
+static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) {
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ __Pyx_Py_XDECREF_SET(
+ __Pyx_CyFunction_GetClassObj(f),
+ ((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#else
+ __Pyx_Py_XDECREF_SET(
+ ((PyCMethodObject *) (f))->mm_class,
+ (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL));
+#endif
+}
+static PyObject *
+__Pyx_CyFunction_get_doc_locked(__pyx_CyFunctionObject *op)
+{
+ if (unlikely(op->func_doc == NULL)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func_doc = PyObject_GetAttrString(op->func, "__doc__");
+ if (unlikely(!op->func_doc)) return NULL;
+#else
+ if (((PyCFunctionObject*)op)->m_ml->ml_doc) {
+ op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc);
+ if (unlikely(op->func_doc == NULL))
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+#endif
+ }
+ Py_INCREF(op->func_doc);
+ return op->func_doc;
+}
+static PyObject *
+__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(closure);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_doc_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (value == NULL) {
+ value = Py_None;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_doc, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_name_locked(__pyx_CyFunctionObject *op)
+{
+ if (unlikely(op->func_name == NULL)) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func_name = PyObject_GetAttrString(op->func, "__name__");
+#else
+ op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name);
+#endif
+ if (unlikely(op->func_name == NULL))
+ return NULL;
+ }
+ Py_INCREF(op->func_name);
+ return op->func_name;
+}
+static PyObject *
+__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context)
+{
+ PyObject *result = NULL;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_name_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__name__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_name, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ PyObject *result;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ Py_INCREF(op->func_qualname);
+ result = op->func_qualname;
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__qualname__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_qualname, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *
+__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(op->func_dict == NULL)) {
+ op->func_dict = PyDict_New();
+ if (unlikely(op->func_dict == NULL))
+ return NULL;
+ }
+ Py_INCREF(op->func_dict);
+ return op->func_dict;
+}
+#endif
+static PyObject *
+__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(op->func_globals);
+ return op->func_globals;
+}
+static PyObject *
+__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context)
+{
+ CYTHON_UNUSED_VAR(op);
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+static PyObject *
+__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context)
+{
+ PyObject* result = (op->func_code) ? op->func_code : Py_None;
+ CYTHON_UNUSED_VAR(context);
+ Py_INCREF(result);
+ return result;
+}
+static int
+__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) {
+ int result = 0;
+ PyObject *res = op->defaults_getter((PyObject *) op);
+ if (unlikely(!res))
+ return -1;
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ op->defaults_tuple = PyTuple_GET_ITEM(res, 0);
+ Py_INCREF(op->defaults_tuple);
+ op->defaults_kwdict = PyTuple_GET_ITEM(res, 1);
+ Py_INCREF(op->defaults_kwdict);
+ #else
+ op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0);
+ if (unlikely(!op->defaults_tuple)) result = -1;
+ else {
+ op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1);
+ if (unlikely(!op->defaults_kwdict)) result = -1;
+ }
+ #endif
+ Py_DECREF(res);
+ return result;
+}
+static int
+__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value) {
+ value = Py_None;
+ } else if (unlikely(value != Py_None && !PyTuple_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__defaults__ must be set to a tuple object");
+ return -1;
+ }
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not "
+ "currently affect the values used in function calls", 1);
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->defaults_tuple, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_defaults_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_tuple;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
+ result = op->defaults_tuple;
+ } else {
+ result = Py_None;
+ }
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) {
+ PyObject* result = NULL;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_defaults_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value) {
+ value = Py_None;
+ } else if (unlikely(value != Py_None && !PyDict_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__kwdefaults__ must be set to a dict object");
+ return -1;
+ }
+ PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not "
+ "currently affect the values used in function calls", 1);
+ Py_INCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_kwdefaults_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_kwdict;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL;
+ result = op->defaults_kwdict;
+ } else {
+ result = Py_None;
+ }
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) {
+ PyObject* result;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_kwdefaults_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static int
+__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ if (!value || value == Py_None) {
+ value = NULL;
+ } else if (unlikely(!PyDict_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__annotations__ must be set to a dict object");
+ return -1;
+ }
+ Py_XINCREF(value);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ __Pyx_Py_XDECREF_SET(op->func_annotations, value);
+ __Pyx_END_CRITICAL_SECTION();
+ return 0;
+}
+static PyObject *
+__Pyx_CyFunction_get_annotations_locked(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->func_annotations;
+ if (unlikely(!result)) {
+ result = PyDict_New();
+ if (unlikely(!result)) return NULL;
+ op->func_annotations = result;
+ }
+ Py_INCREF(result);
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(context);
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ result = __Pyx_CyFunction_get_annotations_locked(op);
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static PyObject *
+__Pyx_CyFunction_get_is_coroutine_value(__pyx_CyFunctionObject *op) {
+ int is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE;
+ if (is_coroutine) {
+ PyObject *is_coroutine_value, *module, *fromlist, *marker = __pyx_mstate_global->__pyx_n_u_is_coroutine;
+ fromlist = PyList_New(1);
+ if (unlikely(!fromlist)) return NULL;
+ Py_INCREF(marker);
+#if CYTHON_ASSUME_SAFE_MACROS
+ PyList_SET_ITEM(fromlist, 0, marker);
+#else
+ if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) {
+ Py_DECREF(marker);
+ Py_DECREF(fromlist);
+ return NULL;
+ }
+#endif
+ module = PyImport_ImportModuleLevelObject(__pyx_mstate_global->__pyx_n_u_asyncio_coroutines, NULL, NULL, fromlist, 0);
+ Py_DECREF(fromlist);
+ if (unlikely(!module)) goto ignore;
+ is_coroutine_value = __Pyx_PyObject_GetAttrStr(module, marker);
+ Py_DECREF(module);
+ if (likely(is_coroutine_value)) {
+ return is_coroutine_value;
+ }
+ignore:
+ PyErr_Clear();
+ }
+ return __Pyx_PyBool_FromLong(is_coroutine);
+}
+static PyObject *
+__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) {
+ PyObject *result;
+ CYTHON_UNUSED_VAR(context);
+ if (op->func_is_coroutine) {
+ return __Pyx_NewRef(op->func_is_coroutine);
+ }
+ result = __Pyx_CyFunction_get_is_coroutine_value(op);
+ if (unlikely(!result))
+ return NULL;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ if (op->func_is_coroutine) {
+ Py_DECREF(result);
+ result = __Pyx_NewRef(op->func_is_coroutine);
+ } else {
+ op->func_is_coroutine = __Pyx_NewRef(result);
+ }
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static void __Pyx_CyFunction_raise_argument_count_error(__pyx_CyFunctionObject *func, const char* message, Py_ssize_t size) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL);
+ if (!py_name) return;
+ PyErr_Format(PyExc_TypeError,
+ "%.200S() %s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ py_name, message, size);
+ Py_DECREF(py_name);
+#else
+ const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name;
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() %s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ name, message, size);
+#endif
+}
+static void __Pyx_CyFunction_raise_type_error(__pyx_CyFunctionObject *func, const char* message) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL);
+ if (!py_name) return;
+ PyErr_Format(PyExc_TypeError,
+ "%.200S() %s",
+ py_name, message);
+ Py_DECREF(py_name);
+#else
+ const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name;
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() %s",
+ name, message);
+#endif
+}
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *
+__Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ return PyObject_GetAttrString(op->func, "__module__");
+}
+static int
+__Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ return PyObject_SetAttrString(op->func, "__module__", value);
+}
+#endif
+static PyGetSetDef __pyx_CyFunction_getsets[] = {
+ {"func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
+ {"__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
+ {"func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
+ {"__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
+ {"__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ {"func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)PyObject_GenericSetDict, 0, 0},
+ {"__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)PyObject_GenericSetDict, 0, 0},
+#else
+ {"func_dict", (getter)PyObject_GenericGetDict, (setter)PyObject_GenericSetDict, 0, 0},
+ {"__dict__", (getter)PyObject_GenericGetDict, (setter)PyObject_GenericSetDict, 0, 0},
+#endif
+ {"func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
+ {"__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
+ {"func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
+ {"__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
+ {"func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
+ {"__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
+ {"func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {"__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {"__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0},
+ {"__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0},
+ {"_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0},
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0},
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyMemberDef __pyx_CyFunction_members[] = {
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ {"__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0},
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ {"__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0},
+#endif
+#if CYTHON_METH_FASTCALL
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0},
+#else
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0},
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0},
+#else
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0},
+#endif
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyObject *
+__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args)
+{
+ PyObject *result = NULL;
+ CYTHON_UNUSED_VAR(args);
+ __Pyx_BEGIN_CRITICAL_SECTION(m);
+ Py_INCREF(m->func_qualname);
+ result = m->func_qualname;
+ __Pyx_END_CRITICAL_SECTION();
+ return result;
+}
+static PyMethodDef __pyx_CyFunction_methods[] = {
+ {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0},
+ {0, 0, 0, 0}
+};
+#if CYTHON_COMPILING_IN_LIMITED_API
+#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist)
+#else
+#define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist)
+#endif
+static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunctionObject *cf = (PyCFunctionObject*) op;
+#endif
+ if (unlikely(op == NULL))
+ return NULL;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ op->func = PyCFunction_NewEx(ml, (PyObject*)op, module);
+ if (unlikely(!op->func)) return NULL;
+#endif
+ op->flags = flags;
+ __Pyx_CyFunction_weakreflist(op) = NULL;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ cf->m_ml = ml;
+ cf->m_self = (PyObject *) op;
+#endif
+ Py_XINCREF(closure);
+ op->func_closure = closure;
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ Py_XINCREF(module);
+ cf->m_module = module;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ op->func_dict = NULL;
+#endif
+ op->func_name = NULL;
+ Py_INCREF(qualname);
+ op->func_qualname = qualname;
+ op->func_doc = NULL;
+#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
+ op->func_classobj = NULL;
+#else
+ ((PyCMethodObject*)op)->mm_class = NULL;
+#endif
+ op->func_globals = globals;
+ Py_INCREF(op->func_globals);
+ Py_XINCREF(code);
+ op->func_code = code;
+ op->defaults = NULL;
+ op->defaults_tuple = NULL;
+ op->defaults_kwdict = NULL;
+ op->defaults_getter = NULL;
+ op->func_annotations = NULL;
+ op->func_is_coroutine = NULL;
+#if CYTHON_METH_FASTCALL
+ switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) {
+ case METH_NOARGS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS;
+ break;
+ case METH_O:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O;
+ break;
+ case METH_METHOD | METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD;
+ break;
+ case METH_FASTCALL | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS;
+ break;
+ case METH_VARARGS | METH_KEYWORDS:
+ __Pyx_CyFunction_func_vectorcall(op) = NULL;
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
+ Py_DECREF(op);
+ return NULL;
+ }
+#endif
+ return (PyObject *) op;
+}
+static int
+__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
+{
+ Py_CLEAR(m->func_closure);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ Py_CLEAR(m->func);
+#else
+ Py_CLEAR(((PyCFunctionObject*)m)->m_module);
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ Py_CLEAR(m->func_dict);
+#elif PY_VERSION_HEX < 0x030d0000
+ _PyObject_ClearManagedDict((PyObject*)m);
+#else
+ PyObject_ClearManagedDict((PyObject*)m);
+#endif
+ Py_CLEAR(m->func_name);
+ Py_CLEAR(m->func_qualname);
+ Py_CLEAR(m->func_doc);
+ Py_CLEAR(m->func_globals);
+ Py_CLEAR(m->func_code);
+#if !CYTHON_COMPILING_IN_LIMITED_API
+#if PY_VERSION_HEX < 0x030900B1
+ Py_CLEAR(__Pyx_CyFunction_GetClassObj(m));
+#else
+ {
+ PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class;
+ ((PyCMethodObject *) (m))->mm_class = NULL;
+ Py_XDECREF(cls);
+ }
+#endif
+#endif
+ Py_CLEAR(m->defaults_tuple);
+ Py_CLEAR(m->defaults_kwdict);
+ Py_CLEAR(m->func_annotations);
+ Py_CLEAR(m->func_is_coroutine);
+ Py_CLEAR(m->defaults);
+ return 0;
+}
+static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m)
+{
+ if (__Pyx_CyFunction_weakreflist(m) != NULL)
+ PyObject_ClearWeakRefs((PyObject *) m);
+ __Pyx_CyFunction_clear(m);
+ __Pyx_PyHeapTypeObject_GC_Del(m);
+}
+static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
+{
+ PyObject_GC_UnTrack(m);
+ __Pyx__CyFunction_dealloc(m);
+}
+static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg)
+{
+ {
+ int e = __Pyx_call_type_traverse((PyObject*)m, 1, visit, arg);
+ if (e) return e;
+ }
+ Py_VISIT(m->func_closure);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(m->func);
+#else
+ Py_VISIT(((PyCFunctionObject*)m)->m_module);
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(m->func_dict);
+#else
+ {
+ int e =
+#if PY_VERSION_HEX < 0x030d0000
+ _PyObject_VisitManagedDict
+#else
+ PyObject_VisitManagedDict
+#endif
+ ((PyObject*)m, visit, arg);
+ if (e != 0) return e;
+ }
+#endif
+ __Pyx_VISIT_CONST(m->func_name);
+ __Pyx_VISIT_CONST(m->func_qualname);
+ Py_VISIT(m->func_doc);
+ Py_VISIT(m->func_globals);
+ __Pyx_VISIT_CONST(m->func_code);
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ Py_VISIT(__Pyx_CyFunction_GetClassObj(m));
+#endif
+ Py_VISIT(m->defaults_tuple);
+ Py_VISIT(m->defaults_kwdict);
+ Py_VISIT(m->func_is_coroutine);
+ Py_VISIT(m->defaults);
+ return 0;
+}
+static PyObject*
+__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
+{
+ PyObject *repr;
+ __Pyx_BEGIN_CRITICAL_SECTION(op);
+ repr = PyUnicode_FromFormat("",
+ op->func_qualname, (void *)op);
+ __Pyx_END_CRITICAL_SECTION();
+ return repr;
+}
+static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *f = ((__pyx_CyFunctionObject*)func)->func;
+ PyCFunction meth;
+ int flags;
+ meth = PyCFunction_GetFunction(f);
+ if (unlikely(!meth)) return NULL;
+ flags = PyCFunction_GetFlags(f);
+ if (unlikely(flags < 0)) return NULL;
+#else
+ PyCFunctionObject* f = (PyCFunctionObject*)func;
+ PyCFunction meth = f->m_ml->ml_meth;
+ int flags = f->m_ml->ml_flags;
+#endif
+ Py_ssize_t size;
+ switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) {
+ case METH_VARARGS:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0))
+ return (*meth)(self, arg);
+ break;
+ case METH_VARARGS | METH_KEYWORDS:
+ return (*(PyCFunctionWithKeywords)(void(*)(void))meth)(self, arg, kw);
+ case METH_NOARGS:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
+#if CYTHON_ASSUME_SAFE_SIZE
+ size = PyTuple_GET_SIZE(arg);
+#else
+ size = PyTuple_Size(arg);
+ if (unlikely(size < 0)) return NULL;
+#endif
+ if (likely(size == 0))
+ return (*meth)(self, NULL);
+ __Pyx_CyFunction_raise_argument_count_error(
+ (__pyx_CyFunctionObject*)func,
+ "takes no arguments", size);
+ return NULL;
+ }
+ break;
+ case METH_O:
+ if (likely(kw == NULL || PyDict_Size(kw) == 0)) {
+#if CYTHON_ASSUME_SAFE_SIZE
+ size = PyTuple_GET_SIZE(arg);
+#else
+ size = PyTuple_Size(arg);
+ if (unlikely(size < 0)) return NULL;
+#endif
+ if (likely(size == 1)) {
+ PyObject *result, *arg0;
+ #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ arg0 = PyTuple_GET_ITEM(arg, 0);
+ #else
+ arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL;
+ #endif
+ result = (*meth)(self, arg0);
+ #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS)
+ Py_DECREF(arg0);
+ #endif
+ return result;
+ }
+ __Pyx_CyFunction_raise_argument_count_error(
+ (__pyx_CyFunctionObject*)func,
+ "takes exactly one argument", size);
+ return NULL;
+ }
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction");
+ return NULL;
+ }
+ __Pyx_CyFunction_raise_type_error(
+ (__pyx_CyFunctionObject*)func, "takes no keyword arguments");
+ return NULL;
+}
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) {
+ PyObject *self, *result;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)func)->m_self;
+#endif
+ result = __Pyx_CyFunction_CallMethod(func, self, arg, kw);
+ return result;
+}
+static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) {
+ PyObject *result;
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+ __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc);
+ if (vc) {
+#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE
+ return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw);
+#else
+ (void) &__Pyx_PyVectorcall_FastCallDict;
+ return PyVectorcall_Call(func, args, kw);
+#endif
+ }
+#endif
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
+ Py_ssize_t argc;
+ PyObject *new_args;
+ PyObject *self;
+#if CYTHON_ASSUME_SAFE_SIZE
+ argc = PyTuple_GET_SIZE(args);
+#else
+ argc = PyTuple_Size(args);
+ if (unlikely(argc < 0)) return NULL;
+#endif
+ new_args = PyTuple_GetSlice(args, 1, argc);
+ if (unlikely(!new_args))
+ return NULL;
+ self = PyTuple_GetItem(args, 0);
+ if (unlikely(!self)) {
+ Py_DECREF(new_args);
+ PyErr_Format(PyExc_TypeError,
+ "unbound method %.200S() needs an argument",
+ cyfunc->func_qualname);
+ return NULL;
+ }
+ result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw);
+ Py_DECREF(new_args);
+ } else {
+ result = __Pyx_CyFunction_Call(func, args, kw);
+ }
+ return result;
+}
+#if CYTHON_METH_FASTCALL && CYTHON_VECTORCALL
+static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames)
+{
+ int ret = 0;
+ if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) {
+ if (unlikely(nargs < 1)) {
+ __Pyx_CyFunction_raise_type_error(
+ cyfunc, "needs an argument");
+ return -1;
+ }
+ ret = 1;
+ }
+ if (unlikely(kwnames) && unlikely(__Pyx_PyTuple_GET_SIZE(kwnames))) {
+ __Pyx_CyFunction_raise_type_error(
+ cyfunc, "takes no keyword arguments");
+ return -1;
+ }
+ return ret;
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ if (unlikely(nargs != 0)) {
+ __Pyx_CyFunction_raise_argument_count_error(
+ cyfunc, "takes no arguments", nargs);
+ return NULL;
+ }
+ return meth(self, NULL);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ if (unlikely(nargs != 1)) {
+ __Pyx_CyFunction_raise_argument_count_error(
+ cyfunc, "takes exactly one argument", nargs);
+ return NULL;
+ }
+ return meth(self, args[0]);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ return ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))meth)(self, args, nargs, kwnames);
+}
+static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func;
+ PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc);
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ PyObject *self;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ PyCFunction meth = PyCFunction_GetFunction(cyfunc->func);
+ if (unlikely(!meth)) return NULL;
+#else
+ PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth;
+#endif
+ switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) {
+ case 1:
+ self = args[0];
+ args += 1;
+ nargs -= 1;
+ break;
+ case 0:
+#if CYTHON_COMPILING_IN_LIMITED_API
+ self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func);
+ if (unlikely(!self) && PyErr_Occurred()) return NULL;
+#else
+ self = ((PyCFunctionObject*)cyfunc)->m_self;
+#endif
+ break;
+ default:
+ return NULL;
+ }
+ #if PY_VERSION_HEX < 0x030e00A6
+ size_t nargs_value = (size_t) nargs;
+ #else
+ Py_ssize_t nargs_value = nargs;
+ #endif
+ return ((__Pyx_PyCMethod)(void(*)(void))meth)(self, cls, args, nargs_value, kwnames);
+}
+#endif
+static PyType_Slot __pyx_CyFunctionType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc},
+ {Py_tp_repr, (void *)__Pyx_CyFunction_repr},
+ {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod},
+ {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse},
+ {Py_tp_clear, (void *)__Pyx_CyFunction_clear},
+ {Py_tp_methods, (void *)__pyx_CyFunction_methods},
+ {Py_tp_members, (void *)__pyx_CyFunction_members},
+ {Py_tp_getset, (void *)__pyx_CyFunction_getsets},
+ {Py_tp_descr_get, (void *)__Pyx_PyMethod_New},
+ {0, 0},
+};
+static PyType_Spec __pyx_CyFunctionType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "cython_function_or_method",
+ sizeof(__pyx_CyFunctionObject),
+ 0,
+#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR
+ Py_TPFLAGS_METHOD_DESCRIPTOR |
+#endif
+#if CYTHON_METH_FASTCALL
+#if defined(Py_TPFLAGS_HAVE_VECTORCALL)
+ Py_TPFLAGS_HAVE_VECTORCALL |
+#elif defined(_Py_TPFLAGS_HAVE_VECTORCALL)
+ _Py_TPFLAGS_HAVE_VECTORCALL |
+#endif
+#endif // CYTHON_METH_FASTCALL
+#if PY_VERSION_HEX >= 0x030C0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_TPFLAGS_MANAGED_DICT |
+#endif
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
+ __pyx_CyFunctionType_slots
+};
+static int __pyx_CyFunction_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ mstate->__pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(
+ mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_CyFunctionType_spec, NULL);
+ if (unlikely(mstate->__pyx_CyFunctionType == NULL)) {
+ return -1;
+ }
+ return 0;
+}
+static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func, PyTypeObject *defaults_type) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults = PyObject_CallObject((PyObject*)defaults_type, NULL); // _PyObject_New(defaults_type);
+ if (unlikely(!m->defaults))
+ return NULL;
+ return m->defaults;
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults_tuple = tuple;
+ Py_INCREF(tuple);
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults_kwdict = dict;
+ Py_INCREF(dict);
+}
+static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->func_annotations = dict;
+ Py_INCREF(dict);
+}
+
+/* CythonFunction */
+static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname,
+ PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) {
+ PyObject *op = __Pyx_CyFunction_Init(
+ PyObject_GC_New(__pyx_CyFunctionObject, __pyx_mstate_global->__pyx_CyFunctionType),
+ ml, flags, qualname, closure, module, globals, code
+ );
+ if (likely(op)) {
+ PyObject_GC_Track(op);
+ }
+ return op;
+}
+
+/* ObjectGetItem */
+#if CYTHON_USE_TYPE_SLOTS
+static PyObject *__Pyx_PyObject_GetIndex(PyObject *obj, PyObject *index) {
+ PyObject *runerr = NULL;
+ Py_ssize_t key_value;
+ key_value = __Pyx_PyIndex_AsSsize_t(index);
+ if (likely(key_value != -1 || !(runerr = PyErr_Occurred()))) {
+ return __Pyx_GetItemInt_Fast(obj, key_value, 0, 1, 1, 1);
+ }
+ if (PyErr_GivenExceptionMatches(runerr, PyExc_OverflowError)) {
+ __Pyx_TypeName index_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(index));
+ PyErr_Clear();
+ PyErr_Format(PyExc_IndexError,
+ "cannot fit '" __Pyx_FMT_TYPENAME "' into an index-sized integer", index_type_name);
+ __Pyx_DECREF_TypeName(index_type_name);
+ }
+ return NULL;
+}
+static PyObject *__Pyx_PyObject_GetItem_Slow(PyObject *obj, PyObject *key) {
+ __Pyx_TypeName obj_type_name;
+ if (likely(PyType_Check(obj))) {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(obj, __pyx_mstate_global->__pyx_n_u_class_getitem);
+ if (!meth) {
+ PyErr_Clear();
+ } else {
+ PyObject *result = __Pyx_PyObject_CallOneArg(meth, key);
+ Py_DECREF(meth);
+ return result;
+ }
+ }
+ obj_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(obj));
+ PyErr_Format(PyExc_TypeError,
+ "'" __Pyx_FMT_TYPENAME "' object is not subscriptable", obj_type_name);
+ __Pyx_DECREF_TypeName(obj_type_name);
+ return NULL;
+}
+static PyObject *__Pyx_PyObject_GetItem(PyObject *obj, PyObject *key) {
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyMappingMethods *mm = tp->tp_as_mapping;
+ PySequenceMethods *sm = tp->tp_as_sequence;
+ if (likely(mm && mm->mp_subscript)) {
+ return mm->mp_subscript(obj, key);
+ }
+ if (likely(sm && sm->sq_item)) {
+ return __Pyx_PyObject_GetIndex(obj, key);
+ }
+ return __Pyx_PyObject_GetItem_Slow(obj, key);
+}
+#endif
+
+/* CLineInTraceback (used by AddTraceback) */
+#if CYTHON_CLINE_IN_TRACEBACK && CYTHON_CLINE_IN_TRACEBACK_RUNTIME
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+#define __Pyx_PyProbablyModule_GetDict(o) __Pyx_XNewRef(PyModule_GetDict(o))
+#elif !CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+#define __Pyx_PyProbablyModule_GetDict(o) PyObject_GenericGetDict(o, NULL);
+#else
+PyObject* __Pyx_PyProbablyModule_GetDict(PyObject *o) {
+ PyObject **dict_ptr = _PyObject_GetDictPtr(o);
+ return dict_ptr ? __Pyx_XNewRef(*dict_ptr) : NULL;
+}
+#endif
+static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) {
+ PyObject *use_cline = NULL;
+ PyObject *ptype, *pvalue, *ptraceback;
+ PyObject *cython_runtime_dict;
+ CYTHON_MAYBE_UNUSED_VAR(tstate);
+ if (unlikely(!__pyx_mstate_global->__pyx_cython_runtime)) {
+ return c_line;
+ }
+ __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
+ cython_runtime_dict = __Pyx_PyProbablyModule_GetDict(__pyx_mstate_global->__pyx_cython_runtime);
+ if (likely(cython_runtime_dict)) {
+ __PYX_PY_DICT_LOOKUP_IF_MODIFIED(
+ use_cline, cython_runtime_dict,
+ __Pyx_PyDict_SetDefault(cython_runtime_dict, __pyx_mstate_global->__pyx_n_u_cline_in_traceback, Py_False))
+ }
+ if (use_cline == NULL || use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) {
+ c_line = 0;
+ }
+ Py_XDECREF(use_cline);
+ Py_XDECREF(cython_runtime_dict);
+ __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
+ return c_line;
+}
+#endif
+
+/* CodeObjectCache (used by AddTraceback) */
+static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) {
+ int start = 0, mid = 0, end = count - 1;
+ if (end >= 0 && code_line > entries[end].code_line) {
+ return count;
+ }
+ while (start < end) {
+ mid = start + (end - start) / 2;
+ if (code_line < entries[mid].code_line) {
+ end = mid;
+ } else if (code_line > entries[mid].code_line) {
+ start = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ if (code_line <= entries[mid].code_line) {
+ return mid;
+ } else {
+ return mid + 1;
+ }
+}
+static __Pyx_CachedCodeObjectType *__pyx__find_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line) {
+ __Pyx_CachedCodeObjectType* code_object;
+ int pos;
+ if (unlikely(!code_line) || unlikely(!code_cache->entries)) {
+ return NULL;
+ }
+ pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line);
+ if (unlikely(pos >= code_cache->count) || unlikely(code_cache->entries[pos].code_line != code_line)) {
+ return NULL;
+ }
+ code_object = code_cache->entries[pos].code_object;
+ Py_INCREF(code_object);
+ return code_object;
+}
+static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line) {
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS
+ (void)__pyx__find_code_object;
+ return NULL; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just miss.
+#else
+ struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_nonatomic_int_type old_count = __pyx_atomic_incr_acq_rel(&code_cache->accessor_count);
+ if (old_count < 0) {
+ __pyx_atomic_decr_acq_rel(&code_cache->accessor_count);
+ return NULL;
+ }
+#endif
+ __Pyx_CachedCodeObjectType *result = __pyx__find_code_object(code_cache, code_line);
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_decr_acq_rel(&code_cache->accessor_count);
+#endif
+ return result;
+#endif
+}
+static void __pyx__insert_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line, __Pyx_CachedCodeObjectType* code_object)
+{
+ int pos, i;
+ __Pyx_CodeObjectCacheEntry* entries = code_cache->entries;
+ if (unlikely(!code_line)) {
+ return;
+ }
+ if (unlikely(!entries)) {
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry));
+ if (likely(entries)) {
+ code_cache->entries = entries;
+ code_cache->max_count = 64;
+ code_cache->count = 1;
+ entries[0].code_line = code_line;
+ entries[0].code_object = code_object;
+ Py_INCREF(code_object);
+ }
+ return;
+ }
+ pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line);
+ if ((pos < code_cache->count) && unlikely(code_cache->entries[pos].code_line == code_line)) {
+ __Pyx_CachedCodeObjectType* tmp = entries[pos].code_object;
+ entries[pos].code_object = code_object;
+ Py_INCREF(code_object);
+ Py_DECREF(tmp);
+ return;
+ }
+ if (code_cache->count == code_cache->max_count) {
+ int new_max = code_cache->max_count + 64;
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc(
+ code_cache->entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry));
+ if (unlikely(!entries)) {
+ return;
+ }
+ code_cache->entries = entries;
+ code_cache->max_count = new_max;
+ }
+ for (i=code_cache->count; i>pos; i--) {
+ entries[i] = entries[i-1];
+ }
+ entries[pos].code_line = code_line;
+ entries[pos].code_object = code_object;
+ code_cache->count++;
+ Py_INCREF(code_object);
+}
+static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object) {
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS
+ (void)__pyx__insert_code_object;
+ return; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just fail.
+#else
+ struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache;
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_nonatomic_int_type expected = 0;
+ if (!__pyx_atomic_int_cmp_exchange(&code_cache->accessor_count, &expected, INT_MIN)) {
+ return;
+ }
+#endif
+ __pyx__insert_code_object(code_cache, code_line, code_object);
+#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING
+ __pyx_atomic_sub(&code_cache->accessor_count, INT_MIN);
+#endif
+#endif
+}
+
+/* AddTraceback */
+#include "compile.h"
+#include "frameobject.h"
+#include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API && !defined(PYPY_VERSION)
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+#if CYTHON_COMPILING_IN_LIMITED_API
+static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict,
+ PyObject *firstlineno, PyObject *name) {
+ PyObject *replace = NULL;
+ if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL;
+ if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL;
+ replace = PyObject_GetAttrString(code, "replace");
+ if (likely(replace)) {
+ PyObject *result = PyObject_Call(replace, __pyx_mstate_global->__pyx_empty_tuple, scratch_dict);
+ Py_DECREF(replace);
+ return result;
+ }
+ PyErr_Clear();
+ return NULL;
+}
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL;
+ PyObject *replace = NULL, *getframe = NULL, *frame = NULL;
+ PyObject *exc_type, *exc_value, *exc_traceback;
+ int success = 0;
+ if (c_line) {
+ c_line = __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line);
+ }
+ PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
+ code_object = __pyx_find_code_object(c_line ? -c_line : py_line);
+ if (!code_object) {
+ code_object = Py_CompileString("_getframe()", filename, Py_eval_input);
+ if (unlikely(!code_object)) goto bad;
+ py_py_line = PyLong_FromLong(py_line);
+ if (unlikely(!py_py_line)) goto bad;
+ if (c_line) {
+ py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ } else {
+ py_funcname = PyUnicode_FromString(funcname);
+ }
+ if (unlikely(!py_funcname)) goto bad;
+ dict = PyDict_New();
+ if (unlikely(!dict)) goto bad;
+ {
+ PyObject *old_code_object = code_object;
+ code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname);
+ Py_DECREF(old_code_object);
+ }
+ if (unlikely(!code_object)) goto bad;
+ __pyx_insert_code_object(c_line ? -c_line : py_line, code_object);
+ } else {
+ dict = PyDict_New();
+ }
+ getframe = PySys_GetObject("_getframe");
+ if (unlikely(!getframe)) goto bad;
+ if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad;
+ frame = PyEval_EvalCode(code_object, dict, dict);
+ if (unlikely(!frame) || frame == Py_None) goto bad;
+ success = 1;
+ bad:
+ PyErr_Restore(exc_type, exc_value, exc_traceback);
+ Py_XDECREF(code_object);
+ Py_XDECREF(py_py_line);
+ Py_XDECREF(py_funcname);
+ Py_XDECREF(dict);
+ Py_XDECREF(replace);
+ if (success) {
+ PyTraceBack_Here(
+ (struct _frame*)frame);
+ }
+ Py_XDECREF(frame);
+}
+#else
+static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
+ const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = NULL;
+ PyObject *py_funcname = NULL;
+ if (c_line) {
+ py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ if (!py_funcname) goto bad;
+ funcname = PyUnicode_AsUTF8(py_funcname);
+ if (!funcname) goto bad;
+ }
+ py_code = PyCode_NewEmpty(filename, funcname, py_line);
+ Py_XDECREF(py_funcname);
+ return py_code;
+bad:
+ Py_XDECREF(py_funcname);
+ return NULL;
+}
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = 0;
+ PyFrameObject *py_frame = 0;
+ PyThreadState *tstate = __Pyx_PyThreadState_Current;
+ PyObject *ptype, *pvalue, *ptraceback;
+ if (c_line) {
+ c_line = __Pyx_CLineForTraceback(tstate, c_line);
+ }
+ py_code = __pyx_find_code_object(c_line ? -c_line : py_line);
+ if (!py_code) {
+ __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
+ py_code = __Pyx_CreateCodeObjectForTraceback(
+ funcname, c_line, py_line, filename);
+ if (!py_code) {
+ /* If the code object creation fails, then we should clear the
+ fetched exception references and propagate the new exception */
+ Py_XDECREF(ptype);
+ Py_XDECREF(pvalue);
+ Py_XDECREF(ptraceback);
+ goto bad;
+ }
+ __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
+ __pyx_insert_code_object(c_line ? -c_line : py_line, py_code);
+ }
+ py_frame = PyFrame_New(
+ tstate, /*PyThreadState *tstate,*/
+ py_code, /*PyCodeObject *code,*/
+ __pyx_mstate_global->__pyx_d, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ if (!py_frame) goto bad;
+ __Pyx_PyFrame_SetLineNumber(py_frame, py_line);
+ PyTraceBack_Here(py_frame);
+bad:
+ Py_XDECREF(py_code);
+ Py_XDECREF(py_frame);
+}
+#endif
+
+/* Declarations */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+ #ifdef __cplusplus
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ return ::std::complex< double >(x, y);
+ }
+ #else
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ return x + y*(__pyx_t_double_complex)_Complex_I;
+ }
+ #endif
+#else
+ static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) {
+ __pyx_t_double_complex z;
+ z.real = x;
+ z.imag = y;
+ return z;
+ }
+#endif
+
+/* Arithmetic */
+#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus)
+#else
+ static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ return (a.real == b.real) && (a.imag == b.imag);
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real + b.real;
+ z.imag = a.imag + b.imag;
+ return z;
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real - b.real;
+ z.imag = a.imag - b.imag;
+ return z;
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ z.real = a.real * b.real - a.imag * b.imag;
+ z.imag = a.real * b.imag + a.imag * b.real;
+ return z;
+ }
+ #if 1
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ if (b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real);
+ } else if (fabs(b.real) >= fabs(b.imag)) {
+ if (b.real == 0 && b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.imag);
+ } else {
+ double r = b.imag / b.real;
+ double s = (double)(1.0) / (b.real + b.imag * r);
+ return __pyx_t_double_complex_from_parts(
+ (a.real + a.imag * r) * s, (a.imag - a.real * r) * s);
+ }
+ } else {
+ double r = b.real / b.imag;
+ double s = (double)(1.0) / (b.imag + b.real * r);
+ return __pyx_t_double_complex_from_parts(
+ (a.real * r + a.imag) * s, (a.imag * r - a.real) * s);
+ }
+ }
+ #else
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ if (b.imag == 0) {
+ return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real);
+ } else {
+ double denom = b.real * b.real + b.imag * b.imag;
+ return __pyx_t_double_complex_from_parts(
+ (a.real * b.real + a.imag * b.imag) / denom,
+ (a.imag * b.real - a.real * b.imag) / denom);
+ }
+ }
+ #endif
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex a) {
+ __pyx_t_double_complex z;
+ z.real = -a.real;
+ z.imag = -a.imag;
+ return z;
+ }
+ static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex a) {
+ return (a.real == 0) && (a.imag == 0);
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex a) {
+ __pyx_t_double_complex z;
+ z.real = a.real;
+ z.imag = -a.imag;
+ return z;
+ }
+ #if 1
+ static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex z) {
+ #if !defined(HAVE_HYPOT) || defined(_MSC_VER)
+ return sqrt(z.real*z.real + z.imag*z.imag);
+ #else
+ return hypot(z.real, z.imag);
+ #endif
+ }
+ static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex a, __pyx_t_double_complex b) {
+ __pyx_t_double_complex z;
+ double r, lnr, theta, z_r, z_theta;
+ if (b.imag == 0 && b.real == (int)b.real) {
+ if (b.real < 0) {
+ double denom = a.real * a.real + a.imag * a.imag;
+ a.real = a.real / denom;
+ a.imag = -a.imag / denom;
+ b.real = -b.real;
+ }
+ switch ((int)b.real) {
+ case 0:
+ z.real = 1;
+ z.imag = 0;
+ return z;
+ case 1:
+ return a;
+ case 2:
+ return __Pyx_c_prod_double(a, a);
+ case 3:
+ z = __Pyx_c_prod_double(a, a);
+ return __Pyx_c_prod_double(z, a);
+ case 4:
+ z = __Pyx_c_prod_double(a, a);
+ return __Pyx_c_prod_double(z, z);
+ }
+ }
+ if (a.imag == 0) {
+ if (a.real == 0) {
+ return a;
+ } else if ((b.imag == 0) && (a.real >= 0)) {
+ z.real = pow(a.real, b.real);
+ z.imag = 0;
+ return z;
+ } else if (a.real > 0) {
+ r = a.real;
+ theta = 0;
+ } else {
+ r = -a.real;
+ theta = atan2(0.0, -1.0);
+ }
+ } else {
+ r = __Pyx_c_abs_double(a);
+ theta = atan2(a.imag, a.real);
+ }
+ lnr = log(r);
+ z_r = exp(lnr * b.real - theta * b.imag);
+ z_theta = theta * b.real + lnr * b.imag;
+ z.real = z_r * cos(z_theta);
+ z.imag = z_r * sin(z_theta);
+ return z;
+ }
+ #endif
+#endif
+
+/* FromPy */
+static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject* o) {
+#if CYTHON_COMPILING_IN_LIMITED_API
+ double real=-1.0, imag=-1.0;
+ real = PyComplex_RealAsDouble(o);
+ if (unlikely(real == -1.0 && PyErr_Occurred())) goto end;
+ imag = PyComplex_ImagAsDouble(o);
+ end:
+ return __pyx_t_double_complex_from_parts(
+ (double)real, (double)imag
+ );
+#else
+ Py_complex cval;
+#if !CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_GRAAL
+ if (PyComplex_CheckExact(o))
+ cval = ((PyComplexObject *)o)->cval;
+ else
+#endif
+ cval = PyComplex_AsCComplex(o);
+ return __pyx_t_double_complex_from_parts(
+ (double)cval.real,
+ (double)cval.imag);
+#endif
+}
+
+/* CIntFromPyVerify */
+#define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0)
+#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1)
+#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\
+ {\
+ func_type value = func_value;\
+ if (sizeof(target_type) < sizeof(func_type)) {\
+ if (unlikely(value != (func_type) (target_type) value)) {\
+ func_type zero = 0;\
+ if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\
+ return (target_type) -1;\
+ if (is_unsigned && unlikely(value < zero))\
+ goto raise_neg_overflow;\
+ else\
+ goto raise_overflow;\
+ }\
+ }\
+ return (target_type) value;\
+ }
+
+/* CIntFromPy */
+static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *x) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const int neg_one = (int) -1, const_zero = (int) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (unlikely(!PyLong_Check(x))) {
+ int val;
+ PyObject *tmp = __Pyx_PyNumber_Long(x);
+ if (!tmp) return (int) -1;
+ val = __Pyx_PyLong_As_int(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (unlikely(__Pyx_PyLong_IsNeg(x))) {
+ goto raise_neg_overflow;
+ } else if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_DigitCount(x)) {
+ case 2:
+ if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 2 * PyLong_SHIFT)) {
+ return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 3 * PyLong_SHIFT)) {
+ return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) >= 4 * PyLong_SHIFT)) {
+ return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ }
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (int) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if ((sizeof(int) <= sizeof(unsigned long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if ((sizeof(int) <= sizeof(unsigned PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_SignedDigitCount(x)) {
+ case -2:
+ if ((8 * sizeof(int) - 1 > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) {
+ return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) {
+ return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ }
+ }
+#endif
+ if ((sizeof(int) <= sizeof(long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x))
+ } else if ((sizeof(int) <= sizeof(PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+ int val;
+ int ret = -1;
+#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_ssize_t bytes_copied = PyLong_AsNativeBytes(
+ x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0));
+ if (unlikely(bytes_copied == -1)) {
+ } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) {
+ goto raise_overflow;
+ } else {
+ ret = 0;
+ }
+#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray)
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ ret = _PyLong_AsByteArray((PyLongObject *)x,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+#else
+ PyObject *v;
+ PyObject *stepval = NULL, *mask = NULL, *shift = NULL;
+ int bits, remaining_bits, is_negative = 0;
+ int chunk_size = (sizeof(long) < 8) ? 30 : 62;
+ if (likely(PyLong_CheckExact(x))) {
+ v = __Pyx_NewRef(x);
+ } else {
+ v = PyNumber_Long(x);
+ if (unlikely(!v)) return (int) -1;
+ assert(PyLong_CheckExact(v));
+ }
+ {
+ int result = PyObject_RichCompareBool(v, Py_False, Py_LT);
+ if (unlikely(result < 0)) {
+ Py_DECREF(v);
+ return (int) -1;
+ }
+ is_negative = result == 1;
+ }
+ if (is_unsigned && unlikely(is_negative)) {
+ Py_DECREF(v);
+ goto raise_neg_overflow;
+ } else if (is_negative) {
+ stepval = PyNumber_Invert(v);
+ Py_DECREF(v);
+ if (unlikely(!stepval))
+ return (int) -1;
+ } else {
+ stepval = v;
+ }
+ v = NULL;
+ val = (int) 0;
+ mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done;
+ shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done;
+ for (bits = 0; bits < (int) sizeof(int) * 8 - chunk_size; bits += chunk_size) {
+ PyObject *tmp, *digit;
+ long idigit;
+ digit = PyNumber_And(stepval, mask);
+ if (unlikely(!digit)) goto done;
+ idigit = PyLong_AsLong(digit);
+ Py_DECREF(digit);
+ if (unlikely(idigit < 0)) goto done;
+ val |= ((int) idigit) << bits;
+ tmp = PyNumber_Rshift(stepval, shift);
+ if (unlikely(!tmp)) goto done;
+ Py_DECREF(stepval); stepval = tmp;
+ }
+ Py_DECREF(shift); shift = NULL;
+ Py_DECREF(mask); mask = NULL;
+ {
+ long idigit = PyLong_AsLong(stepval);
+ if (unlikely(idigit < 0)) goto done;
+ remaining_bits = ((int) sizeof(int) * 8) - bits - (is_unsigned ? 0 : 1);
+ if (unlikely(idigit >= (1L << remaining_bits)))
+ goto raise_overflow;
+ val |= ((int) idigit) << bits;
+ }
+ if (!is_unsigned) {
+ if (unlikely(val & (((int) 1) << (sizeof(int) * 8 - 1))))
+ goto raise_overflow;
+ if (is_negative)
+ val = ~val;
+ }
+ ret = 0;
+ done:
+ Py_XDECREF(shift);
+ Py_XDECREF(mask);
+ Py_XDECREF(stepval);
+#endif
+ if (unlikely(ret))
+ return (int) -1;
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to int");
+ return (int) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to int");
+ return (int) -1;
+}
+
+/* PyObjectVectorCallKwBuilder (used by CIntToPy) */
+#if CYTHON_VECTORCALL
+static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ (void)__Pyx_PyObject_FastCallDict;
+ if (__Pyx_PyTuple_SET_ITEM(builder, n, key) != (0)) return -1;
+ Py_INCREF(key);
+ args[n] = value;
+ return 0;
+}
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ (void)__Pyx_VectorcallBuilder_AddArgStr;
+ if (unlikely(!PyUnicode_Check(key))) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ return -1;
+ }
+ return __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n);
+}
+static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
+ PyObject *pyKey = PyUnicode_FromString(key);
+ if (!pyKey) return -1;
+ return __Pyx_VectorcallBuilder_AddArg(pyKey, value, builder, args, n);
+}
+#else // CYTHON_VECTORCALL
+CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, CYTHON_UNUSED PyObject **args, CYTHON_UNUSED int n) {
+ if (unlikely(!PyUnicode_Check(key))) {
+ PyErr_SetString(PyExc_TypeError, "keywords must be strings");
+ return -1;
+ }
+ return PyDict_SetItem(builder, key, value);
+}
+#endif
+
+/* CIntToPy */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_int(int value) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const int neg_one = (int) -1, const_zero = (int) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (is_unsigned) {
+ if (sizeof(int) < sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(int) <= sizeof(unsigned long)) {
+ return PyLong_FromUnsignedLong((unsigned long) value);
+#if !CYTHON_COMPILING_IN_PYPY
+ } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) {
+ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
+#endif
+ }
+ } else {
+ if (sizeof(int) <= sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) {
+ return PyLong_FromLongLong((PY_LONG_LONG) value);
+ }
+ }
+ {
+ unsigned char *bytes = (unsigned char *)&value;
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4
+ if (is_unsigned) {
+ return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1);
+ } else {
+ return PyLong_FromNativeBytes(bytes, sizeof(value), -1);
+ }
+#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ return _PyLong_FromByteArray(bytes, sizeof(int),
+ little, !is_unsigned);
+#else
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ PyObject *from_bytes, *result = NULL, *kwds = NULL;
+ PyObject *py_bytes = NULL, *order_str = NULL;
+ from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes");
+ if (!from_bytes) return NULL;
+ py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(int));
+ if (!py_bytes) goto limited_bad;
+ order_str = PyUnicode_FromString(little ? "little" : "big");
+ if (!order_str) goto limited_bad;
+ {
+ PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str };
+ if (!is_unsigned) {
+ kwds = __Pyx_MakeVectorcallBuilderKwds(1);
+ if (!kwds) goto limited_bad;
+ if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad;
+ }
+ result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds);
+ }
+ limited_bad:
+ Py_XDECREF(kwds);
+ Py_XDECREF(order_str);
+ Py_XDECREF(py_bytes);
+ Py_XDECREF(from_bytes);
+ return result;
+#endif
+ }
+}
+
+/* CIntToPy */
+static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const long neg_one = (long) -1, const_zero = (long) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (is_unsigned) {
+ if (sizeof(long) < sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(unsigned long)) {
+ return PyLong_FromUnsignedLong((unsigned long) value);
+#if !CYTHON_COMPILING_IN_PYPY
+ } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
+ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
+#endif
+ }
+ } else {
+ if (sizeof(long) <= sizeof(long)) {
+ return PyLong_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
+ return PyLong_FromLongLong((PY_LONG_LONG) value);
+ }
+ }
+ {
+ unsigned char *bytes = (unsigned char *)&value;
+#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4
+ if (is_unsigned) {
+ return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1);
+ } else {
+ return PyLong_FromNativeBytes(bytes, sizeof(value), -1);
+ }
+#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ return _PyLong_FromByteArray(bytes, sizeof(long),
+ little, !is_unsigned);
+#else
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ PyObject *from_bytes, *result = NULL, *kwds = NULL;
+ PyObject *py_bytes = NULL, *order_str = NULL;
+ from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes");
+ if (!from_bytes) return NULL;
+ py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(long));
+ if (!py_bytes) goto limited_bad;
+ order_str = PyUnicode_FromString(little ? "little" : "big");
+ if (!order_str) goto limited_bad;
+ {
+ PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str };
+ if (!is_unsigned) {
+ kwds = __Pyx_MakeVectorcallBuilderKwds(1);
+ if (!kwds) goto limited_bad;
+ if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad;
+ }
+ result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds);
+ }
+ limited_bad:
+ Py_XDECREF(kwds);
+ Py_XDECREF(order_str);
+ Py_XDECREF(py_bytes);
+ Py_XDECREF(from_bytes);
+ return result;
+#endif
+ }
+}
+
+/* FormatTypeName */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030d0000
+static __Pyx_TypeName
+__Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp)
+{
+ PyObject *module = NULL, *name = NULL, *result = NULL;
+ #if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+ name = __Pyx_PyObject_GetAttrStr((PyObject *)tp,
+ __pyx_mstate_global->__pyx_n_u_qualname);
+ #else
+ name = PyType_GetQualName(tp);
+ #endif
+ if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) goto bad;
+ module = __Pyx_PyObject_GetAttrStr((PyObject *)tp,
+ __pyx_mstate_global->__pyx_n_u_module);
+ if (unlikely(module == NULL) || unlikely(!PyUnicode_Check(module))) goto bad;
+ if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) {
+ result = name;
+ name = NULL;
+ goto done;
+ }
+ result = PyUnicode_FromFormat("%U.%U", module, name);
+ if (unlikely(result == NULL)) goto bad;
+ done:
+ Py_XDECREF(name);
+ Py_XDECREF(module);
+ return result;
+ bad:
+ PyErr_Clear();
+ if (name) {
+ result = name;
+ name = NULL;
+ } else {
+ result = __Pyx_NewRef(__pyx_mstate_global->__pyx_kp_u__2);
+ }
+ goto done;
+}
+#endif
+
+/* CIntFromPy */
+static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *x) {
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ const long neg_one = (long) -1, const_zero = (long) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+ const int is_unsigned = neg_one > const_zero;
+ if (unlikely(!PyLong_Check(x))) {
+ long val;
+ PyObject *tmp = __Pyx_PyNumber_Long(x);
+ if (!tmp) return (long) -1;
+ val = __Pyx_PyLong_As_long(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (unlikely(__Pyx_PyLong_IsNeg(x))) {
+ goto raise_neg_overflow;
+ } else if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_DigitCount(x)) {
+ case 2:
+ if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 2 * PyLong_SHIFT)) {
+ return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 3 * PyLong_SHIFT)) {
+ return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) >= 4 * PyLong_SHIFT)) {
+ return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ }
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (long) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if ((sizeof(long) <= sizeof(unsigned long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if ((sizeof(long) <= sizeof(unsigned PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ if (__Pyx_PyLong_IsCompact(x)) {
+ __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x))
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(x);
+ assert(__Pyx_PyLong_DigitCount(x) > 1);
+ switch (__Pyx_PyLong_SignedDigitCount(x)) {
+ case -2:
+ if ((8 * sizeof(long) - 1 > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) {
+ return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) {
+ if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) {
+ return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ }
+ }
+#endif
+ if ((sizeof(long) <= sizeof(long))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x))
+ } else if ((sizeof(long) <= sizeof(PY_LONG_LONG))) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+ long val;
+ int ret = -1;
+#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_ssize_t bytes_copied = PyLong_AsNativeBytes(
+ x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0));
+ if (unlikely(bytes_copied == -1)) {
+ } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) {
+ goto raise_overflow;
+ } else {
+ ret = 0;
+ }
+#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray)
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ ret = _PyLong_AsByteArray((PyLongObject *)x,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+#else
+ PyObject *v;
+ PyObject *stepval = NULL, *mask = NULL, *shift = NULL;
+ int bits, remaining_bits, is_negative = 0;
+ int chunk_size = (sizeof(long) < 8) ? 30 : 62;
+ if (likely(PyLong_CheckExact(x))) {
+ v = __Pyx_NewRef(x);
+ } else {
+ v = PyNumber_Long(x);
+ if (unlikely(!v)) return (long) -1;
+ assert(PyLong_CheckExact(v));
+ }
+ {
+ int result = PyObject_RichCompareBool(v, Py_False, Py_LT);
+ if (unlikely(result < 0)) {
+ Py_DECREF(v);
+ return (long) -1;
+ }
+ is_negative = result == 1;
+ }
+ if (is_unsigned && unlikely(is_negative)) {
+ Py_DECREF(v);
+ goto raise_neg_overflow;
+ } else if (is_negative) {
+ stepval = PyNumber_Invert(v);
+ Py_DECREF(v);
+ if (unlikely(!stepval))
+ return (long) -1;
+ } else {
+ stepval = v;
+ }
+ v = NULL;
+ val = (long) 0;
+ mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done;
+ shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done;
+ for (bits = 0; bits < (int) sizeof(long) * 8 - chunk_size; bits += chunk_size) {
+ PyObject *tmp, *digit;
+ long idigit;
+ digit = PyNumber_And(stepval, mask);
+ if (unlikely(!digit)) goto done;
+ idigit = PyLong_AsLong(digit);
+ Py_DECREF(digit);
+ if (unlikely(idigit < 0)) goto done;
+ val |= ((long) idigit) << bits;
+ tmp = PyNumber_Rshift(stepval, shift);
+ if (unlikely(!tmp)) goto done;
+ Py_DECREF(stepval); stepval = tmp;
+ }
+ Py_DECREF(shift); shift = NULL;
+ Py_DECREF(mask); mask = NULL;
+ {
+ long idigit = PyLong_AsLong(stepval);
+ if (unlikely(idigit < 0)) goto done;
+ remaining_bits = ((int) sizeof(long) * 8) - bits - (is_unsigned ? 0 : 1);
+ if (unlikely(idigit >= (1L << remaining_bits)))
+ goto raise_overflow;
+ val |= ((long) idigit) << bits;
+ }
+ if (!is_unsigned) {
+ if (unlikely(val & (((long) 1) << (sizeof(long) * 8 - 1))))
+ goto raise_overflow;
+ if (is_negative)
+ val = ~val;
+ }
+ ret = 0;
+ done:
+ Py_XDECREF(shift);
+ Py_XDECREF(mask);
+ Py_XDECREF(stepval);
+#endif
+ if (unlikely(ret))
+ return (long) -1;
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to long");
+ return (long) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to long");
+ return (long) -1;
+}
+
+/* FastTypeChecks */
+#if CYTHON_COMPILING_IN_CPYTHON
+static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) {
+ while (a) {
+ a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*);
+ if (a == b)
+ return 1;
+ }
+ return b == &PyBaseObject_Type;
+}
+static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (a == b) return 1;
+ mro = a->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ return __Pyx_InBases(a, b);
+}
+static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) {
+ PyObject *mro;
+ if (cls == a || cls == b) return 1;
+ mro = cls->tp_mro;
+ if (likely(mro)) {
+ Py_ssize_t i, n;
+ n = PyTuple_GET_SIZE(mro);
+ for (i = 0; i < n; i++) {
+ PyObject *base = PyTuple_GET_ITEM(mro, i);
+ if (base == (PyObject *)a || base == (PyObject *)b)
+ return 1;
+ }
+ return 0;
+ }
+ return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b);
+}
+static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) {
+ if (exc_type1) {
+ return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2);
+ } else {
+ return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2);
+ }
+}
+static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) {
+ Py_ssize_t i, n;
+ assert(PyExceptionClass_Check(exc_type));
+ n = PyTuple_GET_SIZE(tuple);
+ for (i=0; i>= 8;
+ ++i;
+ }
+ __Pyx_cached_runtime_version = version;
+ }
+}
+#endif
+static unsigned long __Pyx_get_runtime_version(void) {
+#if __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ return Py_Version & ~0xFFUL;
+#else
+ return __Pyx_cached_runtime_version;
+#endif
+}
+
+/* SwapException (used by CoroutineBase) */
+#if CYTHON_FAST_THREAD_STATE
+static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_value = exc_info->exc_value;
+ exc_info->exc_value = *value;
+ if (tmp_value == NULL || tmp_value == Py_None) {
+ Py_XDECREF(tmp_value);
+ tmp_value = NULL;
+ tmp_type = NULL;
+ tmp_tb = NULL;
+ } else {
+ tmp_type = (PyObject*) Py_TYPE(tmp_value);
+ Py_INCREF(tmp_type);
+ #if CYTHON_COMPILING_IN_CPYTHON
+ tmp_tb = ((PyBaseExceptionObject*) tmp_value)->traceback;
+ Py_XINCREF(tmp_tb);
+ #else
+ tmp_tb = PyException_GetTraceback(tmp_value);
+ #endif
+ }
+ #elif CYTHON_USE_EXC_INFO_STACK
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ tmp_type = exc_info->exc_type;
+ tmp_value = exc_info->exc_value;
+ tmp_tb = exc_info->exc_traceback;
+ exc_info->exc_type = *type;
+ exc_info->exc_value = *value;
+ exc_info->exc_traceback = *tb;
+ #else
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ tstate->exc_type = *type;
+ tstate->exc_value = *value;
+ tstate->exc_traceback = *tb;
+ #endif
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+#else
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyErr_GetExcInfo(&tmp_type, &tmp_value, &tmp_tb);
+ PyErr_SetExcInfo(*type, *value, *tb);
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+#endif
+
+/* IterNextPlain (used by CoroutineBase) */
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void) {
+ if (unlikely(!__pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache))
+ __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache = __Pyx_GetBuiltinName(__pyx_mstate_global->__pyx_n_u_next);
+ return __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache;
+}
+#endif
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator) {
+#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ PyObject *result;
+ PyObject *next = __Pyx_GetBuiltinNext_LimitedAPI();
+ if (unlikely(!next)) return NULL;
+ result = PyObject_CallFunctionObjArgs(next, iterator, NULL);
+ return result;
+#else
+ (void)__Pyx_GetBuiltinName; // only for early limited API
+ iternextfunc iternext = __Pyx_PyObject_GetIterNextFunc(iterator);
+ assert(iternext);
+ return iternext(iterator);
+#endif
+}
+
+/* PyObjectCall2Args (used by PyObjectCallMethod1) */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) {
+ PyObject *args[3] = {NULL, arg1, arg2};
+ return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+}
+
+/* PyObjectCallMethod1 (used by CoroutineBase) */
+#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
+static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
+ PyObject *result = __Pyx_PyObject_CallOneArg(method, arg);
+ Py_DECREF(method);
+ return result;
+}
+#endif
+static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
+#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
+ PyObject *args[2] = {obj, arg};
+ (void) __Pyx_PyObject_CallOneArg;
+ (void) __Pyx_PyObject_Call2Args;
+ return PyObject_VectorcallMethod(method_name, args, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+#else
+ PyObject *method = NULL, *result;
+ int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
+ if (likely(is_method)) {
+ result = __Pyx_PyObject_Call2Args(method, obj, arg);
+ Py_DECREF(method);
+ return result;
+ }
+ if (unlikely(!method)) return NULL;
+ return __Pyx__PyObject_CallMethod1(method, arg);
+#endif
+}
+
+/* ReturnWithStopIteration (used by CoroutineBase) */
+static void __Pyx__ReturnWithStopIteration(PyObject* value, int async);
+static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext) {
+ if (value == Py_None) {
+ if (async || !iternext)
+ PyErr_SetNone(async ? PyExc_StopAsyncIteration : PyExc_StopIteration);
+ return;
+ }
+ __Pyx__ReturnWithStopIteration(value, async);
+}
+static void __Pyx__ReturnWithStopIteration(PyObject* value, int async) {
+#if CYTHON_COMPILING_IN_CPYTHON
+ __Pyx_PyThreadState_declare
+#endif
+ PyObject *exc;
+ PyObject *exc_type = async ? PyExc_StopAsyncIteration : PyExc_StopIteration;
+#if CYTHON_COMPILING_IN_CPYTHON
+ if ((PY_VERSION_HEX >= (0x030C00A6)) || unlikely(PyTuple_Check(value) || PyExceptionInstance_Check(value))) {
+ if (PY_VERSION_HEX >= (0x030e00A1)) {
+ exc = __Pyx_PyObject_CallOneArg(exc_type, value);
+ } else {
+ PyObject *args_tuple = PyTuple_New(1);
+ if (unlikely(!args_tuple)) return;
+ Py_INCREF(value);
+ PyTuple_SET_ITEM(args_tuple, 0, value);
+ exc = PyObject_Call(exc_type, args_tuple, NULL);
+ Py_DECREF(args_tuple);
+ }
+ if (unlikely(!exc)) return;
+ } else {
+ Py_INCREF(value);
+ exc = value;
+ }
+ #if CYTHON_FAST_THREAD_STATE
+ __Pyx_PyThreadState_assign
+ #if CYTHON_USE_EXC_INFO_STACK
+ if (!__pyx_tstate->exc_info->exc_value)
+ #else
+ if (!__pyx_tstate->exc_type)
+ #endif
+ {
+ Py_INCREF(exc_type);
+ __Pyx_ErrRestore(exc_type, exc, NULL);
+ return;
+ }
+ #endif
+#else
+ exc = __Pyx_PyObject_CallOneArg(exc_type, value);
+ if (unlikely(!exc)) return;
+#endif
+ PyErr_SetObject(exc_type, exc);
+ Py_DECREF(exc);
+}
+
+/* CoroutineBase (used by Generator) */
+#if !CYTHON_COMPILING_IN_LIMITED_API
+#include
+#if PY_VERSION_HEX >= 0x030b00a6 && !defined(PYPY_VERSION)
+ #ifndef Py_BUILD_CORE
+ #define Py_BUILD_CORE 1
+ #endif
+ #include "internal/pycore_frame.h"
+#endif
+#endif // CYTHON_COMPILING_IN_LIMITED_API
+static CYTHON_INLINE void
+__Pyx_Coroutine_Undelegate(__pyx_CoroutineObject *gen) {
+#if CYTHON_USE_AM_SEND
+ gen->yieldfrom_am_send = NULL;
+#endif
+ Py_CLEAR(gen->yieldfrom);
+}
+static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *__pyx_tstate, PyObject **pvalue) {
+ PyObject *et, *ev, *tb;
+ PyObject *value = NULL;
+ CYTHON_UNUSED_VAR(__pyx_tstate);
+ __Pyx_ErrFetch(&et, &ev, &tb);
+ if (!et) {
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ Py_INCREF(Py_None);
+ *pvalue = Py_None;
+ return 0;
+ }
+ if (likely(et == PyExc_StopIteration)) {
+ if (!ev) {
+ Py_INCREF(Py_None);
+ value = Py_None;
+ }
+ else if (likely(__Pyx_IS_TYPE(ev, (PyTypeObject*)PyExc_StopIteration))) {
+ #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL
+ value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value);
+ if (unlikely(!value)) goto limited_api_failure;
+ #else
+ value = ((PyStopIterationObject *)ev)->value;
+ Py_INCREF(value);
+ #endif
+ Py_DECREF(ev);
+ }
+ else if (unlikely(PyTuple_Check(ev))) {
+ Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(ev);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(tuple_size < 0)) {
+ Py_XDECREF(tb);
+ Py_DECREF(ev);
+ Py_DECREF(et);
+ return -1;
+ }
+ #endif
+ if (tuple_size >= 1) {
+#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
+ value = PyTuple_GET_ITEM(ev, 0);
+ Py_INCREF(value);
+#elif CYTHON_ASSUME_SAFE_MACROS
+ value = PySequence_ITEM(ev, 0);
+#else
+ value = PySequence_GetItem(ev, 0);
+ if (!value) goto limited_api_failure;
+#endif
+ } else {
+ Py_INCREF(Py_None);
+ value = Py_None;
+ }
+ Py_DECREF(ev);
+ }
+ else if (!__Pyx_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) {
+ value = ev;
+ }
+ if (likely(value)) {
+ Py_XDECREF(tb);
+ Py_DECREF(et);
+ *pvalue = value;
+ return 0;
+ }
+ } else if (!__Pyx_PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) {
+ __Pyx_ErrRestore(et, ev, tb);
+ return -1;
+ }
+ PyErr_NormalizeException(&et, &ev, &tb);
+ if (unlikely(!PyObject_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration))) {
+ __Pyx_ErrRestore(et, ev, tb);
+ return -1;
+ }
+ Py_XDECREF(tb);
+ Py_DECREF(et);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value);
+#else
+ value = ((PyStopIterationObject *)ev)->value;
+ Py_INCREF(value);
+#endif
+ Py_DECREF(ev);
+#if CYTHON_COMPILING_IN_LIMITED_API
+ if (unlikely(!value)) return -1;
+#endif
+ *pvalue = value;
+ return 0;
+#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL || !CYTHON_ASSUME_SAFE_MACROS
+ limited_api_failure:
+ Py_XDECREF(et);
+ Py_XDECREF(tb);
+ Py_XDECREF(ev);
+ return -1;
+#endif
+}
+static CYTHON_INLINE
+__Pyx_PySendResult __Pyx_Coroutine_status_from_result(PyObject **retval) {
+ if (*retval) {
+ return PYGEN_NEXT;
+ } else if (likely(__Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, retval) == 0)) {
+ return PYGEN_RETURN;
+ } else {
+ return PYGEN_ERROR;
+ }
+}
+static CYTHON_INLINE
+void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *exc_state) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_CLEAR(exc_state->exc_value);
+#else
+ PyObject *t, *v, *tb;
+ t = exc_state->exc_type;
+ v = exc_state->exc_value;
+ tb = exc_state->exc_traceback;
+ exc_state->exc_type = NULL;
+ exc_state->exc_value = NULL;
+ exc_state->exc_traceback = NULL;
+ Py_XDECREF(t);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+#endif
+}
+#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL)
+static void __Pyx__Coroutine_AlreadyRunningError(__pyx_CoroutineObject *gen) {
+ const char *msg;
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check((PyObject*)gen)) {
+ msg = "coroutine already executing";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact((PyObject*)gen)) {
+ msg = "async generator already executing";
+ #endif
+ } else {
+ msg = "generator already executing";
+ }
+ PyErr_SetString(PyExc_ValueError, msg);
+}
+static void __Pyx_Coroutine_AlreadyTerminatedError(PyObject *gen, PyObject *value, int closing) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ CYTHON_MAYBE_UNUSED_VAR(closing);
+ #ifdef __Pyx_Coroutine_USED
+ if (!closing && __Pyx_Coroutine_Check(gen)) {
+ PyErr_SetString(PyExc_RuntimeError, "cannot reuse already awaited coroutine");
+ } else
+ #endif
+ if (value) {
+ #ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(gen))
+ PyErr_SetNone(PyExc_StopAsyncIteration);
+ else
+ #endif
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+}
+static
+__Pyx_PySendResult __Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, PyObject **result, int closing) {
+ __Pyx_PyThreadState_declare
+ PyThreadState *tstate;
+ __Pyx_ExcInfoStruct *exc_state;
+ PyObject *retval;
+ assert(__Pyx_Coroutine_get_is_running(self)); // Callers should ensure is_running
+ if (unlikely(self->resume_label == -1)) {
+ __Pyx_Coroutine_AlreadyTerminatedError((PyObject*)self, value, closing);
+ return PYGEN_ERROR;
+ }
+#if CYTHON_FAST_THREAD_STATE
+ __Pyx_PyThreadState_assign
+ tstate = __pyx_tstate;
+#else
+ tstate = __Pyx_PyThreadState_Current;
+#endif
+ exc_state = &self->gi_exc_state;
+ if (exc_state->exc_value) {
+ #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY
+ #else
+ PyObject *exc_tb;
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #elif PY_VERSION_HEX >= 0x030B00a4
+ exc_tb = ((PyBaseExceptionObject*) exc_state->exc_value)->traceback;
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+ if (exc_tb) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
+ PyFrameObject *f = tb->tb_frame;
+ assert(f->f_back == NULL);
+ #if PY_VERSION_HEX >= 0x030B00A1
+ f->f_back = PyThreadState_GetFrame(tstate);
+ #else
+ Py_XINCREF(tstate->frame);
+ f->f_back = tstate->frame;
+ #endif
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON
+ Py_DECREF(exc_tb);
+ #endif
+ }
+ #endif
+ }
+#if CYTHON_USE_EXC_INFO_STACK
+ exc_state->previous_item = tstate->exc_info;
+ tstate->exc_info = exc_state;
+#else
+ if (exc_state->exc_type) {
+ __Pyx_ExceptionSwap(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
+ } else {
+ __Pyx_Coroutine_ExceptionClear(exc_state);
+ __Pyx_ExceptionSave(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback);
+ }
+#endif
+ retval = self->body(self, tstate, value);
+#if CYTHON_USE_EXC_INFO_STACK
+ exc_state = &self->gi_exc_state;
+ tstate->exc_info = exc_state->previous_item;
+ exc_state->previous_item = NULL;
+ __Pyx_Coroutine_ResetFrameBackpointer(exc_state);
+#endif
+ *result = retval;
+ if (self->resume_label == -1) {
+ return likely(retval) ? PYGEN_RETURN : PYGEN_ERROR;
+ }
+ return PYGEN_NEXT;
+}
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state) {
+#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API
+ CYTHON_UNUSED_VAR(exc_state);
+#else
+ PyObject *exc_tb;
+ #if PY_VERSION_HEX >= 0x030B00a4
+ if (!exc_state->exc_value) return;
+ exc_tb = PyException_GetTraceback(exc_state->exc_value);
+ #else
+ exc_tb = exc_state->exc_traceback;
+ #endif
+ if (likely(exc_tb)) {
+ PyTracebackObject *tb = (PyTracebackObject *) exc_tb;
+ PyFrameObject *f = tb->tb_frame;
+ Py_CLEAR(f->f_back);
+ #if PY_VERSION_HEX >= 0x030B00a4
+ Py_DECREF(exc_tb);
+ #endif
+ }
+#endif
+}
+#define __Pyx_Coroutine_MethodReturnFromResult(gen, result, retval, iternext)\
+ ((result) == PYGEN_NEXT ? (retval) : __Pyx__Coroutine_MethodReturnFromResult(gen, result, retval, iternext))
+static PyObject *
+__Pyx__Coroutine_MethodReturnFromResult(PyObject* gen, __Pyx_PySendResult result, PyObject *retval, int iternext) {
+ CYTHON_MAYBE_UNUSED_VAR(gen);
+ if (likely(result == PYGEN_RETURN)) {
+ int is_async = 0;
+ #ifdef __Pyx_AsyncGen_USED
+ is_async = __Pyx_AsyncGen_CheckExact(gen);
+ #endif
+ __Pyx_ReturnWithStopIteration(retval, is_async, iternext);
+ Py_XDECREF(retval);
+ }
+ return NULL;
+}
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE
+PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) {
+#if PY_VERSION_HEX <= 0x030A00A1
+ return _PyGen_Send(gen, arg);
+#else
+ PyObject *result;
+ if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) {
+ if (PyAsyncGen_CheckExact(gen)) {
+ assert(result == Py_None);
+ PyErr_SetNone(PyExc_StopAsyncIteration);
+ }
+ else if (result == Py_None) {
+ PyErr_SetNone(PyExc_StopIteration);
+ }
+ else {
+#if PY_VERSION_HEX < 0x030d00A1
+ _PyGen_SetStopIterationValue(result);
+#else
+ if (!PyTuple_Check(result) && !PyExceptionInstance_Check(result)) {
+ PyErr_SetObject(PyExc_StopIteration, result);
+ } else {
+ PyObject *exc = __Pyx_PyObject_CallOneArg(PyExc_StopIteration, result);
+ if (likely(exc != NULL)) {
+ PyErr_SetObject(PyExc_StopIteration, exc);
+ Py_DECREF(exc);
+ }
+ }
+#endif
+ }
+ Py_DECREF(result);
+ result = NULL;
+ }
+ return result;
+#endif
+}
+#endif
+static CYTHON_INLINE __Pyx_PySendResult
+__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen, PyObject** retval) {
+ __Pyx_PySendResult result;
+ PyObject *val = NULL;
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ __Pyx_Coroutine_Undelegate(gen);
+ __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, &val);
+ result = __Pyx_Coroutine_SendEx(gen, val, retval, 0);
+ Py_XDECREF(val);
+ return result;
+}
+#if CYTHON_USE_AM_SEND
+static __Pyx_PySendResult
+__Pyx_Coroutine_SendToDelegate(__pyx_CoroutineObject *gen, __Pyx_pyiter_sendfunc gen_am_send, PyObject *value, PyObject **retval) {
+ PyObject *ret = NULL;
+ __Pyx_PySendResult delegate_result, result;
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ delegate_result = gen_am_send(gen->yieldfrom, value, &ret);
+ if (delegate_result == PYGEN_NEXT) {
+ assert (ret != NULL);
+ *retval = ret;
+ return PYGEN_NEXT;
+ }
+ assert (delegate_result != PYGEN_ERROR || ret == NULL);
+ __Pyx_Coroutine_Undelegate(gen);
+ result = __Pyx_Coroutine_SendEx(gen, ret, retval, 0);
+ Py_XDECREF(ret);
+ return result;
+}
+#endif
+static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_AmSend(self, value, &retval);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0);
+}
+static __Pyx_PySendResult
+__Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval) {
+ __Pyx_PySendResult result;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ *retval = __Pyx_Coroutine_AlreadyRunningError(gen);
+ return PYGEN_ERROR;
+ }
+ #if CYTHON_USE_AM_SEND
+ if (gen->yieldfrom_am_send) {
+ result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, value, retval);
+ } else
+ #endif
+ if (gen->yieldfrom) {
+ PyObject *yf = gen->yieldfrom;
+ PyObject *ret;
+ #if !CYTHON_USE_AM_SEND
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, value);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, value);
+ } else
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ ret = __Pyx_async_gen_asend_send(yf, value);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ if (PyCoro_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+ } else
+ #endif
+ #endif
+ {
+ #if !CYTHON_COMPILING_IN_LIMITED_API || __PYX_LIMITED_VERSION_HEX >= 0x03080000
+ if (value == Py_None && PyIter_Check(yf))
+ ret = __Pyx_PyIter_Next_Plain(yf);
+ else
+ #endif
+ ret = __Pyx_PyObject_CallMethod1(yf, __pyx_mstate_global->__pyx_n_u_send, value);
+ }
+ if (likely(ret)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ *retval = ret;
+ return PYGEN_NEXT;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, retval);
+ } else {
+ result = __Pyx_Coroutine_SendEx(gen, value, retval, 0);
+ }
+ __Pyx_Coroutine_unset_is_running(gen);
+ return result;
+}
+static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) {
+ __Pyx_PySendResult result;
+ PyObject *retval = NULL;
+ CYTHON_UNUSED_VAR(gen);
+ assert(__Pyx_Coroutine_get_is_running(gen));
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ result = __Pyx_Coroutine_Close(yf, &retval);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_Check(yf)) {
+ result = __Pyx_Coroutine_Close(yf, &retval);
+ } else
+ if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ result = __Pyx_CoroutineAwait_Close((__pyx_CoroutineAwaitObject*)yf);
+ } else
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ if (__pyx_PyAsyncGenASend_CheckExact(yf)) {
+ retval = __Pyx_async_gen_asend_close(yf, NULL);
+ result = PYGEN_RETURN;
+ } else
+ if (__pyx_PyAsyncGenAThrow_CheckExact(yf)) {
+ retval = __Pyx_async_gen_athrow_close(yf, NULL);
+ result = PYGEN_RETURN;
+ } else
+ #endif
+ {
+ PyObject *meth;
+ result = PYGEN_RETURN;
+ meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_close);
+ if (unlikely(!meth)) {
+ if (unlikely(PyErr_Occurred())) {
+ PyErr_WriteUnraisable(yf);
+ }
+ } else {
+ retval = __Pyx_PyObject_CallNoArg(meth);
+ Py_DECREF(meth);
+ if (unlikely(!retval)) {
+ result = PYGEN_ERROR;
+ }
+ }
+ }
+ Py_XDECREF(retval);
+ return result == PYGEN_ERROR ? -1 : 0;
+}
+static PyObject *__Pyx_Generator_Next(PyObject *self) {
+ __Pyx_PySendResult result;
+ PyObject *retval = NULL;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ }
+ #if CYTHON_USE_AM_SEND
+ if (gen->yieldfrom_am_send) {
+ result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, Py_None, &retval);
+ } else
+ #endif
+ if (gen->yieldfrom) {
+ PyObject *yf = gen->yieldfrom;
+ PyObject *ret;
+ #ifdef __Pyx_Generator_USED
+ if (__Pyx_Generator_CheckExact(yf)) {
+ ret = __Pyx_Generator_Next(yf);
+ } else
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__Pyx_Coroutine_CheckExact(yf)) {
+ ret = __Pyx_Coroutine_Send(yf, Py_None);
+ } else
+ #endif
+ #if CYTHON_COMPILING_IN_CPYTHON && (PY_VERSION_HEX < 0x030A00A3 || !CYTHON_USE_AM_SEND)
+ if (PyGen_CheckExact(yf)) {
+ ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL);
+ } else
+ #endif
+ ret = __Pyx_PyIter_Next_Plain(yf);
+ if (likely(ret)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return ret;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, &retval);
+ } else {
+ result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0);
+ }
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 1);
+}
+static PyObject *__Pyx_Coroutine_Close_Method(PyObject *self, PyObject *arg) {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result;
+ CYTHON_UNUSED_VAR(arg);
+ result = __Pyx_Coroutine_Close(self, &retval);
+ if (unlikely(result == PYGEN_ERROR))
+ return NULL;
+ Py_XDECREF(retval);
+ Py_RETURN_NONE;
+}
+static __Pyx_PySendResult
+__Pyx_Coroutine_Close(PyObject *self, PyObject **retval) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ __Pyx_PySendResult result;
+ PyObject *yf;
+ int err = 0;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ *retval = __Pyx_Coroutine_AlreadyRunningError(gen);
+ return PYGEN_ERROR;
+ }
+ yf = gen->yieldfrom;
+ if (yf) {
+ Py_INCREF(yf);
+ err = __Pyx_Coroutine_CloseIter(gen, yf);
+ __Pyx_Coroutine_Undelegate(gen);
+ Py_DECREF(yf);
+ }
+ if (err == 0)
+ PyErr_SetNone(PyExc_GeneratorExit);
+ result = __Pyx_Coroutine_SendEx(gen, NULL, retval, 1);
+ if (result == PYGEN_ERROR) {
+ __Pyx_PyThreadState_declare
+ __Pyx_PyThreadState_assign
+ __Pyx_Coroutine_unset_is_running(gen);
+ if (!__Pyx_PyErr_Occurred()) {
+ return PYGEN_RETURN;
+ } else if (likely(__Pyx_PyErr_ExceptionMatches2(PyExc_GeneratorExit, PyExc_StopIteration))) {
+ __Pyx_PyErr_Clear();
+ return PYGEN_RETURN;
+ }
+ return PYGEN_ERROR;
+ } else if (likely(result == PYGEN_RETURN && *retval == Py_None)) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return PYGEN_RETURN;
+ } else {
+ const char *msg;
+ Py_DECREF(*retval);
+ *retval = NULL;
+ if ((0)) {
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_Coroutine_Check(self)) {
+ msg = "coroutine ignored GeneratorExit";
+ #endif
+ #ifdef __Pyx_AsyncGen_USED
+ } else if (__Pyx_AsyncGen_CheckExact(self)) {
+ msg = "async generator ignored GeneratorExit";
+ #endif
+ } else {
+ msg = "generator ignored GeneratorExit";
+ }
+ PyErr_SetString(PyExc_RuntimeError, msg);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return PYGEN_ERROR;
+ }
+}
+static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject *val, PyObject *tb,
+ PyObject *args, int close_on_genexit) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ PyObject *yf;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen)))
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ yf = gen->yieldfrom;
+ if (yf) {
+ __Pyx_PySendResult result;
+ PyObject *ret;
+ Py_INCREF(yf);
+ if (__Pyx_PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && close_on_genexit) {
+ int err = __Pyx_Coroutine_CloseIter(gen, yf);
+ Py_DECREF(yf);
+ __Pyx_Coroutine_Undelegate(gen);
+ if (err < 0)
+ goto propagate_exception;
+ goto throw_here;
+ }
+ if (0
+ #ifdef __Pyx_Generator_USED
+ || __Pyx_Generator_CheckExact(yf)
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ || __Pyx_Coroutine_Check(yf)
+ #endif
+ ) {
+ ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit);
+ #ifdef __Pyx_Coroutine_USED
+ } else if (__Pyx_CoroutineAwait_CheckExact(yf)) {
+ ret = __Pyx__Coroutine_Throw(((__pyx_CoroutineAwaitObject*)yf)->coroutine, typ, val, tb, args, close_on_genexit);
+ #endif
+ } else {
+ PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_throw);
+ if (unlikely(!meth)) {
+ Py_DECREF(yf);
+ if (unlikely(PyErr_Occurred())) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return NULL;
+ }
+ __Pyx_Coroutine_Undelegate(gen);
+ goto throw_here;
+ }
+ if (likely(args)) {
+ ret = __Pyx_PyObject_Call(meth, args, NULL);
+ } else {
+ PyObject *cargs[4] = {NULL, typ, val, tb};
+ ret = __Pyx_PyObject_FastCall(meth, cargs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET);
+ }
+ Py_DECREF(meth);
+ }
+ Py_DECREF(yf);
+ if (ret) {
+ __Pyx_Coroutine_unset_is_running(gen);
+ return ret;
+ }
+ result = __Pyx_Coroutine_FinishDelegation(gen, &ret);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, ret, 0);
+ }
+throw_here:
+ __Pyx_Raise(typ, val, tb, NULL);
+propagate_exception:
+ {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, NULL, &retval, 0);
+ __Pyx_Coroutine_unset_is_running(gen);
+ return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0);
+ }
+}
+static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) {
+ PyObject *typ;
+ PyObject *val = NULL;
+ PyObject *tb = NULL;
+ if (unlikely(!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)))
+ return NULL;
+ return __Pyx__Coroutine_Throw(self, typ, val, tb, args, 1);
+}
+static CYTHON_INLINE int __Pyx_Coroutine_traverse_excstate(__Pyx_ExcInfoStruct *exc_state, visitproc visit, void *arg) {
+#if PY_VERSION_HEX >= 0x030B00a4
+ Py_VISIT(exc_state->exc_value);
+#else
+ Py_VISIT(exc_state->exc_type);
+ Py_VISIT(exc_state->exc_value);
+ Py_VISIT(exc_state->exc_traceback);
+#endif
+ return 0;
+}
+static int __Pyx_Coroutine_traverse(__pyx_CoroutineObject *gen, visitproc visit, void *arg) {
+ {
+ int e = __Pyx_call_type_traverse((PyObject*)gen, 1, visit, arg);
+ if (e) return e;
+ }
+ Py_VISIT(gen->closure);
+ Py_VISIT(gen->classobj);
+ Py_VISIT(gen->yieldfrom);
+ return __Pyx_Coroutine_traverse_excstate(&gen->gi_exc_state, visit, arg);
+}
+static int __Pyx_Coroutine_clear(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ Py_CLEAR(gen->closure);
+ Py_CLEAR(gen->classobj);
+ __Pyx_Coroutine_Undelegate(gen);
+ __Pyx_Coroutine_ExceptionClear(&gen->gi_exc_state);
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)gen)->ag_finalizer);
+ }
+#endif
+ Py_CLEAR(gen->gi_code);
+ Py_CLEAR(gen->gi_frame);
+ Py_CLEAR(gen->gi_name);
+ Py_CLEAR(gen->gi_qualname);
+ Py_CLEAR(gen->gi_modulename);
+ return 0;
+}
+static void __Pyx_Coroutine_dealloc(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ PyObject_GC_UnTrack(gen);
+ #if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ if (gen->gi_weakreflist != NULL)
+ #endif
+ PyObject_ClearWeakRefs(self);
+ if (gen->resume_label >= 0) {
+ PyObject_GC_Track(self);
+#if CYTHON_USE_TP_FINALIZE
+ if (unlikely(PyObject_CallFinalizerFromDealloc(self)))
+#else
+ {
+ destructor del = __Pyx_PyObject_GetSlot(gen, tp_del, destructor);
+ if (del) del(self);
+ }
+ if (unlikely(Py_REFCNT(self) > 0))
+#endif
+ {
+ return;
+ }
+ PyObject_GC_UnTrack(self);
+ }
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ /* We have to handle this case for asynchronous generators
+ right here, because this code has to be between UNTRACK
+ and GC_Del. */
+ Py_CLEAR(((__pyx_PyAsyncGenObject*)self)->ag_finalizer);
+ }
+#endif
+ __Pyx_Coroutine_clear(self);
+ __Pyx_PyHeapTypeObject_GC_Del(gen);
+}
+#if CYTHON_USE_TP_FINALIZE
+static void __Pyx_Coroutine_del(PyObject *self) {
+ PyObject *error_type, *error_value, *error_traceback;
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
+ __Pyx_PyThreadState_declare
+ if (gen->resume_label < 0) {
+ return;
+ }
+ __Pyx_PyThreadState_assign
+ __Pyx_ErrFetch(&error_type, &error_value, &error_traceback);
+#ifdef __Pyx_AsyncGen_USED
+ if (__Pyx_AsyncGen_CheckExact(self)) {
+ __pyx_PyAsyncGenObject *agen = (__pyx_PyAsyncGenObject*)self;
+ PyObject *finalizer = agen->ag_finalizer;
+ if (finalizer && !agen->ag_closed) {
+ PyObject *res = __Pyx_PyObject_CallOneArg(finalizer, self);
+ if (unlikely(!res)) {
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_DECREF(res);
+ }
+ __Pyx_ErrRestore(error_type, error_value, error_traceback);
+ return;
+ }
+ }
+#endif
+ if (unlikely(gen->resume_label == 0 && !error_value)) {
+#ifdef __Pyx_Coroutine_USED
+#ifdef __Pyx_Generator_USED
+ if (!__Pyx_Generator_CheckExact(self))
+#endif
+ {
+ PyObject_GC_UnTrack(self);
+ if (unlikely(PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname) < 0))
+ PyErr_WriteUnraisable(self);
+ PyObject_GC_Track(self);
+ }
+#endif
+ } else {
+ PyObject *retval = NULL;
+ __Pyx_PySendResult result = __Pyx_Coroutine_Close(self, &retval);
+ if (result == PYGEN_ERROR) {
+ PyErr_WriteUnraisable(self);
+ } else {
+ Py_XDECREF(retval);
+ }
+ }
+ __Pyx_ErrRestore(error_type, error_value, error_traceback);
+}
+#endif
+static PyObject *
+__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self, void *context)
+{
+ PyObject *name = self->gi_name;
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
+}
+static int
+__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__name__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_Py_XDECREF_SET(self->gi_name, value);
+ return 0;
+}
+static PyObject *
+__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self, void *context)
+{
+ PyObject *name = self->gi_qualname;
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(!name)) name = Py_None;
+ Py_INCREF(name);
+ return name;
+}
+static int
+__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value, void *context)
+{
+ CYTHON_UNUSED_VAR(context);
+ if (unlikely(value == NULL || !PyUnicode_Check(value))) {
+ PyErr_SetString(PyExc_TypeError,
+ "__qualname__ must be set to a string object");
+ return -1;
+ }
+ Py_INCREF(value);
+ __Pyx_Py_XDECREF_SET(self->gi_qualname, value);
+ return 0;
+}
+static PyObject *
+__Pyx__Coroutine_get_frame(__pyx_CoroutineObject *self)
+{
+#if !CYTHON_COMPILING_IN_LIMITED_API
+ PyObject *frame;
+ #if PY_VERSION_HEX >= 0x030d0000
+ Py_BEGIN_CRITICAL_SECTION(self);
+ #endif
+ frame = self->gi_frame;
+ if (!frame) {
+ if (unlikely(!self->gi_code)) {
+ Py_RETURN_NONE;
+ }
+ PyObject *globals = PyDict_New();
+ if (unlikely(!globals)) return NULL;
+ frame = (PyObject *) PyFrame_New(
+ PyThreadState_Get(), /*PyThreadState *tstate,*/
+ (PyCodeObject*) self->gi_code, /*PyCodeObject *code,*/
+ globals, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ Py_DECREF(globals);
+ if (unlikely(!frame))
+ return NULL;
+ if (unlikely(self->gi_frame)) {
+ Py_DECREF(frame);
+ frame = self->gi_frame;
+ } else {
+ self->gi_frame = frame;
+ }
+ }
+ Py_INCREF(frame);
+ #if PY_VERSION_HEX >= 0x030d0000
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return frame;
+#else
+ CYTHON_UNUSED_VAR(self);
+ Py_RETURN_NONE;
+#endif
+}
+static PyObject *
+__Pyx_Coroutine_get_frame(__pyx_CoroutineObject *self, void *context) {
+ CYTHON_UNUSED_VAR(context);
+ PyObject *frame = self->gi_frame;
+ if (frame)
+ return __Pyx_NewRef(frame);
+ return __Pyx__Coroutine_get_frame(self);
+}
+static __pyx_CoroutineObject *__Pyx__Coroutine_New(
+ PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
+ __pyx_CoroutineObject *gen = PyObject_GC_New(__pyx_CoroutineObject, type);
+ if (unlikely(!gen))
+ return NULL;
+ return __Pyx__Coroutine_NewInit(gen, body, code, closure, name, qualname, module_name);
+}
+static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
+ __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure,
+ PyObject *name, PyObject *qualname, PyObject *module_name) {
+ gen->body = body;
+ gen->closure = closure;
+ Py_XINCREF(closure);
+ gen->is_running = 0;
+ gen->resume_label = 0;
+ gen->classobj = NULL;
+ gen->yieldfrom = NULL;
+ gen->yieldfrom_am_send = NULL;
+ #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_LIMITED_API
+ gen->gi_exc_state.exc_value = NULL;
+ #else
+ gen->gi_exc_state.exc_type = NULL;
+ gen->gi_exc_state.exc_value = NULL;
+ gen->gi_exc_state.exc_traceback = NULL;
+ #endif
+#if CYTHON_USE_EXC_INFO_STACK
+ gen->gi_exc_state.previous_item = NULL;
+#endif
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ gen->gi_weakreflist = NULL;
+#endif
+ Py_XINCREF(qualname);
+ gen->gi_qualname = qualname;
+ Py_XINCREF(name);
+ gen->gi_name = name;
+ Py_XINCREF(module_name);
+ gen->gi_modulename = module_name;
+ Py_XINCREF(code);
+ gen->gi_code = code;
+ gen->gi_frame = NULL;
+ PyObject_GC_Track(gen);
+ return gen;
+}
+static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen) {
+ char result;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ result = gen->is_running;
+ gen->is_running = 1;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return result;
+}
+static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen) {
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ assert(gen->is_running);
+ gen->is_running = 0;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+}
+static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen) {
+ char result;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_BEGIN_CRITICAL_SECTION(gen);
+ #endif
+ result = gen->is_running;
+ #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_END_CRITICAL_SECTION();
+ #endif
+ return result;
+}
+static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure) {
+ CYTHON_UNUSED_VAR(closure);
+ char result = __Pyx_Coroutine_get_is_running((__pyx_CoroutineObject*)gen);
+ if (result) Py_RETURN_TRUE;
+ else Py_RETURN_FALSE;
+}
+#if __PYX_HAS_PY_AM_SEND == 2
+static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send) {
+ Py_ssize_t ptr_offset = (char*)(type->tp_as_async) - (char*)type;
+ if (ptr_offset < 0 || ptr_offset > type->tp_basicsize) {
+ return;
+ }
+ memcpy((void*)static_amsend_methods, (void*)(type->tp_as_async), sizeof(*type->tp_as_async));
+ static_amsend_methods->am_send = am_send;
+ type->tp_as_async = __Pyx_SlotTpAsAsync(static_amsend_methods);
+}
+#endif
+static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg) {
+ CYTHON_UNUSED_VAR(arg);
+ __Pyx_TypeName self_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE((PyObject*)self));
+ PyErr_Format(PyExc_TypeError, "cannot pickle '" __Pyx_FMT_TYPENAME "' object",
+ self_type_name);
+ __Pyx_DECREF_TypeName(self_type_name);
+ return NULL;
+}
+
+/* Generator */
+static PyMethodDef __pyx_Generator_methods[] = {
+ {"send", (PyCFunction) __Pyx_Coroutine_Send, METH_O,
+ PyDoc_STR("send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")},
+ {"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS,
+ PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")},
+ {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS,
+ PyDoc_STR("close() -> raise GeneratorExit inside generator.")},
+ {"__reduce_ex__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_O, 0},
+ {"__reduce__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_NOARGS, 0},
+ {0, 0, 0, 0}
+};
+static PyMemberDef __pyx_Generator_memberlist[] = {
+ {"gi_yieldfrom", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY,
+ PyDoc_STR("object being iterated by 'yield from', or None")},
+ {"gi_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL},
+ {"__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0},
+#if PY_VERSION_HEX < 0x030C0000 || CYTHON_COMPILING_IN_LIMITED_API
+ {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0},
+#endif
+ {0, 0, 0, 0, 0}
+};
+static PyGetSetDef __pyx_Generator_getsets[] = {
+ {"__name__", (getter)__Pyx_Coroutine_get_name, (setter)__Pyx_Coroutine_set_name,
+ PyDoc_STR("name of the generator"), 0},
+ {"__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname,
+ PyDoc_STR("qualified name of the generator"), 0},
+ {"gi_frame", (getter)__Pyx_Coroutine_get_frame, NULL,
+ PyDoc_STR("Frame of the generator"), 0},
+ {"gi_running", __Pyx_Coroutine_get_is_running_getter, NULL, NULL, NULL},
+ {0, 0, 0, 0, 0}
+};
+static PyType_Slot __pyx_GeneratorType_slots[] = {
+ {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc},
+ {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse},
+ {Py_tp_iter, (void *)PyObject_SelfIter},
+ {Py_tp_iternext, (void *)__Pyx_Generator_Next},
+ {Py_tp_methods, (void *)__pyx_Generator_methods},
+ {Py_tp_members, (void *)__pyx_Generator_memberlist},
+ {Py_tp_getset, (void *)__pyx_Generator_getsets},
+ {Py_tp_getattro, (void *) PyObject_GenericGetAttr},
+#if CYTHON_USE_TP_FINALIZE
+ {Py_tp_finalize, (void *)__Pyx_Coroutine_del},
+#endif
+#if __PYX_HAS_PY_AM_SEND == 1
+ {Py_am_send, (void *)__Pyx_Coroutine_AmSend},
+#endif
+ {0, 0},
+};
+static PyType_Spec __pyx_GeneratorType_spec = {
+ __PYX_TYPE_MODULE_PREFIX "generator",
+ sizeof(__pyx_CoroutineObject),
+ 0,
+#if PY_VERSION_HEX >= 0x030C0000 && !CYTHON_COMPILING_IN_LIMITED_API
+ Py_TPFLAGS_MANAGED_WEAKREF |
+#endif
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | __Pyx_TPFLAGS_HAVE_AM_SEND,
+ __pyx_GeneratorType_slots
+};
+#if __PYX_HAS_PY_AM_SEND == 2
+static __Pyx_PyAsyncMethodsStruct __pyx_Generator_as_async;
+#endif
+static int __pyx_Generator_init(PyObject *module) {
+ __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module);
+ mstate->__pyx_GeneratorType = __Pyx_FetchCommonTypeFromSpec(
+ mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_GeneratorType_spec, NULL);
+ if (unlikely(!mstate->__pyx_GeneratorType)) {
+ return -1;
+ }
+#if __PYX_HAS_PY_AM_SEND == 2
+ __Pyx_SetBackportTypeAmSend(mstate->__pyx_GeneratorType, &__pyx_Generator_as_async, &__Pyx_Coroutine_AmSend);
+#endif
+ return 0;
+}
+static PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self) {
+ __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self;
+ PyObject *retval = NULL;
+ if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) {
+ return __Pyx_Coroutine_AlreadyRunningError(gen);
+ }
+ __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0);
+ __Pyx_Coroutine_unset_is_running(gen);
+ (void) result;
+ assert (result == PYGEN_RETURN || result == PYGEN_ERROR);
+ assert ((result == PYGEN_RETURN && retval != NULL) || (result == PYGEN_ERROR && retval == NULL));
+ return retval;
+}
+
+/* CheckBinaryVersion */
+static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer) {
+ const unsigned long MAJOR_MINOR = 0xFFFF0000UL;
+ if ((rt_version & MAJOR_MINOR) == (ct_version & MAJOR_MINOR))
+ return 0;
+ if (likely(allow_newer && (rt_version & MAJOR_MINOR) > (ct_version & MAJOR_MINOR)))
+ return 1;
+ {
+ char message[200];
+ PyOS_snprintf(message, sizeof(message),
+ "compile time Python version %d.%d "
+ "of module '%.100s' "
+ "%s "
+ "runtime version %d.%d",
+ (int) (ct_version >> 24), (int) ((ct_version >> 16) & 0xFF),
+ __Pyx_MODULE_NAME,
+ (allow_newer) ? "was newer than" : "does not match",
+ (int) (rt_version >> 24), (int) ((rt_version >> 16) & 0xFF)
+ );
+ return PyErr_WarnEx(NULL, message, 1);
+ }
+}
+
+/* NewCodeObj */
+#if CYTHON_COMPILING_IN_LIMITED_API
+ static PyObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f,
+ PyObject *code, PyObject *c, PyObject* n, PyObject *v,
+ PyObject *fv, PyObject *cell, PyObject* fn,
+ PyObject *name, int fline, PyObject *lnos) {
+ PyObject *exception_table = NULL;
+ PyObject *types_module=NULL, *code_type=NULL, *result=NULL;
+ #if __PYX_LIMITED_VERSION_HEX < 0x030b0000
+ PyObject *version_info;
+ PyObject *py_minor_version = NULL;
+ #endif
+ long minor_version = 0;
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030b0000
+ minor_version = 11;
+ #else
+ if (!(version_info = PySys_GetObject("version_info"))) goto end;
+ if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end;
+ minor_version = PyLong_AsLong(py_minor_version);
+ Py_DECREF(py_minor_version);
+ if (minor_version == -1 && PyErr_Occurred()) goto end;
+ #endif
+ if (!(types_module = PyImport_ImportModule("types"))) goto end;
+ if (!(code_type = PyObject_GetAttrString(types_module, "CodeType"))) goto end;
+ if (minor_version <= 7) {
+ (void)p;
+ result = PyObject_CallFunction(code_type, "iiiiiOOOOOOiOOO", a, k, l, s, f, code,
+ c, n, v, fn, name, fline, lnos, fv, cell);
+ } else if (minor_version <= 10) {
+ result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOiOOO", a,p, k, l, s, f, code,
+ c, n, v, fn, name, fline, lnos, fv, cell);
+ } else {
+ if (!(exception_table = PyBytes_FromStringAndSize(NULL, 0))) goto end;
+ result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOOiOOOO", a,p, k, l, s, f, code,
+ c, n, v, fn, name, name, fline, lnos, exception_table, fv, cell);
+ }
+ end:
+ Py_XDECREF(code_type);
+ Py_XDECREF(exception_table);
+ Py_XDECREF(types_module);
+ if (type) {
+ PyErr_Restore(type, value, traceback);
+ }
+ return result;
+ }
+#elif PY_VERSION_HEX >= 0x030B0000
+ static PyCodeObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f,
+ PyObject *code, PyObject *c, PyObject* n, PyObject *v,
+ PyObject *fv, PyObject *cell, PyObject* fn,
+ PyObject *name, int fline, PyObject *lnos) {
+ PyCodeObject *result;
+ result =
+ #if PY_VERSION_HEX >= 0x030C0000
+ PyUnstable_Code_NewWithPosOnlyArgs
+ #else
+ PyCode_NewWithPosOnlyArgs
+ #endif
+ (a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, name, fline, lnos, __pyx_mstate_global->__pyx_empty_bytes);
+ #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030c00A1
+ if (likely(result))
+ result->_co_firsttraceable = 0;
+ #endif
+ return result;
+ }
+#elif !CYTHON_COMPILING_IN_PYPY
+ #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#else
+ #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#endif
+static PyObject* __Pyx_PyCode_New(
+ const __Pyx_PyCode_New_function_description descr,
+ PyObject * const *varnames,
+ PyObject *filename,
+ PyObject *funcname,
+ PyObject *line_table,
+ PyObject *tuple_dedup_map
+) {
+ PyObject *code_obj = NULL, *varnames_tuple_dedup = NULL, *code_bytes = NULL;
+ Py_ssize_t var_count = (Py_ssize_t) descr.nlocals;
+ PyObject *varnames_tuple = PyTuple_New(var_count);
+ if (unlikely(!varnames_tuple)) return NULL;
+ for (Py_ssize_t i=0; i < var_count; i++) {
+ Py_INCREF(varnames[i]);
+ if (__Pyx_PyTuple_SET_ITEM(varnames_tuple, i, varnames[i]) != (0)) goto done;
+ }
+ #if CYTHON_COMPILING_IN_LIMITED_API
+ varnames_tuple_dedup = PyDict_GetItem(tuple_dedup_map, varnames_tuple);
+ if (!varnames_tuple_dedup) {
+ if (unlikely(PyDict_SetItem(tuple_dedup_map, varnames_tuple, varnames_tuple) < 0)) goto done;
+ varnames_tuple_dedup = varnames_tuple;
+ }
+ #else
+ varnames_tuple_dedup = PyDict_SetDefault(tuple_dedup_map, varnames_tuple, varnames_tuple);
+ if (unlikely(!varnames_tuple_dedup)) goto done;
+ #endif
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_INCREF(varnames_tuple_dedup);
+ #endif
+ if (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table != NULL && !CYTHON_COMPILING_IN_GRAAL) {
+ Py_ssize_t line_table_length = __Pyx_PyBytes_GET_SIZE(line_table);
+ #if !CYTHON_ASSUME_SAFE_SIZE
+ if (unlikely(line_table_length == -1)) goto done;
+ #endif
+ Py_ssize_t code_len = (line_table_length * 2 + 4) & ~3LL;
+ code_bytes = PyBytes_FromStringAndSize(NULL, code_len);
+ if (unlikely(!code_bytes)) goto done;
+ char* c_code_bytes = PyBytes_AsString(code_bytes);
+ if (unlikely(!c_code_bytes)) goto done;
+ memset(c_code_bytes, 0, (size_t) code_len);
+ }
+ code_obj = (PyObject*) __Pyx__PyCode_New(
+ (int) descr.argcount,
+ (int) descr.num_posonly_args,
+ (int) descr.num_kwonly_args,
+ (int) descr.nlocals,
+ 0,
+ (int) descr.flags,
+ code_bytes ? code_bytes : __pyx_mstate_global->__pyx_empty_bytes,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ varnames_tuple_dedup,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ __pyx_mstate_global->__pyx_empty_tuple,
+ filename,
+ funcname,
+ (int) descr.first_line,
+ (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table) ? line_table : __pyx_mstate_global->__pyx_empty_bytes
+ );
+done:
+ Py_XDECREF(code_bytes);
+ #if CYTHON_AVOID_BORROWED_REFS
+ Py_XDECREF(varnames_tuple_dedup);
+ #endif
+ Py_DECREF(varnames_tuple);
+ return code_obj;
+}
+
+/* DecompressString */
+static PyObject *__Pyx_DecompressString(const char *s, Py_ssize_t length, int algo) {
+ PyObject *module, *decompress, *compressed_bytes, *decompressed;
+ const char* module_name = algo == 3 ? "compression.zstd" : algo == 2 ? "bz2" : "zlib";
+ PyObject *methodname = PyUnicode_FromString("decompress");
+ if (unlikely(!methodname)) return NULL;
+ #if __PYX_LIMITED_VERSION_HEX >= 0x030e0000
+ if (algo == 3) {
+ PyObject *fromlist = Py_BuildValue("[O]", methodname);
+ if (unlikely(!fromlist)) return NULL;
+ module = PyImport_ImportModuleLevel("compression.zstd", NULL, NULL, fromlist, 0);
+ Py_DECREF(fromlist);
+ } else
+ #endif
+ module = PyImport_ImportModule(module_name);
+ if (unlikely(!module)) goto import_failed;
+ decompress = PyObject_GetAttr(module, methodname);
+ if (unlikely(!decompress)) goto import_failed;
+ {
+ #ifdef __cplusplus
+ char *memview_bytes = const_cast(s);
+ #else
+ #if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wcast-qual"
+ #elif !defined(__INTEL_COMPILER) && defined(__GNUC__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-qual"
+ #endif
+ char *memview_bytes = (char*) s;
+ #if defined(__clang__)
+ #pragma clang diagnostic pop
+ #elif !defined(__INTEL_COMPILER) && defined(__GNUC__)
+ #pragma GCC diagnostic pop
+ #endif
+ #endif
+ #if CYTHON_COMPILING_IN_LIMITED_API && !defined(PyBUF_READ)
+ int memview_flags = 0x100;
+ #else
+ int memview_flags = PyBUF_READ;
+ #endif
+ compressed_bytes = PyMemoryView_FromMemory(memview_bytes, length, memview_flags);
+ }
+ if (unlikely(!compressed_bytes)) {
+ Py_DECREF(decompress);
+ goto bad;
+ }
+ decompressed = PyObject_CallFunctionObjArgs(decompress, compressed_bytes, NULL);
+ Py_DECREF(compressed_bytes);
+ Py_DECREF(decompress);
+ Py_DECREF(module);
+ Py_DECREF(methodname);
+ return decompressed;
+import_failed:
+ PyErr_Format(PyExc_ImportError,
+ "Failed to import '%.20s.decompress' - cannot initialise module strings. "
+ "String compression was configured with the C macro 'CYTHON_COMPRESS_STRINGS=%d'.",
+ module_name, algo);
+bad:
+ Py_XDECREF(module);
+ Py_DECREF(methodname);
+ return NULL;
+}
+
+#include
+static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) {
+ size_t len = strlen(s);
+ if (unlikely(len > (size_t) PY_SSIZE_T_MAX)) {
+ PyErr_SetString(PyExc_OverflowError, "byte string is too long");
+ return -1;
+ }
+ return (Py_ssize_t) len;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) {
+ Py_ssize_t len = __Pyx_ssize_strlen(c_str);
+ if (unlikely(len < 0)) return NULL;
+ return __Pyx_PyUnicode_FromStringAndSize(c_str, len);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char* c_str) {
+ Py_ssize_t len = __Pyx_ssize_strlen(c_str);
+ if (unlikely(len < 0)) return NULL;
+ return PyByteArray_FromStringAndSize(c_str, len);
+}
+static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) {
+ Py_ssize_t ignore;
+ return __Pyx_PyObject_AsStringAndSize(o, &ignore);
+}
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+ if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL;
+#if CYTHON_COMPILING_IN_LIMITED_API
+ {
+ const char* result;
+ Py_ssize_t unicode_length;
+ CYTHON_MAYBE_UNUSED_VAR(unicode_length); // only for __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ #if __PYX_LIMITED_VERSION_HEX < 0x030A0000
+ if (unlikely(PyArg_Parse(o, "s#", &result, length) < 0)) return NULL;
+ #else
+ result = PyUnicode_AsUTF8AndSize(o, length);
+ #endif
+ #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ unicode_length = PyUnicode_GetLength(o);
+ if (unlikely(unicode_length < 0)) return NULL;
+ if (unlikely(unicode_length != *length)) {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+ #endif
+ return result;
+ }
+#else
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ if (likely(PyUnicode_IS_ASCII(o))) {
+ *length = PyUnicode_GET_LENGTH(o);
+ return PyUnicode_AsUTF8(o);
+ } else {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+#else
+ return PyUnicode_AsUTF8AndSize(o, length);
+#endif
+#endif
+}
+#endif
+static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8
+ if (PyUnicode_Check(o)) {
+ return __Pyx_PyUnicode_AsStringAndSize(o, length);
+ } else
+#endif
+ if (PyByteArray_Check(o)) {
+#if (CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS) || (CYTHON_COMPILING_IN_PYPY && (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)))
+ *length = PyByteArray_GET_SIZE(o);
+ return PyByteArray_AS_STRING(o);
+#else
+ *length = PyByteArray_Size(o);
+ if (*length == -1) return NULL;
+ return PyByteArray_AsString(o);
+#endif
+ } else
+ {
+ char* result;
+ int r = PyBytes_AsStringAndSize(o, &result, length);
+ if (unlikely(r < 0)) {
+ return NULL;
+ } else {
+ return result;
+ }
+ }
+}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
+ int is_true = x == Py_True;
+ if (is_true | (x == Py_False) | (x == Py_None)) return is_true;
+ else return PyObject_IsTrue(x);
+}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) {
+ int retval;
+ if (unlikely(!x)) return -1;
+ retval = __Pyx_PyObject_IsTrue(x);
+ Py_DECREF(x);
+ return retval;
+}
+static PyObject* __Pyx_PyNumber_LongWrongResultType(PyObject* result) {
+ __Pyx_TypeName result_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(result));
+ if (PyLong_Check(result)) {
+ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
+ "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). "
+ "The ability to return an instance of a strict subclass of int is deprecated, "
+ "and may be removed in a future version of Python.",
+ result_type_name)) {
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+ }
+ __Pyx_DECREF_TypeName(result_type_name);
+ return result;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "__int__ returned non-int (type " __Pyx_FMT_TYPENAME ")",
+ result_type_name);
+ __Pyx_DECREF_TypeName(result_type_name);
+ Py_DECREF(result);
+ return NULL;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x) {
+#if CYTHON_USE_TYPE_SLOTS
+ PyNumberMethods *m;
+#endif
+ PyObject *res = NULL;
+ if (likely(PyLong_Check(x)))
+ return __Pyx_NewRef(x);
+#if CYTHON_USE_TYPE_SLOTS
+ m = Py_TYPE(x)->tp_as_number;
+ if (likely(m && m->nb_int)) {
+ res = m->nb_int(x);
+ }
+#else
+ if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) {
+ res = PyNumber_Long(x);
+ }
+#endif
+ if (likely(res)) {
+ if (unlikely(!PyLong_CheckExact(res))) {
+ return __Pyx_PyNumber_LongWrongResultType(res);
+ }
+ }
+ else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "an integer is required");
+ }
+ return res;
+}
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
+ Py_ssize_t ival;
+ PyObject *x;
+ if (likely(PyLong_CheckExact(b))) {
+ #if CYTHON_USE_PYLONG_INTERNALS
+ if (likely(__Pyx_PyLong_IsCompact(b))) {
+ return __Pyx_PyLong_CompactValue(b);
+ } else {
+ const digit* digits = __Pyx_PyLong_Digits(b);
+ const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(b);
+ switch (size) {
+ case 2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ }
+ }
+ #endif
+ return PyLong_AsSsize_t(b);
+ }
+ x = PyNumber_Index(b);
+ if (!x) return -1;
+ ival = PyLong_AsSsize_t(x);
+ Py_DECREF(x);
+ return ival;
+}
+static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) {
+ if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) {
+ return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o);
+ } else {
+ Py_ssize_t ival;
+ PyObject *x;
+ x = PyNumber_Index(o);
+ if (!x) return -1;
+ ival = PyLong_AsLong(x);
+ Py_DECREF(x);
+ return ival;
+ }
+}
+static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b) {
+ CYTHON_UNUSED_VAR(b);
+ return __Pyx_NewRef(Py_None);
+}
+static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
+ return __Pyx_NewRef(b ? Py_True: Py_False);
+}
+static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t ival) {
+ return PyLong_FromSize_t(ival);
+}
+
+
+/* MultiPhaseInitModuleState */
+#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE
+#ifndef CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+#if (CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX >= 0x030C0000)
+ #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 1
+#else
+ #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 0
+#endif
+#endif
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE && !CYTHON_ATOMICS
+#error "Module state with PEP489 requires atomics. Currently that's one of\
+ C11, C++11, gcc atomic intrinsics or MSVC atomic intrinsics"
+#endif
+#if !CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+#define __Pyx_ModuleStateLookup_Lock()
+#define __Pyx_ModuleStateLookup_Unlock()
+#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d0000
+static PyMutex __Pyx_ModuleStateLookup_mutex = {0};
+#define __Pyx_ModuleStateLookup_Lock() PyMutex_Lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() PyMutex_Unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(__cplusplus) && __cplusplus >= 201103L
+#include
+static std::mutex __Pyx_ModuleStateLookup_mutex;
+#define __Pyx_ModuleStateLookup_Lock() __Pyx_ModuleStateLookup_mutex.lock()
+#define __Pyx_ModuleStateLookup_Unlock() __Pyx_ModuleStateLookup_mutex.unlock()
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201112L) && !defined(__STDC_NO_THREADS__)
+#include
+static mtx_t __Pyx_ModuleStateLookup_mutex;
+static once_flag __Pyx_ModuleStateLookup_mutex_once_flag = ONCE_FLAG_INIT;
+static void __Pyx_ModuleStateLookup_initialize_mutex(void) {
+ mtx_init(&__Pyx_ModuleStateLookup_mutex, mtx_plain);
+}
+#define __Pyx_ModuleStateLookup_Lock()\
+ call_once(&__Pyx_ModuleStateLookup_mutex_once_flag, __Pyx_ModuleStateLookup_initialize_mutex);\
+ mtx_lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() mtx_unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(HAVE_PTHREAD_H)
+#include
+static pthread_mutex_t __Pyx_ModuleStateLookup_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define __Pyx_ModuleStateLookup_Lock() pthread_mutex_lock(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() pthread_mutex_unlock(&__Pyx_ModuleStateLookup_mutex)
+#elif defined(_WIN32)
+#include // synchapi.h on its own doesn't work
+static SRWLOCK __Pyx_ModuleStateLookup_mutex = SRWLOCK_INIT;
+#define __Pyx_ModuleStateLookup_Lock() AcquireSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex)
+#define __Pyx_ModuleStateLookup_Unlock() ReleaseSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex)
+#else
+#error "No suitable lock available for CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE.\
+ Requires C standard >= C11, or C++ standard >= C++11,\
+ or pthreads, or the Windows 32 API, or Python >= 3.13."
+#endif
+typedef struct {
+ int64_t id;
+ PyObject *module;
+} __Pyx_InterpreterIdAndModule;
+typedef struct {
+ char interpreter_id_as_index;
+ Py_ssize_t count;
+ Py_ssize_t allocated;
+ __Pyx_InterpreterIdAndModule table[1];
+} __Pyx_ModuleStateLookupData;
+#define __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE 32
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static __pyx_atomic_int_type __Pyx_ModuleStateLookup_read_counter = 0;
+#endif
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static __pyx_atomic_ptr_type __Pyx_ModuleStateLookup_data = 0;
+#else
+static __Pyx_ModuleStateLookupData* __Pyx_ModuleStateLookup_data = NULL;
+#endif
+static __Pyx_InterpreterIdAndModule* __Pyx_State_FindModuleStateLookupTableLowerBound(
+ __Pyx_InterpreterIdAndModule* table,
+ Py_ssize_t count,
+ int64_t interpreterId) {
+ __Pyx_InterpreterIdAndModule* begin = table;
+ __Pyx_InterpreterIdAndModule* end = begin + count;
+ if (begin->id == interpreterId) {
+ return begin;
+ }
+ while ((end - begin) > __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) {
+ __Pyx_InterpreterIdAndModule* halfway = begin + (end - begin)/2;
+ if (halfway->id == interpreterId) {
+ return halfway;
+ }
+ if (halfway->id < interpreterId) {
+ begin = halfway;
+ } else {
+ end = halfway;
+ }
+ }
+ for (; begin < end; ++begin) {
+ if (begin->id >= interpreterId) return begin;
+ }
+ return begin;
+}
+static PyObject *__Pyx_State_FindModule(CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return NULL;
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData* data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data);
+ {
+ __pyx_atomic_incr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+ if (likely(data)) {
+ __Pyx_ModuleStateLookupData* new_data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_acquire(&__Pyx_ModuleStateLookup_data);
+ if (likely(data == new_data)) {
+ goto read_finished;
+ }
+ }
+ __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+ __Pyx_ModuleStateLookup_Lock();
+ __pyx_atomic_incr_relaxed(&__Pyx_ModuleStateLookup_read_counter);
+ data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data);
+ __Pyx_ModuleStateLookup_Unlock();
+ }
+ read_finished:;
+#else
+ __Pyx_ModuleStateLookupData* data = __Pyx_ModuleStateLookup_data;
+#endif
+ __Pyx_InterpreterIdAndModule* found = NULL;
+ if (unlikely(!data)) goto end;
+ if (data->interpreter_id_as_index) {
+ if (interpreter_id < data->count) {
+ found = data->table+interpreter_id;
+ }
+ } else {
+ found = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ data->table, data->count, interpreter_id);
+ }
+ end:
+ {
+ PyObject *result=NULL;
+ if (found && found->id == interpreter_id) {
+ result = found->module;
+ }
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter);
+#endif
+ return result;
+ }
+}
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+static void __Pyx_ModuleStateLookup_wait_until_no_readers(void) {
+ while (__pyx_atomic_load(&__Pyx_ModuleStateLookup_read_counter) != 0);
+}
+#else
+#define __Pyx_ModuleStateLookup_wait_until_no_readers()
+#endif
+static int __Pyx_State_AddModuleInterpIdAsIndex(__Pyx_ModuleStateLookupData **old_data, PyObject* module, int64_t interpreter_id) {
+ Py_ssize_t to_allocate = (*old_data)->allocated;
+ while (to_allocate <= interpreter_id) {
+ if (to_allocate == 0) to_allocate = 1;
+ else to_allocate *= 2;
+ }
+ __Pyx_ModuleStateLookupData *new_data = *old_data;
+ if (to_allocate != (*old_data)->allocated) {
+ new_data = (__Pyx_ModuleStateLookupData *)realloc(
+ *old_data,
+ sizeof(__Pyx_ModuleStateLookupData)+(to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule));
+ if (!new_data) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ for (Py_ssize_t i = new_data->allocated; i < to_allocate; ++i) {
+ new_data->table[i].id = i;
+ new_data->table[i].module = NULL;
+ }
+ new_data->allocated = to_allocate;
+ }
+ new_data->table[interpreter_id].module = module;
+ if (new_data->count < interpreter_id+1) {
+ new_data->count = interpreter_id+1;
+ }
+ *old_data = new_data;
+ return 0;
+}
+static void __Pyx_State_ConvertFromInterpIdAsIndex(__Pyx_ModuleStateLookupData *data) {
+ __Pyx_InterpreterIdAndModule *read = data->table;
+ __Pyx_InterpreterIdAndModule *write = data->table;
+ __Pyx_InterpreterIdAndModule *end = read + data->count;
+ for (; readmodule) {
+ write->id = read->id;
+ write->module = read->module;
+ ++write;
+ }
+ }
+ data->count = write - data->table;
+ for (; writeid = 0;
+ write->module = NULL;
+ }
+ data->interpreter_id_as_index = 0;
+}
+static int __Pyx_State_AddModule(PyObject* module, CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return -1;
+ int result = 0;
+ __Pyx_ModuleStateLookup_Lock();
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData *old_data = (__Pyx_ModuleStateLookupData *)
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0);
+#else
+ __Pyx_ModuleStateLookupData *old_data = __Pyx_ModuleStateLookup_data;
+#endif
+ __Pyx_ModuleStateLookupData *new_data = old_data;
+ if (!new_data) {
+ new_data = (__Pyx_ModuleStateLookupData *)calloc(1, sizeof(__Pyx_ModuleStateLookupData));
+ if (!new_data) {
+ result = -1;
+ PyErr_NoMemory();
+ goto end;
+ }
+ new_data->allocated = 1;
+ new_data->interpreter_id_as_index = 1;
+ }
+ __Pyx_ModuleStateLookup_wait_until_no_readers();
+ if (new_data->interpreter_id_as_index) {
+ if (interpreter_id < __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) {
+ result = __Pyx_State_AddModuleInterpIdAsIndex(&new_data, module, interpreter_id);
+ goto end;
+ }
+ __Pyx_State_ConvertFromInterpIdAsIndex(new_data);
+ }
+ {
+ Py_ssize_t insert_at = 0;
+ {
+ __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ new_data->table, new_data->count, interpreter_id);
+ assert(lower_bound);
+ insert_at = lower_bound - new_data->table;
+ if (unlikely(insert_at < new_data->count && lower_bound->id == interpreter_id)) {
+ lower_bound->module = module;
+ goto end; // already in table, nothing more to do
+ }
+ }
+ if (new_data->count+1 >= new_data->allocated) {
+ Py_ssize_t to_allocate = (new_data->count+1)*2;
+ new_data =
+ (__Pyx_ModuleStateLookupData*)realloc(
+ new_data,
+ sizeof(__Pyx_ModuleStateLookupData) +
+ (to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule));
+ if (!new_data) {
+ result = -1;
+ new_data = old_data;
+ PyErr_NoMemory();
+ goto end;
+ }
+ new_data->allocated = to_allocate;
+ }
+ ++new_data->count;
+ int64_t last_id = interpreter_id;
+ PyObject *last_module = module;
+ for (Py_ssize_t i=insert_at; icount; ++i) {
+ int64_t current_id = new_data->table[i].id;
+ new_data->table[i].id = last_id;
+ last_id = current_id;
+ PyObject *current_module = new_data->table[i].module;
+ new_data->table[i].module = last_module;
+ last_module = current_module;
+ }
+ }
+ end:
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, new_data);
+#else
+ __Pyx_ModuleStateLookup_data = new_data;
+#endif
+ __Pyx_ModuleStateLookup_Unlock();
+ return result;
+}
+static int __Pyx_State_RemoveModule(CYTHON_UNUSED void* dummy) {
+ int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get());
+ if (interpreter_id == -1) return -1;
+ __Pyx_ModuleStateLookup_Lock();
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __Pyx_ModuleStateLookupData *data = (__Pyx_ModuleStateLookupData *)
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0);
+#else
+ __Pyx_ModuleStateLookupData *data = __Pyx_ModuleStateLookup_data;
+#endif
+ if (data->interpreter_id_as_index) {
+ if (interpreter_id < data->count) {
+ data->table[interpreter_id].module = NULL;
+ }
+ goto done;
+ }
+ {
+ __Pyx_ModuleStateLookup_wait_until_no_readers();
+ __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound(
+ data->table, data->count, interpreter_id);
+ if (!lower_bound) goto done;
+ if (lower_bound->id != interpreter_id) goto done;
+ __Pyx_InterpreterIdAndModule *end = data->table+data->count;
+ for (;lower_boundid = (lower_bound+1)->id;
+ lower_bound->module = (lower_bound+1)->module;
+ }
+ }
+ --data->count;
+ if (data->count == 0) {
+ free(data);
+ data = NULL;
+ }
+ done:
+#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE
+ __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, data);
+#else
+ __Pyx_ModuleStateLookup_data = data;
+#endif
+ __Pyx_ModuleStateLookup_Unlock();
+ return 0;
+}
+#endif
+
+/* #### Code section: utility_code_pragmas_end ### */
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+
+
+/* #### Code section: end ### */
+#endif /* Py_PYTHON_H */
diff --git a/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.py b/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fe3e18b086d8eb4ad0a7b7a89a8a1b269bf752c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/qu2cu/qu2cu.py
@@ -0,0 +1,405 @@
+# cython: language_level=3
+# distutils: define_macros=CYTHON_TRACE_NOGIL=1
+
+# Copyright 2023 Google Inc. All Rights Reserved.
+# Copyright 2023 Behdad Esfahbod. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+try:
+ import cython
+except (AttributeError, ImportError):
+ # if cython not installed, use mock module with no-op decorators and types
+ from fontTools.misc import cython
+COMPILED = cython.compiled
+
+from fontTools.misc.bezierTools import splitCubicAtTC
+from collections import namedtuple
+import math
+from typing import (
+ List,
+ Tuple,
+ Union,
+)
+
+
+__all__ = ["quadratic_to_curves"]
+
+
+# Copied from cu2qu
+@cython.cfunc
+@cython.returns(cython.int)
+@cython.locals(
+ tolerance=cython.double,
+ p0=cython.complex,
+ p1=cython.complex,
+ p2=cython.complex,
+ p3=cython.complex,
+)
+@cython.locals(mid=cython.complex, deriv3=cython.complex)
+def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+ """Check if a cubic Bezier lies within a given distance of the origin.
+
+ "Origin" means *the* origin (0,0), not the start of the curve. Note that no
+ checks are made on the start and end positions of the curve; this function
+ only checks the inside of the curve.
+
+ Args:
+ p0 (complex): Start point of curve.
+ p1 (complex): First handle of curve.
+ p2 (complex): Second handle of curve.
+ p3 (complex): End point of curve.
+ tolerance (double): Distance from origin.
+
+ Returns:
+ bool: True if the cubic Bezier ``p`` entirely lies within a distance
+ ``tolerance`` of the origin, False otherwise.
+ """
+ # First check p2 then p1, as p2 has higher error early on.
+ if abs(p2) <= tolerance and abs(p1) <= tolerance:
+ return True
+
+ # Split.
+ mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
+ if abs(mid) > tolerance:
+ return False
+ deriv3 = (p3 + p2 - p1 - p0) * 0.125
+ return cubic_farthest_fit_inside(
+ p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
+ ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance)
+
+
+@cython.locals(
+ p0=cython.complex,
+ p1=cython.complex,
+ p2=cython.complex,
+ p1_2_3=cython.complex,
+)
+def elevate_quadratic(p0, p1, p2):
+ """Given a quadratic bezier curve, return its degree-elevated cubic."""
+
+ # https://pomax.github.io/bezierinfo/#reordering
+ p1_2_3 = p1 * (2 / 3)
+ return (
+ p0,
+ (p0 * (1 / 3) + p1_2_3),
+ (p2 * (1 / 3) + p1_2_3),
+ p2,
+ )
+
+
+@cython.cfunc
+@cython.locals(
+ start=cython.int,
+ n=cython.int,
+ k=cython.int,
+ prod_ratio=cython.double,
+ sum_ratio=cython.double,
+ ratio=cython.double,
+ t=cython.double,
+ p0=cython.complex,
+ p1=cython.complex,
+ p2=cython.complex,
+ p3=cython.complex,
+)
+def merge_curves(curves, start, n):
+ """Give a cubic-Bezier spline, reconstruct one cubic-Bezier
+ that has the same endpoints and tangents and approxmates
+ the spline."""
+
+ # Reconstruct the t values of the cut segments
+ prod_ratio = 1.0
+ sum_ratio = 1.0
+ ts = [1]
+ for k in range(1, n):
+ ck = curves[start + k]
+ c_before = curves[start + k - 1]
+
+ # |t_(k+1) - t_k| / |t_k - t_(k - 1)| = ratio
+ assert ck[0] == c_before[3]
+ ratio = abs(ck[1] - ck[0]) / abs(c_before[3] - c_before[2])
+
+ prod_ratio *= ratio
+ sum_ratio += prod_ratio
+ ts.append(sum_ratio)
+
+ # (t(n) - t(n - 1)) / (t_(1) - t(0)) = prod_ratio
+
+ ts = [t / sum_ratio for t in ts[:-1]]
+
+ p0 = curves[start][0]
+ p1 = curves[start][1]
+ p2 = curves[start + n - 1][2]
+ p3 = curves[start + n - 1][3]
+
+ # Build the curve by scaling the control-points.
+ p1 = p0 + (p1 - p0) / (ts[0] if ts else 1)
+ p2 = p3 + (p2 - p3) / ((1 - ts[-1]) if ts else 1)
+
+ curve = (p0, p1, p2, p3)
+
+ return curve, ts
+
+
+@cython.locals(
+ count=cython.int,
+ num_offcurves=cython.int,
+ i=cython.int,
+ off1=cython.complex,
+ off2=cython.complex,
+ on=cython.complex,
+)
+def add_implicit_on_curves(p):
+ q = list(p)
+ count = 0
+ num_offcurves = len(p) - 2
+ for i in range(1, num_offcurves):
+ off1 = p[i]
+ off2 = p[i + 1]
+ on = off1 + (off2 - off1) * 0.5
+ q.insert(i + 1 + count, on)
+ count += 1
+ return q
+
+
+Point = Union[Tuple[float, float], complex]
+
+
+@cython.locals(
+ cost=cython.int,
+ is_complex=cython.int,
+)
+def quadratic_to_curves(
+ quads: List[List[Point]],
+ max_err: float = 0.5,
+ all_cubic: bool = False,
+) -> List[Tuple[Point, ...]]:
+ """Converts a connecting list of quadratic splines to a list of quadratic
+ and cubic curves.
+
+ A quadratic spline is specified as a list of points. Either each point is
+ a 2-tuple of X,Y coordinates, or each point is a complex number with
+ real/imaginary components representing X,Y coordinates.
+
+ The first and last points are on-curve points and the rest are off-curve
+ points, with an implied on-curve point in the middle between every two
+ consequtive off-curve points.
+
+ Returns:
+ The output is a list of tuples of points. Points are represented
+ in the same format as the input, either as 2-tuples or complex numbers.
+
+ Each tuple is either of length three, for a quadratic curve, or four,
+ for a cubic curve. Each curve's last point is the same as the next
+ curve's first point.
+
+ Args:
+ quads: quadratic splines
+
+ max_err: absolute error tolerance; defaults to 0.5
+
+ all_cubic: if True, only cubic curves are generated; defaults to False
+ """
+ is_complex = type(quads[0][0]) is complex
+ if not is_complex:
+ quads = [[complex(x, y) for (x, y) in p] for p in quads]
+
+ q = [quads[0][0]]
+ costs = [1]
+ cost = 1
+ for p in quads:
+ assert q[-1] == p[0]
+ for i in range(len(p) - 2):
+ cost += 1
+ costs.append(cost)
+ costs.append(cost)
+ qq = add_implicit_on_curves(p)[1:]
+ costs.pop()
+ q.extend(qq)
+ cost += 1
+ costs.append(cost)
+
+ curves = spline_to_curves(q, costs, max_err, all_cubic)
+
+ if not is_complex:
+ curves = [tuple((c.real, c.imag) for c in curve) for curve in curves]
+ return curves
+
+
+Solution = namedtuple("Solution", ["num_points", "error", "start_index", "is_cubic"])
+
+
+@cython.locals(
+ i=cython.int,
+ j=cython.int,
+ k=cython.int,
+ start=cython.int,
+ i_sol_count=cython.int,
+ j_sol_count=cython.int,
+ this_sol_count=cython.int,
+ tolerance=cython.double,
+ err=cython.double,
+ error=cython.double,
+ i_sol_error=cython.double,
+ j_sol_error=cython.double,
+ all_cubic=cython.int,
+ is_cubic=cython.int,
+ count=cython.int,
+ p0=cython.complex,
+ p1=cython.complex,
+ p2=cython.complex,
+ p3=cython.complex,
+ v=cython.complex,
+ u=cython.complex,
+)
+def spline_to_curves(q, costs, tolerance=0.5, all_cubic=False):
+ """
+ q: quadratic spline with alternating on-curve / off-curve points.
+
+ costs: cumulative list of encoding cost of q in terms of number of
+ points that need to be encoded. Implied on-curve points do not
+ contribute to the cost. If all points need to be encoded, then
+ costs will be range(1, len(q)+1).
+ """
+
+ assert len(q) >= 3, "quadratic spline requires at least 3 points"
+
+ # Elevate quadratic segments to cubic
+ elevated_quadratics = [
+ elevate_quadratic(*q[i : i + 3]) for i in range(0, len(q) - 2, 2)
+ ]
+
+ # Find sharp corners; they have to be oncurves for sure.
+ forced = set()
+ for i in range(1, len(elevated_quadratics)):
+ p0 = elevated_quadratics[i - 1][2]
+ p1 = elevated_quadratics[i][0]
+ p2 = elevated_quadratics[i][1]
+ if abs(p1 - p0) + abs(p2 - p1) > tolerance + abs(p2 - p0):
+ forced.add(i)
+
+ # Dynamic-Programming to find the solution with fewest number of
+ # cubic curves, and within those the one with smallest error.
+ sols = [Solution(0, 0, 0, False)]
+ impossible = Solution(len(elevated_quadratics) * 3 + 1, 0, 1, False)
+ start = 0
+ for i in range(1, len(elevated_quadratics) + 1):
+ best_sol = impossible
+ for j in range(start, i):
+ j_sol_count, j_sol_error = sols[j].num_points, sols[j].error
+
+ if not all_cubic:
+ # Solution with quadratics between j:i
+ this_count = costs[2 * i - 1] - costs[2 * j] + 1
+ i_sol_count = j_sol_count + this_count
+ i_sol_error = j_sol_error
+ i_sol = Solution(i_sol_count, i_sol_error, i - j, False)
+ if i_sol < best_sol:
+ best_sol = i_sol
+
+ if this_count <= 3:
+ # Can't get any better than this in the path below
+ continue
+
+ # Fit elevated_quadratics[j:i] into one cubic
+ try:
+ curve, ts = merge_curves(elevated_quadratics, j, i - j)
+ except ZeroDivisionError:
+ continue
+
+ # Now reconstruct the segments from the fitted curve
+ reconstructed_iter = splitCubicAtTC(*curve, *ts)
+ reconstructed = []
+
+ # Knot errors
+ error = 0
+ for k, reconst in enumerate(reconstructed_iter):
+ orig = elevated_quadratics[j + k]
+ err = abs(reconst[3] - orig[3])
+ error = max(error, err)
+ if error > tolerance:
+ break
+ reconstructed.append(reconst)
+ if error > tolerance:
+ # Not feasible
+ continue
+
+ # Interior errors
+ for k, reconst in enumerate(reconstructed):
+ orig = elevated_quadratics[j + k]
+ p0, p1, p2, p3 = tuple(v - u for v, u in zip(reconst, orig))
+
+ if not cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+ error = tolerance + 1
+ break
+ if error > tolerance:
+ # Not feasible
+ continue
+
+ # Save best solution
+ i_sol_count = j_sol_count + 3
+ i_sol_error = max(j_sol_error, error)
+ i_sol = Solution(i_sol_count, i_sol_error, i - j, True)
+ if i_sol < best_sol:
+ best_sol = i_sol
+
+ if i_sol_count == 3:
+ # Can't get any better than this
+ break
+
+ sols.append(best_sol)
+ if i in forced:
+ start = i
+
+ # Reconstruct solution
+ splits = []
+ cubic = []
+ i = len(sols) - 1
+ while i:
+ count, is_cubic = sols[i].start_index, sols[i].is_cubic
+ splits.append(i)
+ cubic.append(is_cubic)
+ i -= count
+ curves = []
+ j = 0
+ for i, is_cubic in reversed(list(zip(splits, cubic))):
+ if is_cubic:
+ curves.append(merge_curves(elevated_quadratics, j, i - j)[0])
+ else:
+ for k in range(j, i):
+ curves.append(q[k * 2 : k * 2 + 3])
+ j = i
+
+ return curves
+
+
+def main():
+ from fontTools.cu2qu.benchmark import generate_curve
+ from fontTools.cu2qu import curve_to_quadratic
+
+ tolerance = 0.05
+ reconstruct_tolerance = tolerance * 1
+ curve = generate_curve()
+ quadratics = curve_to_quadratic(curve, tolerance)
+ print(
+ "cu2qu tolerance %g. qu2cu tolerance %g." % (tolerance, reconstruct_tolerance)
+ )
+ print("One random cubic turned into %d quadratics." % len(quadratics))
+ curves = quadratic_to_curves([quadratics], reconstruct_tolerance)
+ print("Those quadratics turned back into %d cubics. " % len(curves))
+ print("Original curve:", curve)
+ print("Reconstructed curve(s):", curves)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/lib/python3.12/site-packages/fontTools/svgLib/__init__.py b/lib/python3.12/site-packages/fontTools/svgLib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c049006bf2e58bed7786432e7ec84c8c8b40edf0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/svgLib/__init__.py
@@ -0,0 +1,3 @@
+from .path import SVGPath, parse_path
+
+__all__ = ["SVGPath", "parse_path"]
diff --git a/lib/python3.12/site-packages/fontTools/svgLib/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/svgLib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dc0920590b244a690947a8c2b3aadda4266166be
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/svgLib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/svgLib/path/__init__.py b/lib/python3.12/site-packages/fontTools/svgLib/path/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..043b4dbe1deb2d3a56e06fa43ed30694e7149544
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/svgLib/path/__init__.py
@@ -0,0 +1,65 @@
+from fontTools.pens.transformPen import TransformPen
+from fontTools.misc import etree
+from fontTools.misc.textTools import tostr
+from .parser import parse_path
+from .shapes import PathBuilder
+
+
+__all__ = [tostr(s) for s in ("SVGPath", "parse_path")]
+
+
+class SVGPath(object):
+ """Parse SVG ``path`` elements from a file or string, and draw them
+ onto a glyph object that supports the FontTools Pen protocol.
+
+ For example, reading from an SVG file and drawing to a Defcon Glyph:
+
+ .. code-block::
+
+ import defcon
+ glyph = defcon.Glyph()
+ pen = glyph.getPen()
+ svg = SVGPath("path/to/a.svg")
+ svg.draw(pen)
+
+ Or reading from a string containing SVG data, using the alternative
+ 'fromstring' (a class method):
+
+ .. code-block::
+
+ data = ' 1:
+ rx *= sqrt(radii_scale)
+ ry *= sqrt(radii_scale)
+ self.rx, self.ry = rx, ry
+
+ point_transform = Scale(1 / rx, 1 / ry).rotate(-self.angle)
+
+ point1 = _map_point(point_transform, self.current_point)
+ point2 = _map_point(point_transform, self.target_point)
+ delta = point2 - point1
+
+ d = delta.real * delta.real + delta.imag * delta.imag
+ scale_factor_squared = max(1 / d - 0.25, 0.0)
+
+ scale_factor = sqrt(scale_factor_squared)
+ if self.sweep == self.large:
+ scale_factor = -scale_factor
+
+ delta *= scale_factor
+ center_point = (point1 + point2) * 0.5
+ center_point += complex(-delta.imag, delta.real)
+ point1 -= center_point
+ point2 -= center_point
+
+ theta1 = atan2(point1.imag, point1.real)
+ theta2 = atan2(point2.imag, point2.real)
+
+ theta_arc = theta2 - theta1
+ if theta_arc < 0 and self.sweep:
+ theta_arc += TWO_PI
+ elif theta_arc > 0 and not self.sweep:
+ theta_arc -= TWO_PI
+
+ self.theta1 = theta1
+ self.theta2 = theta1 + theta_arc
+ self.theta_arc = theta_arc
+ self.center_point = center_point
+
+ return True
+
+ def _decompose_to_cubic_curves(self):
+ if self.center_point is None and not self._parametrize():
+ return
+
+ point_transform = Identity.rotate(self.angle).scale(self.rx, self.ry)
+
+ # Some results of atan2 on some platform implementations are not exact
+ # enough. So that we get more cubic curves than expected here. Adding 0.001f
+ # reduces the count of sgements to the correct count.
+ num_segments = int(ceil(fabs(self.theta_arc / (PI_OVER_TWO + 0.001))))
+ for i in range(num_segments):
+ start_theta = self.theta1 + i * self.theta_arc / num_segments
+ end_theta = self.theta1 + (i + 1) * self.theta_arc / num_segments
+
+ t = (4 / 3) * tan(0.25 * (end_theta - start_theta))
+ if not isfinite(t):
+ return
+
+ sin_start_theta = sin(start_theta)
+ cos_start_theta = cos(start_theta)
+ sin_end_theta = sin(end_theta)
+ cos_end_theta = cos(end_theta)
+
+ point1 = complex(
+ cos_start_theta - t * sin_start_theta,
+ sin_start_theta + t * cos_start_theta,
+ )
+ point1 += self.center_point
+ target_point = complex(cos_end_theta, sin_end_theta)
+ target_point += self.center_point
+ point2 = target_point
+ point2 += complex(t * sin_end_theta, -t * cos_end_theta)
+
+ point1 = _map_point(point_transform, point1)
+ point2 = _map_point(point_transform, point2)
+ target_point = _map_point(point_transform, target_point)
+
+ yield point1, point2, target_point
+
+ def draw(self, pen):
+ for point1, point2, target_point in self._decompose_to_cubic_curves():
+ pen.curveTo(
+ (point1.real, point1.imag),
+ (point2.real, point2.imag),
+ (target_point.real, target_point.imag),
+ )
diff --git a/lib/python3.12/site-packages/fontTools/svgLib/path/parser.py b/lib/python3.12/site-packages/fontTools/svgLib/path/parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..18c8e77f7f7e5636530fa4424083f97d4208d810
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/svgLib/path/parser.py
@@ -0,0 +1,322 @@
+# SVG Path specification parser.
+# This is an adaptation from 'svg.path' by Lennart Regebro (@regebro),
+# modified so that the parser takes a FontTools Pen object instead of
+# returning a list of svg.path Path objects.
+# The original code can be found at:
+# https://github.com/regebro/svg.path/blob/4f9b6e3/src/svg/path/parser.py
+# Copyright (c) 2013-2014 Lennart Regebro
+# License: MIT
+
+from .arc import EllipticalArc
+import re
+
+
+COMMANDS = set("MmZzLlHhVvCcSsQqTtAa")
+ARC_COMMANDS = set("Aa")
+UPPERCASE = set("MZLHVCSQTA")
+
+COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
+
+# https://www.w3.org/TR/css-syntax-3/#number-token-diagram
+# but -6.e-5 will be tokenized as "-6" then "-5" and confuse parsing
+FLOAT_RE = re.compile(
+ r"[-+]?" # optional sign
+ r"(?:"
+ r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?" # int/float
+ r"|"
+ r"(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)" # float with leading dot (e.g. '.42')
+ r")"
+)
+BOOL_RE = re.compile("^[01]")
+SEPARATOR_RE = re.compile(f"[, \t]")
+
+
+def _tokenize_path(pathdef):
+ arc_cmd = None
+ for x in COMMAND_RE.split(pathdef):
+ if x in COMMANDS:
+ arc_cmd = x if x in ARC_COMMANDS else None
+ yield x
+ continue
+
+ if arc_cmd:
+ try:
+ yield from _tokenize_arc_arguments(x)
+ except ValueError as e:
+ raise ValueError(f"Invalid arc command: '{arc_cmd}{x}'") from e
+ else:
+ for token in FLOAT_RE.findall(x):
+ yield token
+
+
+ARC_ARGUMENT_TYPES = (
+ ("rx", FLOAT_RE),
+ ("ry", FLOAT_RE),
+ ("x-axis-rotation", FLOAT_RE),
+ ("large-arc-flag", BOOL_RE),
+ ("sweep-flag", BOOL_RE),
+ ("x", FLOAT_RE),
+ ("y", FLOAT_RE),
+)
+
+
+def _tokenize_arc_arguments(arcdef):
+ raw_args = [s for s in SEPARATOR_RE.split(arcdef) if s]
+ if not raw_args:
+ raise ValueError(f"Not enough arguments: '{arcdef}'")
+ raw_args.reverse()
+
+ i = 0
+ while raw_args:
+ arg = raw_args.pop()
+
+ name, pattern = ARC_ARGUMENT_TYPES[i]
+ match = pattern.search(arg)
+ if not match:
+ raise ValueError(f"Invalid argument for '{name}' parameter: {arg!r}")
+
+ j, k = match.span()
+ yield arg[j:k]
+ arg = arg[k:]
+
+ if arg:
+ raw_args.append(arg)
+
+ # wrap around every 7 consecutive arguments
+ if i == 6:
+ i = 0
+ else:
+ i += 1
+
+ if i != 0:
+ raise ValueError(f"Not enough arguments: '{arcdef}'")
+
+
+def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):
+ """Parse SVG path definition (i.e. "d" attribute of elements)
+ and call a 'pen' object's moveTo, lineTo, curveTo, qCurveTo and closePath
+ methods.
+
+ If 'current_pos' (2-float tuple) is provided, the initial moveTo will
+ be relative to that instead being absolute.
+
+ If the pen has an "arcTo" method, it is called with the original values
+ of the elliptical arc curve commands:
+
+ .. code-block::
+
+ pen.arcTo(rx, ry, rotation, arc_large, arc_sweep, (x, y))
+
+ Otherwise, the arcs are approximated by series of cubic Bezier segments
+ ("curveTo"), one every 90 degrees.
+ """
+ # In the SVG specs, initial movetos are absolute, even if
+ # specified as 'm'. This is the default behavior here as well.
+ # But if you pass in a current_pos variable, the initial moveto
+ # will be relative to that current_pos. This is useful.
+ current_pos = complex(*current_pos)
+
+ elements = list(_tokenize_path(pathdef))
+ # Reverse for easy use of .pop()
+ elements.reverse()
+
+ start_pos = None
+ command = None
+ last_control = None
+
+ have_arcTo = hasattr(pen, "arcTo")
+
+ while elements:
+ if elements[-1] in COMMANDS:
+ # New command.
+ last_command = command # Used by S and T
+ command = elements.pop()
+ absolute = command in UPPERCASE
+ command = command.upper()
+ else:
+ # If this element starts with numbers, it is an implicit command
+ # and we don't change the command. Check that it's allowed:
+ if command is None:
+ raise ValueError(
+ "Unallowed implicit command in %s, position %s"
+ % (pathdef, len(pathdef.split()) - len(elements))
+ )
+ last_command = command # Used by S and T
+
+ if command == "M":
+ # Moveto command.
+ x = elements.pop()
+ y = elements.pop()
+ pos = float(x) + float(y) * 1j
+ if absolute:
+ current_pos = pos
+ else:
+ current_pos += pos
+
+ # M is not preceded by Z; it's an open subpath
+ if start_pos is not None:
+ pen.endPath()
+
+ pen.moveTo((current_pos.real, current_pos.imag))
+
+ # when M is called, reset start_pos
+ # This behavior of Z is defined in svg spec:
+ # http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
+ start_pos = current_pos
+
+ # Implicit moveto commands are treated as lineto commands.
+ # So we set command to lineto here, in case there are
+ # further implicit commands after this moveto.
+ command = "L"
+
+ elif command == "Z":
+ # Close path
+ if current_pos != start_pos:
+ pen.lineTo((start_pos.real, start_pos.imag))
+ pen.closePath()
+ current_pos = start_pos
+ start_pos = None
+ command = None # You can't have implicit commands after closing.
+
+ elif command == "L":
+ x = elements.pop()
+ y = elements.pop()
+ pos = float(x) + float(y) * 1j
+ if not absolute:
+ pos += current_pos
+ pen.lineTo((pos.real, pos.imag))
+ current_pos = pos
+
+ elif command == "H":
+ x = elements.pop()
+ pos = float(x) + current_pos.imag * 1j
+ if not absolute:
+ pos += current_pos.real
+ pen.lineTo((pos.real, pos.imag))
+ current_pos = pos
+
+ elif command == "V":
+ y = elements.pop()
+ pos = current_pos.real + float(y) * 1j
+ if not absolute:
+ pos += current_pos.imag * 1j
+ pen.lineTo((pos.real, pos.imag))
+ current_pos = pos
+
+ elif command == "C":
+ control1 = float(elements.pop()) + float(elements.pop()) * 1j
+ control2 = float(elements.pop()) + float(elements.pop()) * 1j
+ end = float(elements.pop()) + float(elements.pop()) * 1j
+
+ if not absolute:
+ control1 += current_pos
+ control2 += current_pos
+ end += current_pos
+
+ pen.curveTo(
+ (control1.real, control1.imag),
+ (control2.real, control2.imag),
+ (end.real, end.imag),
+ )
+ current_pos = end
+ last_control = control2
+
+ elif command == "S":
+ # Smooth curve. First control point is the "reflection" of
+ # the second control point in the previous path.
+
+ if last_command not in "CS":
+ # If there is no previous command or if the previous command
+ # was not an C, c, S or s, assume the first control point is
+ # coincident with the current point.
+ control1 = current_pos
+ else:
+ # The first control point is assumed to be the reflection of
+ # the second control point on the previous command relative
+ # to the current point.
+ control1 = current_pos + current_pos - last_control
+
+ control2 = float(elements.pop()) + float(elements.pop()) * 1j
+ end = float(elements.pop()) + float(elements.pop()) * 1j
+
+ if not absolute:
+ control2 += current_pos
+ end += current_pos
+
+ pen.curveTo(
+ (control1.real, control1.imag),
+ (control2.real, control2.imag),
+ (end.real, end.imag),
+ )
+ current_pos = end
+ last_control = control2
+
+ elif command == "Q":
+ control = float(elements.pop()) + float(elements.pop()) * 1j
+ end = float(elements.pop()) + float(elements.pop()) * 1j
+
+ if not absolute:
+ control += current_pos
+ end += current_pos
+
+ pen.qCurveTo((control.real, control.imag), (end.real, end.imag))
+ current_pos = end
+ last_control = control
+
+ elif command == "T":
+ # Smooth curve. Control point is the "reflection" of
+ # the second control point in the previous path.
+
+ if last_command not in "QT":
+ # If there is no previous command or if the previous command
+ # was not an Q, q, T or t, assume the first control point is
+ # coincident with the current point.
+ control = current_pos
+ else:
+ # The control point is assumed to be the reflection of
+ # the control point on the previous command relative
+ # to the current point.
+ control = current_pos + current_pos - last_control
+
+ end = float(elements.pop()) + float(elements.pop()) * 1j
+
+ if not absolute:
+ end += current_pos
+
+ pen.qCurveTo((control.real, control.imag), (end.real, end.imag))
+ current_pos = end
+ last_control = control
+
+ elif command == "A":
+ rx = abs(float(elements.pop()))
+ ry = abs(float(elements.pop()))
+ rotation = float(elements.pop())
+ arc_large = bool(int(elements.pop()))
+ arc_sweep = bool(int(elements.pop()))
+ end = float(elements.pop()) + float(elements.pop()) * 1j
+
+ if not absolute:
+ end += current_pos
+
+ # if the pen supports arcs, pass the values unchanged, otherwise
+ # approximate the arc with a series of cubic bezier curves
+ if have_arcTo:
+ pen.arcTo(
+ rx,
+ ry,
+ rotation,
+ arc_large,
+ arc_sweep,
+ (end.real, end.imag),
+ )
+ else:
+ arc = arc_class(
+ current_pos, rx, ry, rotation, arc_large, arc_sweep, end
+ )
+ arc.draw(pen)
+
+ current_pos = end
+
+ # no final Z command, it's an open path
+ if start_pos is not None:
+ pen.endPath()
diff --git a/lib/python3.12/site-packages/fontTools/svgLib/path/shapes.py b/lib/python3.12/site-packages/fontTools/svgLib/path/shapes.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f22e6c6a3e4d24636e710f1920ebf04a822b159
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/svgLib/path/shapes.py
@@ -0,0 +1,183 @@
+import re
+
+
+def _prefer_non_zero(*args):
+ for arg in args:
+ if arg != 0:
+ return arg
+ return 0.0
+
+
+def _ntos(n):
+ # %f likes to add unnecessary 0's, %g isn't consistent about # decimals
+ return ("%.3f" % n).rstrip("0").rstrip(".")
+
+
+def _strip_xml_ns(tag):
+ # ElementTree API doesn't provide a way to ignore XML namespaces in tags
+ # so we here strip them ourselves: cf. https://bugs.python.org/issue18304
+ return tag.split("}", 1)[1] if "}" in tag else tag
+
+
+def _transform(raw_value):
+ # TODO assumes a 'matrix' transform.
+ # No other transform functions are supported at the moment.
+ # https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
+ # start simple: if you aren't exactly matrix(...) then no love
+ match = re.match(r"matrix\((.*)\)", raw_value)
+ if not match:
+ raise NotImplementedError
+ matrix = tuple(float(p) for p in re.split(r"\s+|,", match.group(1)))
+ if len(matrix) != 6:
+ raise ValueError("wrong # of terms in %s" % raw_value)
+ return matrix
+
+
+class PathBuilder(object):
+ def __init__(self):
+ self.paths = []
+ self.transforms = []
+
+ def _start_path(self, initial_path=""):
+ self.paths.append(initial_path)
+ self.transforms.append(None)
+
+ def _end_path(self):
+ self._add("z")
+
+ def _add(self, path_snippet):
+ path = self.paths[-1]
+ if path:
+ path += " " + path_snippet
+ else:
+ path = path_snippet
+ self.paths[-1] = path
+
+ def _move(self, c, x, y):
+ self._add("%s%s,%s" % (c, _ntos(x), _ntos(y)))
+
+ def M(self, x, y):
+ self._move("M", x, y)
+
+ def m(self, x, y):
+ self._move("m", x, y)
+
+ def _arc(self, c, rx, ry, x, y, large_arc):
+ self._add(
+ "%s%s,%s 0 %d 1 %s,%s"
+ % (c, _ntos(rx), _ntos(ry), large_arc, _ntos(x), _ntos(y))
+ )
+
+ def A(self, rx, ry, x, y, large_arc=0):
+ self._arc("A", rx, ry, x, y, large_arc)
+
+ def a(self, rx, ry, x, y, large_arc=0):
+ self._arc("a", rx, ry, x, y, large_arc)
+
+ def _vhline(self, c, x):
+ self._add("%s%s" % (c, _ntos(x)))
+
+ def H(self, x):
+ self._vhline("H", x)
+
+ def h(self, x):
+ self._vhline("h", x)
+
+ def V(self, y):
+ self._vhline("V", y)
+
+ def v(self, y):
+ self._vhline("v", y)
+
+ def _line(self, c, x, y):
+ self._add("%s%s,%s" % (c, _ntos(x), _ntos(y)))
+
+ def L(self, x, y):
+ self._line("L", x, y)
+
+ def l(self, x, y):
+ self._line("l", x, y)
+
+ def _parse_line(self, line):
+ x1 = float(line.attrib.get("x1", 0))
+ y1 = float(line.attrib.get("y1", 0))
+ x2 = float(line.attrib.get("x2", 0))
+ y2 = float(line.attrib.get("y2", 0))
+
+ self._start_path()
+ self.M(x1, y1)
+ self.L(x2, y2)
+
+ def _parse_rect(self, rect):
+ x = float(rect.attrib.get("x", 0))
+ y = float(rect.attrib.get("y", 0))
+ w = float(rect.attrib.get("width"))
+ h = float(rect.attrib.get("height"))
+ rx = float(rect.attrib.get("rx", 0))
+ ry = float(rect.attrib.get("ry", 0))
+
+ rx = _prefer_non_zero(rx, ry)
+ ry = _prefer_non_zero(ry, rx)
+ # TODO there are more rules for adjusting rx, ry
+
+ self._start_path()
+ self.M(x + rx, y)
+ self.H(x + w - rx)
+ if rx > 0:
+ self.A(rx, ry, x + w, y + ry)
+ self.V(y + h - ry)
+ if rx > 0:
+ self.A(rx, ry, x + w - rx, y + h)
+ self.H(x + rx)
+ if rx > 0:
+ self.A(rx, ry, x, y + h - ry)
+ self.V(y + ry)
+ if rx > 0:
+ self.A(rx, ry, x + rx, y)
+ self._end_path()
+
+ def _parse_path(self, path):
+ if "d" in path.attrib:
+ self._start_path(initial_path=path.attrib["d"])
+
+ def _parse_polygon(self, poly):
+ if "points" in poly.attrib:
+ self._start_path("M" + poly.attrib["points"])
+ self._end_path()
+
+ def _parse_polyline(self, poly):
+ if "points" in poly.attrib:
+ self._start_path("M" + poly.attrib["points"])
+
+ def _parse_circle(self, circle):
+ cx = float(circle.attrib.get("cx", 0))
+ cy = float(circle.attrib.get("cy", 0))
+ r = float(circle.attrib.get("r"))
+
+ # arc doesn't seem to like being a complete shape, draw two halves
+ self._start_path()
+ self.M(cx - r, cy)
+ self.A(r, r, cx + r, cy, large_arc=1)
+ self.A(r, r, cx - r, cy, large_arc=1)
+
+ def _parse_ellipse(self, ellipse):
+ cx = float(ellipse.attrib.get("cx", 0))
+ cy = float(ellipse.attrib.get("cy", 0))
+ rx = float(ellipse.attrib.get("rx"))
+ ry = float(ellipse.attrib.get("ry"))
+
+ # arc doesn't seem to like being a complete shape, draw two halves
+ self._start_path()
+ self.M(cx - rx, cy)
+ self.A(rx, ry, cx + rx, cy, large_arc=1)
+ self.A(rx, ry, cx - rx, cy, large_arc=1)
+
+ def add_path_from_element(self, el):
+ tag = _strip_xml_ns(el.tag)
+ parse_fn = getattr(self, "_parse_%s" % tag.lower(), None)
+ if not callable(parse_fn):
+ return False
+ parse_fn(el)
+ if "transform" in el.attrib:
+ self.transforms[-1] = _transform(el.attrib["transform"])
+ return True
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__init__.py b/lib/python3.12/site-packages/fontTools/ttLib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6061761f31f1be1afbdf8a123526734f40168a1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/__init__.py
@@ -0,0 +1,30 @@
+"""fontTools.ttLib -- a package for dealing with TrueType fonts."""
+
+from fontTools.config import OPTIONS
+from fontTools.misc.loggingTools import deprecateFunction
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+OPTIMIZE_FONT_SPEED = OPTIONS["fontTools.ttLib:OPTIMIZE_FONT_SPEED"]
+
+
+class TTLibError(Exception):
+ pass
+
+
+class TTLibFileIsCollectionError(TTLibError):
+ pass
+
+
+@deprecateFunction("use logging instead", category=DeprecationWarning)
+def debugmsg(msg):
+ import time
+
+ print(msg + time.strftime(" (%H:%M:%S)", time.localtime(time.time())))
+
+
+from fontTools.ttLib.ttFont import *
+from fontTools.ttLib.ttCollection import TTCollection
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__main__.py b/lib/python3.12/site-packages/fontTools/ttLib/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..720bdb7e7991bd845813684de7a211c650d99174
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/__main__.py
@@ -0,0 +1,148 @@
+import sys
+from fontTools.ttLib import OPTIMIZE_FONT_SPEED, TTLibError, TTLibFileIsCollectionError
+from fontTools.ttLib.ttFont import *
+from fontTools.ttLib.ttCollection import TTCollection
+
+
+def main(args=None):
+ """Open/save fonts with TTFont() or TTCollection()
+
+ ./fonttools ttLib [-oFILE] [-yNUMBER] files...
+
+ If multiple files are given on the command-line,
+ they are each opened (as a font or collection),
+ and added to the font list.
+
+ If -o (output-file) argument is given, the font
+ list is then saved to the output file, either as
+ a single font, if there is only one font, or as
+ a collection otherwise.
+
+ If -y (font-number) argument is given, only the
+ specified font from collections is opened.
+
+ The above allow extracting a single font from a
+ collection, or combining multiple fonts into a
+ collection.
+
+ If --lazy or --no-lazy are give, those are passed
+ to the TTFont() or TTCollection() constructors.
+ """
+ from fontTools import configLogger
+
+ if args is None:
+ args = sys.argv[1:]
+
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ "fonttools ttLib",
+ description="Open/save fonts with TTFont() or TTCollection()",
+ epilog="""
+ If multiple files are given on the command-line,
+ they are each opened (as a font or collection),
+ and added to the font list.
+
+ The above, when combined with -o / --output,
+ allows for extracting a single font from a
+ collection, or combining multiple fonts into a
+ collection.
+ """,
+ )
+ parser.add_argument("font", metavar="font", nargs="*", help="Font file.")
+ parser.add_argument(
+ "-t", "--table", metavar="table", action="append", help="Tables to decompile."
+ )
+ parser.add_argument(
+ "-o", "--output", metavar="FILE", default=None, help="Output file."
+ )
+ parser.add_argument(
+ "-y", metavar="NUMBER", default=-1, help="Font number to load from collections."
+ )
+ parser.add_argument(
+ "--lazy", action="store_true", default=None, help="Load fonts lazily."
+ )
+ parser.add_argument(
+ "--no-lazy", dest="lazy", action="store_false", help="Load fonts immediately."
+ )
+ parser.add_argument(
+ "--flavor",
+ dest="flavor",
+ default=None,
+ help="Flavor of output font. 'woff' or 'woff2'.",
+ )
+ parser.add_argument(
+ "--no-recalc-timestamp",
+ dest="recalcTimestamp",
+ action="store_false",
+ help="Keep the original font 'modified' timestamp.",
+ )
+ parser.add_argument(
+ "-b",
+ dest="recalcBBoxes",
+ action="store_false",
+ help="Don't recalc glyph bounding boxes: use the values in the original font.",
+ )
+ parser.add_argument(
+ "--optimize-font-speed",
+ action="store_true",
+ help=(
+ "Enable optimizations that prioritize speed over file size. This "
+ "mainly affects how glyf table and gvar / VARC tables are compiled."
+ ),
+ )
+ options = parser.parse_args(args)
+
+ fontNumber = int(options.y) if options.y is not None else None
+ outFile = options.output
+ lazy = options.lazy
+ flavor = options.flavor
+ tables = options.table
+ recalcBBoxes = options.recalcBBoxes
+ recalcTimestamp = options.recalcTimestamp
+ optimizeFontSpeed = options.optimize_font_speed
+
+ fonts = []
+ for f in options.font:
+ try:
+ font = TTFont(
+ f,
+ recalcBBoxes=recalcBBoxes,
+ recalcTimestamp=recalcTimestamp,
+ fontNumber=fontNumber,
+ lazy=lazy,
+ )
+ if optimizeFontSpeed:
+ font.cfg[OPTIMIZE_FONT_SPEED] = optimizeFontSpeed
+ fonts.append(font)
+ except TTLibFileIsCollectionError:
+ collection = TTCollection(f, lazy=lazy)
+ fonts.extend(collection.fonts)
+
+ if tables is None:
+ if lazy is False:
+ tables = ["*"]
+ elif optimizeFontSpeed:
+ tables = {"glyf", "gvar", "VARC"}.intersection(font.keys())
+ else:
+ tables = []
+ for font in fonts:
+ if "GlyphOrder" in tables:
+ font.getGlyphOrder()
+ for table in tables if "*" not in tables else font.keys():
+ font[table] # Decompiles
+
+ if outFile is not None:
+ if len(fonts) == 1:
+ fonts[0].flavor = flavor
+ fonts[0].save(outFile)
+ else:
+ if flavor is not None:
+ raise TTLibError("Cannot set flavor for collections.")
+ collection = TTCollection()
+ collection.fonts = fonts
+ collection.save(outFile)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f9a0ae36893e7bf3f187426f8a2584f7cdcf4ee8
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e2ed1ef3418d7ee65823202703e19e4d904d00af
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/__main__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/macUtils.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/macUtils.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e5b3f72e8dbc8b2ae8cfc4101419d98a0b3741f8
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/macUtils.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/removeOverlaps.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/removeOverlaps.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8683a9cac12bce8d08995c51b891d34a28e28f38
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/removeOverlaps.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/reorderGlyphs.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/reorderGlyphs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8add75e668431e2a0d4a214fb4ceb0413639ccbe
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/reorderGlyphs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/scaleUpem.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/scaleUpem.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..465dccb21048f24b991a6f6783313314f5814e8d
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/scaleUpem.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/sfnt.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/sfnt.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..32247b329f2924b15ece8f2777c593ba3263bb81
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/sfnt.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/standardGlyphOrder.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/standardGlyphOrder.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7cb6532bae175bf66eae15a2c12c3432726857e4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/standardGlyphOrder.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttCollection.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttCollection.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d58ea3e0fea5164f56e9c51f9cbfe8c3463a1c00
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttCollection.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttFont.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttFont.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7fbbcdb1ace19f2439aad2f4edaaa0f9ebbb76d9
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttFont.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttGlyphSet.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttGlyphSet.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f3a998b963e0f800015e55380934d621b5f3d32c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttGlyphSet.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttVisitor.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttVisitor.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cf212e69a289b72d75b30ff723dd3d2d62269976
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/ttVisitor.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/woff2.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/woff2.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9c698d8041bfb63848c0f00831d95a09c7bbc28b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/__pycache__/woff2.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/macUtils.py b/lib/python3.12/site-packages/fontTools/ttLib/macUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..0959a6fc2776ff4b7ff968031191aa05c6ec50a4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/macUtils.py
@@ -0,0 +1,54 @@
+"""ttLib.macUtils.py -- Various Mac-specific stuff."""
+
+from io import BytesIO
+from fontTools.misc.macRes import ResourceReader, ResourceError
+
+
+def getSFNTResIndices(path):
+ """Determine whether a file has a 'sfnt' resource fork or not."""
+ try:
+ reader = ResourceReader(path)
+ indices = reader.getIndices("sfnt")
+ reader.close()
+ return indices
+ except ResourceError:
+ return []
+
+
+def openTTFonts(path):
+ """Given a pathname, return a list of TTFont objects. In the case
+ of a flat TTF/OTF file, the list will contain just one font object;
+ but in the case of a Mac font suitcase it will contain as many
+ font objects as there are sfnt resources in the file.
+ """
+ from fontTools import ttLib
+
+ fonts = []
+ sfnts = getSFNTResIndices(path)
+ if not sfnts:
+ fonts.append(ttLib.TTFont(path))
+ else:
+ for index in sfnts:
+ fonts.append(ttLib.TTFont(path, index))
+ if not fonts:
+ raise ttLib.TTLibError("no fonts found in file '%s'" % path)
+ return fonts
+
+
+class SFNTResourceReader(BytesIO):
+ """Simple read-only file wrapper for 'sfnt' resources."""
+
+ def __init__(self, path, res_name_or_index):
+ from fontTools import ttLib
+
+ reader = ResourceReader(path)
+ if isinstance(res_name_or_index, str):
+ rsrc = reader.getNamedResource("sfnt", res_name_or_index)
+ else:
+ rsrc = reader.getIndResource("sfnt", res_name_or_index)
+ if rsrc is None:
+ raise ttLib.TTLibError("sfnt resource not found: %s" % res_name_or_index)
+ reader.close()
+ self.rsrc = rsrc
+ super(SFNTResourceReader, self).__init__(rsrc.data)
+ self.name = path
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/sfnt.py b/lib/python3.12/site-packages/fontTools/ttLib/sfnt.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0ed17ae2945fbf55dbbba9e897783d6e25b7cc8
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/sfnt.py
@@ -0,0 +1,664 @@
+"""ttLib/sfnt.py -- low-level module to deal with the sfnt file format.
+
+Defines two public classes:
+
+- SFNTReader
+- SFNTWriter
+
+(Normally you don't have to use these classes explicitly; they are
+used automatically by ttLib.TTFont.)
+
+The reading and writing of sfnt files is separated in two distinct
+classes, since whenever the number of tables changes or whenever
+a table's length changes you need to rewrite the whole file anyway.
+"""
+
+from __future__ import annotations
+
+from collections.abc import KeysView
+from io import BytesIO
+from types import SimpleNamespace
+from fontTools.misc.textTools import Tag
+from fontTools.misc import sstruct
+from fontTools.ttLib import TTLibError, TTLibFileIsCollectionError
+import struct
+from collections import OrderedDict
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+class SFNTReader(object):
+ def __new__(cls, *args, **kwargs):
+ """Return an instance of the SFNTReader sub-class which is compatible
+ with the input file type.
+ """
+ if args and cls is SFNTReader:
+ infile = args[0]
+ infile.seek(0)
+ sfntVersion = Tag(infile.read(4))
+ infile.seek(0)
+ if sfntVersion == "wOF2":
+ # return new WOFF2Reader object
+ from fontTools.ttLib.woff2 import WOFF2Reader
+
+ return object.__new__(WOFF2Reader)
+ # return default object
+ return object.__new__(cls)
+
+ def __init__(self, file, checkChecksums=0, fontNumber=-1):
+ self.file = file
+ self.checkChecksums = checkChecksums
+
+ self.flavor = None
+ self.flavorData = None
+ self.DirectoryEntry = SFNTDirectoryEntry
+ self.file.seek(0)
+ self.sfntVersion = self.file.read(4)
+ self.file.seek(0)
+ if self.sfntVersion == b"ttcf":
+ header = readTTCHeader(self.file)
+ numFonts = header.numFonts
+ if not 0 <= fontNumber < numFonts:
+ raise TTLibFileIsCollectionError(
+ "specify a font number between 0 and %d (inclusive)"
+ % (numFonts - 1)
+ )
+ self.numFonts = numFonts
+ self.file.seek(header.offsetTable[fontNumber])
+ data = self.file.read(sfntDirectorySize)
+ if len(data) != sfntDirectorySize:
+ raise TTLibError("Not a Font Collection (not enough data)")
+ sstruct.unpack(sfntDirectoryFormat, data, self)
+ elif self.sfntVersion == b"wOFF":
+ self.flavor = "woff"
+ self.DirectoryEntry = WOFFDirectoryEntry
+ data = self.file.read(woffDirectorySize)
+ if len(data) != woffDirectorySize:
+ raise TTLibError("Not a WOFF font (not enough data)")
+ sstruct.unpack(woffDirectoryFormat, data, self)
+ else:
+ data = self.file.read(sfntDirectorySize)
+ if len(data) != sfntDirectorySize:
+ raise TTLibError("Not a TrueType or OpenType font (not enough data)")
+ sstruct.unpack(sfntDirectoryFormat, data, self)
+ self.sfntVersion = Tag(self.sfntVersion)
+
+ if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
+ raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
+ tables: dict[Tag, DirectoryEntry] = {}
+ for i in range(self.numTables):
+ entry = self.DirectoryEntry()
+ entry.fromFile(self.file)
+ tag = Tag(entry.tag)
+ tables[tag] = entry
+ self.tables = OrderedDict(sorted(tables.items(), key=lambda i: i[1].offset))
+
+ # Load flavor data if any
+ if self.flavor == "woff":
+ self.flavorData = WOFFFlavorData(self)
+
+ def has_key(self, tag: str | bytes) -> bool:
+ return tag in self.tables
+
+ __contains__ = has_key
+
+ def keys(self) -> KeysView[Tag]:
+ return self.tables.keys()
+
+ def __getitem__(self, tag: str | bytes) -> bytes:
+ """Fetch the raw table data."""
+ entry = self.tables[Tag(tag)]
+ data = entry.loadData(self.file)
+ if self.checkChecksums:
+ if tag == "head":
+ # Beh: we have to special-case the 'head' table.
+ checksum = calcChecksum(data[:8] + b"\0\0\0\0" + data[12:])
+ else:
+ checksum = calcChecksum(data)
+ if self.checkChecksums > 1:
+ # Be obnoxious, and barf when it's wrong
+ assert checksum == entry.checkSum, "bad checksum for '%s' table" % tag
+ elif checksum != entry.checkSum:
+ # Be friendly, and just log a warning.
+ log.warning("bad checksum for '%s' table", tag)
+ return data
+
+ def __delitem__(self, tag: str | bytes) -> None:
+ del self.tables[Tag(tag)]
+
+ def close(self) -> None:
+ self.file.close()
+
+ # We define custom __getstate__ and __setstate__ to make SFNTReader pickle-able
+ # and deepcopy-able. When a TTFont is loaded as lazy=True, SFNTReader holds a
+ # reference to an external file object which is not pickleable. So in __getstate__
+ # we store the file name and current position, and in __setstate__ we reopen the
+ # same named file after unpickling.
+
+ def __getstate__(self):
+ if isinstance(self.file, BytesIO):
+ # BytesIO is already pickleable, return the state unmodified
+ return self.__dict__
+
+ # remove unpickleable file attribute, and only store its name and pos
+ state = self.__dict__.copy()
+ del state["file"]
+ state["_filename"] = self.file.name
+ state["_filepos"] = self.file.tell()
+ return state
+
+ def __setstate__(self, state):
+ if "file" not in state:
+ self.file = open(state.pop("_filename"), "rb")
+ self.file.seek(state.pop("_filepos"))
+ self.__dict__.update(state)
+
+
+# default compression level for WOFF 1.0 tables and metadata
+ZLIB_COMPRESSION_LEVEL = 6
+
+# if set to True, use zopfli instead of zlib for compressing WOFF 1.0.
+# The Python bindings are available at https://pypi.python.org/pypi/zopfli
+USE_ZOPFLI = False
+
+# mapping between zlib's compression levels and zopfli's 'numiterations'.
+# Use lower values for files over several MB in size or it will be too slow
+ZOPFLI_LEVELS = {
+ # 0: 0, # can't do 0 iterations...
+ 1: 1,
+ 2: 3,
+ 3: 5,
+ 4: 8,
+ 5: 10,
+ 6: 15,
+ 7: 25,
+ 8: 50,
+ 9: 100,
+}
+
+
+def compress(data, level=ZLIB_COMPRESSION_LEVEL):
+ """Compress 'data' to Zlib format. If 'USE_ZOPFLI' variable is True,
+ zopfli is used instead of the zlib module.
+ The compression 'level' must be between 0 and 9. 1 gives best speed,
+ 9 gives best compression (0 gives no compression at all).
+ The default value is a compromise between speed and compression (6).
+ """
+ if not (0 <= level <= 9):
+ raise ValueError("Bad compression level: %s" % level)
+ if not USE_ZOPFLI or level == 0:
+ from zlib import compress
+
+ return compress(data, level)
+ else:
+ from zopfli.zlib import compress
+
+ return compress(data, numiterations=ZOPFLI_LEVELS[level])
+
+
+class SFNTWriter(object):
+ def __new__(cls, *args, **kwargs):
+ """Return an instance of the SFNTWriter sub-class which is compatible
+ with the specified 'flavor'.
+ """
+ flavor = None
+ if kwargs and "flavor" in kwargs:
+ flavor = kwargs["flavor"]
+ elif args and len(args) > 3:
+ flavor = args[3]
+ if cls is SFNTWriter:
+ if flavor == "woff2":
+ # return new WOFF2Writer object
+ from fontTools.ttLib.woff2 import WOFF2Writer
+
+ return object.__new__(WOFF2Writer)
+ # return default object
+ return object.__new__(cls)
+
+ def __init__(
+ self,
+ file,
+ numTables,
+ sfntVersion="\000\001\000\000",
+ flavor=None,
+ flavorData=None,
+ ):
+ self.file = file
+ self.numTables = numTables
+ self.sfntVersion = Tag(sfntVersion)
+ self.flavor = flavor
+ self.flavorData = flavorData
+
+ if self.flavor == "woff":
+ self.directoryFormat = woffDirectoryFormat
+ self.directorySize = woffDirectorySize
+ self.DirectoryEntry = WOFFDirectoryEntry
+
+ self.signature = "wOFF"
+
+ # to calculate WOFF checksum adjustment, we also need the original SFNT offsets
+ self.origNextTableOffset = (
+ sfntDirectorySize + numTables * sfntDirectoryEntrySize
+ )
+ else:
+ assert not self.flavor, "Unknown flavor '%s'" % self.flavor
+ self.directoryFormat = sfntDirectoryFormat
+ self.directorySize = sfntDirectorySize
+ self.DirectoryEntry = SFNTDirectoryEntry
+
+ from fontTools.ttLib import getSearchRange
+
+ self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(
+ numTables, 16
+ )
+
+ self.directoryOffset = self.file.tell()
+ self.nextTableOffset = (
+ self.directoryOffset
+ + self.directorySize
+ + numTables * self.DirectoryEntry.formatSize
+ )
+ # clear out directory area
+ self.file.seek(self.nextTableOffset)
+ # make sure we're actually where we want to be. (old cStringIO bug)
+ self.file.write(b"\0" * (self.nextTableOffset - self.file.tell()))
+ self.tables = OrderedDict()
+
+ def setEntry(self, tag, entry):
+ if tag in self.tables:
+ raise TTLibError("cannot rewrite '%s' table" % tag)
+
+ self.tables[tag] = entry
+
+ def __setitem__(self, tag, data):
+ """Write raw table data to disk."""
+ if tag in self.tables:
+ raise TTLibError("cannot rewrite '%s' table" % tag)
+
+ entry = self.DirectoryEntry()
+ entry.tag = tag
+ entry.offset = self.nextTableOffset
+ if tag == "head":
+ entry.checkSum = calcChecksum(data[:8] + b"\0\0\0\0" + data[12:])
+ self.headTable = data
+ entry.uncompressed = True
+ else:
+ entry.checkSum = calcChecksum(data)
+ entry.saveData(self.file, data)
+
+ if self.flavor == "woff":
+ entry.origOffset = self.origNextTableOffset
+ self.origNextTableOffset += (entry.origLength + 3) & ~3
+
+ self.nextTableOffset = self.nextTableOffset + ((entry.length + 3) & ~3)
+ # Add NUL bytes to pad the table data to a 4-byte boundary.
+ # Don't depend on f.seek() as we need to add the padding even if no
+ # subsequent write follows (seek is lazy), ie. after the final table
+ # in the font.
+ self.file.write(b"\0" * (self.nextTableOffset - self.file.tell()))
+ assert self.nextTableOffset == self.file.tell()
+
+ self.setEntry(tag, entry)
+
+ def __getitem__(self, tag):
+ return self.tables[tag]
+
+ def close(self):
+ """All tables must have been written to disk. Now write the
+ directory.
+ """
+ tables = sorted(self.tables.items())
+ if len(tables) != self.numTables:
+ raise TTLibError(
+ "wrong number of tables; expected %d, found %d"
+ % (self.numTables, len(tables))
+ )
+
+ if self.flavor == "woff":
+ self.signature = b"wOFF"
+ self.reserved = 0
+
+ self.totalSfntSize = 12
+ self.totalSfntSize += 16 * len(tables)
+ for tag, entry in tables:
+ self.totalSfntSize += (entry.origLength + 3) & ~3
+
+ data = self.flavorData if self.flavorData else WOFFFlavorData()
+ if data.majorVersion is not None and data.minorVersion is not None:
+ self.majorVersion = data.majorVersion
+ self.minorVersion = data.minorVersion
+ else:
+ if hasattr(self, "headTable"):
+ self.majorVersion, self.minorVersion = struct.unpack(
+ ">HH", self.headTable[4:8]
+ )
+ else:
+ self.majorVersion = self.minorVersion = 0
+ if data.metaData:
+ self.metaOrigLength = len(data.metaData)
+ self.file.seek(0, 2)
+ self.metaOffset = self.file.tell()
+ compressedMetaData = compress(data.metaData)
+ self.metaLength = len(compressedMetaData)
+ self.file.write(compressedMetaData)
+ else:
+ self.metaOffset = self.metaLength = self.metaOrigLength = 0
+ if data.privData:
+ self.file.seek(0, 2)
+ off = self.file.tell()
+ paddedOff = (off + 3) & ~3
+ self.file.write(b"\0" * (paddedOff - off))
+ self.privOffset = self.file.tell()
+ self.privLength = len(data.privData)
+ self.file.write(data.privData)
+ else:
+ self.privOffset = self.privLength = 0
+
+ self.file.seek(0, 2)
+ self.length = self.file.tell()
+
+ else:
+ assert not self.flavor, "Unknown flavor '%s'" % self.flavor
+ pass
+
+ directory = sstruct.pack(self.directoryFormat, self)
+
+ self.file.seek(self.directoryOffset + self.directorySize)
+ seenHead = 0
+ for tag, entry in tables:
+ if tag == "head":
+ seenHead = 1
+ directory = directory + entry.toString()
+ if seenHead:
+ self.writeMasterChecksum(directory)
+ self.file.seek(self.directoryOffset)
+ self.file.write(directory)
+
+ def _calcMasterChecksum(self, directory):
+ # calculate checkSumAdjustment
+ checksums = []
+ for tag in self.tables.keys():
+ checksums.append(self.tables[tag].checkSum)
+
+ if self.DirectoryEntry != SFNTDirectoryEntry:
+ # Create a SFNT directory for checksum calculation purposes
+ from fontTools.ttLib import getSearchRange
+
+ self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(
+ self.numTables, 16
+ )
+ directory = sstruct.pack(sfntDirectoryFormat, self)
+ tables = sorted(self.tables.items())
+ for tag, entry in tables:
+ sfntEntry = SFNTDirectoryEntry()
+ sfntEntry.tag = entry.tag
+ sfntEntry.checkSum = entry.checkSum
+ sfntEntry.offset = entry.origOffset
+ sfntEntry.length = entry.origLength
+ directory = directory + sfntEntry.toString()
+
+ directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
+ assert directory_end == len(directory)
+
+ checksums.append(calcChecksum(directory))
+ checksum = sum(checksums) & 0xFFFFFFFF
+ # BiboAfba!
+ checksumadjustment = (0xB1B0AFBA - checksum) & 0xFFFFFFFF
+ return checksumadjustment
+
+ def writeMasterChecksum(self, directory):
+ checksumadjustment = self._calcMasterChecksum(directory)
+ # write the checksum to the file
+ self.file.seek(self.tables["head"].offset + 8)
+ self.file.write(struct.pack(">L", checksumadjustment))
+
+ def reordersTables(self):
+ return False
+
+
+# -- sfnt directory helpers and cruft
+
+ttcHeaderFormat = """
+ > # big endian
+ TTCTag: 4s # "ttcf"
+ Version: L # 0x00010000 or 0x00020000
+ numFonts: L # number of fonts
+ # OffsetTable[numFonts]: L # array with offsets from beginning of file
+ # ulDsigTag: L # version 2.0 only
+ # ulDsigLength: L # version 2.0 only
+ # ulDsigOffset: L # version 2.0 only
+"""
+
+ttcHeaderSize = sstruct.calcsize(ttcHeaderFormat)
+
+sfntDirectoryFormat = """
+ > # big endian
+ sfntVersion: 4s
+ numTables: H # number of tables
+ searchRange: H # (max2 <= numTables)*16
+ entrySelector: H # log2(max2 <= numTables)
+ rangeShift: H # numTables*16-searchRange
+"""
+
+sfntDirectorySize = sstruct.calcsize(sfntDirectoryFormat)
+
+sfntDirectoryEntryFormat = """
+ > # big endian
+ tag: 4s
+ checkSum: L
+ offset: L
+ length: L
+"""
+
+sfntDirectoryEntrySize = sstruct.calcsize(sfntDirectoryEntryFormat)
+
+woffDirectoryFormat = """
+ > # big endian
+ signature: 4s # "wOFF"
+ sfntVersion: 4s
+ length: L # total woff file size
+ numTables: H # number of tables
+ reserved: H # set to 0
+ totalSfntSize: L # uncompressed size
+ majorVersion: H # major version of WOFF file
+ minorVersion: H # minor version of WOFF file
+ metaOffset: L # offset to metadata block
+ metaLength: L # length of compressed metadata
+ metaOrigLength: L # length of uncompressed metadata
+ privOffset: L # offset to private data block
+ privLength: L # length of private data block
+"""
+
+woffDirectorySize = sstruct.calcsize(woffDirectoryFormat)
+
+woffDirectoryEntryFormat = """
+ > # big endian
+ tag: 4s
+ offset: L
+ length: L # compressed length
+ origLength: L # original length
+ checkSum: L # original checksum
+"""
+
+woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
+
+
+class DirectoryEntry(object):
+ def __init__(self):
+ self.uncompressed = False # if True, always embed entry raw
+
+ def fromFile(self, file):
+ sstruct.unpack(self.format, file.read(self.formatSize), self)
+
+ def fromString(self, str):
+ sstruct.unpack(self.format, str, self)
+
+ def toString(self):
+ return sstruct.pack(self.format, self)
+
+ def __repr__(self):
+ if hasattr(self, "tag"):
+ return "<%s '%s' at %x>" % (self.__class__.__name__, self.tag, id(self))
+ else:
+ return "<%s at %x>" % (self.__class__.__name__, id(self))
+
+ def loadData(self, file):
+ file.seek(self.offset)
+ data = file.read(self.length)
+ assert len(data) == self.length
+ if hasattr(self.__class__, "decodeData"):
+ data = self.decodeData(data)
+ return data
+
+ def saveData(self, file, data):
+ if hasattr(self.__class__, "encodeData"):
+ data = self.encodeData(data)
+ self.length = len(data)
+ file.seek(self.offset)
+ file.write(data)
+
+ def decodeData(self, rawData):
+ return rawData
+
+ def encodeData(self, data):
+ return data
+
+
+class SFNTDirectoryEntry(DirectoryEntry):
+ format = sfntDirectoryEntryFormat
+ formatSize = sfntDirectoryEntrySize
+
+
+class WOFFDirectoryEntry(DirectoryEntry):
+ format = woffDirectoryEntryFormat
+ formatSize = woffDirectoryEntrySize
+
+ def __init__(self):
+ super(WOFFDirectoryEntry, self).__init__()
+ # With fonttools<=3.1.2, the only way to set a different zlib
+ # compression level for WOFF directory entries was to set the class
+ # attribute 'zlibCompressionLevel'. This is now replaced by a globally
+ # defined `ZLIB_COMPRESSION_LEVEL`, which is also applied when
+ # compressing the metadata. For backward compatibility, we still
+ # use the class attribute if it was already set.
+ if not hasattr(WOFFDirectoryEntry, "zlibCompressionLevel"):
+ self.zlibCompressionLevel = ZLIB_COMPRESSION_LEVEL
+
+ def decodeData(self, rawData):
+ import zlib
+
+ if self.length == self.origLength:
+ data = rawData
+ else:
+ assert self.length < self.origLength
+ data = zlib.decompress(rawData)
+ assert len(data) == self.origLength
+ return data
+
+ def encodeData(self, data):
+ self.origLength = len(data)
+ if not self.uncompressed:
+ compressedData = compress(data, self.zlibCompressionLevel)
+ if self.uncompressed or len(compressedData) >= self.origLength:
+ # Encode uncompressed
+ rawData = data
+ self.length = self.origLength
+ else:
+ rawData = compressedData
+ self.length = len(rawData)
+ return rawData
+
+
+class WOFFFlavorData:
+ Flavor = "woff"
+
+ def __init__(self, reader=None):
+ self.majorVersion = None
+ self.minorVersion = None
+ self.metaData = None
+ self.privData = None
+ if reader:
+ self.majorVersion = reader.majorVersion
+ self.minorVersion = reader.minorVersion
+ if reader.metaLength:
+ reader.file.seek(reader.metaOffset)
+ rawData = reader.file.read(reader.metaLength)
+ assert len(rawData) == reader.metaLength
+ data = self._decompress(rawData)
+ assert len(data) == reader.metaOrigLength
+ self.metaData = data
+ if reader.privLength:
+ reader.file.seek(reader.privOffset)
+ data = reader.file.read(reader.privLength)
+ assert len(data) == reader.privLength
+ self.privData = data
+
+ def _decompress(self, rawData):
+ import zlib
+
+ return zlib.decompress(rawData)
+
+
+def calcChecksum(data):
+ """Calculate the checksum for an arbitrary block of data.
+
+ If the data length is not a multiple of four, it assumes
+ it is to be padded with null byte.
+
+ >>> print(calcChecksum(b"abcd"))
+ 1633837924
+ >>> print(calcChecksum(b"abcdxyz"))
+ 3655064932
+ """
+ remainder = len(data) % 4
+ if remainder:
+ data += b"\0" * (4 - remainder)
+ value = 0
+ blockSize = 4096
+ assert blockSize % 4 == 0
+ for i in range(0, len(data), blockSize):
+ block = data[i : i + blockSize]
+ longs = struct.unpack(">%dL" % (len(block) // 4), block)
+ value = (value + sum(longs)) & 0xFFFFFFFF
+ return value
+
+
+def readTTCHeader(file):
+ file.seek(0)
+ data = file.read(ttcHeaderSize)
+ if len(data) != ttcHeaderSize:
+ raise TTLibError("Not a Font Collection (not enough data)")
+ self = SimpleNamespace()
+ sstruct.unpack(ttcHeaderFormat, data, self)
+ if self.TTCTag != "ttcf":
+ raise TTLibError("Not a Font Collection")
+ assert self.Version == 0x00010000 or self.Version == 0x00020000, (
+ "unrecognized TTC version 0x%08x" % self.Version
+ )
+ self.offsetTable = struct.unpack(
+ ">%dL" % self.numFonts, file.read(self.numFonts * 4)
+ )
+ if self.Version == 0x00020000:
+ pass # ignoring version 2.0 signatures
+ return self
+
+
+def writeTTCHeader(file, numFonts):
+ self = SimpleNamespace()
+ self.TTCTag = "ttcf"
+ self.Version = 0x00010000
+ self.numFonts = numFonts
+ file.seek(0)
+ file.write(sstruct.pack(ttcHeaderFormat, self))
+ offset = file.tell()
+ file.write(struct.pack(">%dL" % self.numFonts, *([0] * self.numFonts)))
+ return offset
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/B_A_S_E_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/B_A_S_E_.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f4b1c3c5a7a3fe53de316dfd9b23fe5096eeb37
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/B_A_S_E_.py
@@ -0,0 +1,14 @@
+from .otBase import BaseTTXConverter
+
+
+class table_B_A_S_E_(BaseTTXConverter):
+ """Baseline table
+
+ The ``BASE`` table contains information needed to align glyphs in
+ different scripts, from different fonts, or at different sizes
+ within the same line of text.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/base
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/BitmapGlyphMetrics.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/BitmapGlyphMetrics.py
new file mode 100644
index 0000000000000000000000000000000000000000..10b4f828213b8320d54eefed3d5e66f2ba532101
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/BitmapGlyphMetrics.py
@@ -0,0 +1,64 @@
+# Since bitmap glyph metrics are shared between EBLC and EBDT
+# this class gets its own python file.
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+import logging
+
+
+log = logging.getLogger(__name__)
+
+bigGlyphMetricsFormat = """
+ > # big endian
+ height: B
+ width: B
+ horiBearingX: b
+ horiBearingY: b
+ horiAdvance: B
+ vertBearingX: b
+ vertBearingY: b
+ vertAdvance: B
+"""
+
+smallGlyphMetricsFormat = """
+ > # big endian
+ height: B
+ width: B
+ BearingX: b
+ BearingY: b
+ Advance: B
+"""
+
+
+class BitmapGlyphMetrics(object):
+ def toXML(self, writer, ttFont):
+ writer.begintag(self.__class__.__name__)
+ writer.newline()
+ for metricName in sstruct.getformat(self.__class__.binaryFormat)[1]:
+ writer.simpletag(metricName, value=getattr(self, metricName))
+ writer.newline()
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ metricNames = set(sstruct.getformat(self.__class__.binaryFormat)[1])
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ # Make sure this is a metric that is needed by GlyphMetrics.
+ if name in metricNames:
+ vars(self)[name] = safeEval(attrs["value"])
+ else:
+ log.warning(
+ "unknown name '%s' being ignored in %s.",
+ name,
+ self.__class__.__name__,
+ )
+
+
+class BigGlyphMetrics(BitmapGlyphMetrics):
+ binaryFormat = bigGlyphMetricsFormat
+
+
+class SmallGlyphMetrics(BitmapGlyphMetrics):
+ binaryFormat = smallGlyphMetricsFormat
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_D_T_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_D_T_.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f9924745ebb4519be2d0cdcddadb86cc741cf8b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_D_T_.py
@@ -0,0 +1,113 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Matt Fontaine
+
+
+from fontTools.misc.textTools import bytesjoin
+from fontTools.misc import sstruct
+from . import E_B_D_T_
+from .BitmapGlyphMetrics import (
+ BigGlyphMetrics,
+ bigGlyphMetricsFormat,
+ SmallGlyphMetrics,
+ smallGlyphMetricsFormat,
+)
+from .E_B_D_T_ import (
+ BitmapGlyph,
+ BitmapPlusSmallMetricsMixin,
+ BitmapPlusBigMetricsMixin,
+)
+import struct
+
+
+class table_C_B_D_T_(E_B_D_T_.table_E_B_D_T_):
+ """Color Bitmap Data table
+
+ The ``CBDT`` table contains color bitmap data for glyphs. It must
+ be used in concert with the ``CBLC`` table.
+
+ It is backwards-compatible with the monochrome/grayscale ``EBDT`` table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt
+ """
+
+ # Change the data locator table being referenced.
+ locatorName = "CBLC"
+
+ # Modify the format class accessor for color bitmap use.
+ def getImageFormatClass(self, imageFormat):
+ try:
+ return E_B_D_T_.table_E_B_D_T_.getImageFormatClass(self, imageFormat)
+ except KeyError:
+ return cbdt_bitmap_classes[imageFormat]
+
+
+# Helper method for removing export features not supported by color bitmaps.
+# Write data in the parent class will default to raw if an option is unsupported.
+def _removeUnsupportedForColor(dataFunctions):
+ dataFunctions = dict(dataFunctions)
+ del dataFunctions["row"]
+ return dataFunctions
+
+
+class ColorBitmapGlyph(BitmapGlyph):
+ fileExtension = ".png"
+ xmlDataFunctions = _removeUnsupportedForColor(BitmapGlyph.xmlDataFunctions)
+
+
+class cbdt_bitmap_format_17(BitmapPlusSmallMetricsMixin, ColorBitmapGlyph):
+ def decompile(self):
+ self.metrics = SmallGlyphMetrics()
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
+ (dataLen,) = struct.unpack(">L", data[:4])
+ data = data[4:]
+
+ # For the image data cut it to the size specified by dataLen.
+ assert dataLen <= len(data), "Data overun in format 17"
+ self.imageData = data[:dataLen]
+
+ def compile(self, ttFont):
+ dataList = []
+ dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
+ dataList.append(struct.pack(">L", len(self.imageData)))
+ dataList.append(self.imageData)
+ return bytesjoin(dataList)
+
+
+class cbdt_bitmap_format_18(BitmapPlusBigMetricsMixin, ColorBitmapGlyph):
+ def decompile(self):
+ self.metrics = BigGlyphMetrics()
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
+ (dataLen,) = struct.unpack(">L", data[:4])
+ data = data[4:]
+
+ # For the image data cut it to the size specified by dataLen.
+ assert dataLen <= len(data), "Data overun in format 18"
+ self.imageData = data[:dataLen]
+
+ def compile(self, ttFont):
+ dataList = []
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ dataList.append(struct.pack(">L", len(self.imageData)))
+ dataList.append(self.imageData)
+ return bytesjoin(dataList)
+
+
+class cbdt_bitmap_format_19(ColorBitmapGlyph):
+ def decompile(self):
+ (dataLen,) = struct.unpack(">L", self.data[:4])
+ data = self.data[4:]
+
+ assert dataLen <= len(data), "Data overun in format 19"
+ self.imageData = data[:dataLen]
+
+ def compile(self, ttFont):
+ return struct.pack(">L", len(self.imageData)) + self.imageData
+
+
+# Dict for CBDT extended formats.
+cbdt_bitmap_classes = {
+ 17: cbdt_bitmap_format_17,
+ 18: cbdt_bitmap_format_18,
+ 19: cbdt_bitmap_format_19,
+}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_L_C_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_L_C_.py
new file mode 100644
index 0000000000000000000000000000000000000000..0673fb7ed2215f481f303c5b98c9941accd916bf
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_B_L_C_.py
@@ -0,0 +1,19 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Matt Fontaine
+
+from . import E_B_L_C_
+
+
+class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_):
+ """Color Bitmap Location table
+
+ The ``CBLC`` table contains the locations of color bitmaps for glyphs. It must
+ be used in concert with the ``CBDT`` table.
+
+ It is backwards-compatible with the monochrome/grayscale ``EBLC`` table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cblc
+ """
+
+ dependencies = ["CBDT"]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F_.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d974ced88e9d9a1e9c0494c4d0eac996ec315b8
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F_.py
@@ -0,0 +1,61 @@
+from io import BytesIO
+from fontTools import cffLib
+from . import DefaultTable
+
+
+class table_C_F_F_(DefaultTable.DefaultTable):
+ """Compact Font Format table (version 1)
+
+ The ``CFF`` table embeds a CFF-formatted font. The CFF font format
+ predates OpenType and could be used as a standalone font file, but the
+ ``CFF`` table is also used to package CFF fonts into an OpenType
+ container.
+
+ .. note::
+ ``CFF`` has been succeeded by ``CFF2``, which eliminates much of
+ the redundancy incurred by embedding CFF version 1 in an OpenType
+ font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cff
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.cff = cffLib.CFFFontSet()
+ self._gaveGlyphOrder = False
+
+ def decompile(self, data, otFont):
+ self.cff.decompile(BytesIO(data), otFont, isCFF2=False)
+ assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
+
+ def compile(self, otFont):
+ f = BytesIO()
+ self.cff.compile(f, otFont, isCFF2=False)
+ return f.getvalue()
+
+ def haveGlyphNames(self):
+ if hasattr(self.cff[self.cff.fontNames[0]], "ROS"):
+ return False # CID-keyed font
+ else:
+ return True
+
+ def getGlyphOrder(self):
+ if self._gaveGlyphOrder:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("illegal use of getGlyphOrder()")
+ self._gaveGlyphOrder = True
+ return self.cff[self.cff.fontNames[0]].getGlyphOrder()
+
+ def setGlyphOrder(self, glyphOrder):
+ pass
+ # XXX
+ # self.cff[self.cff.fontNames[0]].setGlyphOrder(glyphOrder)
+
+ def toXML(self, writer, otFont):
+ self.cff.toXML(writer)
+
+ def fromXML(self, name, attrs, content, otFont):
+ if not hasattr(self, "cff"):
+ self.cff = cffLib.CFFFontSet()
+ self.cff.fromXML(name, attrs, content, otFont)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F__2.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F__2.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff07682d24f0399b6579f529ae70cf4fd87abf21
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_F_F__2.py
@@ -0,0 +1,26 @@
+from io import BytesIO
+from fontTools.ttLib.tables.C_F_F_ import table_C_F_F_
+
+
+class table_C_F_F__2(table_C_F_F_):
+ """Compact Font Format version 2 table
+
+ The ``CFF2`` table contains glyph data for a CFF2-flavored OpenType
+ font.
+
+ .. note::
+ ``CFF2`` is the successor to ``CFF``, and eliminates much of
+ the redundancy incurred by embedding CFF version 1 in an OpenType
+ font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cff2
+ """
+
+ def decompile(self, data, otFont):
+ self.cff.decompile(BytesIO(data), otFont, isCFF2=True)
+ assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
+
+ def compile(self, otFont):
+ f = BytesIO()
+ self.cff.compile(f, otFont, isCFF2=True)
+ return f.getvalue()
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_O_L_R_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_O_L_R_.py
new file mode 100644
index 0000000000000000000000000000000000000000..266a11c41dffc5eed42ee446c0268373104cbf7a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_O_L_R_.py
@@ -0,0 +1,165 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod
+
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+
+
+class table_C_O_L_R_(DefaultTable.DefaultTable):
+ """Color table
+
+ The ``COLR`` table defines color presentation of outline glyphs. It must
+ be used in concert with the ``CPAL`` table, which contains the color
+ descriptors used.
+
+ This table is structured so that you can treat it like a dictionary keyed by glyph name.
+
+ ``ttFont['COLR'][]`` will return the color layers for any glyph.
+
+ ``ttFont['COLR'][] = `` will set the color layers for any glyph.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/colr
+ """
+
+ @staticmethod
+ def _decompileColorLayersV0(table):
+ if not table.LayerRecordArray:
+ return {}
+ colorLayerLists = {}
+ layerRecords = table.LayerRecordArray.LayerRecord
+ numLayerRecords = len(layerRecords)
+ for baseRec in table.BaseGlyphRecordArray.BaseGlyphRecord:
+ baseGlyph = baseRec.BaseGlyph
+ firstLayerIndex = baseRec.FirstLayerIndex
+ numLayers = baseRec.NumLayers
+ assert firstLayerIndex + numLayers <= numLayerRecords
+ layers = []
+ for i in range(firstLayerIndex, firstLayerIndex + numLayers):
+ layerRec = layerRecords[i]
+ layers.append(LayerRecord(layerRec.LayerGlyph, layerRec.PaletteIndex))
+ colorLayerLists[baseGlyph] = layers
+ return colorLayerLists
+
+ def _toOTTable(self, ttFont):
+ from . import otTables
+ from fontTools.colorLib.builder import populateCOLRv0
+
+ tableClass = getattr(otTables, self.tableTag)
+ table = tableClass()
+ table.Version = self.version
+
+ populateCOLRv0(
+ table,
+ {
+ baseGlyph: [(layer.name, layer.colorID) for layer in layers]
+ for baseGlyph, layers in self.ColorLayers.items()
+ },
+ glyphMap=ttFont.getReverseGlyphMap(rebuild=True),
+ )
+ return table
+
+ def decompile(self, data, ttFont):
+ from .otBase import OTTableReader
+ from . import otTables
+
+ # We use otData to decompile, but we adapt the decompiled otTables to the
+ # existing COLR v0 API for backward compatibility.
+ reader = OTTableReader(data, tableTag=self.tableTag)
+ tableClass = getattr(otTables, self.tableTag)
+ table = tableClass()
+ table.decompile(reader, ttFont)
+
+ self.version = table.Version
+ if self.version == 0:
+ self.ColorLayers = self._decompileColorLayersV0(table)
+ else:
+ # for new versions, keep the raw otTables around
+ self.table = table
+
+ def compile(self, ttFont):
+ from .otBase import OTTableWriter
+
+ if hasattr(self, "table"):
+ table = self.table
+ else:
+ table = self._toOTTable(ttFont)
+
+ writer = OTTableWriter(tableTag=self.tableTag)
+ table.compile(writer, ttFont)
+ return writer.getAllData()
+
+ def toXML(self, writer, ttFont):
+ if hasattr(self, "table"):
+ self.table.toXML2(writer, ttFont)
+ else:
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ for baseGlyph in sorted(self.ColorLayers.keys(), key=ttFont.getGlyphID):
+ writer.begintag("ColorGlyph", name=baseGlyph)
+ writer.newline()
+ for layer in self.ColorLayers[baseGlyph]:
+ layer.toXML(writer, ttFont)
+ writer.endtag("ColorGlyph")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version": # old COLR v0 API
+ setattr(self, name, safeEval(attrs["value"]))
+ elif name == "ColorGlyph":
+ if not hasattr(self, "ColorLayers"):
+ self.ColorLayers = {}
+ glyphName = attrs["name"]
+ for element in content:
+ if isinstance(element, str):
+ continue
+ layers = []
+ for element in content:
+ if isinstance(element, str):
+ continue
+ layer = LayerRecord()
+ layer.fromXML(element[0], element[1], element[2], ttFont)
+ layers.append(layer)
+ self.ColorLayers[glyphName] = layers
+ else: # new COLR v1 API
+ from . import otTables
+
+ if not hasattr(self, "table"):
+ tableClass = getattr(otTables, self.tableTag)
+ self.table = tableClass()
+ self.table.fromXML(name, attrs, content, ttFont)
+ self.table.populateDefaults()
+ self.version = self.table.Version
+
+ def __getitem__(self, glyphName):
+ if not isinstance(glyphName, str):
+ raise TypeError(f"expected str, found {type(glyphName).__name__}")
+ return self.ColorLayers[glyphName]
+
+ def __setitem__(self, glyphName, value):
+ if not isinstance(glyphName, str):
+ raise TypeError(f"expected str, found {type(glyphName).__name__}")
+ if value is not None:
+ self.ColorLayers[glyphName] = value
+ elif glyphName in self.ColorLayers:
+ del self.ColorLayers[glyphName]
+
+ def __delitem__(self, glyphName):
+ del self.ColorLayers[glyphName]
+
+
+class LayerRecord(object):
+ def __init__(self, name=None, colorID=None):
+ self.name = name
+ self.colorID = colorID
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("layer", name=self.name, colorID=self.colorID)
+ writer.newline()
+
+ def fromXML(self, eltname, attrs, content, ttFont):
+ for name, value in attrs.items():
+ if name == "name":
+ setattr(self, name, value)
+ else:
+ setattr(self, name, safeEval(value))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/C_P_A_L_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_P_A_L_.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ec8c843c516f2ac31bbbc9ff2dd0166a3a0e159
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/C_P_A_L_.py
@@ -0,0 +1,305 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod
+
+from fontTools.misc.textTools import bytesjoin, safeEval
+from . import DefaultTable
+import array
+from collections import namedtuple
+import struct
+import sys
+
+
+class table_C_P_A_L_(DefaultTable.DefaultTable):
+ """Color Palette table
+
+ The ``CPAL`` table contains a set of one or more color palettes. The color
+ records in each palette can be referenced by the ``COLR`` table to specify
+ the colors used in a color glyph.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cpal
+ """
+
+ NO_NAME_ID = 0xFFFF
+ DEFAULT_PALETTE_TYPE = 0
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.palettes = []
+ self.paletteTypes = []
+ self.paletteLabels = []
+ self.paletteEntryLabels = []
+
+ def decompile(self, data, ttFont):
+ (
+ self.version,
+ self.numPaletteEntries,
+ numPalettes,
+ numColorRecords,
+ goffsetFirstColorRecord,
+ ) = struct.unpack(">HHHHL", data[:12])
+ assert (
+ self.version <= 1
+ ), "Version of CPAL table is higher than I know how to handle"
+ self.palettes = []
+ pos = 12
+ for i in range(numPalettes):
+ startIndex = struct.unpack(">H", data[pos : pos + 2])[0]
+ assert startIndex + self.numPaletteEntries <= numColorRecords
+ pos += 2
+ palette = []
+ ppos = goffsetFirstColorRecord + startIndex * 4
+ for j in range(self.numPaletteEntries):
+ palette.append(Color(*struct.unpack(">BBBB", data[ppos : ppos + 4])))
+ ppos += 4
+ self.palettes.append(palette)
+ if self.version == 0:
+ offsetToPaletteTypeArray = 0
+ offsetToPaletteLabelArray = 0
+ offsetToPaletteEntryLabelArray = 0
+ else:
+ pos = 12 + numPalettes * 2
+ (
+ offsetToPaletteTypeArray,
+ offsetToPaletteLabelArray,
+ offsetToPaletteEntryLabelArray,
+ ) = struct.unpack(">LLL", data[pos : pos + 12])
+ self.paletteTypes = self._decompileUInt32Array(
+ data,
+ offsetToPaletteTypeArray,
+ numPalettes,
+ default=self.DEFAULT_PALETTE_TYPE,
+ )
+ self.paletteLabels = self._decompileUInt16Array(
+ data, offsetToPaletteLabelArray, numPalettes, default=self.NO_NAME_ID
+ )
+ self.paletteEntryLabels = self._decompileUInt16Array(
+ data,
+ offsetToPaletteEntryLabelArray,
+ self.numPaletteEntries,
+ default=self.NO_NAME_ID,
+ )
+
+ def _decompileUInt16Array(self, data, offset, numElements, default=0):
+ if offset == 0:
+ return [default] * numElements
+ result = array.array("H", data[offset : offset + 2 * numElements])
+ if sys.byteorder != "big":
+ result.byteswap()
+ assert len(result) == numElements, result
+ return result.tolist()
+
+ def _decompileUInt32Array(self, data, offset, numElements, default=0):
+ if offset == 0:
+ return [default] * numElements
+ result = array.array("I", data[offset : offset + 4 * numElements])
+ if sys.byteorder != "big":
+ result.byteswap()
+ assert len(result) == numElements, result
+ return result.tolist()
+
+ def compile(self, ttFont):
+ colorRecordIndices, colorRecords = self._compileColorRecords()
+ paletteTypes = self._compilePaletteTypes()
+ paletteLabels = self._compilePaletteLabels()
+ paletteEntryLabels = self._compilePaletteEntryLabels()
+ numColorRecords = len(colorRecords) // 4
+ offsetToFirstColorRecord = 12 + len(colorRecordIndices)
+ if self.version >= 1:
+ offsetToFirstColorRecord += 12
+ header = struct.pack(
+ ">HHHHL",
+ self.version,
+ self.numPaletteEntries,
+ len(self.palettes),
+ numColorRecords,
+ offsetToFirstColorRecord,
+ )
+ if self.version == 0:
+ dataList = [header, colorRecordIndices, colorRecords]
+ else:
+ pos = offsetToFirstColorRecord + len(colorRecords)
+ if len(paletteTypes) == 0:
+ offsetToPaletteTypeArray = 0
+ else:
+ offsetToPaletteTypeArray = pos
+ pos += len(paletteTypes)
+ if len(paletteLabels) == 0:
+ offsetToPaletteLabelArray = 0
+ else:
+ offsetToPaletteLabelArray = pos
+ pos += len(paletteLabels)
+ if len(paletteEntryLabels) == 0:
+ offsetToPaletteEntryLabelArray = 0
+ else:
+ offsetToPaletteEntryLabelArray = pos
+ pos += len(paletteLabels)
+ header1 = struct.pack(
+ ">LLL",
+ offsetToPaletteTypeArray,
+ offsetToPaletteLabelArray,
+ offsetToPaletteEntryLabelArray,
+ )
+ dataList = [
+ header,
+ colorRecordIndices,
+ header1,
+ colorRecords,
+ paletteTypes,
+ paletteLabels,
+ paletteEntryLabels,
+ ]
+ return bytesjoin(dataList)
+
+ def _compilePalette(self, palette):
+ assert len(palette) == self.numPaletteEntries
+ pack = lambda c: struct.pack(">BBBB", c.blue, c.green, c.red, c.alpha)
+ return bytesjoin([pack(color) for color in palette])
+
+ def _compileColorRecords(self):
+ colorRecords, colorRecordIndices, pool = [], [], {}
+ for palette in self.palettes:
+ packedPalette = self._compilePalette(palette)
+ if packedPalette in pool:
+ index = pool[packedPalette]
+ else:
+ index = len(colorRecords)
+ colorRecords.append(packedPalette)
+ pool[packedPalette] = index
+ colorRecordIndices.append(struct.pack(">H", index * self.numPaletteEntries))
+ return bytesjoin(colorRecordIndices), bytesjoin(colorRecords)
+
+ def _compilePaletteTypes(self):
+ if self.version == 0 or not any(self.paletteTypes):
+ return b""
+ assert len(self.paletteTypes) == len(self.palettes)
+ result = bytesjoin([struct.pack(">I", ptype) for ptype in self.paletteTypes])
+ assert len(result) == 4 * len(self.palettes)
+ return result
+
+ def _compilePaletteLabels(self):
+ if self.version == 0 or all(l == self.NO_NAME_ID for l in self.paletteLabels):
+ return b""
+ assert len(self.paletteLabels) == len(self.palettes)
+ result = bytesjoin([struct.pack(">H", label) for label in self.paletteLabels])
+ assert len(result) == 2 * len(self.palettes)
+ return result
+
+ def _compilePaletteEntryLabels(self):
+ if self.version == 0 or all(
+ l == self.NO_NAME_ID for l in self.paletteEntryLabels
+ ):
+ return b""
+ assert len(self.paletteEntryLabels) == self.numPaletteEntries
+ result = bytesjoin(
+ [struct.pack(">H", label) for label in self.paletteEntryLabels]
+ )
+ assert len(result) == 2 * self.numPaletteEntries
+ return result
+
+ def toXML(self, writer, ttFont):
+ numPalettes = len(self.palettes)
+ paletteLabels = {i: nameID for (i, nameID) in enumerate(self.paletteLabels)}
+ paletteTypes = {i: typ for (i, typ) in enumerate(self.paletteTypes)}
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ writer.simpletag("numPaletteEntries", value=self.numPaletteEntries)
+ writer.newline()
+ for index, palette in enumerate(self.palettes):
+ attrs = {"index": index}
+ paletteType = paletteTypes.get(index, self.DEFAULT_PALETTE_TYPE)
+ paletteLabel = paletteLabels.get(index, self.NO_NAME_ID)
+ if self.version > 0 and paletteLabel != self.NO_NAME_ID:
+ attrs["label"] = paletteLabel
+ if self.version > 0 and paletteType != self.DEFAULT_PALETTE_TYPE:
+ attrs["type"] = paletteType
+ writer.begintag("palette", **attrs)
+ writer.newline()
+ if (
+ self.version > 0
+ and paletteLabel != self.NO_NAME_ID
+ and ttFont
+ and "name" in ttFont
+ ):
+ name = ttFont["name"].getDebugName(paletteLabel)
+ if name is not None:
+ writer.comment(name)
+ writer.newline()
+ assert len(palette) == self.numPaletteEntries
+ for cindex, color in enumerate(palette):
+ color.toXML(writer, ttFont, cindex)
+ writer.endtag("palette")
+ writer.newline()
+ if self.version > 0 and not all(
+ l == self.NO_NAME_ID for l in self.paletteEntryLabels
+ ):
+ writer.begintag("paletteEntryLabels")
+ writer.newline()
+ for index, label in enumerate(self.paletteEntryLabels):
+ if label != self.NO_NAME_ID:
+ writer.simpletag("label", index=index, value=label)
+ if self.version > 0 and label and ttFont and "name" in ttFont:
+ name = ttFont["name"].getDebugName(label)
+ if name is not None:
+ writer.comment(name)
+ writer.newline()
+ writer.endtag("paletteEntryLabels")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "palette":
+ self.paletteLabels.append(int(attrs.get("label", self.NO_NAME_ID)))
+ self.paletteTypes.append(int(attrs.get("type", self.DEFAULT_PALETTE_TYPE)))
+ palette = []
+ for element in content:
+ if isinstance(element, str):
+ continue
+ attrs = element[1]
+ color = Color.fromHex(attrs["value"])
+ palette.append(color)
+ self.palettes.append(palette)
+ elif name == "paletteEntryLabels":
+ colorLabels = {}
+ for element in content:
+ if isinstance(element, str):
+ continue
+ elementName, elementAttr, _ = element
+ if elementName == "label":
+ labelIndex = safeEval(elementAttr["index"])
+ nameID = safeEval(elementAttr["value"])
+ colorLabels[labelIndex] = nameID
+ self.paletteEntryLabels = [
+ colorLabels.get(i, self.NO_NAME_ID)
+ for i in range(self.numPaletteEntries)
+ ]
+ elif "value" in attrs:
+ value = safeEval(attrs["value"])
+ setattr(self, name, value)
+ if name == "numPaletteEntries":
+ self.paletteEntryLabels = [self.NO_NAME_ID] * self.numPaletteEntries
+
+
+class Color(namedtuple("Color", "blue green red alpha")):
+ def hex(self):
+ return "#%02X%02X%02X%02X" % (self.red, self.green, self.blue, self.alpha)
+
+ def __repr__(self):
+ return self.hex()
+
+ def toXML(self, writer, ttFont, index=None):
+ writer.simpletag("color", value=self.hex(), index=index)
+ writer.newline()
+
+ @classmethod
+ def fromHex(cls, value):
+ if value[0] == "#":
+ value = value[1:]
+ red = int(value[0:2], 16)
+ green = int(value[2:4], 16)
+ blue = int(value[4:6], 16)
+ alpha = int(value[6:8], 16) if len(value) >= 8 else 0xFF
+ return cls(red=red, green=green, blue=blue, alpha=alpha)
+
+ @classmethod
+ def fromRGBA(cls, red, green, blue, alpha):
+ return cls(red=red, green=green, blue=blue, alpha=alpha)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/D_S_I_G_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/D_S_I_G_.py
new file mode 100644
index 0000000000000000000000000000000000000000..f89cc29e49f6be5e88ebf7b6daa0139a7c5d2ae2
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/D_S_I_G_.py
@@ -0,0 +1,158 @@
+from fontTools.misc.textTools import bytesjoin, strjoin, tobytes, tostr, safeEval
+from fontTools.misc import sstruct
+from . import DefaultTable
+import base64
+
+DSIG_HeaderFormat = """
+ > # big endian
+ ulVersion: L
+ usNumSigs: H
+ usFlag: H
+"""
+# followed by an array of usNumSigs DSIG_Signature records
+DSIG_SignatureFormat = """
+ > # big endian
+ ulFormat: L
+ ulLength: L # length includes DSIG_SignatureBlock header
+ ulOffset: L
+"""
+# followed by an array of usNumSigs DSIG_SignatureBlock records,
+# each followed immediately by the pkcs7 bytes
+DSIG_SignatureBlockFormat = """
+ > # big endian
+ usReserved1: H
+ usReserved2: H
+ cbSignature: l # length of following raw pkcs7 data
+"""
+
+#
+# NOTE
+# the DSIG table format allows for SignatureBlocks residing
+# anywhere in the table and possibly in a different order as
+# listed in the array after the first table header
+#
+# this implementation does not keep track of any gaps and/or data
+# before or after the actual signature blocks while decompiling,
+# and puts them in the same physical order as listed in the header
+# on compilation with no padding whatsoever.
+#
+
+
+class table_D_S_I_G_(DefaultTable.DefaultTable):
+ """Digital Signature table
+
+ The ``DSIG`` table contains cryptographic signatures for the font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/dsig
+ """
+
+ def decompile(self, data, ttFont):
+ dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self)
+ assert self.ulVersion == 1, "DSIG ulVersion must be 1"
+ assert self.usFlag & ~1 == 0, "DSIG usFlag must be 0x1 or 0x0"
+ self.signatureRecords = sigrecs = []
+ for n in range(self.usNumSigs):
+ sigrec, newData = sstruct.unpack2(
+ DSIG_SignatureFormat, newData, SignatureRecord()
+ )
+ assert sigrec.ulFormat == 1, (
+ "DSIG signature record #%d ulFormat must be 1" % n
+ )
+ sigrecs.append(sigrec)
+ for sigrec in sigrecs:
+ dummy, newData = sstruct.unpack2(
+ DSIG_SignatureBlockFormat, data[sigrec.ulOffset :], sigrec
+ )
+ assert sigrec.usReserved1 == 0, (
+ "DSIG signature record #%d usReserverd1 must be 0" % n
+ )
+ assert sigrec.usReserved2 == 0, (
+ "DSIG signature record #%d usReserverd2 must be 0" % n
+ )
+ sigrec.pkcs7 = newData[: sigrec.cbSignature]
+
+ def compile(self, ttFont):
+ packed = sstruct.pack(DSIG_HeaderFormat, self)
+ headers = [packed]
+ offset = len(packed) + self.usNumSigs * sstruct.calcsize(DSIG_SignatureFormat)
+ data = []
+ for sigrec in self.signatureRecords:
+ # first pack signature block
+ sigrec.cbSignature = len(sigrec.pkcs7)
+ packed = sstruct.pack(DSIG_SignatureBlockFormat, sigrec) + sigrec.pkcs7
+ data.append(packed)
+ # update redundant length field
+ sigrec.ulLength = len(packed)
+ # update running table offset
+ sigrec.ulOffset = offset
+ headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec))
+ offset += sigrec.ulLength
+ if offset % 2:
+ # Pad to even bytes
+ data.append(b"\0")
+ return bytesjoin(headers + data)
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.comment(
+ "note that the Digital Signature will be invalid after recompilation!"
+ )
+ xmlWriter.newline()
+ xmlWriter.simpletag(
+ "tableHeader",
+ version=self.ulVersion,
+ numSigs=self.usNumSigs,
+ flag="0x%X" % self.usFlag,
+ )
+ for sigrec in self.signatureRecords:
+ xmlWriter.newline()
+ sigrec.toXML(xmlWriter, ttFont)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "tableHeader":
+ self.signatureRecords = []
+ self.ulVersion = safeEval(attrs["version"])
+ self.usNumSigs = safeEval(attrs["numSigs"])
+ self.usFlag = safeEval(attrs["flag"])
+ return
+ if name == "SignatureRecord":
+ sigrec = SignatureRecord()
+ sigrec.fromXML(name, attrs, content, ttFont)
+ self.signatureRecords.append(sigrec)
+
+
+pem_spam = lambda l, spam={
+ "-----BEGIN PKCS7-----": True,
+ "-----END PKCS7-----": True,
+ "": True,
+}: not spam.get(l.strip())
+
+
+def b64encode(b):
+ s = base64.b64encode(b)
+ # Line-break at 76 chars.
+ items = []
+ while s:
+ items.append(tostr(s[:76]))
+ items.append("\n")
+ s = s[76:]
+ return strjoin(items)
+
+
+class SignatureRecord(object):
+ def __repr__(self):
+ return "<%s: %s>" % (self.__class__.__name__, self.__dict__)
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(self.__class__.__name__, format=self.ulFormat)
+ writer.newline()
+ writer.write_noindent("-----BEGIN PKCS7-----\n")
+ writer.write_noindent(b64encode(self.pkcs7))
+ writer.write_noindent("-----END PKCS7-----\n")
+ writer.endtag(self.__class__.__name__)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.ulFormat = safeEval(attrs["format"])
+ self.usReserved1 = safeEval(attrs.get("reserved1", "0"))
+ self.usReserved2 = safeEval(attrs.get("reserved2", "0"))
+ self.pkcs7 = base64.b64decode(tobytes(strjoin(filter(pem_spam, content))))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/D__e_b_g.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/D__e_b_g.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb1653d79d4f99ad729b25c96346459c40a4681e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/D__e_b_g.py
@@ -0,0 +1,35 @@
+import json
+from textwrap import indent
+
+from . import DefaultTable
+from fontTools.misc.textTools import tostr
+
+
+class table_D__e_b_g(DefaultTable.DefaultTable):
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.data = {}
+
+ def decompile(self, data, ttFont):
+ self.data = json.loads(data)
+
+ def compile(self, ttFont):
+ return json.dumps(self.data).encode("utf-8")
+
+ def toXML(self, writer, ttFont):
+ # make sure json indentation inside CDATA block matches XMLWriter's
+ data = json.dumps(self.data, indent=len(writer.indentwhite))
+ prefix = tostr(writer.indentwhite) * (writer.indentlevel + 1)
+ # but don't indent the first json line so it's adjacent to `" % (self.tableTag, id(self))
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_D_T_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_D_T_.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea23e30f83d38f77587f29cf40b3eec41f715be7
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_D_T_.py
@@ -0,0 +1,835 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import (
+ bytechr,
+ byteord,
+ bytesjoin,
+ strjoin,
+ safeEval,
+ readHex,
+ hexStr,
+ deHexStr,
+)
+from .BitmapGlyphMetrics import (
+ BigGlyphMetrics,
+ bigGlyphMetricsFormat,
+ SmallGlyphMetrics,
+ smallGlyphMetricsFormat,
+)
+from . import DefaultTable
+import itertools
+import os
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+ebdtTableVersionFormat = """
+ > # big endian
+ version: 16.16F
+"""
+
+ebdtComponentFormat = """
+ > # big endian
+ glyphCode: H
+ xOffset: b
+ yOffset: b
+"""
+
+
+class table_E_B_D_T_(DefaultTable.DefaultTable):
+ """Embedded Bitmap Data table
+
+ The ``EBDT`` table contains monochrome or grayscale bitmap data for
+ glyphs. It must be used in concert with the ``EBLC`` table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt
+ """
+
+ # Keep a reference to the name of the data locator table.
+ locatorName = "EBLC"
+
+ # This method can be overridden in subclasses to support new formats
+ # without changing the other implementation. Also can be used as a
+ # convenience method for coverting a font file to an alternative format.
+ def getImageFormatClass(self, imageFormat):
+ return ebdt_bitmap_classes[imageFormat]
+
+ def decompile(self, data, ttFont):
+ # Get the version but don't advance the slice.
+ # Most of the lookup for this table is done relative
+ # to the begining so slice by the offsets provided
+ # in the EBLC table.
+ sstruct.unpack2(ebdtTableVersionFormat, data, self)
+
+ # Keep a dict of glyphs that have been seen so they aren't remade.
+ # This dict maps intervals of data to the BitmapGlyph.
+ glyphDict = {}
+
+ # Pull out the EBLC table and loop through glyphs.
+ # A strike is a concept that spans both tables.
+ # The actual bitmap data is stored in the EBDT.
+ locator = ttFont[self.__class__.locatorName]
+ self.strikeData = []
+ for curStrike in locator.strikes:
+ bitmapGlyphDict = {}
+ self.strikeData.append(bitmapGlyphDict)
+ for indexSubTable in curStrike.indexSubTables:
+ dataIter = zip(indexSubTable.names, indexSubTable.locations)
+ for curName, curLoc in dataIter:
+ # Don't create duplicate data entries for the same glyphs.
+ # Instead just use the structures that already exist if they exist.
+ if curLoc in glyphDict:
+ curGlyph = glyphDict[curLoc]
+ else:
+ curGlyphData = data[slice(*curLoc)]
+ imageFormatClass = self.getImageFormatClass(
+ indexSubTable.imageFormat
+ )
+ curGlyph = imageFormatClass(curGlyphData, ttFont)
+ glyphDict[curLoc] = curGlyph
+ bitmapGlyphDict[curName] = curGlyph
+
+ def compile(self, ttFont):
+ dataList = []
+ dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
+ dataSize = len(dataList[0])
+
+ # Keep a dict of glyphs that have been seen so they aren't remade.
+ # This dict maps the id of the BitmapGlyph to the interval
+ # in the data.
+ glyphDict = {}
+
+ # Go through the bitmap glyph data. Just in case the data for a glyph
+ # changed the size metrics should be recalculated. There are a variety
+ # of formats and they get stored in the EBLC table. That is why
+ # recalculation is defered to the EblcIndexSubTable class and just
+ # pass what is known about bitmap glyphs from this particular table.
+ locator = ttFont[self.__class__.locatorName]
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
+ for curIndexSubTable in curStrike.indexSubTables:
+ dataLocations = []
+ for curName in curIndexSubTable.names:
+ # Handle the data placement based on seeing the glyph or not.
+ # Just save a reference to the location if the glyph has already
+ # been saved in compile. This code assumes that glyphs will only
+ # be referenced multiple times from indexFormat5. By luck the
+ # code may still work when referencing poorly ordered fonts with
+ # duplicate references. If there is a font that is unlucky the
+ # respective compile methods for the indexSubTables will fail
+ # their assertions. All fonts seem to follow this assumption.
+ # More complicated packing may be needed if a counter-font exists.
+ glyph = curGlyphDict[curName]
+ objectId = id(glyph)
+ if objectId not in glyphDict:
+ data = glyph.compile(ttFont)
+ data = curIndexSubTable.padBitmapData(data)
+ startByte = dataSize
+ dataSize += len(data)
+ endByte = dataSize
+ dataList.append(data)
+ dataLoc = (startByte, endByte)
+ glyphDict[objectId] = dataLoc
+ else:
+ dataLoc = glyphDict[objectId]
+ dataLocations.append(dataLoc)
+ # Just use the new data locations in the indexSubTable.
+ # The respective compile implementations will take care
+ # of any of the problems in the convertion that may arise.
+ curIndexSubTable.locations = dataLocations
+
+ return bytesjoin(dataList)
+
+ def toXML(self, writer, ttFont):
+ # When exporting to XML if one of the data export formats
+ # requires metrics then those metrics may be in the locator.
+ # In this case populate the bitmaps with "export metrics".
+ if ttFont.bitmapGlyphDataFormat in ("row", "bitwise"):
+ locator = ttFont[self.__class__.locatorName]
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
+ for curIndexSubTable in curStrike.indexSubTables:
+ for curName in curIndexSubTable.names:
+ glyph = curGlyphDict[curName]
+ # I'm not sure which metrics have priority here.
+ # For now if both metrics exist go with glyph metrics.
+ if hasattr(glyph, "metrics"):
+ glyph.exportMetrics = glyph.metrics
+ else:
+ glyph.exportMetrics = curIndexSubTable.metrics
+ glyph.exportBitDepth = curStrike.bitmapSizeTable.bitDepth
+
+ writer.simpletag("header", [("version", self.version)])
+ writer.newline()
+ locator = ttFont[self.__class__.locatorName]
+ for strikeIndex, bitmapGlyphDict in enumerate(self.strikeData):
+ writer.begintag("strikedata", [("index", strikeIndex)])
+ writer.newline()
+ for curName, curBitmap in bitmapGlyphDict.items():
+ curBitmap.toXML(strikeIndex, curName, writer, ttFont)
+ writer.endtag("strikedata")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "header":
+ self.version = safeEval(attrs["version"])
+ elif name == "strikedata":
+ if not hasattr(self, "strikeData"):
+ self.strikeData = []
+ strikeIndex = safeEval(attrs["index"])
+
+ bitmapGlyphDict = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
+ imageFormat = safeEval(name[len(_bitmapGlyphSubclassPrefix) :])
+ glyphName = attrs["name"]
+ imageFormatClass = self.getImageFormatClass(imageFormat)
+ curGlyph = imageFormatClass(None, None)
+ curGlyph.fromXML(name, attrs, content, ttFont)
+ assert glyphName not in bitmapGlyphDict, (
+ "Duplicate glyphs with the same name '%s' in the same strike."
+ % glyphName
+ )
+ bitmapGlyphDict[glyphName] = curGlyph
+ else:
+ log.warning("%s being ignored by %s", name, self.__class__.__name__)
+
+ # Grow the strike data array to the appropriate size. The XML
+ # format allows the strike index value to be out of order.
+ if strikeIndex >= len(self.strikeData):
+ self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
+ assert (
+ self.strikeData[strikeIndex] is None
+ ), "Duplicate strike EBDT indices."
+ self.strikeData[strikeIndex] = bitmapGlyphDict
+
+
+class EbdtComponent(object):
+ def toXML(self, writer, ttFont):
+ writer.begintag("ebdtComponent", [("name", self.name)])
+ writer.newline()
+ for componentName in sstruct.getformat(ebdtComponentFormat)[1][1:]:
+ writer.simpletag(componentName, value=getattr(self, componentName))
+ writer.newline()
+ writer.endtag("ebdtComponent")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.name = attrs["name"]
+ componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name in componentNames:
+ vars(self)[name] = safeEval(attrs["value"])
+ else:
+ log.warning("unknown name '%s' being ignored by EbdtComponent.", name)
+
+
+# Helper functions for dealing with binary.
+
+
+def _data2binary(data, numBits):
+ binaryList = []
+ for curByte in data:
+ value = byteord(curByte)
+ numBitsCut = min(8, numBits)
+ for i in range(numBitsCut):
+ if value & 0x1:
+ binaryList.append("1")
+ else:
+ binaryList.append("0")
+ value = value >> 1
+ numBits -= numBitsCut
+ return strjoin(binaryList)
+
+
+def _binary2data(binary):
+ byteList = []
+ for bitLoc in range(0, len(binary), 8):
+ byteString = binary[bitLoc : bitLoc + 8]
+ curByte = 0
+ for curBit in reversed(byteString):
+ curByte = curByte << 1
+ if curBit == "1":
+ curByte |= 1
+ byteList.append(bytechr(curByte))
+ return bytesjoin(byteList)
+
+
+def _memoize(f):
+ class memodict(dict):
+ def __missing__(self, key):
+ ret = f(key)
+ if isinstance(key, int) or len(key) == 1:
+ self[key] = ret
+ return ret
+
+ return memodict().__getitem__
+
+
+# 00100111 -> 11100100 per byte, not to be confused with little/big endian.
+# Bitmap data per byte is in the order that binary is written on the page
+# with the least significant bit as far right as possible. This is the
+# opposite of what makes sense algorithmically and hence this function.
+@_memoize
+def _reverseBytes(data):
+ r"""
+ >>> bin(ord(_reverseBytes(0b00100111)))
+ '0b11100100'
+ >>> _reverseBytes(b'\x00\xf0')
+ b'\x00\x0f'
+ """
+ if isinstance(data, bytes) and len(data) != 1:
+ return bytesjoin(map(_reverseBytes, data))
+ byte = byteord(data)
+ result = 0
+ for i in range(8):
+ result = result << 1
+ result |= byte & 1
+ byte = byte >> 1
+ return bytechr(result)
+
+
+# This section of code is for reading and writing image data to/from XML.
+
+
+def _writeRawImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
+ writer.begintag("rawimagedata")
+ writer.newline()
+ writer.dumphex(bitmapObject.imageData)
+ writer.endtag("rawimagedata")
+ writer.newline()
+
+
+def _readRawImageData(bitmapObject, name, attrs, content, ttFont):
+ bitmapObject.imageData = readHex(content)
+
+
+def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
+ metrics = bitmapObject.exportMetrics
+ del bitmapObject.exportMetrics
+ bitDepth = bitmapObject.exportBitDepth
+ del bitmapObject.exportBitDepth
+
+ writer.begintag(
+ "rowimagedata", bitDepth=bitDepth, width=metrics.width, height=metrics.height
+ )
+ writer.newline()
+ for curRow in range(metrics.height):
+ rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
+ writer.simpletag("row", value=hexStr(rowData))
+ writer.newline()
+ writer.endtag("rowimagedata")
+ writer.newline()
+
+
+def _readRowImageData(bitmapObject, name, attrs, content, ttFont):
+ bitDepth = safeEval(attrs["bitDepth"])
+ metrics = SmallGlyphMetrics()
+ metrics.width = safeEval(attrs["width"])
+ metrics.height = safeEval(attrs["height"])
+
+ dataRows = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ # Chop off 'imagedata' from the tag to get just the option.
+ if name == "row":
+ dataRows.append(deHexStr(attr["value"]))
+ bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics)
+
+
+def _writeBitwiseImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
+ metrics = bitmapObject.exportMetrics
+ del bitmapObject.exportMetrics
+ bitDepth = bitmapObject.exportBitDepth
+ del bitmapObject.exportBitDepth
+
+ # A dict for mapping binary to more readable/artistic ASCII characters.
+ binaryConv = {"0": ".", "1": "@"}
+
+ writer.begintag(
+ "bitwiseimagedata",
+ bitDepth=bitDepth,
+ width=metrics.width,
+ height=metrics.height,
+ )
+ writer.newline()
+ for curRow in range(metrics.height):
+ rowData = bitmapObject.getRow(
+ curRow, bitDepth=1, metrics=metrics, reverseBytes=True
+ )
+ rowData = _data2binary(rowData, metrics.width)
+ # Make the output a readable ASCII art form.
+ rowData = strjoin(map(binaryConv.get, rowData))
+ writer.simpletag("row", value=rowData)
+ writer.newline()
+ writer.endtag("bitwiseimagedata")
+ writer.newline()
+
+
+def _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
+ bitDepth = safeEval(attrs["bitDepth"])
+ metrics = SmallGlyphMetrics()
+ metrics.width = safeEval(attrs["width"])
+ metrics.height = safeEval(attrs["height"])
+
+ # A dict for mapping from ASCII to binary. All characters are considered
+ # a '1' except space, period and '0' which maps to '0'.
+ binaryConv = {" ": "0", ".": "0", "0": "0"}
+
+ dataRows = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if name == "row":
+ mapParams = zip(attr["value"], itertools.repeat("1"))
+ rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
+ dataRows.append(_binary2data(rowData))
+
+ bitmapObject.setRows(
+ dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True
+ )
+
+
+def _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
+ try:
+ folder = os.path.dirname(writer.file.name)
+ except AttributeError:
+ # fall back to current directory if output file's directory isn't found
+ folder = "."
+ folder = os.path.join(folder, "bitmaps")
+ filename = glyphName + bitmapObject.fileExtension
+ if not os.path.isdir(folder):
+ os.makedirs(folder)
+ folder = os.path.join(folder, "strike%d" % strikeIndex)
+ if not os.path.isdir(folder):
+ os.makedirs(folder)
+
+ fullPath = os.path.join(folder, filename)
+ writer.simpletag("extfileimagedata", value=fullPath)
+ writer.newline()
+
+ with open(fullPath, "wb") as file:
+ file.write(bitmapObject.imageData)
+
+
+def _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
+ fullPath = attrs["value"]
+ with open(fullPath, "rb") as file:
+ bitmapObject.imageData = file.read()
+
+
+# End of XML writing code.
+
+# Important information about the naming scheme. Used for identifying formats
+# in XML.
+_bitmapGlyphSubclassPrefix = "ebdt_bitmap_format_"
+
+
+class BitmapGlyph(object):
+ # For the external file format. This can be changed in subclasses. This way
+ # when the extfile option is turned on files have the form: glyphName.ext
+ # The default is just a flat binary file with no meaning.
+ fileExtension = ".bin"
+
+ # Keep track of reading and writing of various forms.
+ xmlDataFunctions = {
+ "raw": (_writeRawImageData, _readRawImageData),
+ "row": (_writeRowImageData, _readRowImageData),
+ "bitwise": (_writeBitwiseImageData, _readBitwiseImageData),
+ "extfile": (_writeExtFileImageData, _readExtFileImageData),
+ }
+
+ def __init__(self, data, ttFont):
+ self.data = data
+ self.ttFont = ttFont
+ # TODO Currently non-lazy decompilation is untested here...
+ # if not ttFont.lazy:
+ # self.decompile()
+ # del self.data
+
+ def __getattr__(self, attr):
+ # Allow lazy decompile.
+ if attr[:2] == "__":
+ raise AttributeError(attr)
+ if attr == "data":
+ raise AttributeError(attr)
+ self.decompile()
+ del self.data
+ return getattr(self, attr)
+
+ def ensureDecompiled(self, recurse=False):
+ if hasattr(self, "data"):
+ self.decompile()
+ del self.data
+
+ # Not a fan of this but it is needed for safer safety checking.
+ def getFormat(self):
+ return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix) :])
+
+ def toXML(self, strikeIndex, glyphName, writer, ttFont):
+ writer.begintag(self.__class__.__name__, [("name", glyphName)])
+ writer.newline()
+
+ self.writeMetrics(writer, ttFont)
+ # Use the internal write method to write using the correct output format.
+ self.writeData(strikeIndex, glyphName, writer, ttFont)
+
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.readMetrics(name, attrs, content, ttFont)
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if not name.endswith("imagedata"):
+ continue
+ # Chop off 'imagedata' from the tag to get just the option.
+ option = name[: -len("imagedata")]
+ assert option in self.__class__.xmlDataFunctions
+ self.readData(name, attr, content, ttFont)
+
+ # Some of the glyphs have the metrics. This allows for metrics to be
+ # added if the glyph format has them. Default behavior is to do nothing.
+ def writeMetrics(self, writer, ttFont):
+ pass
+
+ # The opposite of write metrics.
+ def readMetrics(self, name, attrs, content, ttFont):
+ pass
+
+ def writeData(self, strikeIndex, glyphName, writer, ttFont):
+ try:
+ writeFunc, readFunc = self.__class__.xmlDataFunctions[
+ ttFont.bitmapGlyphDataFormat
+ ]
+ except KeyError:
+ writeFunc = _writeRawImageData
+ writeFunc(strikeIndex, glyphName, self, writer, ttFont)
+
+ def readData(self, name, attrs, content, ttFont):
+ # Chop off 'imagedata' from the tag to get just the option.
+ option = name[: -len("imagedata")]
+ writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
+ readFunc(self, name, attrs, content, ttFont)
+
+
+# A closure for creating a mixin for the two types of metrics handling.
+# Most of the code is very similar so its easier to deal with here.
+# Everything works just by passing the class that the mixin is for.
+def _createBitmapPlusMetricsMixin(metricsClass):
+ # Both metrics names are listed here to make meaningful error messages.
+ metricStrings = [BigGlyphMetrics.__name__, SmallGlyphMetrics.__name__]
+ curMetricsName = metricsClass.__name__
+ # Find which metrics this is for and determine the opposite name.
+ metricsId = metricStrings.index(curMetricsName)
+ oppositeMetricsName = metricStrings[1 - metricsId]
+
+ class BitmapPlusMetricsMixin(object):
+ def writeMetrics(self, writer, ttFont):
+ self.metrics.toXML(writer, ttFont)
+
+ def readMetrics(self, name, attrs, content, ttFont):
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == curMetricsName:
+ self.metrics = metricsClass()
+ self.metrics.fromXML(name, attrs, content, ttFont)
+ elif name == oppositeMetricsName:
+ log.warning(
+ "Warning: %s being ignored in format %d.",
+ oppositeMetricsName,
+ self.getFormat(),
+ )
+
+ return BitmapPlusMetricsMixin
+
+
+# Since there are only two types of mixin's just create them here.
+BitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
+BitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)
+
+
+# Data that is bit aligned can be tricky to deal with. These classes implement
+# helper functionality for dealing with the data and getting a particular row
+# of bitwise data. Also helps implement fancy data export/import in XML.
+class BitAlignedBitmapMixin(object):
+ def _getBitRange(self, row, bitDepth, metrics):
+ rowBits = bitDepth * metrics.width
+ bitOffset = row * rowBits
+ return (bitOffset, bitOffset + rowBits)
+
+ def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
+ if metrics is None:
+ metrics = self.metrics
+ assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
+
+ # Loop through each byte. This can cover two bytes in the original data or
+ # a single byte if things happen to be aligned. The very last entry might
+ # not be aligned so take care to trim the binary data to size and pad with
+ # zeros in the row data. Bit aligned data is somewhat tricky.
+ #
+ # Example of data cut. Data cut represented in x's.
+ # '|' represents byte boundary.
+ # data = ...0XX|XXXXXX00|000... => XXXXXXXX
+ # or
+ # data = ...0XX|XXXX0000|000... => XXXXXX00
+ # or
+ # data = ...000|XXXXXXXX|000... => XXXXXXXX
+ # or
+ # data = ...000|00XXXX00|000... => XXXX0000
+ #
+ dataList = []
+ bitRange = self._getBitRange(row, bitDepth, metrics)
+ stepRange = bitRange + (8,)
+ for curBit in range(*stepRange):
+ endBit = min(curBit + 8, bitRange[1])
+ numBits = endBit - curBit
+ cutPoint = curBit % 8
+ firstByteLoc = curBit // 8
+ secondByteLoc = endBit // 8
+ if firstByteLoc < secondByteLoc:
+ numBitsCut = 8 - cutPoint
+ else:
+ numBitsCut = endBit - curBit
+ curByte = _reverseBytes(self.imageData[firstByteLoc])
+ firstHalf = byteord(curByte) >> cutPoint
+ firstHalf = ((1 << numBitsCut) - 1) & firstHalf
+ newByte = firstHalf
+ if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
+ curByte = _reverseBytes(self.imageData[secondByteLoc])
+ secondHalf = byteord(curByte) << numBitsCut
+ newByte = (firstHalf | secondHalf) & ((1 << numBits) - 1)
+ dataList.append(bytechr(newByte))
+
+ # The way the data is kept is opposite the algorithm used.
+ data = bytesjoin(dataList)
+ if not reverseBytes:
+ data = _reverseBytes(data)
+ return data
+
+ def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
+ if metrics is None:
+ metrics = self.metrics
+ if not reverseBytes:
+ dataRows = list(map(_reverseBytes, dataRows))
+
+ # Keep track of a list of ordinal values as they are easier to modify
+ # than a list of strings. Map to actual strings later.
+ numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
+ ordDataList = [0] * numBytes
+ for row, data in enumerate(dataRows):
+ bitRange = self._getBitRange(row, bitDepth, metrics)
+ stepRange = bitRange + (8,)
+ for curBit, curByte in zip(range(*stepRange), data):
+ endBit = min(curBit + 8, bitRange[1])
+ cutPoint = curBit % 8
+ firstByteLoc = curBit // 8
+ secondByteLoc = endBit // 8
+ if firstByteLoc < secondByteLoc:
+ numBitsCut = 8 - cutPoint
+ else:
+ numBitsCut = endBit - curBit
+ curByte = byteord(curByte)
+ firstByte = curByte & ((1 << numBitsCut) - 1)
+ ordDataList[firstByteLoc] |= firstByte << cutPoint
+ if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
+ secondByte = (curByte >> numBitsCut) & ((1 << 8 - numBitsCut) - 1)
+ ordDataList[secondByteLoc] |= secondByte
+
+ # Save the image data with the bits going the correct way.
+ self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))
+
+
+class ByteAlignedBitmapMixin(object):
+ def _getByteRange(self, row, bitDepth, metrics):
+ rowBytes = (bitDepth * metrics.width + 7) // 8
+ byteOffset = row * rowBytes
+ return (byteOffset, byteOffset + rowBytes)
+
+ def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
+ if metrics is None:
+ metrics = self.metrics
+ assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
+ byteRange = self._getByteRange(row, bitDepth, metrics)
+ data = self.imageData[slice(*byteRange)]
+ if reverseBytes:
+ data = _reverseBytes(data)
+ return data
+
+ def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
+ if metrics is None:
+ metrics = self.metrics
+ if reverseBytes:
+ dataRows = map(_reverseBytes, dataRows)
+ self.imageData = bytesjoin(dataRows)
+
+
+class ebdt_bitmap_format_1(
+ ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph
+):
+ def decompile(self):
+ self.metrics = SmallGlyphMetrics()
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
+ self.imageData = data
+
+ def compile(self, ttFont):
+ data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
+ return data + self.imageData
+
+
+class ebdt_bitmap_format_2(
+ BitAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph
+):
+ def decompile(self):
+ self.metrics = SmallGlyphMetrics()
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
+ self.imageData = data
+
+ def compile(self, ttFont):
+ data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
+ return data + self.imageData
+
+
+class ebdt_bitmap_format_5(BitAlignedBitmapMixin, BitmapGlyph):
+ def decompile(self):
+ self.imageData = self.data
+
+ def compile(self, ttFont):
+ return self.imageData
+
+
+class ebdt_bitmap_format_6(
+ ByteAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph
+):
+ def decompile(self):
+ self.metrics = BigGlyphMetrics()
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
+ self.imageData = data
+
+ def compile(self, ttFont):
+ data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
+ return data + self.imageData
+
+
+class ebdt_bitmap_format_7(
+ BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph
+):
+ def decompile(self):
+ self.metrics = BigGlyphMetrics()
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
+ self.imageData = data
+
+ def compile(self, ttFont):
+ data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
+ return data + self.imageData
+
+
+class ComponentBitmapGlyph(BitmapGlyph):
+ def toXML(self, strikeIndex, glyphName, writer, ttFont):
+ writer.begintag(self.__class__.__name__, [("name", glyphName)])
+ writer.newline()
+
+ self.writeMetrics(writer, ttFont)
+
+ writer.begintag("components")
+ writer.newline()
+ for curComponent in self.componentArray:
+ curComponent.toXML(writer, ttFont)
+ writer.endtag("components")
+ writer.newline()
+
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.readMetrics(name, attrs, content, ttFont)
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if name == "components":
+ self.componentArray = []
+ for compElement in content:
+ if not isinstance(compElement, tuple):
+ continue
+ name, attrs, content = compElement
+ if name == "ebdtComponent":
+ curComponent = EbdtComponent()
+ curComponent.fromXML(name, attrs, content, ttFont)
+ self.componentArray.append(curComponent)
+ else:
+ log.warning("'%s' being ignored in component array.", name)
+
+
+class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
+ def decompile(self):
+ self.metrics = SmallGlyphMetrics()
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
+ data = data[1:]
+
+ (numComponents,) = struct.unpack(">H", data[:2])
+ data = data[2:]
+ self.componentArray = []
+ for i in range(numComponents):
+ curComponent = EbdtComponent()
+ dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
+ curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
+ self.componentArray.append(curComponent)
+
+ def compile(self, ttFont):
+ dataList = []
+ dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
+ dataList.append(b"\0")
+ dataList.append(struct.pack(">H", len(self.componentArray)))
+ for curComponent in self.componentArray:
+ curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
+ dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
+ return bytesjoin(dataList)
+
+
+class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
+ def decompile(self):
+ self.metrics = BigGlyphMetrics()
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
+ (numComponents,) = struct.unpack(">H", data[:2])
+ data = data[2:]
+ self.componentArray = []
+ for i in range(numComponents):
+ curComponent = EbdtComponent()
+ dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
+ curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
+ self.componentArray.append(curComponent)
+
+ def compile(self, ttFont):
+ dataList = []
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ dataList.append(struct.pack(">H", len(self.componentArray)))
+ for curComponent in self.componentArray:
+ curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
+ dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
+ return bytesjoin(dataList)
+
+
+# Dictionary of bitmap formats to the class representing that format
+# currently only the ones listed in this map are the ones supported.
+ebdt_bitmap_classes = {
+ 1: ebdt_bitmap_format_1,
+ 2: ebdt_bitmap_format_2,
+ 5: ebdt_bitmap_format_5,
+ 6: ebdt_bitmap_format_6,
+ 7: ebdt_bitmap_format_7,
+ 8: ebdt_bitmap_format_8,
+ 9: ebdt_bitmap_format_9,
+}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_L_C_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_L_C_.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff93aa8cf67de480419dbe66b7a4702114cddfd9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/E_B_L_C_.py
@@ -0,0 +1,718 @@
+from fontTools.misc import sstruct
+from . import DefaultTable
+from fontTools.misc.textTools import bytesjoin, safeEval
+from .BitmapGlyphMetrics import (
+ BigGlyphMetrics,
+ bigGlyphMetricsFormat,
+ SmallGlyphMetrics,
+ smallGlyphMetricsFormat,
+)
+import struct
+import itertools
+from collections import deque
+import logging
+
+
+log = logging.getLogger(__name__)
+
+eblcHeaderFormat = """
+ > # big endian
+ version: 16.16F
+ numSizes: I
+"""
+# The table format string is split to handle sbitLineMetrics simply.
+bitmapSizeTableFormatPart1 = """
+ > # big endian
+ indexSubTableArrayOffset: I
+ indexTablesSize: I
+ numberOfIndexSubTables: I
+ colorRef: I
+"""
+# The compound type for hori and vert.
+sbitLineMetricsFormat = """
+ > # big endian
+ ascender: b
+ descender: b
+ widthMax: B
+ caretSlopeNumerator: b
+ caretSlopeDenominator: b
+ caretOffset: b
+ minOriginSB: b
+ minAdvanceSB: b
+ maxBeforeBL: b
+ minAfterBL: b
+ pad1: b
+ pad2: b
+"""
+# hori and vert go between the two parts.
+bitmapSizeTableFormatPart2 = """
+ > # big endian
+ startGlyphIndex: H
+ endGlyphIndex: H
+ ppemX: B
+ ppemY: B
+ bitDepth: B
+ flags: b
+"""
+
+indexSubTableArrayFormat = ">HHL"
+indexSubTableArraySize = struct.calcsize(indexSubTableArrayFormat)
+
+indexSubHeaderFormat = ">HHL"
+indexSubHeaderSize = struct.calcsize(indexSubHeaderFormat)
+
+codeOffsetPairFormat = ">HH"
+codeOffsetPairSize = struct.calcsize(codeOffsetPairFormat)
+
+
+class table_E_B_L_C_(DefaultTable.DefaultTable):
+ """Embedded Bitmap Location table
+
+ The ``EBLC`` table contains the locations of monochrome or grayscale
+ bitmaps for glyphs. It must be used in concert with the ``EBDT`` table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/eblc
+ """
+
+ dependencies = ["EBDT"]
+
+ # This method can be overridden in subclasses to support new formats
+ # without changing the other implementation. Also can be used as a
+ # convenience method for coverting a font file to an alternative format.
+ def getIndexFormatClass(self, indexFormat):
+ return eblc_sub_table_classes[indexFormat]
+
+ def decompile(self, data, ttFont):
+ # Save the original data because offsets are from the start of the table.
+ origData = data
+ i = 0
+
+ dummy = sstruct.unpack(eblcHeaderFormat, data[:8], self)
+ i += 8
+
+ self.strikes = []
+ for curStrikeIndex in range(self.numSizes):
+ curStrike = Strike()
+ self.strikes.append(curStrike)
+ curTable = curStrike.bitmapSizeTable
+ dummy = sstruct.unpack2(
+ bitmapSizeTableFormatPart1, data[i : i + 16], curTable
+ )
+ i += 16
+ for metric in ("hori", "vert"):
+ metricObj = SbitLineMetrics()
+ vars(curTable)[metric] = metricObj
+ dummy = sstruct.unpack2(
+ sbitLineMetricsFormat, data[i : i + 12], metricObj
+ )
+ i += 12
+ dummy = sstruct.unpack(
+ bitmapSizeTableFormatPart2, data[i : i + 8], curTable
+ )
+ i += 8
+
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ for subtableIndex in range(curTable.numberOfIndexSubTables):
+ i = (
+ curTable.indexSubTableArrayOffset
+ + subtableIndex * indexSubTableArraySize
+ )
+
+ tup = struct.unpack(
+ indexSubTableArrayFormat, data[i : i + indexSubTableArraySize]
+ )
+ (firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup
+ i = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
+
+ tup = struct.unpack(
+ indexSubHeaderFormat, data[i : i + indexSubHeaderSize]
+ )
+ (indexFormat, imageFormat, imageDataOffset) = tup
+
+ indexFormatClass = self.getIndexFormatClass(indexFormat)
+ indexSubTable = indexFormatClass(data[i + indexSubHeaderSize :], ttFont)
+ indexSubTable.firstGlyphIndex = firstGlyphIndex
+ indexSubTable.lastGlyphIndex = lastGlyphIndex
+ indexSubTable.additionalOffsetToIndexSubtable = (
+ additionalOffsetToIndexSubtable
+ )
+ indexSubTable.indexFormat = indexFormat
+ indexSubTable.imageFormat = imageFormat
+ indexSubTable.imageDataOffset = imageDataOffset
+ indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
+ curStrike.indexSubTables.append(indexSubTable)
+
+ def compile(self, ttFont):
+ dataList = []
+ self.numSizes = len(self.strikes)
+ dataList.append(sstruct.pack(eblcHeaderFormat, self))
+
+ # Data size of the header + bitmapSizeTable needs to be calculated
+ # in order to form offsets. This value will hold the size of the data
+ # in dataList after all the data is consolidated in dataList.
+ dataSize = len(dataList[0])
+
+ # The table will be structured in the following order:
+ # (0) header
+ # (1) Each bitmapSizeTable [1 ... self.numSizes]
+ # (2) Alternate between indexSubTableArray and indexSubTable
+ # for each bitmapSizeTable present.
+ #
+ # The issue is maintaining the proper offsets when table information
+ # gets moved around. All offsets and size information must be recalculated
+ # when building the table to allow editing within ttLib and also allow easy
+ # import/export to and from XML. All of this offset information is lost
+ # when exporting to XML so everything must be calculated fresh so importing
+ # from XML will work cleanly. Only byte offset and size information is
+ # calculated fresh. Count information like numberOfIndexSubTables is
+ # checked through assertions. If the information in this table was not
+ # touched or was changed properly then these types of values should match.
+ #
+ # The table will be rebuilt the following way:
+ # (0) Precompute the size of all the bitmapSizeTables. This is needed to
+ # compute the offsets properly.
+ # (1) For each bitmapSizeTable compute the indexSubTable and
+ # indexSubTableArray pair. The indexSubTable must be computed first
+ # so that the offset information in indexSubTableArray can be
+ # calculated. Update the data size after each pairing.
+ # (2) Build each bitmapSizeTable.
+ # (3) Consolidate all the data into the main dataList in the correct order.
+
+ for _ in self.strikes:
+ dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1)
+ dataSize += len(("hori", "vert")) * sstruct.calcsize(sbitLineMetricsFormat)
+ dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2)
+
+ indexSubTablePairDataList = []
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ curTable.numberOfIndexSubTables = len(curStrike.indexSubTables)
+ curTable.indexSubTableArrayOffset = dataSize
+
+ # Precompute the size of the indexSubTableArray. This information
+ # is important for correctly calculating the new value for
+ # additionalOffsetToIndexSubtable.
+ sizeOfSubTableArray = (
+ curTable.numberOfIndexSubTables * indexSubTableArraySize
+ )
+ lowerBound = dataSize
+ dataSize += sizeOfSubTableArray
+ upperBound = dataSize
+
+ indexSubTableDataList = []
+ for indexSubTable in curStrike.indexSubTables:
+ indexSubTable.additionalOffsetToIndexSubtable = (
+ dataSize - curTable.indexSubTableArrayOffset
+ )
+ glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
+ indexSubTable.firstGlyphIndex = min(glyphIds)
+ indexSubTable.lastGlyphIndex = max(glyphIds)
+ data = indexSubTable.compile(ttFont)
+ indexSubTableDataList.append(data)
+ dataSize += len(data)
+ curTable.startGlyphIndex = min(
+ ist.firstGlyphIndex for ist in curStrike.indexSubTables
+ )
+ curTable.endGlyphIndex = max(
+ ist.lastGlyphIndex for ist in curStrike.indexSubTables
+ )
+
+ for i in curStrike.indexSubTables:
+ data = struct.pack(
+ indexSubHeaderFormat,
+ i.firstGlyphIndex,
+ i.lastGlyphIndex,
+ i.additionalOffsetToIndexSubtable,
+ )
+ indexSubTablePairDataList.append(data)
+ indexSubTablePairDataList.extend(indexSubTableDataList)
+ curTable.indexTablesSize = dataSize - curTable.indexSubTableArrayOffset
+
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ data = sstruct.pack(bitmapSizeTableFormatPart1, curTable)
+ dataList.append(data)
+ for metric in ("hori", "vert"):
+ metricObj = vars(curTable)[metric]
+ data = sstruct.pack(sbitLineMetricsFormat, metricObj)
+ dataList.append(data)
+ data = sstruct.pack(bitmapSizeTableFormatPart2, curTable)
+ dataList.append(data)
+ dataList.extend(indexSubTablePairDataList)
+
+ return bytesjoin(dataList)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("header", [("version", self.version)])
+ writer.newline()
+ for curIndex, curStrike in enumerate(self.strikes):
+ curStrike.toXML(curIndex, writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "header":
+ self.version = safeEval(attrs["version"])
+ elif name == "strike":
+ if not hasattr(self, "strikes"):
+ self.strikes = []
+ strikeIndex = safeEval(attrs["index"])
+ curStrike = Strike()
+ curStrike.fromXML(name, attrs, content, ttFont, self)
+
+ # Grow the strike array to the appropriate size. The XML format
+ # allows for the strike index value to be out of order.
+ if strikeIndex >= len(self.strikes):
+ self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
+ assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
+ self.strikes[strikeIndex] = curStrike
+
+
+class Strike(object):
+ def __init__(self):
+ self.bitmapSizeTable = BitmapSizeTable()
+ self.indexSubTables = []
+
+ def toXML(self, strikeIndex, writer, ttFont):
+ writer.begintag("strike", [("index", strikeIndex)])
+ writer.newline()
+ self.bitmapSizeTable.toXML(writer, ttFont)
+ writer.comment(
+ "GlyphIds are written but not read. The firstGlyphIndex and\nlastGlyphIndex values will be recalculated by the compiler."
+ )
+ writer.newline()
+ for indexSubTable in self.indexSubTables:
+ indexSubTable.toXML(writer, ttFont)
+ writer.endtag("strike")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont, locator):
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "bitmapSizeTable":
+ self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
+ elif name.startswith(_indexSubTableSubclassPrefix):
+ indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix) :])
+ indexFormatClass = locator.getIndexFormatClass(indexFormat)
+ indexSubTable = indexFormatClass(None, None)
+ indexSubTable.indexFormat = indexFormat
+ indexSubTable.fromXML(name, attrs, content, ttFont)
+ self.indexSubTables.append(indexSubTable)
+
+
+class BitmapSizeTable(object):
+ # Returns all the simple metric names that bitmap size table
+ # cares about in terms of XML creation.
+ def _getXMLMetricNames(self):
+ dataNames = sstruct.getformat(bitmapSizeTableFormatPart1)[1]
+ dataNames = {**dataNames, **sstruct.getformat(bitmapSizeTableFormatPart2)[1]}
+ # Skip the first 3 data names because they are byte offsets and counts.
+ return list(dataNames.keys())[3:]
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("bitmapSizeTable")
+ writer.newline()
+ for metric in ("hori", "vert"):
+ getattr(self, metric).toXML(metric, writer, ttFont)
+ for metricName in self._getXMLMetricNames():
+ writer.simpletag(metricName, value=getattr(self, metricName))
+ writer.newline()
+ writer.endtag("bitmapSizeTable")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ # Create a lookup for all the simple names that make sense to
+ # bitmap size table. Only read the information from these names.
+ dataNames = set(self._getXMLMetricNames())
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "sbitLineMetrics":
+ direction = attrs["direction"]
+ assert direction in (
+ "hori",
+ "vert",
+ ), "SbitLineMetrics direction specified invalid."
+ metricObj = SbitLineMetrics()
+ metricObj.fromXML(name, attrs, content, ttFont)
+ vars(self)[direction] = metricObj
+ elif name in dataNames:
+ vars(self)[name] = safeEval(attrs["value"])
+ else:
+ log.warning("unknown name '%s' being ignored in BitmapSizeTable.", name)
+
+
+class SbitLineMetrics(object):
+ def toXML(self, name, writer, ttFont):
+ writer.begintag("sbitLineMetrics", [("direction", name)])
+ writer.newline()
+ for metricName in sstruct.getformat(sbitLineMetricsFormat)[1]:
+ writer.simpletag(metricName, value=getattr(self, metricName))
+ writer.newline()
+ writer.endtag("sbitLineMetrics")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name in metricNames:
+ vars(self)[name] = safeEval(attrs["value"])
+
+
+# Important information about the naming scheme. Used for identifying subtables.
+_indexSubTableSubclassPrefix = "eblc_index_sub_table_"
+
+
+class EblcIndexSubTable(object):
+ def __init__(self, data, ttFont):
+ self.data = data
+ self.ttFont = ttFont
+ # TODO Currently non-lazy decompiling doesn't work for this class...
+ # if not ttFont.lazy:
+ # self.decompile()
+ # del self.data, self.ttFont
+
+ def __getattr__(self, attr):
+ # Allow lazy decompile.
+ if attr[:2] == "__":
+ raise AttributeError(attr)
+ if attr == "data":
+ raise AttributeError(attr)
+ self.decompile()
+ return getattr(self, attr)
+
+ def ensureDecompiled(self, recurse=False):
+ if hasattr(self, "data"):
+ self.decompile()
+
+ # This method just takes care of the indexSubHeader. Implementing subclasses
+ # should call it to compile the indexSubHeader and then continue compiling
+ # the remainder of their unique format.
+ def compile(self, ttFont):
+ return struct.pack(
+ indexSubHeaderFormat,
+ self.indexFormat,
+ self.imageFormat,
+ self.imageDataOffset,
+ )
+
+ # Creates the XML for bitmap glyphs. Each index sub table basically makes
+ # the same XML except for specific metric information that is written
+ # out via a method call that a subclass implements optionally.
+ def toXML(self, writer, ttFont):
+ writer.begintag(
+ self.__class__.__name__,
+ [
+ ("imageFormat", self.imageFormat),
+ ("firstGlyphIndex", self.firstGlyphIndex),
+ ("lastGlyphIndex", self.lastGlyphIndex),
+ ],
+ )
+ writer.newline()
+ self.writeMetrics(writer, ttFont)
+ # Write out the names as thats all thats needed to rebuild etc.
+ # For font debugging of consecutive formats the ids are also written.
+ # The ids are not read when moving from the XML format.
+ glyphIds = map(ttFont.getGlyphID, self.names)
+ for glyphName, glyphId in zip(self.names, glyphIds):
+ writer.simpletag("glyphLoc", name=glyphName, id=glyphId)
+ writer.newline()
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ # Read all the attributes. Even though the glyph indices are
+ # recalculated, they are still read in case there needs to
+ # be an immediate export of the data.
+ self.imageFormat = safeEval(attrs["imageFormat"])
+ self.firstGlyphIndex = safeEval(attrs["firstGlyphIndex"])
+ self.lastGlyphIndex = safeEval(attrs["lastGlyphIndex"])
+
+ self.readMetrics(name, attrs, content, ttFont)
+
+ self.names = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "glyphLoc":
+ self.names.append(attrs["name"])
+
+ # A helper method that writes the metrics for the index sub table. It also
+ # is responsible for writing the image size for fixed size data since fixed
+ # size is not recalculated on compile. Default behavior is to do nothing.
+ def writeMetrics(self, writer, ttFont):
+ pass
+
+ # A helper method that is the inverse of writeMetrics.
+ def readMetrics(self, name, attrs, content, ttFont):
+ pass
+
+ # This method is for fixed glyph data sizes. There are formats where
+ # the glyph data is fixed but are actually composite glyphs. To handle
+ # this the font spec in indexSubTable makes the data the size of the
+ # fixed size by padding the component arrays. This function abstracts
+ # out this padding process. Input is data unpadded. Output is data
+ # padded only in fixed formats. Default behavior is to return the data.
+ def padBitmapData(self, data):
+ return data
+
+ # Remove any of the glyph locations and names that are flagged as skipped.
+ # This only occurs in formats {1,3}.
+ def removeSkipGlyphs(self):
+ # Determines if a name, location pair is a valid data location.
+ # Skip glyphs are marked when the size is equal to zero.
+ def isValidLocation(args):
+ (name, (startByte, endByte)) = args
+ return startByte < endByte
+
+ # Remove all skip glyphs.
+ dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
+ self.names, self.locations = list(map(list, zip(*dataPairs)))
+
+
+# A closure for creating a custom mixin. This is done because formats 1 and 3
+# are very similar. The only difference between them is the size per offset
+# value. Code put in here should handle both cases generally.
+def _createOffsetArrayIndexSubTableMixin(formatStringForDataType):
+ # Prep the data size for the offset array data format.
+ dataFormat = ">" + formatStringForDataType
+ offsetDataSize = struct.calcsize(dataFormat)
+
+ class OffsetArrayIndexSubTableMixin(object):
+ def decompile(self):
+ numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
+ indexingOffsets = [
+ glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs + 2)
+ ]
+ indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
+ offsetArray = [
+ struct.unpack(dataFormat, self.data[slice(*loc)])[0]
+ for loc in indexingLocations
+ ]
+
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
+ self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
+
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ self.removeSkipGlyphs()
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ # First make sure that all the data lines up properly. Formats 1 and 3
+ # must have all its data lined up consecutively. If not this will fail.
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
+ assert (
+ curLoc[1] == nxtLoc[0]
+ ), "Data must be consecutive in indexSubTable offset formats"
+
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Make sure that all ids are sorted strictly increasing.
+ assert all(glyphIds[i] < glyphIds[i + 1] for i in range(len(glyphIds) - 1))
+
+ # Run a simple algorithm to add skip glyphs to the data locations at
+ # the places where an id is not present.
+ idQueue = deque(glyphIds)
+ locQueue = deque(self.locations)
+ allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ allLocations = []
+ for curId in allGlyphIds:
+ if curId != idQueue[0]:
+ allLocations.append((locQueue[0][0], locQueue[0][0]))
+ else:
+ idQueue.popleft()
+ allLocations.append(locQueue.popleft())
+
+ # Now that all the locations are collected, pack them appropriately into
+ # offsets. This is the form where offset[i] is the location and
+ # offset[i+1]-offset[i] is the size of the data location.
+ offsets = list(allLocations[0]) + [loc[1] for loc in allLocations[1:]]
+ # Image data offset must be less than or equal to the minimum of locations.
+ # This offset may change the value for round tripping but is safer and
+ # allows imageDataOffset to not be required to be in the XML version.
+ self.imageDataOffset = min(offsets)
+ offsetArray = [offset - self.imageDataOffset for offset in offsets]
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList += [
+ struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray
+ ]
+ # Take care of any padding issues. Only occurs in format 3.
+ if offsetDataSize * len(offsetArray) % 4 != 0:
+ dataList.append(struct.pack(dataFormat, 0))
+ return bytesjoin(dataList)
+
+ return OffsetArrayIndexSubTableMixin
+
+
+# A Mixin for functionality shared between the different kinds
+# of fixed sized data handling. Both kinds have big metrics so
+# that kind of special processing is also handled in this mixin.
+class FixedSizeIndexSubTableMixin(object):
+ def writeMetrics(self, writer, ttFont):
+ writer.simpletag("imageSize", value=self.imageSize)
+ writer.newline()
+ self.metrics.toXML(writer, ttFont)
+
+ def readMetrics(self, name, attrs, content, ttFont):
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "imageSize":
+ self.imageSize = safeEval(attrs["value"])
+ elif name == BigGlyphMetrics.__name__:
+ self.metrics = BigGlyphMetrics()
+ self.metrics.fromXML(name, attrs, content, ttFont)
+ elif name == SmallGlyphMetrics.__name__:
+ log.warning(
+ "SmallGlyphMetrics being ignored in format %d.", self.indexFormat
+ )
+
+ def padBitmapData(self, data):
+ # Make sure that the data isn't bigger than the fixed size.
+ assert len(data) <= self.imageSize, (
+ "Data in indexSubTable format %d must be less than the fixed size."
+ % self.indexFormat
+ )
+ # Pad the data so that it matches the fixed size.
+ pad = (self.imageSize - len(data)) * b"\0"
+ return data + pad
+
+
+class eblc_index_sub_table_1(
+ _createOffsetArrayIndexSubTableMixin("L"), EblcIndexSubTable
+):
+ pass
+
+
+class eblc_index_sub_table_2(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
+ def decompile(self):
+ (self.imageSize,) = struct.unpack(">L", self.data[:4])
+ self.metrics = BigGlyphMetrics()
+ sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ offsets = [
+ self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds) + 1)
+ ]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Make sure all the ids are consecutive. This is required by Format 2.
+ assert glyphIds == list(
+ range(self.firstGlyphIndex, self.lastGlyphIndex + 1)
+ ), "Format 2 ids must be consecutive."
+ self.imageDataOffset = min(next(iter(zip(*self.locations))))
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", self.imageSize))
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ return bytesjoin(dataList)
+
+
+class eblc_index_sub_table_3(
+ _createOffsetArrayIndexSubTableMixin("H"), EblcIndexSubTable
+):
+ pass
+
+
+class eblc_index_sub_table_4(EblcIndexSubTable):
+ def decompile(self):
+ (numGlyphs,) = struct.unpack(">L", self.data[:4])
+ data = self.data[4:]
+ indexingOffsets = [
+ glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs + 2)
+ ]
+ indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
+ glyphArray = [
+ struct.unpack(codeOffsetPairFormat, data[slice(*loc)])
+ for loc in indexingLocations
+ ]
+ glyphIds, offsets = list(map(list, zip(*glyphArray)))
+ # There are one too many glyph ids. Get rid of the last one.
+ glyphIds.pop()
+
+ offsets = [offset + self.imageDataOffset for offset in offsets]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ # First make sure that all the data lines up properly. Format 4
+ # must have all its data lined up consecutively. If not this will fail.
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
+ assert (
+ curLoc[1] == nxtLoc[0]
+ ), "Data must be consecutive in indexSubTable format 4"
+
+ offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
+ # Image data offset must be less than or equal to the minimum of locations.
+ # Resetting this offset may change the value for round tripping but is safer
+ # and allows imageDataOffset to not be required to be in the XML version.
+ self.imageDataOffset = min(offsets)
+ offsets = [offset - self.imageDataOffset for offset in offsets]
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Create an iterator over the ids plus a padding value.
+ idsPlusPad = list(itertools.chain(glyphIds, [0]))
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", len(glyphIds)))
+ tmp = [
+ struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)
+ ]
+ dataList += tmp
+ data = bytesjoin(dataList)
+ return data
+
+
+class eblc_index_sub_table_5(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
+ def decompile(self):
+ self.origDataLen = 0
+ (self.imageSize,) = struct.unpack(">L", self.data[:4])
+ data = self.data[4:]
+ self.metrics, data = sstruct.unpack2(
+ bigGlyphMetricsFormat, data, BigGlyphMetrics()
+ )
+ (numGlyphs,) = struct.unpack(">L", data[:4])
+ data = data[4:]
+ glyphIds = [
+ struct.unpack(">H", data[2 * i : 2 * (i + 1)])[0] for i in range(numGlyphs)
+ ]
+
+ offsets = [
+ self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds) + 1)
+ ]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ self.imageDataOffset = min(next(iter(zip(*self.locations))))
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", self.imageSize))
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ dataList.append(struct.pack(">L", len(glyphIds)))
+ dataList += [struct.pack(">H", curId) for curId in glyphIds]
+ if len(glyphIds) % 2 == 1:
+ dataList.append(struct.pack(">H", 0))
+ return bytesjoin(dataList)
+
+
+# Dictionary of indexFormat to the class representing that format.
+eblc_sub_table_classes = {
+ 1: eblc_index_sub_table_1,
+ 2: eblc_index_sub_table_2,
+ 3: eblc_index_sub_table_3,
+ 4: eblc_index_sub_table_4,
+ 5: eblc_index_sub_table_5,
+}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/F_F_T_M_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/F_F_T_M_.py
new file mode 100644
index 0000000000000000000000000000000000000000..20b72346611911a1044ae0d52eb2a5c9efdf5540
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/F_F_T_M_.py
@@ -0,0 +1,52 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.timeTools import timestampFromString, timestampToString
+from . import DefaultTable
+
+FFTMFormat = """
+ > # big endian
+ version: I
+ FFTimeStamp: Q
+ sourceCreated: Q
+ sourceModified: Q
+"""
+
+
+class table_F_F_T_M_(DefaultTable.DefaultTable):
+ """FontForge Time Stamp table
+
+ The ``FFTM`` table is used by the free-software font editor
+ FontForge to record timestamps for the creation and modification
+ of font source (.sfd) files and a timestamp for FontForge's
+ own source code.
+
+ See also https://fontforge.org/docs/techref/non-standard.html
+ """
+
+ def decompile(self, data, ttFont):
+ dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
+
+ def compile(self, ttFont):
+ data = sstruct.pack(FFTMFormat, self)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment(
+ "FontForge's timestamp, font source creation and modification dates"
+ )
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(FFTMFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+ value = timestampToString(value)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+ value = timestampFromString(value)
+ else:
+ value = safeEval(value)
+ setattr(self, name, value)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/F__e_a_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/F__e_a_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..579eb27bdd7f11dcba75eae68bf900a0e298d0cc
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/F__e_a_t.py
@@ -0,0 +1,149 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+from . import grUtils
+import struct
+
+Feat_hdr_format = """
+ >
+ version: 16.16F
+"""
+
+
+class table_F__e_a_t(DefaultTable.DefaultTable):
+ """Feature table
+
+ The ``Feat`` table is used exclusively by the Graphite shaping engine
+ to store features and possible settings specified in GDL. Graphite features
+ determine what rules are applied to transform a glyph stream.
+
+ Not to be confused with ``feat``, or the OpenType Layout tables
+ ``GSUB``/``GPOS``.
+
+ See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.features = {}
+
+ def decompile(self, data, ttFont):
+ (_, data) = sstruct.unpack2(Feat_hdr_format, data, self)
+ self.version = float(floatToFixedToStr(self.version, precisionBits=16))
+ (numFeats,) = struct.unpack(">H", data[:2])
+ data = data[8:]
+ allfeats = []
+ maxsetting = 0
+ for i in range(numFeats):
+ if self.version >= 2.0:
+ (fid, nums, _, offset, flags, lid) = struct.unpack(
+ ">LHHLHH", data[16 * i : 16 * (i + 1)]
+ )
+ offset = int((offset - 12 - 16 * numFeats) / 4)
+ else:
+ (fid, nums, offset, flags, lid) = struct.unpack(
+ ">HHLHH", data[12 * i : 12 * (i + 1)]
+ )
+ offset = int((offset - 12 - 12 * numFeats) / 4)
+ allfeats.append((fid, nums, offset, flags, lid))
+ maxsetting = max(maxsetting, offset + nums)
+ data = data[16 * numFeats :]
+ allsettings = []
+ for i in range(maxsetting):
+ if len(data) >= 4 * (i + 1):
+ (val, lid) = struct.unpack(">HH", data[4 * i : 4 * (i + 1)])
+ allsettings.append((val, lid))
+ for i, f in enumerate(allfeats):
+ (fid, nums, offset, flags, lid) = f
+ fobj = Feature()
+ fobj.flags = flags
+ fobj.label = lid
+ self.features[grUtils.num2tag(fid)] = fobj
+ fobj.settings = {}
+ fobj.default = None
+ fobj.index = i
+ for i in range(offset, offset + nums):
+ if i >= len(allsettings):
+ continue
+ (vid, vlid) = allsettings[i]
+ fobj.settings[vid] = vlid
+ if fobj.default is None:
+ fobj.default = vid
+
+ def compile(self, ttFont):
+ fdat = b""
+ vdat = b""
+ offset = 0
+ for f, v in sorted(self.features.items(), key=lambda x: x[1].index):
+ fnum = grUtils.tag2num(f)
+ if self.version >= 2.0:
+ fdat += struct.pack(
+ ">LHHLHH",
+ grUtils.tag2num(f),
+ len(v.settings),
+ 0,
+ offset * 4 + 12 + 16 * len(self.features),
+ v.flags,
+ v.label,
+ )
+ elif fnum > 65535: # self healing for alphabetic ids
+ self.version = 2.0
+ return self.compile(ttFont)
+ else:
+ fdat += struct.pack(
+ ">HHLHH",
+ grUtils.tag2num(f),
+ len(v.settings),
+ offset * 4 + 12 + 12 * len(self.features),
+ v.flags,
+ v.label,
+ )
+ for s, l in sorted(
+ v.settings.items(), key=lambda x: (-1, x[1]) if x[0] == v.default else x
+ ):
+ vdat += struct.pack(">HH", s, l)
+ offset += len(v.settings)
+ hdr = sstruct.pack(Feat_hdr_format, self)
+ return hdr + struct.pack(">HHL", len(self.features), 0, 0) + fdat + vdat
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", version=self.version)
+ writer.newline()
+ for f, v in sorted(self.features.items(), key=lambda x: x[1].index):
+ writer.begintag(
+ "feature",
+ fid=f,
+ label=v.label,
+ flags=v.flags,
+ default=(v.default if v.default else 0),
+ )
+ writer.newline()
+ for s, l in sorted(v.settings.items()):
+ writer.simpletag("setting", value=s, label=l)
+ writer.newline()
+ writer.endtag("feature")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = float(safeEval(attrs["version"]))
+ elif name == "feature":
+ fid = attrs["fid"]
+ fobj = Feature()
+ fobj.flags = int(safeEval(attrs["flags"]))
+ fobj.label = int(safeEval(attrs["label"]))
+ fobj.default = int(safeEval(attrs.get("default", "0")))
+ fobj.index = len(self.features)
+ self.features[fid] = fobj
+ fobj.settings = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, a, c = element
+ if tag == "setting":
+ fobj.settings[int(safeEval(a["value"]))] = int(safeEval(a["label"]))
+
+
+class Feature(object):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_D_E_F_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_D_E_F_.py
new file mode 100644
index 0000000000000000000000000000000000000000..922a9cb2063777cae486a3369d3d6380f9f626cf
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_D_E_F_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_G_D_E_F_(BaseTTXConverter):
+ """Glyph Definition table
+
+ The ``GDEF`` table stores various glyph properties that are used
+ by OpenType Layout.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/gdef
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_M_A_P_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_M_A_P_.py
new file mode 100644
index 0000000000000000000000000000000000000000..070c61919e174a52cd0acd23d8f32b38e2ee3ecb
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_M_A_P_.py
@@ -0,0 +1,148 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import tobytes, tostr, safeEval
+from . import DefaultTable
+
+GMAPFormat = """
+ > # big endian
+ tableVersionMajor: H
+ tableVersionMinor: H
+ flags: H
+ recordsCount: H
+ recordsOffset: H
+ fontNameLength: H
+"""
+# psFontName is a byte string which follows the record above. This is zero padded
+# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
+
+GMAPRecordFormat1 = """
+ > # big endian
+ UV: L
+ cid: H
+ gid: H
+ ggid: H
+ name: 32s
+"""
+
+
+class GMAPRecord(object):
+ def __init__(self, uv=0, cid=0, gid=0, ggid=0, name=""):
+ self.UV = uv
+ self.cid = cid
+ self.gid = gid
+ self.ggid = ggid
+ self.name = name
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("GMAPRecord")
+ writer.newline()
+ writer.simpletag("UV", value=self.UV)
+ writer.newline()
+ writer.simpletag("cid", value=self.cid)
+ writer.newline()
+ writer.simpletag("gid", value=self.gid)
+ writer.newline()
+ writer.simpletag("glyphletGid", value=self.gid)
+ writer.newline()
+ writer.simpletag("GlyphletName", value=self.name)
+ writer.newline()
+ writer.endtag("GMAPRecord")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ if name == "GlyphletName":
+ self.name = value
+ else:
+ setattr(self, name, safeEval(value))
+
+ def compile(self, ttFont):
+ if self.UV is None:
+ self.UV = 0
+ nameLen = len(self.name)
+ if nameLen < 32:
+ self.name = self.name + "\0" * (32 - nameLen)
+ data = sstruct.pack(GMAPRecordFormat1, self)
+ return data
+
+ def __repr__(self):
+ return (
+ "GMAPRecord[ UV: "
+ + str(self.UV)
+ + ", cid: "
+ + str(self.cid)
+ + ", gid: "
+ + str(self.gid)
+ + ", ggid: "
+ + str(self.ggid)
+ + ", Glyphlet Name: "
+ + str(self.name)
+ + " ]"
+ )
+
+
+class table_G_M_A_P_(DefaultTable.DefaultTable):
+ """Glyphlets GMAP table
+
+ The ``GMAP`` table is used by Adobe's SING Glyphlets.
+
+ See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
+ """
+
+ dependencies = []
+
+ def decompile(self, data, ttFont):
+ dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
+ self.psFontName = tostr(newData[: self.fontNameLength])
+ assert (
+ self.recordsOffset % 4
+ ) == 0, "GMAP error: recordsOffset is not 32 bit aligned."
+ newData = data[self.recordsOffset :]
+ self.gmapRecords = []
+ for i in range(self.recordsCount):
+ gmapRecord, newData = sstruct.unpack2(
+ GMAPRecordFormat1, newData, GMAPRecord()
+ )
+ gmapRecord.name = gmapRecord.name.strip("\0")
+ self.gmapRecords.append(gmapRecord)
+
+ def compile(self, ttFont):
+ self.recordsCount = len(self.gmapRecords)
+ self.fontNameLength = len(self.psFontName)
+ self.recordsOffset = 4 * (((self.fontNameLength + 12) + 3) // 4)
+ data = sstruct.pack(GMAPFormat, self)
+ data = data + tobytes(self.psFontName)
+ data = data + b"\0" * (self.recordsOffset - len(data))
+ for record in self.gmapRecords:
+ data = data + record.compile(ttFont)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(GMAPFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+ writer.simpletag("PSFontName", value=self.psFontName)
+ writer.newline()
+ for gmapRecord in self.gmapRecords:
+ gmapRecord.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "GMAPRecord":
+ if not hasattr(self, "gmapRecords"):
+ self.gmapRecords = []
+ gmapRecord = GMAPRecord()
+ self.gmapRecords.append(gmapRecord)
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ gmapRecord.fromXML(name, attrs, content, ttFont)
+ else:
+ value = attrs["value"]
+ if name == "PSFontName":
+ self.psFontName = value
+ else:
+ setattr(self, name, safeEval(value))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_K_G_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_K_G_.py
new file mode 100644
index 0000000000000000000000000000000000000000..0da99fcda4017d60788ff3cac336b2c6c877936e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_K_G_.py
@@ -0,0 +1,133 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytesjoin, safeEval, readHex
+from . import DefaultTable
+import sys
+import array
+
+GPKGFormat = """
+ > # big endian
+ version: H
+ flags: H
+ numGMAPs: H
+ numGlyplets: H
+"""
+# psFontName is a byte string which follows the record above. This is zero padded
+# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
+
+
+class table_G_P_K_G_(DefaultTable.DefaultTable):
+ """Glyphlets GPKG table
+
+ The ``GPKG`` table is used by Adobe's SING Glyphlets.
+
+ See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
+ """
+
+ def decompile(self, data, ttFont):
+ dummy, newData = sstruct.unpack2(GPKGFormat, data, self)
+
+ GMAPoffsets = array.array("I")
+ endPos = (self.numGMAPs + 1) * 4
+ GMAPoffsets.frombytes(newData[:endPos])
+ if sys.byteorder != "big":
+ GMAPoffsets.byteswap()
+ self.GMAPs = []
+ for i in range(self.numGMAPs):
+ start = GMAPoffsets[i]
+ end = GMAPoffsets[i + 1]
+ self.GMAPs.append(data[start:end])
+ pos = endPos
+ endPos = pos + (self.numGlyplets + 1) * 4
+ glyphletOffsets = array.array("I")
+ glyphletOffsets.frombytes(newData[pos:endPos])
+ if sys.byteorder != "big":
+ glyphletOffsets.byteswap()
+ self.glyphlets = []
+ for i in range(self.numGlyplets):
+ start = glyphletOffsets[i]
+ end = glyphletOffsets[i + 1]
+ self.glyphlets.append(data[start:end])
+
+ def compile(self, ttFont):
+ self.numGMAPs = len(self.GMAPs)
+ self.numGlyplets = len(self.glyphlets)
+ GMAPoffsets = [0] * (self.numGMAPs + 1)
+ glyphletOffsets = [0] * (self.numGlyplets + 1)
+
+ dataList = [sstruct.pack(GPKGFormat, self)]
+
+ pos = len(dataList[0]) + (self.numGMAPs + 1) * 4 + (self.numGlyplets + 1) * 4
+ GMAPoffsets[0] = pos
+ for i in range(1, self.numGMAPs + 1):
+ pos += len(self.GMAPs[i - 1])
+ GMAPoffsets[i] = pos
+ gmapArray = array.array("I", GMAPoffsets)
+ if sys.byteorder != "big":
+ gmapArray.byteswap()
+ dataList.append(gmapArray.tobytes())
+
+ glyphletOffsets[0] = pos
+ for i in range(1, self.numGlyplets + 1):
+ pos += len(self.glyphlets[i - 1])
+ glyphletOffsets[i] = pos
+ glyphletArray = array.array("I", glyphletOffsets)
+ if sys.byteorder != "big":
+ glyphletArray.byteswap()
+ dataList.append(glyphletArray.tobytes())
+ dataList += self.GMAPs
+ dataList += self.glyphlets
+ data = bytesjoin(dataList)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(GPKGFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ writer.begintag("GMAPs")
+ writer.newline()
+ for gmapData in self.GMAPs:
+ writer.begintag("hexdata")
+ writer.newline()
+ writer.dumphex(gmapData)
+ writer.endtag("hexdata")
+ writer.newline()
+ writer.endtag("GMAPs")
+ writer.newline()
+
+ writer.begintag("glyphlets")
+ writer.newline()
+ for glyphletData in self.glyphlets:
+ writer.begintag("hexdata")
+ writer.newline()
+ writer.dumphex(glyphletData)
+ writer.endtag("hexdata")
+ writer.newline()
+ writer.endtag("glyphlets")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "GMAPs":
+ if not hasattr(self, "GMAPs"):
+ self.GMAPs = []
+ for element in content:
+ if isinstance(element, str):
+ continue
+ itemName, itemAttrs, itemContent = element
+ if itemName == "hexdata":
+ self.GMAPs.append(readHex(itemContent))
+ elif name == "glyphlets":
+ if not hasattr(self, "glyphlets"):
+ self.glyphlets = []
+ for element in content:
+ if isinstance(element, str):
+ continue
+ itemName, itemAttrs, itemContent = element
+ if itemName == "hexdata":
+ self.glyphlets.append(readHex(itemContent))
+ else:
+ setattr(self, name, safeEval(attrs["value"]))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_O_S_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_O_S_.py
new file mode 100644
index 0000000000000000000000000000000000000000..03bdc612ead945133c97f4a4d0002e8ca31c60c4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_P_O_S_.py
@@ -0,0 +1,14 @@
+from .otBase import BaseTTXConverter
+
+
+class table_G_P_O_S_(BaseTTXConverter):
+ """Glyph Positioning table
+
+ The ``GPOS`` table stores advanced glyph-positioning data
+ used in OpenType Layout features, such as mark attachment,
+ cursive attachment, kerning, and other position adjustments.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/gpos
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_S_U_B_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_S_U_B_.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca1aff8b0680bf22550c460819627b4d2b14a463
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_S_U_B_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_G_S_U_B_(BaseTTXConverter):
+ """Glyph Substitution table
+
+ The ``GSUB`` table contains glyph-substitution rules used in
+ OpenType Layout.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/gsub
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G_V_A_R_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_V_A_R_.py
new file mode 100644
index 0000000000000000000000000000000000000000..889b1f2a3bd712b68140a6b9102e732ce794587c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G_V_A_R_.py
@@ -0,0 +1,5 @@
+from ._g_v_a_r import table__g_v_a_r
+
+
+class table_G_V_A_R_(table__g_v_a_r):
+ gid_size = 3
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_a_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_a_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe1e0534fb75e372933add1e71799c272a01c3dd
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_a_t.py
@@ -0,0 +1,235 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
+from fontTools.misc.textTools import safeEval
+
+# from itertools import *
+from functools import partial
+from . import DefaultTable
+from . import grUtils
+import struct
+
+
+Glat_format_0 = """
+ > # big endian
+ version: 16.16F
+"""
+
+Glat_format_3 = """
+ >
+ version: 16.16F
+ compression:L # compression scheme or reserved
+"""
+
+Glat_format_1_entry = """
+ >
+ attNum: B # Attribute number of first attribute
+ num: B # Number of attributes in this run
+"""
+Glat_format_23_entry = """
+ >
+ attNum: H # Attribute number of first attribute
+ num: H # Number of attributes in this run
+"""
+
+Glat_format_3_octabox_metrics = """
+ >
+ subboxBitmap: H # Which subboxes exist on 4x4 grid
+ diagNegMin: B # Defines minimum negatively-sloped diagonal (si)
+ diagNegMax: B # Defines maximum negatively-sloped diagonal (sa)
+ diagPosMin: B # Defines minimum positively-sloped diagonal (di)
+ diagPosMax: B # Defines maximum positively-sloped diagonal (da)
+"""
+
+Glat_format_3_subbox_entry = """
+ >
+ left: B # xi
+ right: B # xa
+ bottom: B # yi
+ top: B # ya
+ diagNegMin: B # Defines minimum negatively-sloped diagonal (si)
+ diagNegMax: B # Defines maximum negatively-sloped diagonal (sa)
+ diagPosMin: B # Defines minimum positively-sloped diagonal (di)
+ diagPosMax: B # Defines maximum positively-sloped diagonal (da)
+"""
+
+
+class _Object:
+ pass
+
+
+class _Dict(dict):
+ pass
+
+
+class table_G__l_a_t(DefaultTable.DefaultTable):
+ """Graphite Glyph Attributes table
+
+ See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.scheme = 0
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack2(Glat_format_0, data, self)
+ self.version = float(floatToFixedToStr(self.version, precisionBits=16))
+ if self.version <= 1.9:
+ decoder = partial(self.decompileAttributes12, fmt=Glat_format_1_entry)
+ elif self.version <= 2.9:
+ decoder = partial(self.decompileAttributes12, fmt=Glat_format_23_entry)
+ elif self.version >= 3.0:
+ (data, self.scheme) = grUtils.decompress(data)
+ sstruct.unpack2(Glat_format_3, data, self)
+ self.hasOctaboxes = (self.compression & 1) == 1
+ decoder = self.decompileAttributes3
+
+ gloc = ttFont["Gloc"]
+ self.attributes = {}
+ count = 0
+ for s, e in zip(gloc, gloc[1:]):
+ self.attributes[ttFont.getGlyphName(count)] = decoder(data[s:e])
+ count += 1
+
+ def decompileAttributes12(self, data, fmt):
+ attributes = _Dict()
+ while len(data) > 3:
+ e, data = sstruct.unpack2(fmt, data, _Object())
+ keys = range(e.attNum, e.attNum + e.num)
+ if len(data) >= 2 * e.num:
+ vals = struct.unpack_from((">%dh" % e.num), data)
+ attributes.update(zip(keys, vals))
+ data = data[2 * e.num :]
+ return attributes
+
+ def decompileAttributes3(self, data):
+ if self.hasOctaboxes:
+ o, data = sstruct.unpack2(Glat_format_3_octabox_metrics, data, _Object())
+ numsub = bin(o.subboxBitmap).count("1")
+ o.subboxes = []
+ for b in range(numsub):
+ if len(data) >= 8:
+ subbox, data = sstruct.unpack2(
+ Glat_format_3_subbox_entry, data, _Object()
+ )
+ o.subboxes.append(subbox)
+ attrs = self.decompileAttributes12(data, Glat_format_23_entry)
+ if self.hasOctaboxes:
+ attrs.octabox = o
+ return attrs
+
+ def compile(self, ttFont):
+ data = sstruct.pack(Glat_format_0, self)
+ if self.version <= 1.9:
+ encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
+ elif self.version <= 2.9:
+ encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
+ elif self.version >= 3.0:
+ self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0)
+ data = sstruct.pack(Glat_format_3, self)
+ encoder = self.compileAttributes3
+
+ glocs = []
+ for n in range(len(self.attributes)):
+ glocs.append(len(data))
+ data += encoder(self.attributes[ttFont.getGlyphName(n)])
+ glocs.append(len(data))
+ ttFont["Gloc"].set(glocs)
+
+ if self.version >= 3.0:
+ data = grUtils.compress(self.scheme, data)
+ return data
+
+ def compileAttributes12(self, attrs, fmt):
+ data = b""
+ for e in grUtils.entries(attrs):
+ data += sstruct.pack(fmt, {"attNum": e[0], "num": e[1]}) + struct.pack(
+ (">%dh" % len(e[2])), *e[2]
+ )
+ return data
+
+ def compileAttributes3(self, attrs):
+ if self.hasOctaboxes:
+ o = attrs.octabox
+ data = sstruct.pack(Glat_format_3_octabox_metrics, o)
+ numsub = bin(o.subboxBitmap).count("1")
+ for b in range(numsub):
+ data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b])
+ else:
+ data = ""
+ return data + self.compileAttributes12(attrs, Glat_format_23_entry)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", version=self.version, compressionScheme=self.scheme)
+ writer.newline()
+ for n, a in sorted(
+ self.attributes.items(), key=lambda x: ttFont.getGlyphID(x[0])
+ ):
+ writer.begintag("glyph", name=n)
+ writer.newline()
+ if hasattr(a, "octabox"):
+ o = a.octabox
+ formatstring, names, fixes = sstruct.getformat(
+ Glat_format_3_octabox_metrics
+ )
+ vals = {}
+ for k in names:
+ if k == "subboxBitmap":
+ continue
+ vals[k] = "{:.3f}%".format(getattr(o, k) * 100.0 / 255)
+ vals["bitmap"] = "{:0X}".format(o.subboxBitmap)
+ writer.begintag("octaboxes", **vals)
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(
+ Glat_format_3_subbox_entry
+ )
+ for s in o.subboxes:
+ vals = {}
+ for k in names:
+ vals[k] = "{:.3f}%".format(getattr(s, k) * 100.0 / 255)
+ writer.simpletag("octabox", **vals)
+ writer.newline()
+ writer.endtag("octaboxes")
+ writer.newline()
+ for k, v in sorted(a.items()):
+ writer.simpletag("attribute", index=k, value=v)
+ writer.newline()
+ writer.endtag("glyph")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = float(safeEval(attrs["version"]))
+ self.scheme = int(safeEval(attrs["compressionScheme"]))
+ if name != "glyph":
+ return
+ if not hasattr(self, "attributes"):
+ self.attributes = {}
+ gname = attrs["name"]
+ attributes = _Dict()
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ if tag == "attribute":
+ k = int(safeEval(attrs["index"]))
+ v = int(safeEval(attrs["value"]))
+ attributes[k] = v
+ elif tag == "octaboxes":
+ self.hasOctaboxes = True
+ o = _Object()
+ o.subboxBitmap = int(attrs["bitmap"], 16)
+ o.subboxes = []
+ del attrs["bitmap"]
+ for k, v in attrs.items():
+ setattr(o, k, int(float(v[:-1]) * 255.0 / 100.0 + 0.5))
+ for element in subcontent:
+ if not isinstance(element, tuple):
+ continue
+ (tag, attrs, subcontent) = element
+ so = _Object()
+ for k, v in attrs.items():
+ setattr(so, k, int(float(v[:-1]) * 255.0 / 100.0 + 0.5))
+ o.subboxes.append(so)
+ attributes.octabox = o
+ self.attributes[gname] = attributes
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_o_c.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_o_c.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c3d78ab2e4b993708a83c13efe45c3c4a1eaf92
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/G__l_o_c.py
@@ -0,0 +1,85 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import array
+import sys
+
+
+Gloc_header = """
+ > # big endian
+ version: 16.16F # Table version
+ flags: H # bit 0: 1=long format, 0=short format
+ # bit 1: 1=attribute names, 0=no names
+ numAttribs: H # NUmber of attributes
+"""
+
+
+class table_G__l_o_c(DefaultTable.DefaultTable):
+ """Graphite Index to Glyph Atttributes table
+
+ See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
+ """
+
+ dependencies = ["Glat"]
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.attribIds = None
+ self.numAttribs = 0
+
+ def decompile(self, data, ttFont):
+ _, data = sstruct.unpack2(Gloc_header, data, self)
+ flags = self.flags
+ del self.flags
+ self.locations = array.array("I" if flags & 1 else "H")
+ self.locations.frombytes(data[: len(data) - self.numAttribs * (flags & 2)])
+ if sys.byteorder != "big":
+ self.locations.byteswap()
+ self.attribIds = array.array("H")
+ if flags & 2:
+ self.attribIds.frombytes(data[-self.numAttribs * 2 :])
+ if sys.byteorder != "big":
+ self.attribIds.byteswap()
+
+ def compile(self, ttFont):
+ data = sstruct.pack(
+ Gloc_header,
+ dict(
+ version=1.0,
+ flags=(bool(self.attribIds) << 1) + (self.locations.typecode == "I"),
+ numAttribs=self.numAttribs,
+ ),
+ )
+ if sys.byteorder != "big":
+ self.locations.byteswap()
+ data += self.locations.tobytes()
+ if sys.byteorder != "big":
+ self.locations.byteswap()
+ if self.attribIds:
+ if sys.byteorder != "big":
+ self.attribIds.byteswap()
+ data += self.attribIds.tobytes()
+ if sys.byteorder != "big":
+ self.attribIds.byteswap()
+ return data
+
+ def set(self, locations):
+ long_format = max(locations) >= 65536
+ self.locations = array.array("I" if long_format else "H", locations)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("attributes", number=self.numAttribs)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "attributes":
+ self.numAttribs = int(safeEval(attrs["number"]))
+
+ def __getitem__(self, index):
+ return self.locations[index]
+
+ def __len__(self):
+ return len(self.locations)
+
+ def __iter__(self):
+ return iter(self.locations)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/H_V_A_R_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/H_V_A_R_.py
new file mode 100644
index 0000000000000000000000000000000000000000..48e7fd67cf0f89e5dc6057e5cbae7f07324a0402
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/H_V_A_R_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_H_V_A_R_(BaseTTXConverter):
+ """Horizontal Metrics Variations table
+
+ The ``HVAR`` table contains variations in horizontal glyph metrics
+ in variable fonts.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/hvar
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/J_S_T_F_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/J_S_T_F_.py
new file mode 100644
index 0000000000000000000000000000000000000000..b185f30f3a9a2978c1d4278b920f69969fe79b97
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/J_S_T_F_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_J_S_T_F_(BaseTTXConverter):
+ """Justification table
+
+ The ``JSTF`` table contains glyph substitution and positioning
+ data used to perform text justification.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/jstf
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/L_T_S_H_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/L_T_S_H_.py
new file mode 100644
index 0000000000000000000000000000000000000000..e691c06e9bfdc270127c2a5be33acf6505177d85
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/L_T_S_H_.py
@@ -0,0 +1,58 @@
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
+import array
+
+# XXX I've lowered the strictness, to make sure Apple's own Chicago
+# XXX gets through. They're looking into it, I hope to raise the standards
+# XXX back to normal eventually.
+
+
+class table_L_T_S_H_(DefaultTable.DefaultTable):
+ """Linear Threshold table
+
+ The ``LTSH`` table contains per-glyph settings indicating the ppem sizes
+ at which the advance width metric should be scaled linearly, despite the
+ effects of any TrueType instructions that might otherwise alter the
+ advance width.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/ltsh
+ """
+
+ def decompile(self, data, ttFont):
+ version, numGlyphs = struct.unpack(">HH", data[:4])
+ data = data[4:]
+ assert version == 0, "unknown version: %s" % version
+ assert (len(data) % numGlyphs) < 4, "numGlyphs doesn't match data length"
+ # ouch: the assertion is not true in Chicago!
+ # assert numGlyphs == ttFont['maxp'].numGlyphs
+ yPels = array.array("B")
+ yPels.frombytes(data)
+ self.yPels = {}
+ for i in range(numGlyphs):
+ self.yPels[ttFont.getGlyphName(i)] = yPels[i]
+
+ def compile(self, ttFont):
+ version = 0
+ names = list(self.yPels.keys())
+ numGlyphs = len(names)
+ yPels = [0] * numGlyphs
+ # ouch: the assertion is not true in Chicago!
+ # assert len(self.yPels) == ttFont['maxp'].numGlyphs == numGlyphs
+ for name in names:
+ yPels[ttFont.getGlyphID(name)] = self.yPels[name]
+ yPels = array.array("B", yPels)
+ return struct.pack(">HH", version, numGlyphs) + yPels.tobytes()
+
+ def toXML(self, writer, ttFont):
+ names = sorted(self.yPels.keys())
+ for name in names:
+ writer.simpletag("yPel", name=name, value=self.yPels[name])
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "yPels"):
+ self.yPels = {}
+ if name != "yPel":
+ return # ignore unknown tags
+ self.yPels[attrs["name"]] = safeEval(attrs["value"])
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/M_A_T_H_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_A_T_H_.py
new file mode 100644
index 0000000000000000000000000000000000000000..35d29e9b13e9c8aeca0a95a3e965c52e7c96aed1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_A_T_H_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_M_A_T_H_(BaseTTXConverter):
+ """Mathematical Typesetting table
+
+ The ``MATH`` table contains a variety of information needed to
+ typeset glyphs in mathematical formulas and expressions.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/math
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/M_E_T_A_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_E_T_A_.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a6f8bbf8400a0a39c3a251310c40464b1bbc627
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_E_T_A_.py
@@ -0,0 +1,352 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import byteord, safeEval
+from . import DefaultTable
+import pdb
+import struct
+
+
+METAHeaderFormat = """
+ > # big endian
+ tableVersionMajor: H
+ tableVersionMinor: H
+ metaEntriesVersionMajor: H
+ metaEntriesVersionMinor: H
+ unicodeVersion: L
+ metaFlags: H
+ nMetaRecs: H
+"""
+# This record is followed by nMetaRecs of METAGlyphRecordFormat.
+# This in turn is followd by as many METAStringRecordFormat entries
+# as specified by the METAGlyphRecordFormat entries
+# this is followed by the strings specifried in the METAStringRecordFormat
+METAGlyphRecordFormat = """
+ > # big endian
+ glyphID: H
+ nMetaEntry: H
+"""
+# This record is followd by a variable data length field:
+# USHORT or ULONG hdrOffset
+# Offset from start of META table to the beginning
+# of this glyphs array of ns Metadata string entries.
+# Size determined by metaFlags field
+# METAGlyphRecordFormat entries must be sorted by glyph ID
+
+METAStringRecordFormat = """
+ > # big endian
+ labelID: H
+ stringLen: H
+"""
+# This record is followd by a variable data length field:
+# USHORT or ULONG stringOffset
+# METAStringRecordFormat entries must be sorted in order of labelID
+# There may be more than one entry with the same labelID
+# There may be more than one strign with the same content.
+
+# Strings shall be Unicode UTF-8 encoded, and null-terminated.
+
+METALabelDict = {
+ 0: "MojikumiX4051", # An integer in the range 1-20
+ 1: "UNIUnifiedBaseChars",
+ 2: "BaseFontName",
+ 3: "Language",
+ 4: "CreationDate",
+ 5: "FoundryName",
+ 6: "FoundryCopyright",
+ 7: "OwnerURI",
+ 8: "WritingScript",
+ 10: "StrokeCount",
+ 11: "IndexingRadical",
+}
+
+
+def getLabelString(labelID):
+ try:
+ label = METALabelDict[labelID]
+ except KeyError:
+ label = "Unknown label"
+ return str(label)
+
+
+class table_M_E_T_A_(DefaultTable.DefaultTable):
+ """Glyphlets META table
+
+ The ``META`` table is used by Adobe's SING Glyphlets.
+
+ See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
+ """
+
+ dependencies = []
+
+ def decompile(self, data, ttFont):
+ dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
+ self.glyphRecords = []
+ for i in range(self.nMetaRecs):
+ glyphRecord, newData = sstruct.unpack2(
+ METAGlyphRecordFormat, newData, GlyphRecord()
+ )
+ if self.metaFlags == 0:
+ [glyphRecord.offset] = struct.unpack(">H", newData[:2])
+ newData = newData[2:]
+ elif self.metaFlags == 1:
+ [glyphRecord.offset] = struct.unpack(">H", newData[:4])
+ newData = newData[4:]
+ else:
+ assert 0, (
+ "The metaFlags field in the META table header has a value other than 0 or 1 :"
+ + str(self.metaFlags)
+ )
+ glyphRecord.stringRecs = []
+ newData = data[glyphRecord.offset :]
+ for j in range(glyphRecord.nMetaEntry):
+ stringRec, newData = sstruct.unpack2(
+ METAStringRecordFormat, newData, StringRecord()
+ )
+ if self.metaFlags == 0:
+ [stringRec.offset] = struct.unpack(">H", newData[:2])
+ newData = newData[2:]
+ else:
+ [stringRec.offset] = struct.unpack(">H", newData[:4])
+ newData = newData[4:]
+ stringRec.string = data[
+ stringRec.offset : stringRec.offset + stringRec.stringLen
+ ]
+ glyphRecord.stringRecs.append(stringRec)
+ self.glyphRecords.append(glyphRecord)
+
+ def compile(self, ttFont):
+ offsetOK = 0
+ self.nMetaRecs = len(self.glyphRecords)
+ count = 0
+ while offsetOK != 1:
+ count = count + 1
+ if count > 4:
+ pdb.set_trace()
+ metaData = sstruct.pack(METAHeaderFormat, self)
+ stringRecsOffset = len(metaData) + self.nMetaRecs * (
+ 6 + 2 * (self.metaFlags & 1)
+ )
+ stringRecSize = 6 + 2 * (self.metaFlags & 1)
+ for glyphRec in self.glyphRecords:
+ glyphRec.offset = stringRecsOffset
+ if (glyphRec.offset > 65535) and ((self.metaFlags & 1) == 0):
+ self.metaFlags = self.metaFlags + 1
+ offsetOK = -1
+ break
+ metaData = metaData + glyphRec.compile(self)
+ stringRecsOffset = stringRecsOffset + (
+ glyphRec.nMetaEntry * stringRecSize
+ )
+ # this will be the String Record offset for the next GlyphRecord.
+ if offsetOK == -1:
+ offsetOK = 0
+ continue
+
+ # metaData now contains the header and all of the GlyphRecords. Its length should bw
+ # the offset to the first StringRecord.
+ stringOffset = stringRecsOffset
+ for glyphRec in self.glyphRecords:
+ assert glyphRec.offset == len(
+ metaData
+ ), "Glyph record offset did not compile correctly! for rec:" + str(
+ glyphRec
+ )
+ for stringRec in glyphRec.stringRecs:
+ stringRec.offset = stringOffset
+ if (stringRec.offset > 65535) and ((self.metaFlags & 1) == 0):
+ self.metaFlags = self.metaFlags + 1
+ offsetOK = -1
+ break
+ metaData = metaData + stringRec.compile(self)
+ stringOffset = stringOffset + stringRec.stringLen
+ if offsetOK == -1:
+ offsetOK = 0
+ continue
+
+ if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
+ self.metaFlags = self.metaFlags - 1
+ continue
+ else:
+ offsetOK = 1
+
+ # metaData now contains the header and all of the GlyphRecords and all of the String Records.
+ # Its length should be the offset to the first string datum.
+ for glyphRec in self.glyphRecords:
+ for stringRec in glyphRec.stringRecs:
+ assert stringRec.offset == len(
+ metaData
+ ), "String offset did not compile correctly! for string:" + str(
+ stringRec.string
+ )
+ metaData = metaData + stringRec.string
+
+ return metaData
+
+ def toXML(self, writer, ttFont):
+ writer.comment(
+ "Lengths and number of entries in this table will be recalculated by the compiler"
+ )
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(METAHeaderFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+ for glyphRec in self.glyphRecords:
+ glyphRec.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "GlyphRecord":
+ if not hasattr(self, "glyphRecords"):
+ self.glyphRecords = []
+ glyphRec = GlyphRecord()
+ self.glyphRecords.append(glyphRec)
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ glyphRec.fromXML(name, attrs, content, ttFont)
+ glyphRec.offset = -1
+ glyphRec.nMetaEntry = len(glyphRec.stringRecs)
+ else:
+ setattr(self, name, safeEval(attrs["value"]))
+
+
+class GlyphRecord(object):
+ def __init__(self):
+ self.glyphID = -1
+ self.nMetaEntry = -1
+ self.offset = -1
+ self.stringRecs = []
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("GlyphRecord")
+ writer.newline()
+ writer.simpletag("glyphID", value=self.glyphID)
+ writer.newline()
+ writer.simpletag("nMetaEntry", value=self.nMetaEntry)
+ writer.newline()
+ for stringRec in self.stringRecs:
+ stringRec.toXML(writer, ttFont)
+ writer.endtag("GlyphRecord")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "StringRecord":
+ stringRec = StringRecord()
+ self.stringRecs.append(stringRec)
+ for element in content:
+ if isinstance(element, str):
+ continue
+ stringRec.fromXML(name, attrs, content, ttFont)
+ stringRec.stringLen = len(stringRec.string)
+ else:
+ setattr(self, name, safeEval(attrs["value"]))
+
+ def compile(self, parentTable):
+ data = sstruct.pack(METAGlyphRecordFormat, self)
+ if parentTable.metaFlags == 0:
+ datum = struct.pack(">H", self.offset)
+ elif parentTable.metaFlags == 1:
+ datum = struct.pack(">L", self.offset)
+ data = data + datum
+ return data
+
+ def __repr__(self):
+ return (
+ "GlyphRecord[ glyphID: "
+ + str(self.glyphID)
+ + ", nMetaEntry: "
+ + str(self.nMetaEntry)
+ + ", offset: "
+ + str(self.offset)
+ + " ]"
+ )
+
+
+# XXX The following two functions are really broken around UTF-8 vs Unicode
+
+
+def mapXMLToUTF8(string):
+ uString = str()
+ strLen = len(string)
+ i = 0
+ while i < strLen:
+ prefixLen = 0
+ if string[i : i + 3] == "":
+ prefixLen = 3
+ elif string[i : i + 7] == "&#x":
+ prefixLen = 7
+ if prefixLen:
+ i = i + prefixLen
+ j = i
+ while string[i] != ";":
+ i = i + 1
+ valStr = string[j:i]
+
+ uString = uString + chr(eval("0x" + valStr))
+ else:
+ uString = uString + chr(byteord(string[i]))
+ i = i + 1
+
+ return uString.encode("utf_8")
+
+
+def mapUTF8toXML(string):
+ uString = string.decode("utf_8")
+ string = ""
+ for uChar in uString:
+ i = ord(uChar)
+ if (i < 0x80) and (i > 0x1F):
+ string = string + uChar
+ else:
+ string = string + "" + hex(i)[2:] + ";"
+ return string
+
+
+class StringRecord(object):
+ def toXML(self, writer, ttFont):
+ writer.begintag("StringRecord")
+ writer.newline()
+ writer.simpletag("labelID", value=self.labelID)
+ writer.comment(getLabelString(self.labelID))
+ writer.newline()
+ writer.newline()
+ writer.simpletag("string", value=mapUTF8toXML(self.string))
+ writer.newline()
+ writer.endtag("StringRecord")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ value = attrs["value"]
+ if name == "string":
+ self.string = mapXMLToUTF8(value)
+ else:
+ setattr(self, name, safeEval(value))
+
+ def compile(self, parentTable):
+ data = sstruct.pack(METAStringRecordFormat, self)
+ if parentTable.metaFlags == 0:
+ datum = struct.pack(">H", self.offset)
+ elif parentTable.metaFlags == 1:
+ datum = struct.pack(">L", self.offset)
+ data = data + datum
+ return data
+
+ def __repr__(self):
+ return (
+ "StringRecord [ labelID: "
+ + str(self.labelID)
+ + " aka "
+ + getLabelString(self.labelID)
+ + ", offset: "
+ + str(self.offset)
+ + ", length: "
+ + str(self.stringLen)
+ + ", string: "
+ + self.string
+ + " ]"
+ )
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/M_V_A_R_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_V_A_R_.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7e7e90fb49965b962f502a3ac9135a1c00b0374
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/M_V_A_R_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_M_V_A_R_(BaseTTXConverter):
+ """Metrics Variations table
+
+ The ``MVAR`` table contains variation information for font-wide
+ metrics in a variable font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/mvar
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/O_S_2f_2.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/O_S_2f_2.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a7e5f70bb99a37ad5f1260d62b7406df9361771
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/O_S_2f_2.py
@@ -0,0 +1,752 @@
+from fontTools.misc import sstruct
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.ttLib.tables import DefaultTable
+import bisect
+import logging
+
+
+log = logging.getLogger(__name__)
+
+# panose classification
+
+panoseFormat = """
+ bFamilyType: B
+ bSerifStyle: B
+ bWeight: B
+ bProportion: B
+ bContrast: B
+ bStrokeVariation: B
+ bArmStyle: B
+ bLetterForm: B
+ bMidline: B
+ bXHeight: B
+"""
+
+
+class Panose(object):
+ def __init__(self, **kwargs):
+ _, names, _ = sstruct.getformat(panoseFormat)
+ for name in names:
+ setattr(self, name, kwargs.pop(name, 0))
+ for k in kwargs:
+ raise TypeError(f"Panose() got an unexpected keyword argument {k!r}")
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(panoseFormat)
+ for name in names:
+ writer.simpletag(name, value=getattr(self, name))
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
+
+
+# 'sfnt' OS/2 and Windows Metrics table - 'OS/2'
+
+OS2_format_0 = """
+ > # big endian
+ version: H # version
+ xAvgCharWidth: h # average character width
+ usWeightClass: H # degree of thickness of strokes
+ usWidthClass: H # aspect ratio
+ fsType: H # type flags
+ ySubscriptXSize: h # subscript horizontal font size
+ ySubscriptYSize: h # subscript vertical font size
+ ySubscriptXOffset: h # subscript x offset
+ ySubscriptYOffset: h # subscript y offset
+ ySuperscriptXSize: h # superscript horizontal font size
+ ySuperscriptYSize: h # superscript vertical font size
+ ySuperscriptXOffset: h # superscript x offset
+ ySuperscriptYOffset: h # superscript y offset
+ yStrikeoutSize: h # strikeout size
+ yStrikeoutPosition: h # strikeout position
+ sFamilyClass: h # font family class and subclass
+ panose: 10s # panose classification number
+ ulUnicodeRange1: L # character range
+ ulUnicodeRange2: L # character range
+ ulUnicodeRange3: L # character range
+ ulUnicodeRange4: L # character range
+ achVendID: 4s # font vendor identification
+ fsSelection: H # font selection flags
+ usFirstCharIndex: H # first unicode character index
+ usLastCharIndex: H # last unicode character index
+ sTypoAscender: h # typographic ascender
+ sTypoDescender: h # typographic descender
+ sTypoLineGap: h # typographic line gap
+ usWinAscent: H # Windows ascender
+ usWinDescent: H # Windows descender
+"""
+
+OS2_format_1_addition = """
+ ulCodePageRange1: L
+ ulCodePageRange2: L
+"""
+
+OS2_format_2_addition = (
+ OS2_format_1_addition
+ + """
+ sxHeight: h
+ sCapHeight: h
+ usDefaultChar: H
+ usBreakChar: H
+ usMaxContext: H
+"""
+)
+
+OS2_format_5_addition = (
+ OS2_format_2_addition
+ + """
+ usLowerOpticalPointSize: H
+ usUpperOpticalPointSize: H
+"""
+)
+
+bigendian = " > # big endian\n"
+
+OS2_format_1 = OS2_format_0 + OS2_format_1_addition
+OS2_format_2 = OS2_format_0 + OS2_format_2_addition
+OS2_format_5 = OS2_format_0 + OS2_format_5_addition
+OS2_format_1_addition = bigendian + OS2_format_1_addition
+OS2_format_2_addition = bigendian + OS2_format_2_addition
+OS2_format_5_addition = bigendian + OS2_format_5_addition
+
+
+class table_O_S_2f_2(DefaultTable.DefaultTable):
+ """OS/2 and Windows Metrics table
+
+ The ``OS/2`` table contains a variety of font-wide metrics and
+ parameters that may be useful to an operating system or other
+ software for system-integration purposes.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/os2
+ """
+
+ dependencies = ["head"]
+
+ def decompile(self, data, ttFont):
+ dummy, data = sstruct.unpack2(OS2_format_0, data, self)
+
+ if self.version == 1:
+ dummy, data = sstruct.unpack2(OS2_format_1_addition, data, self)
+ elif self.version in (2, 3, 4):
+ dummy, data = sstruct.unpack2(OS2_format_2_addition, data, self)
+ elif self.version == 5:
+ dummy, data = sstruct.unpack2(OS2_format_5_addition, data, self)
+ self.usLowerOpticalPointSize /= 20
+ self.usUpperOpticalPointSize /= 20
+ elif self.version != 0:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError(
+ "unknown format for OS/2 table: version %s" % self.version
+ )
+ if len(data):
+ log.warning("too much 'OS/2' table data")
+
+ self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
+
+ def compile(self, ttFont):
+ self.updateFirstAndLastCharIndex(ttFont)
+ panose = self.panose
+ head = ttFont["head"]
+ if (self.fsSelection & 1) and not (head.macStyle & 1 << 1):
+ log.warning(
+ "fsSelection bit 0 (italic) and "
+ "head table macStyle bit 1 (italic) should match"
+ )
+ if (self.fsSelection & 1 << 5) and not (head.macStyle & 1):
+ log.warning(
+ "fsSelection bit 5 (bold) and "
+ "head table macStyle bit 0 (bold) should match"
+ )
+ if (self.fsSelection & 1 << 6) and (self.fsSelection & 1 + (1 << 5)):
+ log.warning(
+ "fsSelection bit 6 (regular) is set, "
+ "bits 0 (italic) and 5 (bold) must be clear"
+ )
+ if self.version < 4 and self.fsSelection & 0b1110000000:
+ log.warning(
+ "fsSelection bits 7, 8 and 9 are only defined in "
+ "OS/2 table version 4 and up: version %s",
+ self.version,
+ )
+ self.panose = sstruct.pack(panoseFormat, self.panose)
+ if self.version == 0:
+ data = sstruct.pack(OS2_format_0, self)
+ elif self.version == 1:
+ data = sstruct.pack(OS2_format_1, self)
+ elif self.version in (2, 3, 4):
+ data = sstruct.pack(OS2_format_2, self)
+ elif self.version == 5:
+ d = self.__dict__.copy()
+ d["usLowerOpticalPointSize"] = round(self.usLowerOpticalPointSize * 20)
+ d["usUpperOpticalPointSize"] = round(self.usUpperOpticalPointSize * 20)
+ data = sstruct.pack(OS2_format_5, d)
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError(
+ "unknown format for OS/2 table: version %s" % self.version
+ )
+ self.panose = panose
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment(
+ "The fields 'usFirstCharIndex' and 'usLastCharIndex'\n"
+ "will be recalculated by the compiler"
+ )
+ writer.newline()
+ if self.version == 1:
+ format = OS2_format_1
+ elif self.version in (2, 3, 4):
+ format = OS2_format_2
+ elif self.version == 5:
+ format = OS2_format_5
+ else:
+ format = OS2_format_0
+ formatstring, names, fixes = sstruct.getformat(format)
+ for name in names:
+ value = getattr(self, name)
+ if name == "panose":
+ writer.begintag("panose")
+ writer.newline()
+ value.toXML(writer, ttFont)
+ writer.endtag("panose")
+ elif name in (
+ "ulUnicodeRange1",
+ "ulUnicodeRange2",
+ "ulUnicodeRange3",
+ "ulUnicodeRange4",
+ "ulCodePageRange1",
+ "ulCodePageRange2",
+ ):
+ writer.simpletag(name, value=num2binary(value))
+ elif name in ("fsType", "fsSelection"):
+ writer.simpletag(name, value=num2binary(value, 16))
+ elif name == "achVendID":
+ writer.simpletag(name, value=repr(value)[1:-1])
+ else:
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "panose":
+ self.panose = panose = Panose()
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ panose.fromXML(name, attrs, content, ttFont)
+ elif name in (
+ "ulUnicodeRange1",
+ "ulUnicodeRange2",
+ "ulUnicodeRange3",
+ "ulUnicodeRange4",
+ "ulCodePageRange1",
+ "ulCodePageRange2",
+ "fsType",
+ "fsSelection",
+ ):
+ setattr(self, name, binary2num(attrs["value"]))
+ elif name == "achVendID":
+ setattr(self, name, safeEval("'''" + attrs["value"] + "'''"))
+ else:
+ setattr(self, name, safeEval(attrs["value"]))
+
+ def updateFirstAndLastCharIndex(self, ttFont):
+ if "cmap" not in ttFont:
+ return
+ codes = set()
+ for table in getattr(ttFont["cmap"], "tables", []):
+ if table.isUnicode():
+ codes.update(table.cmap.keys())
+ if codes:
+ minCode = min(codes)
+ maxCode = max(codes)
+ # USHORT cannot hold codepoints greater than 0xFFFF
+ self.usFirstCharIndex = min(0xFFFF, minCode)
+ self.usLastCharIndex = min(0xFFFF, maxCode)
+
+ # misspelled attributes kept for legacy reasons
+
+ @property
+ def usMaxContex(self):
+ return self.usMaxContext
+
+ @usMaxContex.setter
+ def usMaxContex(self, value):
+ self.usMaxContext = value
+
+ @property
+ def fsFirstCharIndex(self):
+ return self.usFirstCharIndex
+
+ @fsFirstCharIndex.setter
+ def fsFirstCharIndex(self, value):
+ self.usFirstCharIndex = value
+
+ @property
+ def fsLastCharIndex(self):
+ return self.usLastCharIndex
+
+ @fsLastCharIndex.setter
+ def fsLastCharIndex(self, value):
+ self.usLastCharIndex = value
+
+ def getUnicodeRanges(self):
+ """Return the set of 'ulUnicodeRange*' bits currently enabled."""
+ bits = set()
+ ul1, ul2 = self.ulUnicodeRange1, self.ulUnicodeRange2
+ ul3, ul4 = self.ulUnicodeRange3, self.ulUnicodeRange4
+ for i in range(32):
+ if ul1 & (1 << i):
+ bits.add(i)
+ if ul2 & (1 << i):
+ bits.add(i + 32)
+ if ul3 & (1 << i):
+ bits.add(i + 64)
+ if ul4 & (1 << i):
+ bits.add(i + 96)
+ return bits
+
+ def setUnicodeRanges(self, bits):
+ """Set the 'ulUnicodeRange*' fields to the specified 'bits'."""
+ ul1, ul2, ul3, ul4 = 0, 0, 0, 0
+ for bit in bits:
+ if 0 <= bit < 32:
+ ul1 |= 1 << bit
+ elif 32 <= bit < 64:
+ ul2 |= 1 << (bit - 32)
+ elif 64 <= bit < 96:
+ ul3 |= 1 << (bit - 64)
+ elif 96 <= bit < 123:
+ ul4 |= 1 << (bit - 96)
+ else:
+ raise ValueError("expected 0 <= int <= 122, found: %r" % bit)
+ self.ulUnicodeRange1, self.ulUnicodeRange2 = ul1, ul2
+ self.ulUnicodeRange3, self.ulUnicodeRange4 = ul3, ul4
+
+ def recalcUnicodeRanges(self, ttFont, pruneOnly=False):
+ """Intersect the codepoints in the font's Unicode cmap subtables with
+ the Unicode block ranges defined in the OpenType specification (v1.7),
+ and set the respective 'ulUnicodeRange*' bits if there is at least ONE
+ intersection.
+ If 'pruneOnly' is True, only clear unused bits with NO intersection.
+ """
+ unicodes = set()
+ for table in ttFont["cmap"].tables:
+ if table.isUnicode():
+ unicodes.update(table.cmap.keys())
+ if pruneOnly:
+ empty = intersectUnicodeRanges(unicodes, inverse=True)
+ bits = self.getUnicodeRanges() - empty
+ else:
+ bits = intersectUnicodeRanges(unicodes)
+ self.setUnicodeRanges(bits)
+ return bits
+
+ def getCodePageRanges(self):
+ """Return the set of 'ulCodePageRange*' bits currently enabled."""
+ bits = set()
+ if self.version < 1:
+ return bits
+ ul1, ul2 = self.ulCodePageRange1, self.ulCodePageRange2
+ for i in range(32):
+ if ul1 & (1 << i):
+ bits.add(i)
+ if ul2 & (1 << i):
+ bits.add(i + 32)
+ return bits
+
+ def setCodePageRanges(self, bits):
+ """Set the 'ulCodePageRange*' fields to the specified 'bits'."""
+ ul1, ul2 = 0, 0
+ for bit in bits:
+ if 0 <= bit < 32:
+ ul1 |= 1 << bit
+ elif 32 <= bit < 64:
+ ul2 |= 1 << (bit - 32)
+ else:
+ raise ValueError(f"expected 0 <= int <= 63, found: {bit:r}")
+ if self.version < 1:
+ self.version = 1
+ self.ulCodePageRange1, self.ulCodePageRange2 = ul1, ul2
+
+ def recalcCodePageRanges(self, ttFont, pruneOnly=False):
+ unicodes = set()
+ for table in ttFont["cmap"].tables:
+ if table.isUnicode():
+ unicodes.update(table.cmap.keys())
+ bits = calcCodePageRanges(unicodes)
+ if pruneOnly:
+ bits &= self.getCodePageRanges()
+ # when no codepage ranges can be enabled, fall back to enabling bit 0
+ # (Latin 1) so that the font works in MS Word:
+ # https://github.com/googlei18n/fontmake/issues/468
+ if not bits:
+ bits = {0}
+ self.setCodePageRanges(bits)
+ return bits
+
+ def recalcAvgCharWidth(self, ttFont):
+ """Recalculate xAvgCharWidth using metrics from ttFont's 'hmtx' table.
+
+ Set it to 0 if the unlikely event 'hmtx' table is not found.
+ """
+ avg_width = 0
+ hmtx = ttFont.get("hmtx")
+ if hmtx is not None:
+ widths = [width for width, _ in hmtx.metrics.values() if width > 0]
+ if widths:
+ avg_width = otRound(sum(widths) / len(widths))
+ self.xAvgCharWidth = avg_width
+ return avg_width
+
+
+# Unicode ranges data from the OpenType OS/2 table specification v1.7
+
+OS2_UNICODE_RANGES = (
+ (("Basic Latin", (0x0000, 0x007F)),),
+ (("Latin-1 Supplement", (0x0080, 0x00FF)),),
+ (("Latin Extended-A", (0x0100, 0x017F)),),
+ (("Latin Extended-B", (0x0180, 0x024F)),),
+ (
+ ("IPA Extensions", (0x0250, 0x02AF)),
+ ("Phonetic Extensions", (0x1D00, 0x1D7F)),
+ ("Phonetic Extensions Supplement", (0x1D80, 0x1DBF)),
+ ),
+ (
+ ("Spacing Modifier Letters", (0x02B0, 0x02FF)),
+ ("Modifier Tone Letters", (0xA700, 0xA71F)),
+ ),
+ (
+ ("Combining Diacritical Marks", (0x0300, 0x036F)),
+ ("Combining Diacritical Marks Supplement", (0x1DC0, 0x1DFF)),
+ ),
+ (("Greek and Coptic", (0x0370, 0x03FF)),),
+ (("Coptic", (0x2C80, 0x2CFF)),),
+ (
+ ("Cyrillic", (0x0400, 0x04FF)),
+ ("Cyrillic Supplement", (0x0500, 0x052F)),
+ ("Cyrillic Extended-A", (0x2DE0, 0x2DFF)),
+ ("Cyrillic Extended-B", (0xA640, 0xA69F)),
+ ),
+ (("Armenian", (0x0530, 0x058F)),),
+ (("Hebrew", (0x0590, 0x05FF)),),
+ (("Vai", (0xA500, 0xA63F)),),
+ (("Arabic", (0x0600, 0x06FF)), ("Arabic Supplement", (0x0750, 0x077F))),
+ (("NKo", (0x07C0, 0x07FF)),),
+ (("Devanagari", (0x0900, 0x097F)),),
+ (("Bengali", (0x0980, 0x09FF)),),
+ (("Gurmukhi", (0x0A00, 0x0A7F)),),
+ (("Gujarati", (0x0A80, 0x0AFF)),),
+ (("Oriya", (0x0B00, 0x0B7F)),),
+ (("Tamil", (0x0B80, 0x0BFF)),),
+ (("Telugu", (0x0C00, 0x0C7F)),),
+ (("Kannada", (0x0C80, 0x0CFF)),),
+ (("Malayalam", (0x0D00, 0x0D7F)),),
+ (("Thai", (0x0E00, 0x0E7F)),),
+ (("Lao", (0x0E80, 0x0EFF)),),
+ (("Georgian", (0x10A0, 0x10FF)), ("Georgian Supplement", (0x2D00, 0x2D2F))),
+ (("Balinese", (0x1B00, 0x1B7F)),),
+ (("Hangul Jamo", (0x1100, 0x11FF)),),
+ (
+ ("Latin Extended Additional", (0x1E00, 0x1EFF)),
+ ("Latin Extended-C", (0x2C60, 0x2C7F)),
+ ("Latin Extended-D", (0xA720, 0xA7FF)),
+ ),
+ (("Greek Extended", (0x1F00, 0x1FFF)),),
+ (
+ ("General Punctuation", (0x2000, 0x206F)),
+ ("Supplemental Punctuation", (0x2E00, 0x2E7F)),
+ ),
+ (("Superscripts And Subscripts", (0x2070, 0x209F)),),
+ (("Currency Symbols", (0x20A0, 0x20CF)),),
+ (("Combining Diacritical Marks For Symbols", (0x20D0, 0x20FF)),),
+ (("Letterlike Symbols", (0x2100, 0x214F)),),
+ (("Number Forms", (0x2150, 0x218F)),),
+ (
+ ("Arrows", (0x2190, 0x21FF)),
+ ("Supplemental Arrows-A", (0x27F0, 0x27FF)),
+ ("Supplemental Arrows-B", (0x2900, 0x297F)),
+ ("Miscellaneous Symbols and Arrows", (0x2B00, 0x2BFF)),
+ ),
+ (
+ ("Mathematical Operators", (0x2200, 0x22FF)),
+ ("Supplemental Mathematical Operators", (0x2A00, 0x2AFF)),
+ ("Miscellaneous Mathematical Symbols-A", (0x27C0, 0x27EF)),
+ ("Miscellaneous Mathematical Symbols-B", (0x2980, 0x29FF)),
+ ),
+ (("Miscellaneous Technical", (0x2300, 0x23FF)),),
+ (("Control Pictures", (0x2400, 0x243F)),),
+ (("Optical Character Recognition", (0x2440, 0x245F)),),
+ (("Enclosed Alphanumerics", (0x2460, 0x24FF)),),
+ (("Box Drawing", (0x2500, 0x257F)),),
+ (("Block Elements", (0x2580, 0x259F)),),
+ (("Geometric Shapes", (0x25A0, 0x25FF)),),
+ (("Miscellaneous Symbols", (0x2600, 0x26FF)),),
+ (("Dingbats", (0x2700, 0x27BF)),),
+ (("CJK Symbols And Punctuation", (0x3000, 0x303F)),),
+ (("Hiragana", (0x3040, 0x309F)),),
+ (
+ ("Katakana", (0x30A0, 0x30FF)),
+ ("Katakana Phonetic Extensions", (0x31F0, 0x31FF)),
+ ),
+ (("Bopomofo", (0x3100, 0x312F)), ("Bopomofo Extended", (0x31A0, 0x31BF))),
+ (("Hangul Compatibility Jamo", (0x3130, 0x318F)),),
+ (("Phags-pa", (0xA840, 0xA87F)),),
+ (("Enclosed CJK Letters And Months", (0x3200, 0x32FF)),),
+ (("CJK Compatibility", (0x3300, 0x33FF)),),
+ (("Hangul Syllables", (0xAC00, 0xD7AF)),),
+ (("Non-Plane 0 *", (0xD800, 0xDFFF)),),
+ (("Phoenician", (0x10900, 0x1091F)),),
+ (
+ ("CJK Unified Ideographs", (0x4E00, 0x9FFF)),
+ ("CJK Radicals Supplement", (0x2E80, 0x2EFF)),
+ ("Kangxi Radicals", (0x2F00, 0x2FDF)),
+ ("Ideographic Description Characters", (0x2FF0, 0x2FFF)),
+ ("CJK Unified Ideographs Extension A", (0x3400, 0x4DBF)),
+ ("CJK Unified Ideographs Extension B", (0x20000, 0x2A6DF)),
+ ("Kanbun", (0x3190, 0x319F)),
+ ),
+ (("Private Use Area (plane 0)", (0xE000, 0xF8FF)),),
+ (
+ ("CJK Strokes", (0x31C0, 0x31EF)),
+ ("CJK Compatibility Ideographs", (0xF900, 0xFAFF)),
+ ("CJK Compatibility Ideographs Supplement", (0x2F800, 0x2FA1F)),
+ ),
+ (("Alphabetic Presentation Forms", (0xFB00, 0xFB4F)),),
+ (("Arabic Presentation Forms-A", (0xFB50, 0xFDFF)),),
+ (("Combining Half Marks", (0xFE20, 0xFE2F)),),
+ (
+ ("Vertical Forms", (0xFE10, 0xFE1F)),
+ ("CJK Compatibility Forms", (0xFE30, 0xFE4F)),
+ ),
+ (("Small Form Variants", (0xFE50, 0xFE6F)),),
+ (("Arabic Presentation Forms-B", (0xFE70, 0xFEFF)),),
+ (("Halfwidth And Fullwidth Forms", (0xFF00, 0xFFEF)),),
+ (("Specials", (0xFFF0, 0xFFFF)),),
+ (("Tibetan", (0x0F00, 0x0FFF)),),
+ (("Syriac", (0x0700, 0x074F)),),
+ (("Thaana", (0x0780, 0x07BF)),),
+ (("Sinhala", (0x0D80, 0x0DFF)),),
+ (("Myanmar", (0x1000, 0x109F)),),
+ (
+ ("Ethiopic", (0x1200, 0x137F)),
+ ("Ethiopic Supplement", (0x1380, 0x139F)),
+ ("Ethiopic Extended", (0x2D80, 0x2DDF)),
+ ),
+ (("Cherokee", (0x13A0, 0x13FF)),),
+ (("Unified Canadian Aboriginal Syllabics", (0x1400, 0x167F)),),
+ (("Ogham", (0x1680, 0x169F)),),
+ (("Runic", (0x16A0, 0x16FF)),),
+ (("Khmer", (0x1780, 0x17FF)), ("Khmer Symbols", (0x19E0, 0x19FF))),
+ (("Mongolian", (0x1800, 0x18AF)),),
+ (("Braille Patterns", (0x2800, 0x28FF)),),
+ (("Yi Syllables", (0xA000, 0xA48F)), ("Yi Radicals", (0xA490, 0xA4CF))),
+ (
+ ("Tagalog", (0x1700, 0x171F)),
+ ("Hanunoo", (0x1720, 0x173F)),
+ ("Buhid", (0x1740, 0x175F)),
+ ("Tagbanwa", (0x1760, 0x177F)),
+ ),
+ (("Old Italic", (0x10300, 0x1032F)),),
+ (("Gothic", (0x10330, 0x1034F)),),
+ (("Deseret", (0x10400, 0x1044F)),),
+ (
+ ("Byzantine Musical Symbols", (0x1D000, 0x1D0FF)),
+ ("Musical Symbols", (0x1D100, 0x1D1FF)),
+ ("Ancient Greek Musical Notation", (0x1D200, 0x1D24F)),
+ ),
+ (("Mathematical Alphanumeric Symbols", (0x1D400, 0x1D7FF)),),
+ (
+ ("Private Use (plane 15)", (0xF0000, 0xFFFFD)),
+ ("Private Use (plane 16)", (0x100000, 0x10FFFD)),
+ ),
+ (
+ ("Variation Selectors", (0xFE00, 0xFE0F)),
+ ("Variation Selectors Supplement", (0xE0100, 0xE01EF)),
+ ),
+ (("Tags", (0xE0000, 0xE007F)),),
+ (("Limbu", (0x1900, 0x194F)),),
+ (("Tai Le", (0x1950, 0x197F)),),
+ (("New Tai Lue", (0x1980, 0x19DF)),),
+ (("Buginese", (0x1A00, 0x1A1F)),),
+ (("Glagolitic", (0x2C00, 0x2C5F)),),
+ (("Tifinagh", (0x2D30, 0x2D7F)),),
+ (("Yijing Hexagram Symbols", (0x4DC0, 0x4DFF)),),
+ (("Syloti Nagri", (0xA800, 0xA82F)),),
+ (
+ ("Linear B Syllabary", (0x10000, 0x1007F)),
+ ("Linear B Ideograms", (0x10080, 0x100FF)),
+ ("Aegean Numbers", (0x10100, 0x1013F)),
+ ),
+ (("Ancient Greek Numbers", (0x10140, 0x1018F)),),
+ (("Ugaritic", (0x10380, 0x1039F)),),
+ (("Old Persian", (0x103A0, 0x103DF)),),
+ (("Shavian", (0x10450, 0x1047F)),),
+ (("Osmanya", (0x10480, 0x104AF)),),
+ (("Cypriot Syllabary", (0x10800, 0x1083F)),),
+ (("Kharoshthi", (0x10A00, 0x10A5F)),),
+ (("Tai Xuan Jing Symbols", (0x1D300, 0x1D35F)),),
+ (
+ ("Cuneiform", (0x12000, 0x123FF)),
+ ("Cuneiform Numbers and Punctuation", (0x12400, 0x1247F)),
+ ),
+ (("Counting Rod Numerals", (0x1D360, 0x1D37F)),),
+ (("Sundanese", (0x1B80, 0x1BBF)),),
+ (("Lepcha", (0x1C00, 0x1C4F)),),
+ (("Ol Chiki", (0x1C50, 0x1C7F)),),
+ (("Saurashtra", (0xA880, 0xA8DF)),),
+ (("Kayah Li", (0xA900, 0xA92F)),),
+ (("Rejang", (0xA930, 0xA95F)),),
+ (("Cham", (0xAA00, 0xAA5F)),),
+ (("Ancient Symbols", (0x10190, 0x101CF)),),
+ (("Phaistos Disc", (0x101D0, 0x101FF)),),
+ (
+ ("Carian", (0x102A0, 0x102DF)),
+ ("Lycian", (0x10280, 0x1029F)),
+ ("Lydian", (0x10920, 0x1093F)),
+ ),
+ (("Domino Tiles", (0x1F030, 0x1F09F)), ("Mahjong Tiles", (0x1F000, 0x1F02F))),
+)
+
+
+_unicodeStarts = []
+_unicodeValues = [None]
+
+
+def _getUnicodeRanges():
+ # build the ranges of codepoints for each unicode range bit, and cache result
+ if not _unicodeStarts:
+ unicodeRanges = [
+ (start, (stop, bit))
+ for bit, blocks in enumerate(OS2_UNICODE_RANGES)
+ for _, (start, stop) in blocks
+ ]
+ for start, (stop, bit) in sorted(unicodeRanges):
+ _unicodeStarts.append(start)
+ _unicodeValues.append((stop, bit))
+ return _unicodeStarts, _unicodeValues
+
+
+def intersectUnicodeRanges(unicodes, inverse=False):
+ """Intersect a sequence of (int) Unicode codepoints with the Unicode block
+ ranges defined in the OpenType specification v1.7, and return the set of
+ 'ulUnicodeRanges' bits for which there is at least ONE intersection.
+ If 'inverse' is True, return the the bits for which there is NO intersection.
+
+ >>> intersectUnicodeRanges([0x0410]) == {9}
+ True
+ >>> intersectUnicodeRanges([0x0410, 0x1F000]) == {9, 57, 122}
+ True
+ >>> intersectUnicodeRanges([0x0410, 0x1F000], inverse=True) == (
+ ... set(range(len(OS2_UNICODE_RANGES))) - {9, 57, 122})
+ True
+ """
+ unicodes = set(unicodes)
+ unicodestarts, unicodevalues = _getUnicodeRanges()
+ bits = set()
+ for code in unicodes:
+ stop, bit = unicodevalues[bisect.bisect(unicodestarts, code)]
+ if code <= stop:
+ bits.add(bit)
+ # The spec says that bit 57 ("Non Plane 0") implies that there's
+ # at least one codepoint beyond the BMP; so I also include all
+ # the non-BMP codepoints here
+ if any(0x10000 <= code < 0x110000 for code in unicodes):
+ bits.add(57)
+ return set(range(len(OS2_UNICODE_RANGES))) - bits if inverse else bits
+
+
+def calcCodePageRanges(unicodes):
+ """Given a set of Unicode codepoints (integers), calculate the
+ corresponding OS/2 CodePage range bits.
+ This is a direct translation of FontForge implementation:
+ https://github.com/fontforge/fontforge/blob/7b2c074/fontforge/tottf.c#L3158
+ """
+ bits = set()
+ hasAscii = set(range(0x20, 0x7E)).issubset(unicodes)
+ hasLineart = ord("┤") in unicodes
+
+ for uni in unicodes:
+ if uni == ord("Þ") and hasAscii:
+ bits.add(0) # Latin 1
+ elif uni == ord("Ľ") and hasAscii:
+ bits.add(1) # Latin 2: Eastern Europe
+ if hasLineart:
+ bits.add(58) # Latin 2
+ elif uni == ord("Б"):
+ bits.add(2) # Cyrillic
+ if ord("Ѕ") in unicodes and hasLineart:
+ bits.add(57) # IBM Cyrillic
+ if ord("╜") in unicodes and hasLineart:
+ bits.add(49) # MS-DOS Russian
+ elif uni == ord("Ά"):
+ bits.add(3) # Greek
+ if hasLineart and ord("½") in unicodes:
+ bits.add(48) # IBM Greek
+ if hasLineart and ord("√") in unicodes:
+ bits.add(60) # Greek, former 437 G
+ elif uni == ord("İ") and hasAscii:
+ bits.add(4) # Turkish
+ if hasLineart:
+ bits.add(56) # IBM turkish
+ elif uni == ord("א"):
+ bits.add(5) # Hebrew
+ if hasLineart and ord("√") in unicodes:
+ bits.add(53) # Hebrew
+ elif uni == ord("ر"):
+ bits.add(6) # Arabic
+ if ord("√") in unicodes:
+ bits.add(51) # Arabic
+ if hasLineart:
+ bits.add(61) # Arabic; ASMO 708
+ elif uni == ord("ŗ") and hasAscii:
+ bits.add(7) # Windows Baltic
+ if hasLineart:
+ bits.add(59) # MS-DOS Baltic
+ elif uni == ord("₫") and hasAscii:
+ bits.add(8) # Vietnamese
+ elif uni == ord("ๅ"):
+ bits.add(16) # Thai
+ elif uni == ord("エ"):
+ bits.add(17) # JIS/Japan
+ elif uni == ord("ㄅ"):
+ bits.add(18) # Chinese: Simplified
+ elif uni == ord("ㄱ"):
+ bits.add(19) # Korean wansung
+ elif uni == ord("央"):
+ bits.add(20) # Chinese: Traditional
+ elif uni == ord("곴"):
+ bits.add(21) # Korean Johab
+ elif uni == ord("♥") and hasAscii:
+ bits.add(30) # OEM Character Set
+ # TODO: Symbol bit has a special meaning (check the spec), we need
+ # to confirm if this is wanted by default.
+ # elif chr(0xF000) <= char <= chr(0xF0FF):
+ # codepageRanges.add(31) # Symbol Character Set
+ elif uni == ord("þ") and hasAscii and hasLineart:
+ bits.add(54) # MS-DOS Icelandic
+ elif uni == ord("╚") and hasAscii:
+ bits.add(62) # WE/Latin 1
+ bits.add(63) # US
+ elif hasAscii and hasLineart and ord("√") in unicodes:
+ if uni == ord("Å"):
+ bits.add(50) # MS-DOS Nordic
+ elif uni == ord("é"):
+ bits.add(52) # MS-DOS Canadian French
+ elif uni == ord("õ"):
+ bits.add(55) # MS-DOS Portuguese
+
+ if hasAscii and ord("‰") in unicodes and ord("∑") in unicodes:
+ bits.add(29) # Macintosh Character Set (US Roman)
+
+ return bits
+
+
+if __name__ == "__main__":
+ import doctest, sys
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/S_I_N_G_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_I_N_G_.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a367a92f2f4b91fb1a29dd257e08d982193deac
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_I_N_G_.py
@@ -0,0 +1,99 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytechr, byteord, tobytes, tostr, safeEval
+from . import DefaultTable
+
+SINGFormat = """
+ > # big endian
+ tableVersionMajor: H
+ tableVersionMinor: H
+ glyphletVersion: H
+ permissions: h
+ mainGID: H
+ unitsPerEm: H
+ vertAdvance: h
+ vertOrigin: h
+ uniqueName: 28s
+ METAMD5: 16s
+ nameLength: 1s
+"""
+# baseGlyphName is a byte string which follows the record above.
+
+
+class table_S_I_N_G_(DefaultTable.DefaultTable):
+ """Glyphlets SING table
+
+ The ``SING`` table is used by Adobe's SING Glyphlets.
+
+ See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
+ """
+
+ dependencies = []
+
+ def decompile(self, data, ttFont):
+ dummy, rest = sstruct.unpack2(SINGFormat, data, self)
+ self.uniqueName = self.decompileUniqueName(self.uniqueName)
+ self.nameLength = byteord(self.nameLength)
+ assert len(rest) == self.nameLength
+ self.baseGlyphName = tostr(rest)
+
+ rawMETAMD5 = self.METAMD5
+ self.METAMD5 = "[" + hex(byteord(self.METAMD5[0]))
+ for char in rawMETAMD5[1:]:
+ self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char))
+ self.METAMD5 = self.METAMD5 + "]"
+
+ def decompileUniqueName(self, data):
+ name = ""
+ for char in data:
+ val = byteord(char)
+ if val == 0:
+ break
+ if (val > 31) or (val < 128):
+ name += chr(val)
+ else:
+ octString = oct(val)
+ if len(octString) > 3:
+ octString = octString[1:] # chop off that leading zero.
+ elif len(octString) < 3:
+ octString.zfill(3)
+ name += "\\" + octString
+ return name
+
+ def compile(self, ttFont):
+ d = self.__dict__.copy()
+ d["nameLength"] = bytechr(len(self.baseGlyphName))
+ d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28)
+ METAMD5List = eval(self.METAMD5)
+ d["METAMD5"] = b""
+ for val in METAMD5List:
+ d["METAMD5"] += bytechr(val)
+ assert len(d["METAMD5"]) == 16, "Failed to pack 16 byte MD5 hash in SING table"
+ data = sstruct.pack(SINGFormat, d)
+ data = data + tobytes(self.baseGlyphName)
+ return data
+
+ def compilecompileUniqueName(self, name, length):
+ nameLen = len(name)
+ if length <= nameLen:
+ name = name[: length - 1] + "\000"
+ else:
+ name += (nameLen - length) * "\000"
+ return name
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(SINGFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+ writer.simpletag("baseGlyphName", value=self.baseGlyphName)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ if name in ["uniqueName", "METAMD5", "baseGlyphName"]:
+ setattr(self, name, value)
+ else:
+ setattr(self, name, safeEval(value))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/S_T_A_T_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_T_A_T_.py
new file mode 100644
index 0000000000000000000000000000000000000000..86e1271a9817ecb71b663e60ffa685cbf9205bd5
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_T_A_T_.py
@@ -0,0 +1,15 @@
+from .otBase import BaseTTXConverter
+
+
+class table_S_T_A_T_(BaseTTXConverter):
+ """Style Attributes table
+
+ The ``STAT`` table records stylistic or typeface-design attributes that
+ differentiate the individual fonts within a font family from one another.
+ Those attributes can be used to assist users when navigating the style
+ variations of a variable font or a family of static fonts.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/stat
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/S_V_G_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_V_G_.py
new file mode 100644
index 0000000000000000000000000000000000000000..76e860c5f627d7b6ed0a3d8fef29efde7b647a70
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/S_V_G_.py
@@ -0,0 +1,223 @@
+"""Compiles/decompiles SVG table.
+
+https://docs.microsoft.com/en-us/typography/opentype/spec/svg
+
+The XML format is:
+
+.. code-block:: xml
+
+
+"""
+
+from fontTools.misc.textTools import bytesjoin, safeEval, strjoin, tobytes, tostr
+from fontTools.misc import sstruct
+from . import DefaultTable
+from collections.abc import Sequence
+from dataclasses import dataclass, astuple
+from io import BytesIO
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+SVG_format_0 = """
+ > # big endian
+ version: H
+ offsetToSVGDocIndex: L
+ reserved: L
+"""
+
+SVG_format_0Size = sstruct.calcsize(SVG_format_0)
+
+doc_index_entry_format_0 = """
+ > # big endian
+ startGlyphID: H
+ endGlyphID: H
+ svgDocOffset: L
+ svgDocLength: L
+"""
+
+doc_index_entry_format_0Size = sstruct.calcsize(doc_index_entry_format_0)
+
+
+class table_S_V_G_(DefaultTable.DefaultTable):
+ """Scalable Vector Graphics table
+
+ The ``SVG`` table contains representations for glyphs in the SVG
+ image format.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/stat
+ """
+
+ def decompile(self, data, ttFont):
+ self.docList = []
+ # Version 0 is the standardized version of the table; and current.
+ # https://www.microsoft.com/typography/otspec/svg.htm
+ sstruct.unpack(SVG_format_0, data[:SVG_format_0Size], self)
+ if self.version != 0:
+ log.warning(
+ "Unknown SVG table version '%s'. Decompiling as version 0.",
+ self.version,
+ )
+ # read in SVG Documents Index
+ # data starts with the first entry of the entry list.
+ pos = subTableStart = self.offsetToSVGDocIndex
+ self.numEntries = struct.unpack(">H", data[pos : pos + 2])[0]
+ pos += 2
+ if self.numEntries > 0:
+ data2 = data[pos:]
+ entries = []
+ for i in range(self.numEntries):
+ record_data = data2[
+ i
+ * doc_index_entry_format_0Size : (i + 1)
+ * doc_index_entry_format_0Size
+ ]
+ docIndexEntry = sstruct.unpack(
+ doc_index_entry_format_0, record_data, DocumentIndexEntry()
+ )
+ entries.append(docIndexEntry)
+
+ for entry in entries:
+ start = entry.svgDocOffset + subTableStart
+ end = start + entry.svgDocLength
+ doc = data[start:end]
+ compressed = False
+ if doc.startswith(b"\x1f\x8b"):
+ import gzip
+
+ bytesIO = BytesIO(doc)
+ with gzip.GzipFile(None, "r", fileobj=bytesIO) as gunzipper:
+ doc = gunzipper.read()
+ del bytesIO
+ compressed = True
+ doc = tostr(doc, "utf_8")
+ self.docList.append(
+ SVGDocument(doc, entry.startGlyphID, entry.endGlyphID, compressed)
+ )
+
+ def compile(self, ttFont):
+ version = 0
+ offsetToSVGDocIndex = (
+ SVG_format_0Size # I start the SVGDocIndex right after the header.
+ )
+ # get SGVDoc info.
+ docList = []
+ entryList = []
+ numEntries = len(self.docList)
+ datum = struct.pack(">H", numEntries)
+ entryList.append(datum)
+ curOffset = len(datum) + doc_index_entry_format_0Size * numEntries
+ seenDocs = {}
+ allCompressed = getattr(self, "compressed", False)
+ for i, doc in enumerate(self.docList):
+ if isinstance(doc, (list, tuple)):
+ doc = SVGDocument(*doc)
+ self.docList[i] = doc
+ docBytes = tobytes(doc.data, encoding="utf_8")
+ if (allCompressed or doc.compressed) and not docBytes.startswith(
+ b"\x1f\x8b"
+ ):
+ import gzip
+
+ bytesIO = BytesIO()
+ # mtime=0 strips the useless timestamp and makes gzip output reproducible;
+ # equivalent to `gzip -n`
+ with gzip.GzipFile(None, "w", fileobj=bytesIO, mtime=0) as gzipper:
+ gzipper.write(docBytes)
+ gzipped = bytesIO.getvalue()
+ if len(gzipped) < len(docBytes):
+ docBytes = gzipped
+ del gzipped, bytesIO
+ docLength = len(docBytes)
+ if docBytes in seenDocs:
+ docOffset = seenDocs[docBytes]
+ else:
+ docOffset = curOffset
+ curOffset += docLength
+ seenDocs[docBytes] = docOffset
+ docList.append(docBytes)
+ entry = struct.pack(
+ ">HHLL", doc.startGlyphID, doc.endGlyphID, docOffset, docLength
+ )
+ entryList.append(entry)
+ entryList.extend(docList)
+ svgDocData = bytesjoin(entryList)
+
+ reserved = 0
+ header = struct.pack(">HLL", version, offsetToSVGDocIndex, reserved)
+ data = [header, svgDocData]
+ data = bytesjoin(data)
+ return data
+
+ def toXML(self, writer, ttFont):
+ for i, doc in enumerate(self.docList):
+ if isinstance(doc, (list, tuple)):
+ doc = SVGDocument(*doc)
+ self.docList[i] = doc
+ attrs = {"startGlyphID": doc.startGlyphID, "endGlyphID": doc.endGlyphID}
+ if doc.compressed:
+ attrs["compressed"] = 1
+ writer.begintag("svgDoc", **attrs)
+ writer.newline()
+ writer.writecdata(doc.data)
+ writer.newline()
+ writer.endtag("svgDoc")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "svgDoc":
+ if not hasattr(self, "docList"):
+ self.docList = []
+ doc = strjoin(content)
+ doc = doc.strip()
+ startGID = int(attrs["startGlyphID"])
+ endGID = int(attrs["endGlyphID"])
+ compressed = bool(safeEval(attrs.get("compressed", "0")))
+ self.docList.append(SVGDocument(doc, startGID, endGID, compressed))
+ else:
+ log.warning("Unknown %s %s", name, content)
+
+
+class DocumentIndexEntry(object):
+ def __init__(self):
+ self.startGlyphID = None # USHORT
+ self.endGlyphID = None # USHORT
+ self.svgDocOffset = None # ULONG
+ self.svgDocLength = None # ULONG
+
+ def __repr__(self):
+ return (
+ "startGlyphID: %s, endGlyphID: %s, svgDocOffset: %s, svgDocLength: %s"
+ % (self.startGlyphID, self.endGlyphID, self.svgDocOffset, self.svgDocLength)
+ )
+
+
+@dataclass
+class SVGDocument(Sequence):
+ data: str
+ startGlyphID: int
+ endGlyphID: int
+ compressed: bool = False
+
+ # Previously, the SVG table's docList attribute contained a lists of 3 items:
+ # [doc, startGlyphID, endGlyphID]; later, we added a `compressed` attribute.
+ # For backward compatibility with code that depends of them being sequences of
+ # fixed length=3, we subclass the Sequence abstract base class and pretend only
+ # the first three items are present. 'compressed' is only accessible via named
+ # attribute lookup like regular dataclasses: i.e. `doc.compressed`, not `doc[3]`
+ def __getitem__(self, index):
+ return astuple(self)[:3][index]
+
+ def __len__(self):
+ return 3
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_f.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_f.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8090af1e520737919e6041c259c112a15695035
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_f.py
@@ -0,0 +1,1040 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
+from fontTools.misc.textTools import byteord, safeEval
+
+# from itertools import *
+from . import DefaultTable
+from . import grUtils
+from array import array
+from functools import reduce
+import struct, re, sys
+
+Silf_hdr_format = """
+ >
+ version: 16.16F
+"""
+
+Silf_hdr_format_3 = """
+ >
+ version: 16.16F
+ compilerVersion: L
+ numSilf: H
+ x
+ x
+"""
+
+Silf_part1_format_v3 = """
+ >
+ ruleVersion: 16.16F
+ passOffset: H
+ pseudosOffset: H
+"""
+
+Silf_part1_format = """
+ >
+ maxGlyphID: H
+ extraAscent: h
+ extraDescent: h
+ numPasses: B
+ iSubst: B
+ iPos: B
+ iJust: B
+ iBidi: B
+ flags: B
+ maxPreContext: B
+ maxPostContext: B
+ attrPseudo: B
+ attrBreakWeight: B
+ attrDirectionality: B
+ attrMirroring: B
+ attrSkipPasses: B
+ numJLevels: B
+"""
+
+Silf_justify_format = """
+ >
+ attrStretch: B
+ attrShrink: B
+ attrStep: B
+ attrWeight: B
+ runto: B
+ x
+ x
+ x
+"""
+
+Silf_part2_format = """
+ >
+ numLigComp: H
+ numUserDefn: B
+ maxCompPerLig: B
+ direction: B
+ attCollisions: B
+ x
+ x
+ x
+ numCritFeatures: B
+"""
+
+Silf_pseudomap_format = """
+ >
+ unicode: L
+ nPseudo: H
+"""
+
+Silf_pseudomap_format_h = """
+ >
+ unicode: H
+ nPseudo: H
+"""
+
+Silf_classmap_format = """
+ >
+ numClass: H
+ numLinear: H
+"""
+
+Silf_lookupclass_format = """
+ >
+ numIDs: H
+ searchRange: H
+ entrySelector: H
+ rangeShift: H
+"""
+
+Silf_lookuppair_format = """
+ >
+ glyphId: H
+ index: H
+"""
+
+Silf_pass_format = """
+ >
+ flags: B
+ maxRuleLoop: B
+ maxRuleContext: B
+ maxBackup: B
+ numRules: H
+ fsmOffset: H
+ pcCode: L
+ rcCode: L
+ aCode: L
+ oDebug: L
+ numRows: H
+ numTransitional: H
+ numSuccess: H
+ numColumns: H
+"""
+
+aCode_info = (
+ ("NOP", 0),
+ ("PUSH_BYTE", "b"),
+ ("PUSH_BYTE_U", "B"),
+ ("PUSH_SHORT", ">h"),
+ ("PUSH_SHORT_U", ">H"),
+ ("PUSH_LONG", ">L"),
+ ("ADD", 0),
+ ("SUB", 0),
+ ("MUL", 0),
+ ("DIV", 0),
+ ("MIN", 0),
+ ("MAX", 0),
+ ("NEG", 0),
+ ("TRUNC8", 0),
+ ("TRUNC16", 0),
+ ("COND", 0),
+ ("AND", 0), # x10
+ ("OR", 0),
+ ("NOT", 0),
+ ("EQUAL", 0),
+ ("NOT_EQ", 0),
+ ("LESS", 0),
+ ("GTR", 0),
+ ("LESS_EQ", 0),
+ ("GTR_EQ", 0),
+ ("NEXT", 0),
+ ("NEXT_N", "b"),
+ ("COPY_NEXT", 0),
+ ("PUT_GLYPH_8BIT_OBS", "B"),
+ ("PUT_SUBS_8BIT_OBS", "bBB"),
+ ("PUT_COPY", "b"),
+ ("INSERT", 0),
+ ("DELETE", 0), # x20
+ ("ASSOC", -1),
+ ("CNTXT_ITEM", "bB"),
+ ("ATTR_SET", "B"),
+ ("ATTR_ADD", "B"),
+ ("ATTR_SUB", "B"),
+ ("ATTR_SET_SLOT", "B"),
+ ("IATTR_SET_SLOT", "BB"),
+ ("PUSH_SLOT_ATTR", "Bb"),
+ ("PUSH_GLYPH_ATTR_OBS", "Bb"),
+ ("PUSH_GLYPH_METRIC", "Bbb"),
+ ("PUSH_FEAT", "Bb"),
+ ("PUSH_ATT_TO_GATTR_OBS", "Bb"),
+ ("PUSH_ATT_TO_GLYPH_METRIC", "Bbb"),
+ ("PUSH_ISLOT_ATTR", "Bbb"),
+ ("PUSH_IGLYPH_ATTR", "Bbb"),
+ ("POP_RET", 0), # x30
+ ("RET_ZERO", 0),
+ ("RET_TRUE", 0),
+ ("IATTR_SET", "BB"),
+ ("IATTR_ADD", "BB"),
+ ("IATTR_SUB", "BB"),
+ ("PUSH_PROC_STATE", "B"),
+ ("PUSH_VERSION", 0),
+ ("PUT_SUBS", ">bHH"),
+ ("PUT_SUBS2", 0),
+ ("PUT_SUBS3", 0),
+ ("PUT_GLYPH", ">H"),
+ ("PUSH_GLYPH_ATTR", ">Hb"),
+ ("PUSH_ATT_TO_GLYPH_ATTR", ">Hb"),
+ ("BITOR", 0),
+ ("BITAND", 0),
+ ("BITNOT", 0), # x40
+ ("BITSET", ">HH"),
+ ("SET_FEAT", "Bb"),
+)
+aCode_map = dict([(x[0], (i, x[1])) for i, x in enumerate(aCode_info)])
+
+
+def disassemble(aCode):
+ codelen = len(aCode)
+ pc = 0
+ res = []
+ while pc < codelen:
+ opcode = byteord(aCode[pc : pc + 1])
+ if opcode > len(aCode_info):
+ instr = aCode_info[0]
+ else:
+ instr = aCode_info[opcode]
+ pc += 1
+ if instr[1] != 0 and pc >= codelen:
+ return res
+ if instr[1] == -1:
+ count = byteord(aCode[pc])
+ fmt = "%dB" % count
+ pc += 1
+ elif instr[1] == 0:
+ fmt = ""
+ else:
+ fmt = instr[1]
+ if fmt == "":
+ res.append(instr[0])
+ continue
+ parms = struct.unpack_from(fmt, aCode[pc:])
+ res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")")
+ pc += struct.calcsize(fmt)
+ return res
+
+
+instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?")
+
+
+def assemble(instrs):
+ res = b""
+ for inst in instrs:
+ m = instre.match(inst)
+ if not m or not m.group(1) in aCode_map:
+ continue
+ opcode, parmfmt = aCode_map[m.group(1)]
+ res += struct.pack("B", opcode)
+ if m.group(2):
+ if parmfmt == 0:
+ continue
+ parms = [int(x) for x in re.split(r",\s*", m.group(2))]
+ if parmfmt == -1:
+ l = len(parms)
+ res += struct.pack(("%dB" % (l + 1)), l, *parms)
+ else:
+ res += struct.pack(parmfmt, *parms)
+ return res
+
+
+def writecode(tag, writer, instrs):
+ writer.begintag(tag)
+ writer.newline()
+ for l in disassemble(instrs):
+ writer.write(l)
+ writer.newline()
+ writer.endtag(tag)
+ writer.newline()
+
+
+def readcode(content):
+ res = []
+ for e in content_string(content).split("\n"):
+ e = e.strip()
+ if not len(e):
+ continue
+ res.append(e)
+ return assemble(res)
+
+
+attrs_info = (
+ "flags",
+ "extraAscent",
+ "extraDescent",
+ "maxGlyphID",
+ "numLigComp",
+ "numUserDefn",
+ "maxCompPerLig",
+ "direction",
+ "lbGID",
+)
+attrs_passindexes = ("iSubst", "iPos", "iJust", "iBidi")
+attrs_contexts = ("maxPreContext", "maxPostContext")
+attrs_attributes = (
+ "attrPseudo",
+ "attrBreakWeight",
+ "attrDirectionality",
+ "attrMirroring",
+ "attrSkipPasses",
+ "attCollisions",
+)
+pass_attrs_info = (
+ "flags",
+ "maxRuleLoop",
+ "maxRuleContext",
+ "maxBackup",
+ "minRulePreContext",
+ "maxRulePreContext",
+ "collisionThreshold",
+)
+pass_attrs_fsm = ("numRows", "numTransitional", "numSuccess", "numColumns")
+
+
+def writesimple(tag, self, writer, *attrkeys):
+ attrs = dict([(k, getattr(self, k)) for k in attrkeys])
+ writer.simpletag(tag, **attrs)
+ writer.newline()
+
+
+def getSimple(self, attrs, *attr_list):
+ for k in attr_list:
+ if k in attrs:
+ setattr(self, k, int(safeEval(attrs[k])))
+
+
+def content_string(contents):
+ res = ""
+ for element in contents:
+ if isinstance(element, tuple):
+ continue
+ res += element
+ return res.strip()
+
+
+def wrapline(writer, dat, length=80):
+ currline = ""
+ for d in dat:
+ if len(currline) > length:
+ writer.write(currline[:-1])
+ writer.newline()
+ currline = ""
+ currline += d + " "
+ if len(currline):
+ writer.write(currline[:-1])
+ writer.newline()
+
+
+class _Object:
+ pass
+
+
+class table_S__i_l_f(DefaultTable.DefaultTable):
+ """Graphite Rules table
+
+ See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.silfs = []
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack2(Silf_hdr_format, data, self)
+ self.version = float(floatToFixedToStr(self.version, precisionBits=16))
+ if self.version >= 5.0:
+ (data, self.scheme) = grUtils.decompress(data)
+ sstruct.unpack2(Silf_hdr_format_3, data, self)
+ base = sstruct.calcsize(Silf_hdr_format_3)
+ elif self.version < 3.0:
+ self.numSilf = struct.unpack(">H", data[4:6])
+ self.scheme = 0
+ self.compilerVersion = 0
+ base = 8
+ else:
+ self.scheme = 0
+ sstruct.unpack2(Silf_hdr_format_3, data, self)
+ base = sstruct.calcsize(Silf_hdr_format_3)
+
+ silfoffsets = struct.unpack_from((">%dL" % self.numSilf), data[base:])
+ for offset in silfoffsets:
+ s = Silf()
+ self.silfs.append(s)
+ s.decompile(data[offset:], ttFont, self.version)
+
+ def compile(self, ttFont):
+ self.numSilf = len(self.silfs)
+ if self.version < 3.0:
+ hdr = sstruct.pack(Silf_hdr_format, self)
+ hdr += struct.pack(">HH", self.numSilf, 0)
+ else:
+ hdr = sstruct.pack(Silf_hdr_format_3, self)
+ offset = len(hdr) + 4 * self.numSilf
+ data = b""
+ for s in self.silfs:
+ hdr += struct.pack(">L", offset)
+ subdata = s.compile(ttFont, self.version)
+ offset += len(subdata)
+ data += subdata
+ if self.version >= 5.0:
+ return grUtils.compress(self.scheme, hdr + data)
+ return hdr + data
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Attributes starting with _ are informative only")
+ writer.newline()
+ writer.simpletag(
+ "version",
+ version=self.version,
+ compilerVersion=self.compilerVersion,
+ compressionScheme=self.scheme,
+ )
+ writer.newline()
+ for s in self.silfs:
+ writer.begintag("silf")
+ writer.newline()
+ s.toXML(writer, ttFont, self.version)
+ writer.endtag("silf")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.scheme = int(safeEval(attrs["compressionScheme"]))
+ self.version = float(safeEval(attrs["version"]))
+ self.compilerVersion = int(safeEval(attrs["compilerVersion"]))
+ return
+ if name == "silf":
+ s = Silf()
+ self.silfs.append(s)
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ s.fromXML(tag, attrs, subcontent, ttFont, self.version)
+
+
+class Silf(object):
+ """A particular Silf subtable"""
+
+ def __init__(self):
+ self.passes = []
+ self.scriptTags = []
+ self.critFeatures = []
+ self.jLevels = []
+ self.pMap = {}
+
+ def decompile(self, data, ttFont, version=2.0):
+ if version >= 3.0:
+ _, data = sstruct.unpack2(Silf_part1_format_v3, data, self)
+ self.ruleVersion = float(
+ floatToFixedToStr(self.ruleVersion, precisionBits=16)
+ )
+ _, data = sstruct.unpack2(Silf_part1_format, data, self)
+ for jlevel in range(self.numJLevels):
+ j, data = sstruct.unpack2(Silf_justify_format, data, _Object())
+ self.jLevels.append(j)
+ _, data = sstruct.unpack2(Silf_part2_format, data, self)
+ if self.numCritFeatures:
+ self.critFeatures = struct.unpack_from(
+ (">%dH" % self.numCritFeatures), data
+ )
+ data = data[self.numCritFeatures * 2 + 1 :]
+ (numScriptTag,) = struct.unpack_from("B", data)
+ if numScriptTag:
+ self.scriptTags = [
+ struct.unpack("4s", data[x : x + 4])[0].decode("ascii")
+ for x in range(1, 1 + 4 * numScriptTag, 4)
+ ]
+ data = data[1 + 4 * numScriptTag :]
+ (self.lbGID,) = struct.unpack(">H", data[:2])
+ if self.numPasses:
+ self.oPasses = struct.unpack(
+ (">%dL" % (self.numPasses + 1)), data[2 : 6 + 4 * self.numPasses]
+ )
+ data = data[6 + 4 * self.numPasses :]
+ (numPseudo,) = struct.unpack(">H", data[:2])
+ for i in range(numPseudo):
+ if version >= 3.0:
+ pseudo = sstruct.unpack(
+ Silf_pseudomap_format, data[8 + 6 * i : 14 + 6 * i], _Object()
+ )
+ else:
+ pseudo = sstruct.unpack(
+ Silf_pseudomap_format_h, data[8 + 4 * i : 12 + 4 * i], _Object()
+ )
+ self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo)
+ data = data[8 + 6 * numPseudo :]
+ currpos = (
+ sstruct.calcsize(Silf_part1_format)
+ + sstruct.calcsize(Silf_justify_format) * self.numJLevels
+ + sstruct.calcsize(Silf_part2_format)
+ + 2 * self.numCritFeatures
+ + 1
+ + 1
+ + 4 * numScriptTag
+ + 6
+ + 4 * self.numPasses
+ + 8
+ + 6 * numPseudo
+ )
+ if version >= 3.0:
+ currpos += sstruct.calcsize(Silf_part1_format_v3)
+ self.classes = Classes()
+ self.classes.decompile(data, ttFont, version)
+ for i in range(self.numPasses):
+ p = Pass()
+ self.passes.append(p)
+ p.decompile(
+ data[self.oPasses[i] - currpos : self.oPasses[i + 1] - currpos],
+ ttFont,
+ version,
+ )
+
+ def compile(self, ttFont, version=2.0):
+ self.numPasses = len(self.passes)
+ self.numJLevels = len(self.jLevels)
+ self.numCritFeatures = len(self.critFeatures)
+ numPseudo = len(self.pMap)
+ data = b""
+ if version >= 3.0:
+ hdroffset = sstruct.calcsize(Silf_part1_format_v3)
+ else:
+ hdroffset = 0
+ data += sstruct.pack(Silf_part1_format, self)
+ for j in self.jLevels:
+ data += sstruct.pack(Silf_justify_format, j)
+ data += sstruct.pack(Silf_part2_format, self)
+ if self.numCritFeatures:
+ data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures)
+ data += struct.pack("BB", 0, len(self.scriptTags))
+ if len(self.scriptTags):
+ tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags]
+ data += b"".join(tdata)
+ data += struct.pack(">H", self.lbGID)
+ self.passOffset = len(data)
+
+ data1 = grUtils.bininfo(numPseudo, 6)
+ currpos = hdroffset + len(data) + 4 * (self.numPasses + 1)
+ self.pseudosOffset = currpos + len(data1)
+ for u, p in sorted(self.pMap.items()):
+ data1 += struct.pack(
+ (">LH" if version >= 3.0 else ">HH"), u, ttFont.getGlyphID(p)
+ )
+ data1 += self.classes.compile(ttFont, version)
+ currpos += len(data1)
+ data2 = b""
+ datao = b""
+ for i, p in enumerate(self.passes):
+ base = currpos + len(data2)
+ datao += struct.pack(">L", base)
+ data2 += p.compile(ttFont, base, version)
+ datao += struct.pack(">L", currpos + len(data2))
+
+ if version >= 3.0:
+ data3 = sstruct.pack(Silf_part1_format_v3, self)
+ else:
+ data3 = b""
+ return data3 + data + datao + data1 + data2
+
+ def toXML(self, writer, ttFont, version=2.0):
+ if version >= 3.0:
+ writer.simpletag("version", ruleVersion=self.ruleVersion)
+ writer.newline()
+ writesimple("info", self, writer, *attrs_info)
+ writesimple("passindexes", self, writer, *attrs_passindexes)
+ writesimple("contexts", self, writer, *attrs_contexts)
+ writesimple("attributes", self, writer, *attrs_attributes)
+ if len(self.jLevels):
+ writer.begintag("justifications")
+ writer.newline()
+ jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format)
+ for i, j in enumerate(self.jLevels):
+ attrs = dict([(k, getattr(j, k)) for k in jnames])
+ writer.simpletag("justify", **attrs)
+ writer.newline()
+ writer.endtag("justifications")
+ writer.newline()
+ if len(self.critFeatures):
+ writer.begintag("critFeatures")
+ writer.newline()
+ writer.write(" ".join(map(str, self.critFeatures)))
+ writer.newline()
+ writer.endtag("critFeatures")
+ writer.newline()
+ if len(self.scriptTags):
+ writer.begintag("scriptTags")
+ writer.newline()
+ writer.write(" ".join(self.scriptTags))
+ writer.newline()
+ writer.endtag("scriptTags")
+ writer.newline()
+ if self.pMap:
+ writer.begintag("pseudoMap")
+ writer.newline()
+ for k, v in sorted(self.pMap.items()):
+ writer.simpletag("pseudo", unicode=hex(k), pseudo=v)
+ writer.newline()
+ writer.endtag("pseudoMap")
+ writer.newline()
+ self.classes.toXML(writer, ttFont, version)
+ if len(self.passes):
+ writer.begintag("passes")
+ writer.newline()
+ for i, p in enumerate(self.passes):
+ writer.begintag("pass", _index=i)
+ writer.newline()
+ p.toXML(writer, ttFont, version)
+ writer.endtag("pass")
+ writer.newline()
+ writer.endtag("passes")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont, version=2.0):
+ if name == "version":
+ self.ruleVersion = float(safeEval(attrs.get("ruleVersion", "0")))
+ if name == "info":
+ getSimple(self, attrs, *attrs_info)
+ elif name == "passindexes":
+ getSimple(self, attrs, *attrs_passindexes)
+ elif name == "contexts":
+ getSimple(self, attrs, *attrs_contexts)
+ elif name == "attributes":
+ getSimple(self, attrs, *attrs_attributes)
+ elif name == "justifications":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ (tag, attrs, subcontent) = element
+ if tag == "justify":
+ j = _Object()
+ for k, v in attrs.items():
+ setattr(j, k, int(v))
+ self.jLevels.append(j)
+ elif name == "critFeatures":
+ self.critFeatures = []
+ element = content_string(content)
+ self.critFeatures.extend(map(int, element.split()))
+ elif name == "scriptTags":
+ self.scriptTags = []
+ element = content_string(content)
+ for n in element.split():
+ self.scriptTags.append(n)
+ elif name == "pseudoMap":
+ self.pMap = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ (tag, attrs, subcontent) = element
+ if tag == "pseudo":
+ k = int(attrs["unicode"], 16)
+ v = attrs["pseudo"]
+ self.pMap[k] = v
+ elif name == "classes":
+ self.classes = Classes()
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ self.classes.fromXML(tag, attrs, subcontent, ttFont, version)
+ elif name == "passes":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ if tag == "pass":
+ p = Pass()
+ for e in subcontent:
+ if not isinstance(e, tuple):
+ continue
+ p.fromXML(e[0], e[1], e[2], ttFont, version)
+ self.passes.append(p)
+
+
+class Classes(object):
+ def __init__(self):
+ self.linear = []
+ self.nonLinear = []
+
+ def decompile(self, data, ttFont, version=2.0):
+ sstruct.unpack2(Silf_classmap_format, data, self)
+ if version >= 4.0:
+ oClasses = struct.unpack(
+ (">%dL" % (self.numClass + 1)), data[4 : 8 + 4 * self.numClass]
+ )
+ else:
+ oClasses = struct.unpack(
+ (">%dH" % (self.numClass + 1)), data[4 : 6 + 2 * self.numClass]
+ )
+ for s, e in zip(oClasses[: self.numLinear], oClasses[1 : self.numLinear + 1]):
+ self.linear.append(
+ ttFont.getGlyphName(x)
+ for x in struct.unpack((">%dH" % ((e - s) / 2)), data[s:e])
+ )
+ for s, e in zip(
+ oClasses[self.numLinear : self.numClass],
+ oClasses[self.numLinear + 1 : self.numClass + 1],
+ ):
+ nonLinids = [
+ struct.unpack(">HH", data[x : x + 4]) for x in range(s + 8, e, 4)
+ ]
+ nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids])
+ self.nonLinear.append(nonLin)
+
+ def compile(self, ttFont, version=2.0):
+ data = b""
+ oClasses = []
+ if version >= 4.0:
+ offset = 8 + 4 * (len(self.linear) + len(self.nonLinear))
+ else:
+ offset = 6 + 2 * (len(self.linear) + len(self.nonLinear))
+ for l in self.linear:
+ oClasses.append(len(data) + offset)
+ gs = [ttFont.getGlyphID(x) for x in l]
+ data += struct.pack((">%dH" % len(l)), *gs)
+ for l in self.nonLinear:
+ oClasses.append(len(data) + offset)
+ gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()]
+ data += grUtils.bininfo(len(gs))
+ data += b"".join([struct.pack(">HH", *x) for x in sorted(gs)])
+ oClasses.append(len(data) + offset)
+ self.numClass = len(oClasses) - 1
+ self.numLinear = len(self.linear)
+ return (
+ sstruct.pack(Silf_classmap_format, self)
+ + struct.pack(
+ ((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)), *oClasses
+ )
+ + data
+ )
+
+ def toXML(self, writer, ttFont, version=2.0):
+ writer.begintag("classes")
+ writer.newline()
+ writer.begintag("linearClasses")
+ writer.newline()
+ for i, l in enumerate(self.linear):
+ writer.begintag("linear", _index=i)
+ writer.newline()
+ wrapline(writer, l)
+ writer.endtag("linear")
+ writer.newline()
+ writer.endtag("linearClasses")
+ writer.newline()
+ writer.begintag("nonLinearClasses")
+ writer.newline()
+ for i, l in enumerate(self.nonLinear):
+ writer.begintag("nonLinear", _index=i + self.numLinear)
+ writer.newline()
+ for inp, ind in l.items():
+ writer.simpletag("map", glyph=inp, index=ind)
+ writer.newline()
+ writer.endtag("nonLinear")
+ writer.newline()
+ writer.endtag("nonLinearClasses")
+ writer.newline()
+ writer.endtag("classes")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont, version=2.0):
+ if name == "linearClasses":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ if tag == "linear":
+ l = content_string(subcontent).split()
+ self.linear.append(l)
+ elif name == "nonLinearClasses":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, attrs, subcontent = element
+ if tag == "nonLinear":
+ l = {}
+ for e in subcontent:
+ if not isinstance(e, tuple):
+ continue
+ tag, attrs, subsubcontent = e
+ if tag == "map":
+ l[attrs["glyph"]] = int(safeEval(attrs["index"]))
+ self.nonLinear.append(l)
+
+
+class Pass(object):
+ def __init__(self):
+ self.colMap = {}
+ self.rules = []
+ self.rulePreContexts = []
+ self.ruleSortKeys = []
+ self.ruleConstraints = []
+ self.passConstraints = b""
+ self.actions = []
+ self.stateTrans = []
+ self.startStates = []
+
+ def decompile(self, data, ttFont, version=2.0):
+ _, data = sstruct.unpack2(Silf_pass_format, data, self)
+ (numRange, _, _, _) = struct.unpack(">4H", data[:8])
+ data = data[8:]
+ for i in range(numRange):
+ (first, last, col) = struct.unpack(">3H", data[6 * i : 6 * i + 6])
+ for g in range(first, last + 1):
+ self.colMap[ttFont.getGlyphName(g)] = col
+ data = data[6 * numRange :]
+ oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data)
+ data = data[2 + 2 * self.numSuccess :]
+ rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data)
+ self.rules = [rules[s:e] for (s, e) in zip(oRuleMap, oRuleMap[1:])]
+ data = data[2 * oRuleMap[-1] :]
+ (self.minRulePreContext, self.maxRulePreContext) = struct.unpack("BB", data[:2])
+ numStartStates = self.maxRulePreContext - self.minRulePreContext + 1
+ self.startStates = struct.unpack(
+ (">%dH" % numStartStates), data[2 : 2 + numStartStates * 2]
+ )
+ data = data[2 + numStartStates * 2 :]
+ self.ruleSortKeys = struct.unpack(
+ (">%dH" % self.numRules), data[: 2 * self.numRules]
+ )
+ data = data[2 * self.numRules :]
+ self.rulePreContexts = struct.unpack(
+ ("%dB" % self.numRules), data[: self.numRules]
+ )
+ data = data[self.numRules :]
+ (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3])
+ oConstraints = list(
+ struct.unpack(
+ (">%dH" % (self.numRules + 1)), data[3 : 5 + self.numRules * 2]
+ )
+ )
+ data = data[5 + self.numRules * 2 :]
+ oActions = list(
+ struct.unpack((">%dH" % (self.numRules + 1)), data[: 2 + self.numRules * 2])
+ )
+ data = data[2 * self.numRules + 2 :]
+ for i in range(self.numTransitional):
+ a = array(
+ "H", data[i * self.numColumns * 2 : (i + 1) * self.numColumns * 2]
+ )
+ if sys.byteorder != "big":
+ a.byteswap()
+ self.stateTrans.append(a)
+ data = data[self.numTransitional * self.numColumns * 2 + 1 :]
+ self.passConstraints = data[:pConstraint]
+ data = data[pConstraint:]
+ for i in range(len(oConstraints) - 2, -1, -1):
+ if oConstraints[i] == 0:
+ oConstraints[i] = oConstraints[i + 1]
+ self.ruleConstraints = [
+ (data[s:e] if (e - s > 1) else b"")
+ for (s, e) in zip(oConstraints, oConstraints[1:])
+ ]
+ data = data[oConstraints[-1] :]
+ self.actions = [
+ (data[s:e] if (e - s > 1) else "") for (s, e) in zip(oActions, oActions[1:])
+ ]
+ data = data[oActions[-1] :]
+ # not using debug
+
+ def compile(self, ttFont, base, version=2.0):
+ # build it all up backwards
+ oActions = reduce(
+ lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.actions + [b""], (0, [])
+ )[1]
+ oConstraints = reduce(
+ lambda a, x: (a[0] + len(x), a[1] + [a[0]]),
+ self.ruleConstraints + [b""],
+ (1, []),
+ )[1]
+ constraintCode = b"\000" + b"".join(self.ruleConstraints)
+ transes = []
+ for t in self.stateTrans:
+ if sys.byteorder != "big":
+ t.byteswap()
+ transes.append(t.tobytes())
+ if sys.byteorder != "big":
+ t.byteswap()
+ if not len(transes):
+ self.startStates = [0]
+ oRuleMap = reduce(
+ lambda a, x: (a[0] + len(x), a[1] + [a[0]]), self.rules + [[]], (0, [])
+ )[1]
+ passRanges = []
+ gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()])
+ for e in grUtils.entries(gidcolmap, sameval=True):
+ if e[1]:
+ passRanges.append((e[0], e[0] + e[1] - 1, e[2][0]))
+ self.numRules = len(self.actions)
+ self.fsmOffset = (
+ sstruct.calcsize(Silf_pass_format)
+ + 8
+ + len(passRanges) * 6
+ + len(oRuleMap) * 2
+ + 2 * oRuleMap[-1]
+ + 2
+ + 2 * len(self.startStates)
+ + 3 * self.numRules
+ + 3
+ + 4 * self.numRules
+ + 4
+ )
+ self.pcCode = (
+ self.fsmOffset + 2 * self.numTransitional * self.numColumns + 1 + base
+ )
+ self.rcCode = self.pcCode + len(self.passConstraints)
+ self.aCode = self.rcCode + len(constraintCode)
+ self.oDebug = 0
+ # now generate output
+ data = sstruct.pack(Silf_pass_format, self)
+ data += grUtils.bininfo(len(passRanges), 6)
+ data += b"".join(struct.pack(">3H", *p) for p in passRanges)
+ data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap)
+ flatrules = reduce(lambda a, x: a + x, self.rules, [])
+ data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules)
+ data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext)
+ data += struct.pack((">%dH" % len(self.startStates)), *self.startStates)
+ data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys)
+ data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts)
+ data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints))
+ data += struct.pack((">%dH" % (self.numRules + 1)), *oConstraints)
+ data += struct.pack((">%dH" % (self.numRules + 1)), *oActions)
+ return (
+ data
+ + b"".join(transes)
+ + struct.pack("B", 0)
+ + self.passConstraints
+ + constraintCode
+ + b"".join(self.actions)
+ )
+
+ def toXML(self, writer, ttFont, version=2.0):
+ writesimple("info", self, writer, *pass_attrs_info)
+ writesimple("fsminfo", self, writer, *pass_attrs_fsm)
+ writer.begintag("colmap")
+ writer.newline()
+ wrapline(
+ writer,
+ [
+ "{}={}".format(*x)
+ for x in sorted(
+ self.colMap.items(), key=lambda x: ttFont.getGlyphID(x[0])
+ )
+ ],
+ )
+ writer.endtag("colmap")
+ writer.newline()
+ writer.begintag("staterulemap")
+ writer.newline()
+ for i, r in enumerate(self.rules):
+ writer.simpletag(
+ "state",
+ number=self.numRows - self.numSuccess + i,
+ rules=" ".join(map(str, r)),
+ )
+ writer.newline()
+ writer.endtag("staterulemap")
+ writer.newline()
+ writer.begintag("rules")
+ writer.newline()
+ for i, action in enumerate(self.actions):
+ writer.begintag(
+ "rule",
+ index=i,
+ precontext=self.rulePreContexts[i],
+ sortkey=self.ruleSortKeys[i],
+ )
+ writer.newline()
+ if len(self.ruleConstraints[i]):
+ writecode("constraint", writer, self.ruleConstraints[i])
+ writecode("action", writer, action)
+ writer.endtag("rule")
+ writer.newline()
+ writer.endtag("rules")
+ writer.newline()
+ if len(self.passConstraints):
+ writecode("passConstraint", writer, self.passConstraints)
+ if len(self.stateTrans):
+ writer.begintag("fsm")
+ writer.newline()
+ writer.begintag("starts")
+ writer.write(" ".join(map(str, self.startStates)))
+ writer.endtag("starts")
+ writer.newline()
+ for i, s in enumerate(self.stateTrans):
+ writer.begintag("row", _i=i)
+ # no newlines here
+ writer.write(" ".join(map(str, s)))
+ writer.endtag("row")
+ writer.newline()
+ writer.endtag("fsm")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont, version=2.0):
+ if name == "info":
+ getSimple(self, attrs, *pass_attrs_info)
+ elif name == "fsminfo":
+ getSimple(self, attrs, *pass_attrs_fsm)
+ elif name == "colmap":
+ e = content_string(content)
+ for w in e.split():
+ x = w.split("=")
+ if len(x) != 2 or x[0] == "" or x[1] == "":
+ continue
+ self.colMap[x[0]] = int(x[1])
+ elif name == "staterulemap":
+ for e in content:
+ if not isinstance(e, tuple):
+ continue
+ tag, a, c = e
+ if tag == "state":
+ self.rules.append([int(x) for x in a["rules"].split(" ")])
+ elif name == "rules":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, a, c = element
+ if tag != "rule":
+ continue
+ self.rulePreContexts.append(int(a["precontext"]))
+ self.ruleSortKeys.append(int(a["sortkey"]))
+ con = b""
+ act = b""
+ for e in c:
+ if not isinstance(e, tuple):
+ continue
+ tag, a, subc = e
+ if tag == "constraint":
+ con = readcode(subc)
+ elif tag == "action":
+ act = readcode(subc)
+ self.actions.append(act)
+ self.ruleConstraints.append(con)
+ elif name == "passConstraint":
+ self.passConstraints = readcode(content)
+ elif name == "fsm":
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, a, c = element
+ if tag == "row":
+ s = array("H")
+ e = content_string(c)
+ s.extend(map(int, e.split()))
+ self.stateTrans.append(s)
+ elif tag == "starts":
+ s = []
+ e = content_string(c)
+ s.extend(map(int, e.split()))
+ self.startStates = s
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_l.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_l.py
new file mode 100644
index 0000000000000000000000000000000000000000..61106ea5284ac67219590d87544d44e2e1f970c2
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/S__i_l_l.py
@@ -0,0 +1,92 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+from . import grUtils
+import struct
+
+Sill_hdr = """
+ >
+ version: 16.16F
+"""
+
+
+class table_S__i_l_l(DefaultTable.DefaultTable):
+ """Graphite Languages table
+
+ See also https://graphite.sil.org/graphite_techAbout#graphite-font-tables
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.langs = {}
+
+ def decompile(self, data, ttFont):
+ (_, data) = sstruct.unpack2(Sill_hdr, data, self)
+ self.version = float(floatToFixedToStr(self.version, precisionBits=16))
+ (numLangs,) = struct.unpack(">H", data[:2])
+ data = data[8:]
+ maxsetting = 0
+ langinfo = []
+ for i in range(numLangs):
+ (langcode, numsettings, offset) = struct.unpack(
+ ">4sHH", data[i * 8 : (i + 1) * 8]
+ )
+ offset = int(offset / 8) - (numLangs + 1)
+ langcode = langcode.replace(b"\000", b"")
+ langinfo.append((langcode.decode("utf-8"), numsettings, offset))
+ maxsetting = max(maxsetting, offset + numsettings)
+ data = data[numLangs * 8 :]
+ finfo = []
+ for i in range(maxsetting):
+ (fid, val, _) = struct.unpack(">LHH", data[i * 8 : (i + 1) * 8])
+ finfo.append((fid, val))
+ self.langs = {}
+ for c, n, o in langinfo:
+ self.langs[c] = []
+ for i in range(o, o + n):
+ self.langs[c].append(finfo[i])
+
+ def compile(self, ttFont):
+ ldat = b""
+ fdat = b""
+ offset = len(self.langs)
+ for c, inf in sorted(self.langs.items()):
+ ldat += struct.pack(">4sHH", c.encode("utf8"), len(inf), 8 * offset + 20)
+ for fid, val in inf:
+ fdat += struct.pack(">LHH", fid, val, 0)
+ offset += len(inf)
+ ldat += struct.pack(">LHH", 0x80808080, 0, 8 * offset + 20)
+ return (
+ sstruct.pack(Sill_hdr, self)
+ + grUtils.bininfo(len(self.langs))
+ + ldat
+ + fdat
+ )
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", version=self.version)
+ writer.newline()
+ for c, inf in sorted(self.langs.items()):
+ writer.begintag("lang", name=c)
+ writer.newline()
+ for fid, val in inf:
+ writer.simpletag("feature", fid=grUtils.num2tag(fid), val=val)
+ writer.newline()
+ writer.endtag("lang")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = float(safeEval(attrs["version"]))
+ elif name == "lang":
+ c = attrs["name"]
+ self.langs[c] = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ tag, a, subcontent = element
+ if tag == "feature":
+ self.langs[c].append(
+ (grUtils.tag2num(a["fid"]), int(safeEval(a["val"])))
+ )
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_B_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_B_.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea19fcee856e75fc8aa494e083b4beed303f831c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_B_.py
@@ -0,0 +1,13 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSIB contains the source text for the ``BASE`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .T_S_I_V_ import table_T_S_I_V_
+
+
+class table_T_S_I_B_(table_T_S_I_V_):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_C_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_C_.py
new file mode 100644
index 0000000000000000000000000000000000000000..14e353cde58b739660b10fe666e26492f5a0069f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_C_.py
@@ -0,0 +1,14 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSIC contains the source text for the Variation CVT window and data for
+the ``cvar`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .otBase import BaseTTXConverter
+
+
+class table_T_S_I_C_(BaseTTXConverter):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_D_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_D_.py
new file mode 100644
index 0000000000000000000000000000000000000000..2bb9455efaeda5baac2c1f425c35019723b592bd
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_D_.py
@@ -0,0 +1,13 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSID contains the source text for the ``GDEF`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .T_S_I_V_ import table_T_S_I_V_
+
+
+class table_T_S_I_D_(table_T_S_I_V_):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_J_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_J_.py
new file mode 100644
index 0000000000000000000000000000000000000000..379b949c6cd380268b139f6c3cfd61fffc43e223
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_J_.py
@@ -0,0 +1,13 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSIJ contains the source text for the ``JSTF`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .T_S_I_V_ import table_T_S_I_V_
+
+
+class table_T_S_I_J_(table_T_S_I_V_):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_P_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_P_.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b1786970489f4511dcd156e5562eb2156f927a3
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_P_.py
@@ -0,0 +1,13 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSIP contains the source text for the ``GPOS`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .T_S_I_V_ import table_T_S_I_V_
+
+
+class table_T_S_I_P_(table_T_S_I_V_):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_S_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_S_.py
new file mode 100644
index 0000000000000000000000000000000000000000..91f36e7c019087f954a7fa779df045a9097fa289
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_S_.py
@@ -0,0 +1,13 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+TSIS contains the source text for the ``GSUB`` table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from .T_S_I_V_ import table_T_S_I_V_
+
+
+class table_T_S_I_S_(table_T_S_I_V_):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_V_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_V_.py
new file mode 100644
index 0000000000000000000000000000000000000000..32af783f26b0f9224e39d3e243f72bfcd1da150b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I_V_.py
@@ -0,0 +1,26 @@
+""" TSI{B,C,D,J,P,S,V} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its table source data.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from fontTools.misc.textTools import strjoin, tobytes, tostr
+from . import asciiTable
+
+
+class table_T_S_I_V_(asciiTable.asciiTable):
+ def toXML(self, writer, ttFont):
+ data = tostr(self.data)
+ # removing null bytes. XXX needed??
+ data = data.split("\0")
+ data = strjoin(data)
+ writer.begintag("source")
+ writer.newline()
+ writer.write_noindent(data.replace("\r", "\n"))
+ writer.newline()
+ writer.endtag("source")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ lines = strjoin(content).split("\n")
+ self.data = tobytes("\r".join(lines[1:-1]))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__0.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__0.py
new file mode 100644
index 0000000000000000000000000000000000000000..d60e783c60873c85487463f151dc25f061cde97f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__0.py
@@ -0,0 +1,70 @@
+"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI0 is the index table containing the lengths and offsets for the glyph
+programs and 'extra' programs ('fpgm', 'prep', and 'cvt') that are contained
+in the TSI1 table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+import logging
+import struct
+
+from . import DefaultTable
+
+log = logging.getLogger(__name__)
+
+tsi0Format = ">HHL"
+
+
+def fixlongs(glyphID, textLength, textOffset):
+ return int(glyphID), int(textLength), textOffset
+
+
+class table_T_S_I__0(DefaultTable.DefaultTable):
+ dependencies = ["TSI1"]
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont["maxp"].numGlyphs
+ indices = []
+ size = struct.calcsize(tsi0Format)
+ numEntries = len(data) // size
+ if numEntries != numGlyphs + 5:
+ diff = numEntries - numGlyphs - 5
+ log.warning(
+ "Number of glyphPrograms differs from the number of glyphs in the font "
+ f"by {abs(diff)} ({numEntries - 5} programs vs. {numGlyphs} glyphs)."
+ )
+ for _ in range(numEntries):
+ glyphID, textLength, textOffset = fixlongs(
+ *struct.unpack(tsi0Format, data[:size])
+ )
+ indices.append((glyphID, textLength, textOffset))
+ data = data[size:]
+ assert len(data) == 0
+ assert indices[-5] == (0xFFFE, 0, 0xABFC1F34), "bad magic number"
+ self.indices = indices[:-5]
+ self.extra_indices = indices[-4:]
+
+ def compile(self, ttFont):
+ if not hasattr(self, "indices"):
+ # We have no corresponding table (TSI1 or TSI3); let's return
+ # no data, which effectively means "ignore us".
+ return b""
+ data = b""
+ for index, textLength, textOffset in self.indices:
+ data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+ data = data + struct.pack(tsi0Format, 0xFFFE, 0, 0xABFC1F34)
+ for index, textLength, textOffset in self.extra_indices:
+ data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+ return data
+
+ def set(self, indices, extra_indices):
+ # gets called by 'TSI1' or 'TSI3'
+ self.indices = indices
+ self.extra_indices = extra_indices
+
+ def toXML(self, writer, ttFont):
+ writer.comment("This table will be calculated by the compiler")
+ writer.newline()
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__1.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__1.py
new file mode 100644
index 0000000000000000000000000000000000000000..19537dac9c512dc009a159cb25bb926eebca61ed
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__1.py
@@ -0,0 +1,163 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI1 contains the text of the glyph programs in the form of low-level assembly
+code, as well as the 'extra' programs 'fpgm', 'ppgm' (i.e. 'prep'), and 'cvt'.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from . import DefaultTable
+from fontTools.misc.loggingTools import LogMixin
+from fontTools.misc.textTools import strjoin, tobytes, tostr
+
+
+class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
+ extras = {0xFFFA: "ppgm", 0xFFFB: "cvt", 0xFFFC: "reserved", 0xFFFD: "fpgm"}
+
+ indextable = "TSI0"
+
+ def decompile(self, data, ttFont):
+ totalLength = len(data)
+ indextable = ttFont[self.indextable]
+ for indices, isExtra in zip(
+ (indextable.indices, indextable.extra_indices), (False, True)
+ ):
+ programs = {}
+ for i, (glyphID, textLength, textOffset) in enumerate(indices):
+ if isExtra:
+ name = self.extras[glyphID]
+ else:
+ name = ttFont.getGlyphName(glyphID)
+ if textOffset > totalLength:
+ self.log.warning("textOffset > totalLength; %r skipped" % name)
+ continue
+ if textLength < 0x8000:
+ # If the length stored in the record is less than 32768, then use
+ # that as the length of the record.
+ pass
+ elif textLength == 0x8000:
+ # If the length is 32768, compute the actual length as follows:
+ isLast = i == (len(indices) - 1)
+ if isLast:
+ if isExtra:
+ # For the last "extra" record (the very last record of the
+ # table), the length is the difference between the total
+ # length of the TSI1 table and the textOffset of the final
+ # record.
+ nextTextOffset = totalLength
+ else:
+ # For the last "normal" record (the last record just prior
+ # to the record containing the "magic number"), the length
+ # is the difference between the textOffset of the record
+ # following the "magic number" (0xFFFE) record (i.e. the
+ # first "extra" record), and the textOffset of the last
+ # "normal" record.
+ nextTextOffset = indextable.extra_indices[0][2]
+ else:
+ # For all other records with a length of 0x8000, the length is
+ # the difference between the textOffset of the record in
+ # question and the textOffset of the next record.
+ nextTextOffset = indices[i + 1][2]
+ assert nextTextOffset >= textOffset, "entries not sorted by offset"
+ if nextTextOffset > totalLength:
+ self.log.warning(
+ "nextTextOffset > totalLength; %r truncated" % name
+ )
+ nextTextOffset = totalLength
+ textLength = nextTextOffset - textOffset
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError(
+ "%r textLength (%d) must not be > 32768" % (name, textLength)
+ )
+ text = data[textOffset : textOffset + textLength]
+ assert len(text) == textLength
+ text = tostr(text, encoding="utf-8")
+ if text:
+ programs[name] = text
+ if isExtra:
+ self.extraPrograms = programs
+ else:
+ self.glyphPrograms = programs
+
+ def compile(self, ttFont):
+ if not hasattr(self, "glyphPrograms"):
+ self.glyphPrograms = {}
+ self.extraPrograms = {}
+ data = b""
+ indextable = ttFont[self.indextable]
+ glyphNames = ttFont.getGlyphOrder()
+
+ indices = []
+ for i, name in enumerate(glyphNames):
+ if len(data) % 2:
+ data = (
+ data + b"\015"
+ ) # align on 2-byte boundaries, fill with return chars. Yum.
+ if name in self.glyphPrograms:
+ text = tobytes(self.glyphPrograms[name], encoding="utf-8")
+ else:
+ text = b""
+ textLength = len(text)
+ if textLength >= 0x8000:
+ textLength = 0x8000
+ indices.append((i, textLength, len(data)))
+ data = data + text
+
+ extra_indices = []
+ for code, name in sorted(self.extras.items()):
+ if len(data) % 2:
+ data = (
+ data + b"\015"
+ ) # align on 2-byte boundaries, fill with return chars.
+ if name in self.extraPrograms:
+ text = tobytes(self.extraPrograms[name], encoding="utf-8")
+ else:
+ text = b""
+ textLength = len(text)
+ if textLength >= 0x8000:
+ textLength = 0x8000
+ extra_indices.append((code, textLength, len(data)))
+ data = data + text
+ indextable.set(indices, extra_indices)
+ return data
+
+ def toXML(self, writer, ttFont):
+ names = sorted(self.glyphPrograms.keys())
+ writer.newline()
+ for name in names:
+ text = self.glyphPrograms[name]
+ if not text:
+ continue
+ writer.begintag("glyphProgram", name=name)
+ writer.newline()
+ writer.write_noindent(text.replace("\r", "\n"))
+ writer.newline()
+ writer.endtag("glyphProgram")
+ writer.newline()
+ writer.newline()
+ extra_names = sorted(self.extraPrograms.keys())
+ for name in extra_names:
+ text = self.extraPrograms[name]
+ if not text:
+ continue
+ writer.begintag("extraProgram", name=name)
+ writer.newline()
+ writer.write_noindent(text.replace("\r", "\n"))
+ writer.newline()
+ writer.endtag("extraProgram")
+ writer.newline()
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "glyphPrograms"):
+ self.glyphPrograms = {}
+ self.extraPrograms = {}
+ lines = strjoin(content).replace("\r", "\n").split("\n")
+ text = "\r".join(lines[1:-1])
+ if name == "glyphProgram":
+ self.glyphPrograms[attrs["name"]] = text
+ elif name == "extraProgram":
+ self.extraPrograms[attrs["name"]] = text
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__2.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__2.py
new file mode 100644
index 0000000000000000000000000000000000000000..63608c60414c8e6cfd67aa6ab46caf4f14a918e1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__2.py
@@ -0,0 +1,17 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI2 is the index table containing the lengths and offsets for the glyph
+programs that are contained in the TSI3 table. It uses the same format as
+the TSI0 table.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI0")
+
+
+class table_T_S_I__2(superclass):
+ dependencies = ["TSI3"]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__3.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__3.py
new file mode 100644
index 0000000000000000000000000000000000000000..e866826db488d178833836f203f5e634cbdfbd3f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__3.py
@@ -0,0 +1,22 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI3 contains the text of the glyph programs in the form of 'VTTTalk' code.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI1")
+
+
+class table_T_S_I__3(superclass):
+ extras = {
+ 0xFFFA: "reserved0",
+ 0xFFFB: "reserved1",
+ 0xFFFC: "reserved2",
+ 0xFFFD: "reserved3",
+ }
+
+ indextable = "TSI2"
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__5.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__5.py
new file mode 100644
index 0000000000000000000000000000000000000000..8aa382e43ca0e4bb33994d2e093459a09ba12a9b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_S_I__5.py
@@ -0,0 +1,60 @@
+"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI5 contains the VTT character groups.
+
+See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
+"""
+
+import array
+import logging
+import sys
+
+from fontTools.misc.textTools import safeEval
+
+from . import DefaultTable
+
+log = logging.getLogger(__name__)
+
+
+class table_T_S_I__5(DefaultTable.DefaultTable):
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont["maxp"].numGlyphs
+ a = array.array("H")
+ a.frombytes(data)
+ if sys.byteorder != "big":
+ a.byteswap()
+ self.glyphGrouping = {}
+ numEntries = len(data) // 2
+ if numEntries != numGlyphs:
+ diff = numEntries - numGlyphs
+ log.warning(
+ "Number of entries differs from the number of glyphs in the font "
+ f"by {abs(diff)} ({numEntries} entries vs. {numGlyphs} glyphs)."
+ )
+ for i in range(numEntries):
+ self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
+
+ def compile(self, ttFont):
+ glyphNames = ttFont.getGlyphOrder()
+ a = array.array("H")
+ for glyphName in glyphNames:
+ a.append(self.glyphGrouping.get(glyphName, 0))
+ if sys.byteorder != "big":
+ a.byteswap()
+ return a.tobytes()
+
+ def toXML(self, writer, ttFont):
+ names = sorted(self.glyphGrouping.keys())
+ for glyphName in names:
+ writer.simpletag(
+ "glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName]
+ )
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "glyphGrouping"):
+ self.glyphGrouping = {}
+ if name != "glyphgroup":
+ return
+ self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/T_T_F_A_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_T_F_A_.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b6b06871a91d9401bcf9fbd99aeb3c30ca0705f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/T_T_F_A_.py
@@ -0,0 +1,14 @@
+from . import asciiTable
+
+
+class table_T_T_F_A_(asciiTable.asciiTable):
+ """ttfautohint parameters table
+
+ The ``TTFA`` table is used by the free-software `ttfautohint` program
+ to record the parameters that `ttfautohint` was called with when it
+ was used to auto-hint the font.
+
+ See also http://freetype.org/ttfautohint/doc/ttfautohint.html#miscellaneous-1
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/TupleVariation.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/TupleVariation.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd6217e2ed9fe4f1076b7a13378ccdaa1308c0f1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/TupleVariation.py
@@ -0,0 +1,884 @@
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+ otRound,
+)
+from fontTools.misc.textTools import safeEval
+import array
+from collections import Counter, defaultdict
+import io
+import logging
+import struct
+import sys
+
+
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+
+EMBEDDED_PEAK_TUPLE = 0x8000
+INTERMEDIATE_REGION = 0x4000
+PRIVATE_POINT_NUMBERS = 0x2000
+
+DELTAS_ARE_ZERO = 0x80
+DELTAS_ARE_WORDS = 0x40
+DELTAS_ARE_LONGS = 0xC0
+DELTAS_SIZE_MASK = 0xC0
+DELTA_RUN_COUNT_MASK = 0x3F
+
+POINTS_ARE_WORDS = 0x80
+POINT_RUN_COUNT_MASK = 0x7F
+
+TUPLES_SHARE_POINT_NUMBERS = 0x8000
+TUPLE_COUNT_MASK = 0x0FFF
+TUPLE_INDEX_MASK = 0x0FFF
+
+log = logging.getLogger(__name__)
+
+
+class TupleVariation(object):
+ def __init__(self, axes, coordinates):
+ self.axes = axes.copy()
+ self.coordinates = list(coordinates)
+
+ def __repr__(self):
+ axes = ",".join(
+ sorted(["%s=%s" % (name, value) for (name, value) in self.axes.items()])
+ )
+ return "" % (axes, self.coordinates)
+
+ def __eq__(self, other):
+ return self.coordinates == other.coordinates and self.axes == other.axes
+
+ def getUsedPoints(self):
+ # Empty set means "all points used".
+ if None not in self.coordinates:
+ return frozenset()
+ used = frozenset([i for i, p in enumerate(self.coordinates) if p is not None])
+ # Return None if no points used.
+ return used if used else None
+
+ def hasImpact(self):
+ """Returns True if this TupleVariation has any visible impact.
+
+ If the result is False, the TupleVariation can be omitted from the font
+ without making any visible difference.
+ """
+ return any(c is not None for c in self.coordinates)
+
+ def toXML(self, writer, axisTags):
+ writer.begintag("tuple")
+ writer.newline()
+ for axis in axisTags:
+ value = self.axes.get(axis)
+ if value is not None:
+ minValue, value, maxValue = value
+ defaultMinValue = min(value, 0.0) # -0.3 --> -0.3; 0.7 --> 0.0
+ defaultMaxValue = max(value, 0.0) # -0.3 --> 0.0; 0.7 --> 0.7
+ if minValue == defaultMinValue and maxValue == defaultMaxValue:
+ writer.simpletag("coord", axis=axis, value=fl2str(value, 14))
+ else:
+ attrs = [
+ ("axis", axis),
+ ("min", fl2str(minValue, 14)),
+ ("value", fl2str(value, 14)),
+ ("max", fl2str(maxValue, 14)),
+ ]
+ writer.simpletag("coord", attrs)
+ writer.newline()
+ wrote_any_deltas = False
+ for i, delta in enumerate(self.coordinates):
+ if type(delta) == tuple and len(delta) == 2:
+ writer.simpletag("delta", pt=i, x=delta[0], y=delta[1])
+ writer.newline()
+ wrote_any_deltas = True
+ elif type(delta) == int:
+ writer.simpletag("delta", cvt=i, value=delta)
+ writer.newline()
+ wrote_any_deltas = True
+ elif delta is not None:
+ log.error("bad delta format")
+ writer.comment("bad delta #%d" % i)
+ writer.newline()
+ wrote_any_deltas = True
+ if not wrote_any_deltas:
+ writer.comment("no deltas")
+ writer.newline()
+ writer.endtag("tuple")
+ writer.newline()
+
+ def fromXML(self, name, attrs, _content):
+ if name == "coord":
+ axis = attrs["axis"]
+ value = str2fl(attrs["value"], 14)
+ defaultMinValue = min(value, 0.0) # -0.3 --> -0.3; 0.7 --> 0.0
+ defaultMaxValue = max(value, 0.0) # -0.3 --> 0.0; 0.7 --> 0.7
+ minValue = str2fl(attrs.get("min", defaultMinValue), 14)
+ maxValue = str2fl(attrs.get("max", defaultMaxValue), 14)
+ self.axes[axis] = (minValue, value, maxValue)
+ elif name == "delta":
+ if "pt" in attrs:
+ point = safeEval(attrs["pt"])
+ x = safeEval(attrs["x"])
+ y = safeEval(attrs["y"])
+ self.coordinates[point] = (x, y)
+ elif "cvt" in attrs:
+ cvt = safeEval(attrs["cvt"])
+ value = safeEval(attrs["value"])
+ self.coordinates[cvt] = value
+ else:
+ log.warning("bad delta format: %s" % ", ".join(sorted(attrs.keys())))
+
+ def compile(
+ self, axisTags, sharedCoordIndices={}, pointData=None, *, optimizeSize=True
+ ):
+ assert set(self.axes.keys()) <= set(axisTags), (
+ "Unknown axis tag found.",
+ self.axes.keys(),
+ axisTags,
+ )
+
+ tupleData = []
+ auxData = []
+
+ if pointData is None:
+ usedPoints = self.getUsedPoints()
+ if usedPoints is None: # Nothing to encode
+ return b"", b""
+ pointData = self.compilePoints(usedPoints)
+
+ coord = self.compileCoord(axisTags)
+ flags = sharedCoordIndices.get(coord)
+ if flags is None:
+ flags = EMBEDDED_PEAK_TUPLE
+ tupleData.append(coord)
+
+ intermediateCoord = self.compileIntermediateCoord(axisTags)
+ if intermediateCoord is not None:
+ flags |= INTERMEDIATE_REGION
+ tupleData.append(intermediateCoord)
+
+ # pointData of b'' implies "use shared points".
+ if pointData:
+ flags |= PRIVATE_POINT_NUMBERS
+ auxData.append(pointData)
+
+ auxData.append(self.compileDeltas(optimizeSize=optimizeSize))
+ auxData = b"".join(auxData)
+
+ tupleData.insert(0, struct.pack(">HH", len(auxData), flags))
+ return b"".join(tupleData), auxData
+
+ def compileCoord(self, axisTags):
+ result = []
+ axes = self.axes
+ for axis in axisTags:
+ triple = axes.get(axis)
+ if triple is None:
+ result.append(b"\0\0")
+ else:
+ result.append(struct.pack(">h", fl2fi(triple[1], 14)))
+ return b"".join(result)
+
+ def compileIntermediateCoord(self, axisTags):
+ needed = False
+ for axis in axisTags:
+ minValue, value, maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
+ defaultMinValue = min(value, 0.0) # -0.3 --> -0.3; 0.7 --> 0.0
+ defaultMaxValue = max(value, 0.0) # -0.3 --> 0.0; 0.7 --> 0.7
+ if (minValue != defaultMinValue) or (maxValue != defaultMaxValue):
+ needed = True
+ break
+ if not needed:
+ return None
+ minCoords = []
+ maxCoords = []
+ for axis in axisTags:
+ minValue, value, maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
+ minCoords.append(struct.pack(">h", fl2fi(minValue, 14)))
+ maxCoords.append(struct.pack(">h", fl2fi(maxValue, 14)))
+ return b"".join(minCoords + maxCoords)
+
+ @staticmethod
+ def decompileCoord_(axisTags, data, offset):
+ coord = {}
+ pos = offset
+ for axis in axisTags:
+ coord[axis] = fi2fl(struct.unpack(">h", data[pos : pos + 2])[0], 14)
+ pos += 2
+ return coord, pos
+
+ @staticmethod
+ def compilePoints(points):
+ # If the set consists of all points in the glyph, it gets encoded with
+ # a special encoding: a single zero byte.
+ #
+ # To use this optimization, points passed in must be empty set.
+ # The following two lines are not strictly necessary as the main code
+ # below would emit the same. But this is most common and faster.
+ if not points:
+ return b"\0"
+
+ # In the 'gvar' table, the packing of point numbers is a little surprising.
+ # It consists of multiple runs, each being a delta-encoded list of integers.
+ # For example, the point set {17, 18, 19, 20, 21, 22, 23} gets encoded as
+ # [6, 17, 1, 1, 1, 1, 1, 1]. The first value (6) is the run length minus 1.
+ # There are two types of runs, with values being either 8 or 16 bit unsigned
+ # integers.
+ points = list(points)
+ points.sort()
+ numPoints = len(points)
+
+ result = bytearray()
+ # The binary representation starts with the total number of points in the set,
+ # encoded into one or two bytes depending on the value.
+ if numPoints < 0x80:
+ result.append(numPoints)
+ else:
+ result.append((numPoints >> 8) | 0x80)
+ result.append(numPoints & 0xFF)
+
+ MAX_RUN_LENGTH = 127
+ pos = 0
+ lastValue = 0
+ while pos < numPoints:
+ runLength = 0
+
+ headerPos = len(result)
+ result.append(0)
+
+ useByteEncoding = None
+ while pos < numPoints and runLength <= MAX_RUN_LENGTH:
+ curValue = points[pos]
+ delta = curValue - lastValue
+ if useByteEncoding is None:
+ useByteEncoding = 0 <= delta <= 0xFF
+ if useByteEncoding and (delta > 0xFF or delta < 0):
+ # we need to start a new run (which will not use byte encoding)
+ break
+ # TODO This never switches back to a byte-encoding from a short-encoding.
+ # That's suboptimal.
+ if useByteEncoding:
+ result.append(delta)
+ else:
+ result.append(delta >> 8)
+ result.append(delta & 0xFF)
+ lastValue = curValue
+ pos += 1
+ runLength += 1
+ if useByteEncoding:
+ result[headerPos] = runLength - 1
+ else:
+ result[headerPos] = (runLength - 1) | POINTS_ARE_WORDS
+
+ return result
+
+ @staticmethod
+ def decompilePoints_(numPoints, data, offset, tableTag):
+ """(numPoints, data, offset, tableTag) --> ([point1, point2, ...], newOffset)"""
+ assert tableTag in ("cvar", "gvar")
+ pos = offset
+ numPointsInData = data[pos]
+ pos += 1
+ if (numPointsInData & POINTS_ARE_WORDS) != 0:
+ numPointsInData = (numPointsInData & POINT_RUN_COUNT_MASK) << 8 | data[pos]
+ pos += 1
+ if numPointsInData == 0:
+ return (range(numPoints), pos)
+
+ result = []
+ while len(result) < numPointsInData:
+ runHeader = data[pos]
+ pos += 1
+ numPointsInRun = (runHeader & POINT_RUN_COUNT_MASK) + 1
+ point = 0
+ if (runHeader & POINTS_ARE_WORDS) != 0:
+ points = array.array("H")
+ pointsSize = numPointsInRun * 2
+ else:
+ points = array.array("B")
+ pointsSize = numPointsInRun
+ points.frombytes(data[pos : pos + pointsSize])
+ if sys.byteorder != "big":
+ points.byteswap()
+
+ assert len(points) == numPointsInRun
+ pos += pointsSize
+
+ result.extend(points)
+
+ # Convert relative to absolute
+ absolute = []
+ current = 0
+ for delta in result:
+ current += delta
+ absolute.append(current)
+ result = absolute
+ del absolute
+
+ badPoints = {str(p) for p in result if p < 0 or p >= numPoints}
+ if badPoints:
+ log.warning(
+ "point %s out of range in '%s' table"
+ % (",".join(sorted(badPoints)), tableTag)
+ )
+ return (result, pos)
+
+ def compileDeltas(self, optimizeSize=True):
+ deltaX = []
+ deltaY = []
+ if self.getCoordWidth() == 2:
+ for c in self.coordinates:
+ if c is None:
+ continue
+ deltaX.append(c[0])
+ deltaY.append(c[1])
+ else:
+ for c in self.coordinates:
+ if c is None:
+ continue
+ deltaX.append(c)
+ bytearr = bytearray()
+ self.compileDeltaValues_(deltaX, bytearr, optimizeSize=optimizeSize)
+ self.compileDeltaValues_(deltaY, bytearr, optimizeSize=optimizeSize)
+ return bytearr
+
+ @staticmethod
+ def compileDeltaValues_(deltas, bytearr=None, *, optimizeSize=True):
+ """[value1, value2, value3, ...] --> bytearray
+
+ Emits a sequence of runs. Each run starts with a
+ byte-sized header whose 6 least significant bits
+ (header & 0x3F) indicate how many values are encoded
+ in this run. The stored length is the actual length
+ minus one; run lengths are thus in the range [1..64].
+ If the header byte has its most significant bit (0x80)
+ set, all values in this run are zero, and no data
+ follows. Otherwise, the header byte is followed by
+ ((header & 0x3F) + 1) signed values. If (header &
+ 0x40) is clear, the delta values are stored as signed
+ bytes; if (header & 0x40) is set, the delta values are
+ signed 16-bit integers.
+ """ # Explaining the format because the 'gvar' spec is hard to understand.
+ if bytearr is None:
+ bytearr = bytearray()
+
+ pos = 0
+ numDeltas = len(deltas)
+
+ if optimizeSize:
+ while pos < numDeltas:
+ value = deltas[pos]
+ if value == 0:
+ pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, bytearr)
+ elif -128 <= value <= 127:
+ pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, bytearr)
+ elif -32768 <= value <= 32767:
+ pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, bytearr)
+ else:
+ pos = TupleVariation.encodeDeltaRunAsLongs_(deltas, pos, bytearr)
+ else:
+ minVal, maxVal = min(deltas), max(deltas)
+ if minVal == 0 == maxVal:
+ pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, bytearr)
+ elif -128 <= minVal <= maxVal <= 127:
+ pos = TupleVariation.encodeDeltaRunAsBytes_(
+ deltas, pos, bytearr, optimizeSize=False
+ )
+ elif -32768 <= minVal <= maxVal <= 32767:
+ pos = TupleVariation.encodeDeltaRunAsWords_(
+ deltas, pos, bytearr, optimizeSize=False
+ )
+ else:
+ pos = TupleVariation.encodeDeltaRunAsLongs_(
+ deltas, pos, bytearr, optimizeSize=False
+ )
+
+ assert pos == numDeltas, (pos, numDeltas)
+
+ return bytearr
+
+ @staticmethod
+ def encodeDeltaRunAsZeroes_(deltas, offset, bytearr):
+ pos = offset
+ numDeltas = len(deltas)
+ while pos < numDeltas and deltas[pos] == 0:
+ pos += 1
+ runLength = pos - offset
+ while runLength >= 64:
+ bytearr.append(DELTAS_ARE_ZERO | 63)
+ runLength -= 64
+ if runLength:
+ bytearr.append(DELTAS_ARE_ZERO | (runLength - 1))
+ return pos
+
+ @staticmethod
+ def encodeDeltaRunAsBytes_(deltas, offset, bytearr, optimizeSize=True):
+ pos = offset
+ numDeltas = len(deltas)
+ while pos < numDeltas:
+ value = deltas[pos]
+ if not (-128 <= value <= 127):
+ break
+ # Within a byte-encoded run of deltas, a single zero
+ # is best stored literally as 0x00 value. However,
+ # if are two or more zeroes in a sequence, it is
+ # better to start a new run. For example, the sequence
+ # of deltas [15, 15, 0, 15, 15] becomes 6 bytes
+ # (04 0F 0F 00 0F 0F) when storing the zero value
+ # literally, but 7 bytes (01 0F 0F 80 01 0F 0F)
+ # when starting a new run.
+ if (
+ optimizeSize
+ and value == 0
+ and pos + 1 < numDeltas
+ and deltas[pos + 1] == 0
+ ):
+ break
+ pos += 1
+ runLength = pos - offset
+ while runLength >= 64:
+ bytearr.append(63)
+ bytearr.extend(array.array("b", deltas[offset : offset + 64]))
+ offset += 64
+ runLength -= 64
+ if runLength:
+ bytearr.append(runLength - 1)
+ bytearr.extend(array.array("b", deltas[offset:pos]))
+ return pos
+
+ @staticmethod
+ def encodeDeltaRunAsWords_(deltas, offset, bytearr, optimizeSize=True):
+ pos = offset
+ numDeltas = len(deltas)
+ while pos < numDeltas:
+ value = deltas[pos]
+
+ # Within a word-encoded run of deltas, it is easiest
+ # to start a new run (with a different encoding)
+ # whenever we encounter a zero value. For example,
+ # the sequence [0x6666, 0, 0x7777] needs 7 bytes when
+ # storing the zero literally (42 66 66 00 00 77 77),
+ # and equally 7 bytes when starting a new run
+ # (40 66 66 80 40 77 77).
+ if optimizeSize and value == 0:
+ break
+
+ # Within a word-encoded run of deltas, a single value
+ # in the range (-128..127) should be encoded literally
+ # because it is more compact. For example, the sequence
+ # [0x6666, 2, 0x7777] becomes 7 bytes when storing
+ # the value literally (42 66 66 00 02 77 77), but 8 bytes
+ # when starting a new run (40 66 66 00 02 40 77 77).
+ if (
+ optimizeSize
+ and (-128 <= value <= 127)
+ and pos + 1 < numDeltas
+ and (-128 <= deltas[pos + 1] <= 127)
+ ):
+ break
+
+ if not (-32768 <= value <= 32767):
+ break
+
+ pos += 1
+ runLength = pos - offset
+ while runLength >= 64:
+ bytearr.append(DELTAS_ARE_WORDS | 63)
+ a = array.array("h", deltas[offset : offset + 64])
+ if sys.byteorder != "big":
+ a.byteswap()
+ bytearr.extend(a)
+ offset += 64
+ runLength -= 64
+ if runLength:
+ bytearr.append(DELTAS_ARE_WORDS | (runLength - 1))
+ a = array.array("h", deltas[offset:pos])
+ if sys.byteorder != "big":
+ a.byteswap()
+ bytearr.extend(a)
+ return pos
+
+ @staticmethod
+ def encodeDeltaRunAsLongs_(deltas, offset, bytearr, optimizeSize=True):
+ pos = offset
+ numDeltas = len(deltas)
+ while pos < numDeltas:
+ value = deltas[pos]
+ if optimizeSize and -32768 <= value <= 32767:
+ break
+ pos += 1
+ runLength = pos - offset
+ while runLength >= 64:
+ bytearr.append(DELTAS_ARE_LONGS | 63)
+ a = array.array("i", deltas[offset : offset + 64])
+ if sys.byteorder != "big":
+ a.byteswap()
+ bytearr.extend(a)
+ offset += 64
+ runLength -= 64
+ if runLength:
+ bytearr.append(DELTAS_ARE_LONGS | (runLength - 1))
+ a = array.array("i", deltas[offset:pos])
+ if sys.byteorder != "big":
+ a.byteswap()
+ bytearr.extend(a)
+ return pos
+
+ @staticmethod
+ def decompileDeltas_(numDeltas, data, offset=0):
+ """(numDeltas, data, offset) --> ([delta, delta, ...], newOffset)"""
+ result = []
+ pos = offset
+ while len(result) < numDeltas if numDeltas is not None else pos < len(data):
+ runHeader = data[pos]
+ pos += 1
+ numDeltasInRun = (runHeader & DELTA_RUN_COUNT_MASK) + 1
+ if (runHeader & DELTAS_SIZE_MASK) == DELTAS_ARE_ZERO:
+ result.extend([0] * numDeltasInRun)
+ else:
+ if (runHeader & DELTAS_SIZE_MASK) == DELTAS_ARE_LONGS:
+ deltas = array.array("i")
+ deltasSize = numDeltasInRun * 4
+ elif (runHeader & DELTAS_SIZE_MASK) == DELTAS_ARE_WORDS:
+ deltas = array.array("h")
+ deltasSize = numDeltasInRun * 2
+ else:
+ deltas = array.array("b")
+ deltasSize = numDeltasInRun
+ deltas.frombytes(data[pos : pos + deltasSize])
+ if sys.byteorder != "big":
+ deltas.byteswap()
+ assert len(deltas) == numDeltasInRun, (len(deltas), numDeltasInRun)
+ pos += deltasSize
+ result.extend(deltas)
+ assert numDeltas is None or len(result) == numDeltas
+ return (result, pos)
+
+ @staticmethod
+ def getTupleSize_(flags, axisCount):
+ size = 4
+ if (flags & EMBEDDED_PEAK_TUPLE) != 0:
+ size += axisCount * 2
+ if (flags & INTERMEDIATE_REGION) != 0:
+ size += axisCount * 4
+ return size
+
+ def getCoordWidth(self):
+ """Return 2 if coordinates are (x, y) as in gvar, 1 if single values
+ as in cvar, or 0 if empty.
+ """
+ firstDelta = next((c for c in self.coordinates if c is not None), None)
+ if firstDelta is None:
+ return 0 # empty or has no impact
+ if type(firstDelta) in (int, float):
+ return 1
+ if type(firstDelta) is tuple and len(firstDelta) == 2:
+ return 2
+ raise TypeError(
+ "invalid type of delta; expected (int or float) number, or "
+ "Tuple[number, number]: %r" % firstDelta
+ )
+
+ def scaleDeltas(self, scalar):
+ if scalar == 1.0:
+ return # no change
+ coordWidth = self.getCoordWidth()
+ self.coordinates = [
+ (
+ None
+ if d is None
+ else d * scalar if coordWidth == 1 else (d[0] * scalar, d[1] * scalar)
+ )
+ for d in self.coordinates
+ ]
+
+ def roundDeltas(self):
+ coordWidth = self.getCoordWidth()
+ self.coordinates = [
+ (
+ None
+ if d is None
+ else otRound(d) if coordWidth == 1 else (otRound(d[0]), otRound(d[1]))
+ )
+ for d in self.coordinates
+ ]
+
+ def calcInferredDeltas(self, origCoords, endPts):
+ from fontTools.varLib.iup import iup_delta
+
+ if self.getCoordWidth() == 1:
+ raise TypeError("Only 'gvar' TupleVariation can have inferred deltas")
+ if None in self.coordinates:
+ if len(self.coordinates) != len(origCoords):
+ raise ValueError(
+ "Expected len(origCoords) == %d; found %d"
+ % (len(self.coordinates), len(origCoords))
+ )
+ self.coordinates = iup_delta(self.coordinates, origCoords, endPts)
+
+ def optimize(self, origCoords, endPts, tolerance=0.5, isComposite=False):
+ from fontTools.varLib.iup import iup_delta_optimize
+
+ if None in self.coordinates:
+ return # already optimized
+
+ deltaOpt = iup_delta_optimize(
+ self.coordinates, origCoords, endPts, tolerance=tolerance
+ )
+ if None in deltaOpt:
+ if isComposite and all(d is None for d in deltaOpt):
+ # Fix for macOS composites
+ # https://github.com/fonttools/fonttools/issues/1381
+ deltaOpt = [(0, 0)] + [None] * (len(deltaOpt) - 1)
+ # Use "optimized" version only if smaller...
+ varOpt = TupleVariation(self.axes, deltaOpt)
+
+ # Shouldn't matter that this is different from fvar...?
+ axisTags = sorted(self.axes.keys())
+ tupleData, auxData = self.compile(axisTags)
+ unoptimizedLength = len(tupleData) + len(auxData)
+ tupleData, auxData = varOpt.compile(axisTags)
+ optimizedLength = len(tupleData) + len(auxData)
+
+ if optimizedLength < unoptimizedLength:
+ self.coordinates = varOpt.coordinates
+
+ def __imul__(self, scalar):
+ self.scaleDeltas(scalar)
+ return self
+
+ def __iadd__(self, other):
+ if not isinstance(other, TupleVariation):
+ return NotImplemented
+ deltas1 = self.coordinates
+ length = len(deltas1)
+ deltas2 = other.coordinates
+ if len(deltas2) != length:
+ raise ValueError("cannot sum TupleVariation deltas with different lengths")
+ # 'None' values have different meanings in gvar vs cvar TupleVariations:
+ # within the gvar, when deltas are not provided explicitly for some points,
+ # they need to be inferred; whereas for the 'cvar' table, if deltas are not
+ # provided for some CVT values, then no adjustments are made (i.e. None == 0).
+ # Thus, we cannot sum deltas for gvar TupleVariations if they contain
+ # inferred inferred deltas (the latter need to be computed first using
+ # 'calcInferredDeltas' method), but we can treat 'None' values in cvar
+ # deltas as if they are zeros.
+ if self.getCoordWidth() == 2:
+ for i, d2 in zip(range(length), deltas2):
+ d1 = deltas1[i]
+ try:
+ deltas1[i] = (d1[0] + d2[0], d1[1] + d2[1])
+ except TypeError:
+ raise ValueError("cannot sum gvar deltas with inferred points")
+ else:
+ for i, d2 in zip(range(length), deltas2):
+ d1 = deltas1[i]
+ if d1 is not None and d2 is not None:
+ deltas1[i] = d1 + d2
+ elif d1 is None and d2 is not None:
+ deltas1[i] = d2
+ # elif d2 is None do nothing
+ return self
+
+
+def decompileSharedTuples(axisTags, sharedTupleCount, data, offset):
+ result = []
+ for _ in range(sharedTupleCount):
+ t, offset = TupleVariation.decompileCoord_(axisTags, data, offset)
+ result.append(t)
+ return result
+
+
+def compileSharedTuples(
+ axisTags, variations, MAX_NUM_SHARED_COORDS=TUPLE_INDEX_MASK + 1
+):
+ coordCount = Counter()
+ for var in variations:
+ coord = var.compileCoord(axisTags)
+ coordCount[coord] += 1
+ # In python < 3.7, most_common() ordering is non-deterministic
+ # so apply a sort to make sure the ordering is consistent.
+ sharedCoords = sorted(
+ coordCount.most_common(MAX_NUM_SHARED_COORDS),
+ key=lambda item: (-item[1], item[0]),
+ )
+ return [c[0] for c in sharedCoords if c[1] > 1]
+
+
+def compileTupleVariationStore(
+ variations,
+ pointCount,
+ axisTags,
+ sharedTupleIndices,
+ useSharedPoints=True,
+ *,
+ optimizeSize=True,
+):
+ # pointCount is actually unused. Keeping for API compat.
+ del pointCount
+ newVariations = []
+ pointDatas = []
+ # Compile all points and figure out sharing if desired
+ sharedPoints = None
+
+ # Collect, count, and compile point-sets for all variation sets
+ pointSetCount = defaultdict(int)
+ for v in variations:
+ points = v.getUsedPoints()
+ if points is None: # Empty variations
+ continue
+ pointSetCount[points] += 1
+ newVariations.append(v)
+ pointDatas.append(points)
+ variations = newVariations
+ del newVariations
+
+ if not variations:
+ return (0, b"", b"")
+
+ n = len(variations[0].coordinates)
+ assert all(
+ len(v.coordinates) == n for v in variations
+ ), "Variation sets have different sizes"
+
+ compiledPoints = {
+ pointSet: TupleVariation.compilePoints(pointSet) for pointSet in pointSetCount
+ }
+
+ tupleVariationCount = len(variations)
+ tuples = []
+ data = []
+
+ if useSharedPoints:
+ # Find point-set which saves most bytes.
+ def key(pn):
+ pointSet = pn[0]
+ count = pn[1]
+ return len(compiledPoints[pointSet]) * (count - 1)
+
+ sharedPoints = max(pointSetCount.items(), key=key)[0]
+
+ data.append(compiledPoints[sharedPoints])
+ tupleVariationCount |= TUPLES_SHARE_POINT_NUMBERS
+
+ # b'' implies "use shared points"
+ pointDatas = [
+ compiledPoints[points] if points != sharedPoints else b""
+ for points in pointDatas
+ ]
+
+ for v, p in zip(variations, pointDatas):
+ thisTuple, thisData = v.compile(
+ axisTags, sharedTupleIndices, pointData=p, optimizeSize=optimizeSize
+ )
+
+ tuples.append(thisTuple)
+ data.append(thisData)
+
+ tuples = b"".join(tuples)
+ data = b"".join(data)
+ return tupleVariationCount, tuples, data
+
+
+def decompileTupleVariationStore(
+ tableTag,
+ axisTags,
+ tupleVariationCount,
+ pointCount,
+ sharedTuples,
+ data,
+ pos,
+ dataPos,
+):
+ numAxes = len(axisTags)
+ result = []
+ if (tupleVariationCount & TUPLES_SHARE_POINT_NUMBERS) != 0:
+ sharedPoints, dataPos = TupleVariation.decompilePoints_(
+ pointCount, data, dataPos, tableTag
+ )
+ else:
+ sharedPoints = []
+ for _ in range(tupleVariationCount & TUPLE_COUNT_MASK):
+ dataSize, flags = struct.unpack(">HH", data[pos : pos + 4])
+ tupleSize = TupleVariation.getTupleSize_(flags, numAxes)
+ tupleData = data[pos : pos + tupleSize]
+ pointDeltaData = data[dataPos : dataPos + dataSize]
+ result.append(
+ decompileTupleVariation_(
+ pointCount,
+ sharedTuples,
+ sharedPoints,
+ tableTag,
+ axisTags,
+ tupleData,
+ pointDeltaData,
+ )
+ )
+ pos += tupleSize
+ dataPos += dataSize
+ return result
+
+
+def decompileTupleVariation_(
+ pointCount, sharedTuples, sharedPoints, tableTag, axisTags, data, tupleData
+):
+ assert tableTag in ("cvar", "gvar"), tableTag
+ flags = struct.unpack(">H", data[2:4])[0]
+ pos = 4
+ if (flags & EMBEDDED_PEAK_TUPLE) == 0:
+ peak = sharedTuples[flags & TUPLE_INDEX_MASK]
+ else:
+ peak, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+ if (flags & INTERMEDIATE_REGION) != 0:
+ start, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+ end, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+ else:
+ start, end = inferRegion_(peak)
+ axes = {}
+ for axis in axisTags:
+ region = start[axis], peak[axis], end[axis]
+ if region != (0.0, 0.0, 0.0):
+ axes[axis] = region
+ pos = 0
+ if (flags & PRIVATE_POINT_NUMBERS) != 0:
+ points, pos = TupleVariation.decompilePoints_(
+ pointCount, tupleData, pos, tableTag
+ )
+ else:
+ points = sharedPoints
+
+ deltas = [None] * pointCount
+
+ if tableTag == "cvar":
+ deltas_cvt, pos = TupleVariation.decompileDeltas_(len(points), tupleData, pos)
+ for p, delta in zip(points, deltas_cvt):
+ if 0 <= p < pointCount:
+ deltas[p] = delta
+
+ elif tableTag == "gvar":
+ deltas_x, pos = TupleVariation.decompileDeltas_(len(points), tupleData, pos)
+ deltas_y, pos = TupleVariation.decompileDeltas_(len(points), tupleData, pos)
+ for p, x, y in zip(points, deltas_x, deltas_y):
+ if 0 <= p < pointCount:
+ deltas[p] = (x, y)
+
+ return TupleVariation(axes, deltas)
+
+
+def inferRegion_(peak):
+ """Infer start and end for a (non-intermediate) region
+
+ This helper function computes the applicability region for
+ variation tuples whose INTERMEDIATE_REGION flag is not set in the
+ TupleVariationHeader structure. Variation tuples apply only to
+ certain regions of the variation space; outside that region, the
+ tuple has no effect. To make the binary encoding more compact,
+ TupleVariationHeaders can omit the intermediateStartTuple and
+ intermediateEndTuple fields.
+ """
+ start, end = {}, {}
+ for axis, value in peak.items():
+ start[axis] = min(value, 0.0) # -0.3 --> -0.3; 0.7 --> 0.0
+ end[axis] = max(value, 0.0) # -0.3 --> 0.0; 0.7 --> 0.7
+ return (start, end)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/V_A_R_C_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_A_R_C_.py
new file mode 100644
index 0000000000000000000000000000000000000000..afc1497d14ffd4aed6668af4cb0587135e5562f9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_A_R_C_.py
@@ -0,0 +1,12 @@
+from .otBase import BaseTTXConverter
+
+
+class table_V_A_R_C_(BaseTTXConverter):
+ """Variable Components table
+
+ The ``VARC`` table contains variation information for composite glyphs.
+
+ See also https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/V_D_M_X_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_D_M_X_.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f8abc46de903fbc300fc4ee4b803cb47bf03ba0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_D_M_X_.py
@@ -0,0 +1,249 @@
+from . import DefaultTable
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+import struct
+
+VDMX_HeaderFmt = """
+ > # big endian
+ version: H # Version number (0 or 1)
+ numRecs: H # Number of VDMX groups present
+ numRatios: H # Number of aspect ratio groupings
+"""
+# the VMDX header is followed by an array of RatRange[numRatios] (i.e. aspect
+# ratio ranges);
+VDMX_RatRangeFmt = """
+ > # big endian
+ bCharSet: B # Character set
+ xRatio: B # Value to use for x-Ratio
+ yStartRatio: B # Starting y-Ratio value
+ yEndRatio: B # Ending y-Ratio value
+"""
+# followed by an array of offset[numRatios] from start of VDMX table to the
+# VDMX Group for this ratio range (offsets will be re-calculated on compile);
+# followed by an array of Group[numRecs] records;
+VDMX_GroupFmt = """
+ > # big endian
+ recs: H # Number of height records in this group
+ startsz: B # Starting yPelHeight
+ endsz: B # Ending yPelHeight
+"""
+# followed by an array of vTable[recs] records.
+VDMX_vTableFmt = """
+ > # big endian
+ yPelHeight: H # yPelHeight to which values apply
+ yMax: h # Maximum value (in pels) for this yPelHeight
+ yMin: h # Minimum value (in pels) for this yPelHeight
+"""
+
+
+class table_V_D_M_X_(DefaultTable.DefaultTable):
+ """Vertical Device Metrics table
+
+ The ``VDMX`` table records changes to the vertical glyph minima
+ and maxima that result from Truetype instructions.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/vdmx
+ """
+
+ def decompile(self, data, ttFont):
+ pos = 0 # track current position from to start of VDMX table
+ dummy, data = sstruct.unpack2(VDMX_HeaderFmt, data, self)
+ pos += sstruct.calcsize(VDMX_HeaderFmt)
+ self.ratRanges = []
+ for i in range(self.numRatios):
+ ratio, data = sstruct.unpack2(VDMX_RatRangeFmt, data)
+ pos += sstruct.calcsize(VDMX_RatRangeFmt)
+ # the mapping between a ratio and a group is defined further below
+ ratio["groupIndex"] = None
+ self.ratRanges.append(ratio)
+ lenOffset = struct.calcsize(">H")
+ _offsets = [] # temporarily store offsets to groups
+ for i in range(self.numRatios):
+ offset = struct.unpack(">H", data[0:lenOffset])[0]
+ data = data[lenOffset:]
+ pos += lenOffset
+ _offsets.append(offset)
+ self.groups = []
+ for groupIndex in range(self.numRecs):
+ # the offset to this group from beginning of the VDMX table
+ currOffset = pos
+ group, data = sstruct.unpack2(VDMX_GroupFmt, data)
+ # the group lenght and bounding sizes are re-calculated on compile
+ recs = group.pop("recs")
+ startsz = group.pop("startsz")
+ endsz = group.pop("endsz")
+ pos += sstruct.calcsize(VDMX_GroupFmt)
+ for j in range(recs):
+ vTable, data = sstruct.unpack2(VDMX_vTableFmt, data)
+ vTableLength = sstruct.calcsize(VDMX_vTableFmt)
+ pos += vTableLength
+ # group is a dict of (yMax, yMin) tuples keyed by yPelHeight
+ group[vTable["yPelHeight"]] = (vTable["yMax"], vTable["yMin"])
+ # make sure startsz and endsz match the calculated values
+ minSize = min(group.keys())
+ maxSize = max(group.keys())
+ assert (
+ startsz == minSize
+ ), "startsz (%s) must equal min yPelHeight (%s): group %d" % (
+ group.startsz,
+ minSize,
+ groupIndex,
+ )
+ assert (
+ endsz == maxSize
+ ), "endsz (%s) must equal max yPelHeight (%s): group %d" % (
+ group.endsz,
+ maxSize,
+ groupIndex,
+ )
+ self.groups.append(group)
+ # match the defined offsets with the current group's offset
+ for offsetIndex, offsetValue in enumerate(_offsets):
+ # when numRecs < numRatios there can more than one ratio range
+ # sharing the same VDMX group
+ if currOffset == offsetValue:
+ # map the group with the ratio range thas has the same
+ # index as the offset to that group (it took me a while..)
+ self.ratRanges[offsetIndex]["groupIndex"] = groupIndex
+ # check that all ratio ranges have a group
+ for i in range(self.numRatios):
+ ratio = self.ratRanges[i]
+ if ratio["groupIndex"] is None:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("no group defined for ratRange %d" % i)
+
+ def _getOffsets(self):
+ """
+ Calculate offsets to VDMX_Group records.
+ For each ratRange return a list of offset values from the beginning of
+ the VDMX table to a VDMX_Group.
+ """
+ lenHeader = sstruct.calcsize(VDMX_HeaderFmt)
+ lenRatRange = sstruct.calcsize(VDMX_RatRangeFmt)
+ lenOffset = struct.calcsize(">H")
+ lenGroupHeader = sstruct.calcsize(VDMX_GroupFmt)
+ lenVTable = sstruct.calcsize(VDMX_vTableFmt)
+ # offset to the first group
+ pos = lenHeader + self.numRatios * lenRatRange + self.numRatios * lenOffset
+ groupOffsets = []
+ for group in self.groups:
+ groupOffsets.append(pos)
+ lenGroup = lenGroupHeader + len(group) * lenVTable
+ pos += lenGroup # offset to next group
+ offsets = []
+ for ratio in self.ratRanges:
+ groupIndex = ratio["groupIndex"]
+ offsets.append(groupOffsets[groupIndex])
+ return offsets
+
+ def compile(self, ttFont):
+ if not (self.version == 0 or self.version == 1):
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError(
+ "unknown format for VDMX table: version %s" % self.version
+ )
+ data = sstruct.pack(VDMX_HeaderFmt, self)
+ for ratio in self.ratRanges:
+ data += sstruct.pack(VDMX_RatRangeFmt, ratio)
+ # recalculate offsets to VDMX groups
+ for offset in self._getOffsets():
+ data += struct.pack(">H", offset)
+ for group in self.groups:
+ recs = len(group)
+ startsz = min(group.keys())
+ endsz = max(group.keys())
+ gHeader = {"recs": recs, "startsz": startsz, "endsz": endsz}
+ data += sstruct.pack(VDMX_GroupFmt, gHeader)
+ for yPelHeight, (yMax, yMin) in sorted(group.items()):
+ vTable = {"yPelHeight": yPelHeight, "yMax": yMax, "yMin": yMin}
+ data += sstruct.pack(VDMX_vTableFmt, vTable)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ writer.begintag("ratRanges")
+ writer.newline()
+ for ratio in self.ratRanges:
+ groupIndex = ratio["groupIndex"]
+ writer.simpletag(
+ "ratRange",
+ bCharSet=ratio["bCharSet"],
+ xRatio=ratio["xRatio"],
+ yStartRatio=ratio["yStartRatio"],
+ yEndRatio=ratio["yEndRatio"],
+ groupIndex=groupIndex,
+ )
+ writer.newline()
+ writer.endtag("ratRanges")
+ writer.newline()
+ writer.begintag("groups")
+ writer.newline()
+ for groupIndex in range(self.numRecs):
+ group = self.groups[groupIndex]
+ recs = len(group)
+ startsz = min(group.keys())
+ endsz = max(group.keys())
+ writer.begintag("group", index=groupIndex)
+ writer.newline()
+ writer.comment("recs=%d, startsz=%d, endsz=%d" % (recs, startsz, endsz))
+ writer.newline()
+ for yPelHeight, (yMax, yMin) in sorted(group.items()):
+ writer.simpletag(
+ "record",
+ [("yPelHeight", yPelHeight), ("yMax", yMax), ("yMin", yMin)],
+ )
+ writer.newline()
+ writer.endtag("group")
+ writer.newline()
+ writer.endtag("groups")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = safeEval(attrs["value"])
+ elif name == "ratRanges":
+ if not hasattr(self, "ratRanges"):
+ self.ratRanges = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "ratRange":
+ if not hasattr(self, "numRatios"):
+ self.numRatios = 1
+ else:
+ self.numRatios += 1
+ ratio = {
+ "bCharSet": safeEval(attrs["bCharSet"]),
+ "xRatio": safeEval(attrs["xRatio"]),
+ "yStartRatio": safeEval(attrs["yStartRatio"]),
+ "yEndRatio": safeEval(attrs["yEndRatio"]),
+ "groupIndex": safeEval(attrs["groupIndex"]),
+ }
+ self.ratRanges.append(ratio)
+ elif name == "groups":
+ if not hasattr(self, "groups"):
+ self.groups = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "group":
+ if not hasattr(self, "numRecs"):
+ self.numRecs = 1
+ else:
+ self.numRecs += 1
+ group = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "record":
+ yPelHeight = safeEval(attrs["yPelHeight"])
+ yMax = safeEval(attrs["yMax"])
+ yMin = safeEval(attrs["yMin"])
+ group[yPelHeight] = (yMax, yMin)
+ self.groups.append(group)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/V_O_R_G_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_O_R_G_.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c53acba431059f58f86dbb1b89755a03f699f94
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_O_R_G_.py
@@ -0,0 +1,165 @@
+from fontTools.misc.textTools import bytesjoin, safeEval
+from . import DefaultTable
+import struct
+
+
+class table_V_O_R_G_(DefaultTable.DefaultTable):
+ """Vertical Origin table
+
+ The ``VORG`` table contains the vertical origin of each glyph
+ in a `CFF` or `CFF2` font.
+
+ This table is structured so that you can treat it like a dictionary keyed by glyph name.
+
+ ``ttFont['VORG'][]`` will return the vertical origin for any glyph.
+
+ ``ttFont['VORG'][] = `` will set the vertical origin for any glyph.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/vorg
+ """
+
+ def decompile(self, data, ttFont):
+ self.getGlyphName = (
+ ttFont.getGlyphName
+ ) # for use in get/set item functions, for access by GID
+ (
+ self.majorVersion,
+ self.minorVersion,
+ self.defaultVertOriginY,
+ self.numVertOriginYMetrics,
+ ) = struct.unpack(">HHhH", data[:8])
+ assert (
+ self.majorVersion <= 1
+ ), "Major version of VORG table is higher than I know how to handle"
+ data = data[8:]
+ vids = []
+ gids = []
+ pos = 0
+ for i in range(self.numVertOriginYMetrics):
+ gid, vOrigin = struct.unpack(">Hh", data[pos : pos + 4])
+ pos += 4
+ gids.append(gid)
+ vids.append(vOrigin)
+
+ self.VOriginRecords = vOrig = {}
+ glyphOrder = ttFont.getGlyphOrder()
+ try:
+ names = [glyphOrder[gid] for gid in gids]
+ except IndexError:
+ getGlyphName = self.getGlyphName
+ names = map(getGlyphName, gids)
+
+ for name, vid in zip(names, vids):
+ vOrig[name] = vid
+
+ def compile(self, ttFont):
+ vorgs = list(self.VOriginRecords.values())
+ names = list(self.VOriginRecords.keys())
+ nameMap = ttFont.getReverseGlyphMap()
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
+ gids = [nameMap[name] for name in names]
+ vOriginTable = list(zip(gids, vorgs))
+ self.numVertOriginYMetrics = len(vorgs)
+ vOriginTable.sort() # must be in ascending GID order
+ dataList = [struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
+ header = struct.pack(
+ ">HHhH",
+ self.majorVersion,
+ self.minorVersion,
+ self.defaultVertOriginY,
+ self.numVertOriginYMetrics,
+ )
+ dataList.insert(0, header)
+ data = bytesjoin(dataList)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("majorVersion", value=self.majorVersion)
+ writer.newline()
+ writer.simpletag("minorVersion", value=self.minorVersion)
+ writer.newline()
+ writer.simpletag("defaultVertOriginY", value=self.defaultVertOriginY)
+ writer.newline()
+ writer.simpletag("numVertOriginYMetrics", value=self.numVertOriginYMetrics)
+ writer.newline()
+ vOriginTable = []
+ glyphNames = self.VOriginRecords.keys()
+ for glyphName in glyphNames:
+ try:
+ gid = ttFont.getGlyphID(glyphName)
+ except:
+ assert 0, (
+ "VORG table contains a glyph name not in ttFont.getGlyphNames(): "
+ + str(glyphName)
+ )
+ vOriginTable.append([gid, glyphName, self.VOriginRecords[glyphName]])
+ vOriginTable.sort()
+ for entry in vOriginTable:
+ vOriginRec = VOriginRecord(entry[1], entry[2])
+ vOriginRec.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "VOriginRecords"):
+ self.VOriginRecords = {}
+ self.getGlyphName = (
+ ttFont.getGlyphName
+ ) # for use in get/set item functions, for access by GID
+ if name == "VOriginRecord":
+ vOriginRec = VOriginRecord()
+ for element in content:
+ if isinstance(element, str):
+ continue
+ name, attrs, content = element
+ vOriginRec.fromXML(name, attrs, content, ttFont)
+ self.VOriginRecords[vOriginRec.glyphName] = vOriginRec.vOrigin
+ elif "value" in attrs:
+ setattr(self, name, safeEval(attrs["value"]))
+
+ def __getitem__(self, glyphSelector):
+ if isinstance(glyphSelector, int):
+ # its a gid, convert to glyph name
+ glyphSelector = self.getGlyphName(glyphSelector)
+
+ if glyphSelector not in self.VOriginRecords:
+ return self.defaultVertOriginY
+
+ return self.VOriginRecords[glyphSelector]
+
+ def __setitem__(self, glyphSelector, value):
+ if isinstance(glyphSelector, int):
+ # its a gid, convert to glyph name
+ glyphSelector = self.getGlyphName(glyphSelector)
+
+ if value != self.defaultVertOriginY:
+ self.VOriginRecords[glyphSelector] = value
+ elif glyphSelector in self.VOriginRecords:
+ del self.VOriginRecords[glyphSelector]
+
+ def __delitem__(self, glyphSelector):
+ del self.VOriginRecords[glyphSelector]
+
+
+class VOriginRecord(object):
+ def __init__(self, name=None, vOrigin=None):
+ self.glyphName = name
+ self.vOrigin = vOrigin
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("VOriginRecord")
+ writer.newline()
+ writer.simpletag("glyphName", value=self.glyphName)
+ writer.newline()
+ writer.simpletag("vOrigin", value=self.vOrigin)
+ writer.newline()
+ writer.endtag("VOriginRecord")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ if name == "glyphName":
+ setattr(self, name, value)
+ else:
+ setattr(self, name, safeEval(value))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/V_V_A_R_.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_V_A_R_.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0c94e348b2c6cdfc856b030477214604c95d7c4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/V_V_A_R_.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table_V_V_A_R_(BaseTTXConverter):
+ """Vertical Metrics Variations table
+
+ The ``VVAR`` table contains variation data for per-glyph vertical metrics
+ in a variable font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/vvar
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__init__.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b111097a804e5c6563860f98a77c2f5c499d60f1
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/__init__.py
@@ -0,0 +1,98 @@
+# DON'T EDIT! This file is generated by MetaTools/buildTableList.py.
+def _moduleFinderHint():
+ """Dummy function to let modulefinder know what tables may be
+ dynamically imported. Generated by MetaTools/buildTableList.py.
+
+ >>> _moduleFinderHint()
+ """
+ from . import B_A_S_E_
+ from . import C_B_D_T_
+ from . import C_B_L_C_
+ from . import C_F_F_
+ from . import C_F_F__2
+ from . import C_O_L_R_
+ from . import C_P_A_L_
+ from . import D_S_I_G_
+ from . import D__e_b_g
+ from . import E_B_D_T_
+ from . import E_B_L_C_
+ from . import F_F_T_M_
+ from . import F__e_a_t
+ from . import G_D_E_F_
+ from . import G_M_A_P_
+ from . import G_P_K_G_
+ from . import G_P_O_S_
+ from . import G_S_U_B_
+ from . import G_V_A_R_
+ from . import G__l_a_t
+ from . import G__l_o_c
+ from . import H_V_A_R_
+ from . import J_S_T_F_
+ from . import L_T_S_H_
+ from . import M_A_T_H_
+ from . import M_E_T_A_
+ from . import M_V_A_R_
+ from . import O_S_2f_2
+ from . import S_I_N_G_
+ from . import S_T_A_T_
+ from . import S_V_G_
+ from . import S__i_l_f
+ from . import S__i_l_l
+ from . import T_S_I_B_
+ from . import T_S_I_C_
+ from . import T_S_I_D_
+ from . import T_S_I_J_
+ from . import T_S_I_P_
+ from . import T_S_I_S_
+ from . import T_S_I_V_
+ from . import T_S_I__0
+ from . import T_S_I__1
+ from . import T_S_I__2
+ from . import T_S_I__3
+ from . import T_S_I__5
+ from . import T_T_F_A_
+ from . import V_A_R_C_
+ from . import V_D_M_X_
+ from . import V_O_R_G_
+ from . import V_V_A_R_
+ from . import _a_n_k_r
+ from . import _a_v_a_r
+ from . import _b_s_l_n
+ from . import _c_i_d_g
+ from . import _c_m_a_p
+ from . import _c_v_a_r
+ from . import _c_v_t
+ from . import _f_e_a_t
+ from . import _f_p_g_m
+ from . import _f_v_a_r
+ from . import _g_a_s_p
+ from . import _g_c_i_d
+ from . import _g_l_y_f
+ from . import _g_v_a_r
+ from . import _h_d_m_x
+ from . import _h_e_a_d
+ from . import _h_h_e_a
+ from . import _h_m_t_x
+ from . import _k_e_r_n
+ from . import _l_c_a_r
+ from . import _l_o_c_a
+ from . import _l_t_a_g
+ from . import _m_a_x_p
+ from . import _m_e_t_a
+ from . import _m_o_r_t
+ from . import _m_o_r_x
+ from . import _n_a_m_e
+ from . import _o_p_b_d
+ from . import _p_o_s_t
+ from . import _p_r_e_p
+ from . import _p_r_o_p
+ from . import _s_b_i_x
+ from . import _t_r_a_k
+ from . import _v_h_e_a
+ from . import _v_m_t_x
+
+
+if __name__ == "__main__":
+ import doctest, sys
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/B_A_S_E_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/B_A_S_E_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..03643222ee60092888d8a69627429a158671c58c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/B_A_S_E_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/BitmapGlyphMetrics.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/BitmapGlyphMetrics.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..59e841eecb11ef03857332dd31389dd631ae437d
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/BitmapGlyphMetrics.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_D_T_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_D_T_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..696e66c53c043a61be93f5d76bd9c3afce6703b5
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_D_T_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_L_C_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_L_C_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e47d9f3061cbc721d8ad871bcae6665c85f9bbac
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_B_L_C_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cbf2cadd85910f096bcc9256be12b6e40ec5f6b4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F__2.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F__2.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2a27cc33bb2557f5caf4f69e26b3fe8933f80df
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_F_F__2.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_O_L_R_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_O_L_R_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..03ea910e345630eb2ca26a131a2084cd676e5c96
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_O_L_R_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_P_A_L_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_P_A_L_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ede0bbe00cbee298bc5fa416ab2197d2d06b35c8
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/C_P_A_L_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D_S_I_G_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D_S_I_G_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9fb2a0dcb39ee8ef3b367a53707f264e54ff79b1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D_S_I_G_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D__e_b_g.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D__e_b_g.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5d96e48da55e76fe326b87279f67b3c373049bce
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/D__e_b_g.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/DefaultTable.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/DefaultTable.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7834bf531bae0c97a3674a552df191e791ac5bdc
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/DefaultTable.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_D_T_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_D_T_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4b4c4a98dce52f7010c6f20e3b71a1474a0bc9b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_D_T_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_L_C_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_L_C_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..896defa23d2e380934a7929323ebee7f14f4703b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/E_B_L_C_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F_F_T_M_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F_F_T_M_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7255024218a6c95d531485005de027dd2c36bfd1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F_F_T_M_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F__e_a_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F__e_a_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..17e33ea060d28318bf2c7d662a4c19462320f4f0
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/F__e_a_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_D_E_F_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_D_E_F_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..323612fc09b5762227a89d1d0e6c616c9f02c427
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_D_E_F_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_M_A_P_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_M_A_P_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c8bca5106f33c596201f2f0c96943492ff6e6ce6
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_M_A_P_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_K_G_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_K_G_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4f6ad6804fbfc83dc9a695fa3a84737cfed75027
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_K_G_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_O_S_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_O_S_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c0fa6221e304febe3c2897e45957f83a9ef7a8f4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_P_O_S_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_S_U_B_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_S_U_B_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b58fdbce7d88819b93cdb597d0aaf4e12bbbe9f4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_S_U_B_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_V_A_R_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_V_A_R_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3f2f9b50238a6d78b4ac5e46eb41162fae985040
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G_V_A_R_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_a_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_a_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a05c05db4d8e4f1464178b6286447405af48fc5d
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_a_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_o_c.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_o_c.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..be4b5c67eee359364856e7fe8854591542f8e9f7
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/G__l_o_c.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/H_V_A_R_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/H_V_A_R_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..528e5eb5dd7621c7fcc91709d16140b5db37462f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/H_V_A_R_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/J_S_T_F_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/J_S_T_F_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d77a8113abc3dcc4e623fb0ede97eaaf3c4da040
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/J_S_T_F_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/L_T_S_H_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/L_T_S_H_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..68b282ec362a8e47353c0d95b93ca89157f6b4ce
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/L_T_S_H_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_A_T_H_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_A_T_H_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..55107ded51bd5b85fcdd92c083108cf4cc56329e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_A_T_H_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_E_T_A_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_E_T_A_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a463454e2bb737e97d0aa218e41d78e59ad08399
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_E_T_A_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_V_A_R_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_V_A_R_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d8ef98546a3c58e65ab96649ddaedbfb63a3f604
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/M_V_A_R_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/O_S_2f_2.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/O_S_2f_2.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..77118118bd21321848207f70501993ee39a828e7
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/O_S_2f_2.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_I_N_G_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_I_N_G_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..80b9913e311955e59717b34dc49936a3c254e9d6
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_I_N_G_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_T_A_T_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_T_A_T_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a27a1f4ca7a2a212e7ed7470c370a563419ddb34
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_T_A_T_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_V_G_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_V_G_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..82e20973a6446ae082fe8dcdf9a37a814536f967
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S_V_G_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_f.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_f.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5e9199cbc5f0412cfe3561bd6970896b3cf3938b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_f.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_l.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_l.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..424952cb6066e1e3602cef7379037e10ea382d67
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/S__i_l_l.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_B_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_B_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..764462f34e6069072ef96c7f0271d39942f4a3bb
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_B_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_C_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_C_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..588a757c30c387c0ea7df09860d71b14c4b59fca
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_C_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_D_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_D_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e7db9e164343b5f51891dcf1d5c12bbd93560e23
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_D_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_J_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_J_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9fc64cb5afa4fd7a5f6fb569595e7bed3b0792e6
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_J_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_P_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_P_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5bccbdf2b31a09286434c04878a3e08cf4f31439
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_P_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_S_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_S_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..363cf6c80b35510db2b2456e6f814564174a659b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_S_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_V_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_V_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8aabc66c9a2be7b73246d42a668e43b08bf90348
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I_V_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__0.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__0.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..65457c61d0f84f0fd7f65129b1eeddfc6b2a4f7c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__0.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__1.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__1.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6d4486d6c4cbd7394e98effe45931480bbe4c187
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__1.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__2.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__2.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ea641c9922852b8953f890bf4388c5ea565e98cc
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__2.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__3.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__3.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2e8029e85ccc34a506f2623505d29a22c1fe9c7b
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__3.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__5.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__5.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..213e6d6690220b0ece4b84500b1b6377eee827b2
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_S_I__5.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_T_F_A_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_T_F_A_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..58d402d31ed5189e1656290c11dd97d938b4c4d4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/T_T_F_A_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/TupleVariation.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/TupleVariation.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4017ae45c00dc9960098e8a59998afd6307afe1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/TupleVariation.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_A_R_C_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_A_R_C_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e7e410a90fef327654cdaa135b9920d3b4af2be6
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_A_R_C_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_D_M_X_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_D_M_X_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7e27d7aab3df7f931380e42993a8e4166b99a8ed
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_D_M_X_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_O_R_G_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_O_R_G_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bd81bd4c7e15ea7076d8a47e8f0925b146408926
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_O_R_G_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_V_A_R_.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_V_A_R_.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3130f049e13fed59aa511215383196cd67cd0a73
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/V_V_A_R_.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..86b4cc7a3a230a31d67e9097bf25e999d4c8d438
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_n_k_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_n_k_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d66e248d6b647d49243231124cec918bae1aa042
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_n_k_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_v_a_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_v_a_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..de81e4f4d3b36e4bed0d7589a59d1a9f29b11b85
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_a_v_a_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_b_s_l_n.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_b_s_l_n.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9a237a6c93b0990be2aaaccc1678f84a1e41092d
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_b_s_l_n.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_i_d_g.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_i_d_g.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8c1ee947ab1b22a60a103780288a618b2b7f7009
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_i_d_g.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_m_a_p.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_m_a_p.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..06a4c0d6aa6ba7c35e0ccb24eb6cfcf482e293bd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_m_a_p.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_a_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_a_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..99ae33034ea8c0bb57986ce7c93ad654efb23ab0
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_a_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5e4ac435233a10150072d74928969e0b12ca0d23
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_c_v_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_e_a_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_e_a_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d131201547a1b6fccb5a70e12ecf491f887eb9d5
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_e_a_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_p_g_m.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_p_g_m.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..284cc8d032e7077fa1c62b0fe467c911871174ab
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_p_g_m.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_v_a_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_v_a_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f154b592d3993c3c38458d6c09aab46119c5de9e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_f_v_a_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_a_s_p.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_a_s_p.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0c6081de532fc1bed0d3baf30c13016f54b5d407
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_a_s_p.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_c_i_d.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_c_i_d.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d3559cebf79665b98d3a5cbd09ac1d8dc43fba35
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_c_i_d.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_l_y_f.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_l_y_f.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..87e2b82c4656e3837a779492502a4b5362cae922
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_l_y_f.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_v_a_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_v_a_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..99146f4104abe40c16c452346b058f27d6512bc0
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_g_v_a_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_d_m_x.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_d_m_x.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..98368bafcd75e9f6e0e0fa6d317bca804cad77e4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_d_m_x.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_e_a_d.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_e_a_d.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5ca2e8db3d2e1e6406f0d6043112898db51dc5ff
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_e_a_d.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_h_e_a.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_h_e_a.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c4e53d58439bf438eedc8623728aeb606b2cf9c9
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_h_e_a.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_m_t_x.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_m_t_x.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d75e566ad869ce353cb1d6656edfe8db81790672
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_h_m_t_x.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_k_e_r_n.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_k_e_r_n.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5dc987be76e1c0bdf1ea74a1b73bc9a41d3afee1
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_k_e_r_n.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_c_a_r.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_c_a_r.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0765b73bd8d0db79c4ec26fe5589fab1d077b54f
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_c_a_r.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_o_c_a.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_o_c_a.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2956cfc8d1f82be684c8cc255b7c0a0c25535e1a
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_o_c_a.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_t_a_g.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_t_a_g.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f9ef618a3fe10e590cfe23ab4dd605c64a4f92ec
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_l_t_a_g.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_a_x_p.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_a_x_p.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3858d61c6f272e6b676e3f0f175de9bfe90b78e4
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_a_x_p.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_e_t_a.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_e_t_a.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..800731403651e0ce3ab3dcce29a0b445b895dfc9
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_e_t_a.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f896ef51c8b5463fc7ab900cea8f29a3b3fa5832
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_x.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_x.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6ba3103f53476deab03f2316f52780282aaa8219
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_m_o_r_x.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_n_a_m_e.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_n_a_m_e.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2819594b4d8aebfe6af76f9bdc05ac70aa3dbc3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_n_a_m_e.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_o_p_b_d.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_o_p_b_d.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ce54188844ad75943be9dcf43f008ce38ab23969
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_o_p_b_d.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_o_s_t.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_o_s_t.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..942a95fe713a3696bd38a1da26c67c29f9bf96c3
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_o_s_t.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_e_p.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_e_p.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..62f7fe65849a723ee598223f10e9c1fc9b0991d7
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_e_p.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_o_p.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_o_p.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ba373be62f19d8d411757ac5fb09c2ce88d6da0e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_p_r_o_p.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_s_b_i_x.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_s_b_i_x.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..03b322ce103b1024f1560c69b48f64e9b281eb35
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_s_b_i_x.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_t_r_a_k.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_t_r_a_k.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a79b33a07986b0126c34be32b22e5d219a59c912
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_t_r_a_k.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_h_e_a.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_h_e_a.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2fe4e745bf317837f9499d3c21eb7916ea68ebde
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_h_e_a.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_m_t_x.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_m_t_x.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7ba893647b00aadbb83239d6f3e20b098d43db26
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/_v_m_t_x.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/asciiTable.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/asciiTable.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ecbff7090389fb63da8ba81bbb734c7f4a328d16
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/asciiTable.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/grUtils.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/grUtils.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0eb95e6757e6b6d6b0f1ade4998888a53629349e
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/grUtils.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otBase.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otBase.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b2f224e3965f14512c01ea5accc84fc04d2d820c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otBase.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otData.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otData.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..58b0c049ff300bb5eda1924b8a83b278b6a34eef
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otData.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otTraverse.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otTraverse.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..de889063ebbb224072ffa70a540b317b6f97f708
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/otTraverse.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixGlyph.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixGlyph.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a85dd4ef9b89e9c5243ad1fc9e1f822831caac01
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixGlyph.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixStrike.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixStrike.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..58d998c7b4e0f9427b4140a1a91e3cf2e215ee26
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/sbixStrike.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/ttProgram.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/ttProgram.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..541f32f2ae77fd94c1ce43041a12dd71b63d6d0a
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ttLib/tables/__pycache__/ttProgram.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_n_k_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_n_k_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..5466d42d4117f9935b54def71e5351fa1a0ee6ab
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_n_k_r.py
@@ -0,0 +1,15 @@
+from .otBase import BaseTTXConverter
+
+
+class table__a_n_k_r(BaseTTXConverter):
+ """Anchor Point table
+
+ The anchor point table provides a way to define anchor points.
+ These are points within the coordinate space of a given glyph,
+ independent of the control points used to render the glyph.
+ Anchor points are used in conjunction with the ``kerx`` table.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_v_a_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_v_a_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb4593712c91388e085e371650e8bff8dfb24740
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_a_v_a_r.py
@@ -0,0 +1,200 @@
+from __future__ import annotations
+
+from typing import Mapping, TYPE_CHECKING
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+)
+from fontTools.misc.textTools import bytesjoin, safeEval
+from fontTools.misc.roundTools import otRound
+from fontTools.varLib.models import piecewiseLinearMap
+from fontTools.varLib.varStore import VarStoreInstancer, NO_VARIATION_INDEX
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+from . import otTables
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+from .otBase import BaseTTXConverter
+
+if TYPE_CHECKING:
+ from ..ttFont import TTFont
+
+
+class table__a_v_a_r(BaseTTXConverter):
+ """Axis Variations table
+
+ This class represents the ``avar`` table of a variable font. The object has one
+ substantive attribute, ``segments``, which maps axis tags to a segments dictionary::
+
+ >>> font["avar"].segments # doctest: +SKIP
+ {'wght': {-1.0: -1.0,
+ 0.0: 0.0,
+ 0.125: 0.11444091796875,
+ 0.25: 0.23492431640625,
+ 0.5: 0.35540771484375,
+ 0.625: 0.5,
+ 0.75: 0.6566162109375,
+ 0.875: 0.81927490234375,
+ 1.0: 1.0},
+ 'ital': {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}}
+
+ Notice that the segments dictionary is made up of normalized values. A valid
+ ``avar`` segment mapping must contain the entries ``-1.0: -1.0, 0.0: 0.0, 1.0: 1.0``.
+ fontTools does not enforce this, so it is your responsibility to ensure that
+ mappings are valid.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/avar
+ """
+
+ dependencies = ["fvar"]
+
+ def __init__(self, tag=None):
+ super().__init__(tag)
+ self.segments = {}
+
+ def compile(self, ttFont):
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ if not hasattr(self, "table"):
+ self.table = otTables.avar()
+ if not hasattr(self.table, "Reserved"):
+ self.table.Reserved = 0
+ self.table.Version = (getattr(self, "majorVersion", 1) << 16) | getattr(
+ self, "minorVersion", 0
+ )
+ self.table.AxisCount = len(axisTags)
+ self.table.AxisSegmentMap = []
+ for axis in axisTags:
+ mappings = self.segments[axis]
+ segmentMap = otTables.AxisSegmentMap()
+ segmentMap.PositionMapCount = len(mappings)
+ segmentMap.AxisValueMap = []
+ for key, value in sorted(mappings.items()):
+ valueMap = otTables.AxisValueMap()
+ valueMap.FromCoordinate = key
+ valueMap.ToCoordinate = value
+ segmentMap.AxisValueMap.append(valueMap)
+ self.table.AxisSegmentMap.append(segmentMap)
+ return super().compile(ttFont)
+
+ def decompile(self, data, ttFont):
+ super().decompile(data, ttFont)
+ self.majorVersion = self.table.Version >> 16
+ self.minorVersion = self.table.Version & 0xFFFF
+ if self.majorVersion not in (1, 2):
+ raise NotImplementedError("Unknown avar table version")
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ for axis in axisTags:
+ self.segments[axis] = {}
+ for axis, segmentMap in zip(axisTags, self.table.AxisSegmentMap):
+ segments = self.segments[axis] = {}
+ for segment in segmentMap.AxisValueMap:
+ segments[segment.FromCoordinate] = segment.ToCoordinate
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag(
+ "version",
+ major=getattr(self, "majorVersion", 1),
+ minor=getattr(self, "minorVersion", 0),
+ )
+ writer.newline()
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ for axis in axisTags:
+ writer.begintag("segment", axis=axis)
+ writer.newline()
+ for key, value in sorted(self.segments[axis].items()):
+ key = fl2str(key, 14)
+ value = fl2str(value, 14)
+ writer.simpletag("mapping", **{"from": key, "to": value})
+ writer.newline()
+ writer.endtag("segment")
+ writer.newline()
+ if getattr(self, "majorVersion", 1) >= 2:
+ if self.table.VarIdxMap:
+ self.table.VarIdxMap.toXML(writer, ttFont, name="VarIdxMap")
+ if self.table.VarStore:
+ self.table.VarStore.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "table"):
+ self.table = otTables.avar()
+ if not hasattr(self.table, "Reserved"):
+ self.table.Reserved = 0
+ if name == "version":
+ self.majorVersion = safeEval(attrs["major"])
+ self.minorVersion = safeEval(attrs["minor"])
+ self.table.Version = (getattr(self, "majorVersion", 1) << 16) | getattr(
+ self, "minorVersion", 0
+ )
+ elif name == "segment":
+ axis = attrs["axis"]
+ segment = self.segments[axis] = {}
+ for element in content:
+ if isinstance(element, tuple):
+ elementName, elementAttrs, _ = element
+ if elementName == "mapping":
+ fromValue = str2fl(elementAttrs["from"], 14)
+ toValue = str2fl(elementAttrs["to"], 14)
+ if fromValue in segment:
+ log.warning(
+ "duplicate entry for %s in axis '%s'", fromValue, axis
+ )
+ segment[fromValue] = toValue
+ else:
+ super().fromXML(name, attrs, content, ttFont)
+
+ def renormalizeLocation(
+ self, location: Mapping[str, float], font: TTFont, dropZeroes: bool = True
+ ) -> dict[str, float]:
+ majorVersion = getattr(self, "majorVersion", 1)
+
+ if majorVersion not in (1, 2):
+ raise NotImplementedError("Unknown avar table version")
+
+ avarSegments = self.segments
+ mappedLocation = {}
+ for axisTag, value in location.items():
+ avarMapping = avarSegments.get(axisTag, None)
+ if avarMapping is not None:
+ value = piecewiseLinearMap(value, avarMapping)
+ mappedLocation[axisTag] = value
+
+ if majorVersion < 2:
+ return mappedLocation
+
+ # Version 2
+
+ varIdxMap = self.table.VarIdxMap
+ varStore = self.table.VarStore
+ axes = font["fvar"].axes
+ if varStore is not None:
+ instancer = VarStoreInstancer(varStore, axes, mappedLocation)
+
+ coords = list(fl2fi(mappedLocation.get(axis.axisTag, 0), 14) for axis in axes)
+
+ out = []
+ for varIdx, v in enumerate(coords):
+
+ if varIdxMap is not None:
+ varIdx = varIdxMap[varIdx]
+
+ if varStore is not None:
+ delta = instancer[varIdx]
+ v += otRound(delta)
+ v = min(max(v, -(1 << 14)), +(1 << 14))
+
+ out.append(v)
+
+ mappedLocation = {
+ axis.axisTag: fi2fl(v, 14)
+ for v, axis in zip(out, axes)
+ if v != 0 or not dropZeroes
+ }
+
+ return mappedLocation
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_b_s_l_n.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_b_s_l_n.py
new file mode 100644
index 0000000000000000000000000000000000000000..85796b0a0479d26104a3fb63d9fe6085be29853a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_b_s_l_n.py
@@ -0,0 +1,15 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+class table__b_s_l_n(BaseTTXConverter):
+ """Baseline table
+
+ The AAT ``bsln`` table is similar in purpose to the OpenType ``BASE``
+ table; it stores per-script baselines to support automatic alignment
+ of lines of text.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_i_d_g.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_i_d_g.py
new file mode 100644
index 0000000000000000000000000000000000000000..c283e5a4dd1619a621a78b20171d38b14752c836
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_i_d_g.py
@@ -0,0 +1,24 @@
+# coding: utf-8
+from .otBase import BaseTTXConverter
+
+
+class table__c_i_d_g(BaseTTXConverter):
+ """CID to Glyph ID table
+
+ The AAT ``cidg`` table has almost the same structure as ``gidc``,
+ just mapping CIDs to GlyphIDs instead of the reverse direction.
+
+ It is useful for fonts that may be used by a PDF renderer in lieu of
+ a font reference with a known glyph collection but no subsetted
+ glyphs. For instance, a PDF can say “please use a font conforming
+ to Adobe-Japan-1”; the ``cidg`` mapping is necessary if the font is,
+ say, a TrueType font. ``gidc`` is lossy for this purpose and is
+ obsoleted by ``cidg``.
+
+ For example, the first font in ``/System/Library/Fonts/PingFang.ttc``
+ (which Apple ships pre-installed on MacOS 10.12.6) has a ``cidg`` table.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_m_a_p.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_m_a_p.py
new file mode 100644
index 0000000000000000000000000000000000000000..e935313a18c1038dceba0218e118470fcbc19bb4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_m_a_p.py
@@ -0,0 +1,1591 @@
+from fontTools.misc.textTools import bytesjoin, safeEval, readHex
+from fontTools.misc.encodingTools import getEncoding
+from fontTools.ttLib import getSearchRange
+from fontTools.unicode import Unicode
+from . import DefaultTable
+import sys
+import struct
+import array
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+def _make_map(font, chars, gids):
+ assert len(chars) == len(gids)
+ glyphNames = font.getGlyphNameMany(gids)
+ cmap = {}
+ for char, gid, name in zip(chars, gids, glyphNames):
+ if gid == 0:
+ continue
+ cmap[char] = name
+ return cmap
+
+
+class table__c_m_a_p(DefaultTable.DefaultTable):
+ """Character to Glyph Index Mapping Table
+
+ This class represents the `cmap `_
+ table, which maps between input characters (in Unicode or other system encodings)
+ and glyphs within the font. The ``cmap`` table contains one or more subtables
+ which determine the mapping of of characters to glyphs across different platforms
+ and encoding systems.
+
+ ``table__c_m_a_p`` objects expose an accessor ``.tables`` which provides access
+ to the subtables, although it is normally easier to retrieve individual subtables
+ through the utility methods described below. To add new subtables to a font,
+ first determine the subtable format (if in doubt use format 4 for glyphs within
+ the BMP, format 12 for glyphs outside the BMP, and format 14 for Unicode Variation
+ Sequences) construct subtable objects with ``CmapSubtable.newSubtable(format)``,
+ and append them to the ``.tables`` list.
+
+ Within a subtable, the mapping of characters to glyphs is provided by the ``.cmap``
+ attribute.
+
+ Example::
+
+ cmap4_0_3 = CmapSubtable.newSubtable(4)
+ cmap4_0_3.platformID = 0
+ cmap4_0_3.platEncID = 3
+ cmap4_0_3.language = 0
+ cmap4_0_3.cmap = { 0xC1: "Aacute" }
+
+ cmap = newTable("cmap")
+ cmap.tableVersion = 0
+ cmap.tables = [cmap4_0_3]
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cmap
+ """
+
+ def getcmap(self, platformID, platEncID):
+ """Returns the first subtable which matches the given platform and encoding.
+
+ Args:
+ platformID (int): The platform ID. Use 0 for Unicode, 1 for Macintosh
+ (deprecated for new fonts), 2 for ISO (deprecated) and 3 for Windows.
+ encodingID (int): Encoding ID. Interpretation depends on the platform ID.
+ See the OpenType specification for details.
+
+ Returns:
+ An object which is a subclass of :py:class:`CmapSubtable` if a matching
+ subtable is found within the font, or ``None`` otherwise.
+ """
+
+ for subtable in self.tables:
+ if subtable.platformID == platformID and subtable.platEncID == platEncID:
+ return subtable
+ return None # not found
+
+ def getBestCmap(
+ self,
+ cmapPreferences=(
+ (3, 10),
+ (0, 6),
+ (0, 4),
+ (3, 1),
+ (0, 3),
+ (0, 2),
+ (0, 1),
+ (0, 0),
+ ),
+ ):
+ """Returns the 'best' Unicode cmap dictionary available in the font
+ or ``None``, if no Unicode cmap subtable is available.
+
+ By default it will search for the following (platformID, platEncID)
+ pairs in order::
+
+ (3, 10), # Windows Unicode full repertoire
+ (0, 6), # Unicode full repertoire (format 13 subtable)
+ (0, 4), # Unicode 2.0 full repertoire
+ (3, 1), # Windows Unicode BMP
+ (0, 3), # Unicode 2.0 BMP
+ (0, 2), # Unicode ISO/IEC 10646
+ (0, 1), # Unicode 1.1
+ (0, 0) # Unicode 1.0
+
+ This particular order matches what HarfBuzz uses to choose what
+ subtable to use by default. This order prefers the largest-repertoire
+ subtable, and among those, prefers the Windows-platform over the
+ Unicode-platform as the former has wider support.
+
+ This order can be customized via the ``cmapPreferences`` argument.
+ """
+ for platformID, platEncID in cmapPreferences:
+ cmapSubtable = self.getcmap(platformID, platEncID)
+ if cmapSubtable is not None:
+ return cmapSubtable.cmap
+ return None # None of the requested cmap subtables were found
+
+ def buildReversed(self):
+ """Builds a reverse mapping dictionary
+
+ Iterates over all Unicode cmap tables and returns a dictionary mapping
+ glyphs to sets of codepoints, such as::
+
+ {
+ 'one': {0x31}
+ 'A': {0x41,0x391}
+ }
+
+ The values are sets of Unicode codepoints because
+ some fonts map different codepoints to the same glyph.
+ For example, ``U+0041 LATIN CAPITAL LETTER A`` and ``U+0391
+ GREEK CAPITAL LETTER ALPHA`` are sometimes the same glyph.
+ """
+ result = {}
+ for subtable in self.tables:
+ if subtable.isUnicode():
+ for codepoint, name in subtable.cmap.items():
+ result.setdefault(name, set()).add(codepoint)
+ return result
+
+ def buildReversedMin(self):
+ result = {}
+ for subtable in self.tables:
+ if subtable.isUnicode():
+ for codepoint, name in subtable.cmap.items():
+ if name in result:
+ result[name] = min(result[name], codepoint)
+ else:
+ result[name] = codepoint
+ return result
+
+ def decompile(self, data, ttFont):
+ tableVersion, numSubTables = struct.unpack(">HH", data[:4])
+ self.tableVersion = int(tableVersion)
+ self.tables = tables = []
+ seenOffsets = {}
+ for i in range(numSubTables):
+ platformID, platEncID, offset = struct.unpack(
+ ">HHl", data[4 + i * 8 : 4 + (i + 1) * 8]
+ )
+ platformID, platEncID = int(platformID), int(platEncID)
+ format, length = struct.unpack(">HH", data[offset : offset + 4])
+ if format in [8, 10, 12, 13]:
+ format, reserved, length = struct.unpack(
+ ">HHL", data[offset : offset + 8]
+ )
+ elif format in [14]:
+ format, length = struct.unpack(">HL", data[offset : offset + 6])
+
+ if not length:
+ log.error(
+ "cmap subtable is reported as having zero length: platformID %s, "
+ "platEncID %s, format %s offset %s. Skipping table.",
+ platformID,
+ platEncID,
+ format,
+ offset,
+ )
+ continue
+ table = CmapSubtable.newSubtable(format)
+ table.platformID = platformID
+ table.platEncID = platEncID
+ # Note that by default we decompile only the subtable header info;
+ # any other data gets decompiled only when an attribute of the
+ # subtable is referenced.
+ table.decompileHeader(data[offset : offset + int(length)], ttFont)
+ if offset in seenOffsets:
+ table.data = None # Mark as decompiled
+ table.cmap = tables[seenOffsets[offset]].cmap
+ else:
+ seenOffsets[offset] = i
+ tables.append(table)
+ if ttFont.lazy is False: # Be lazy for None and True
+ self.ensureDecompiled()
+
+ def ensureDecompiled(self, recurse=False):
+ # The recurse argument is unused, but part of the signature of
+ # ensureDecompiled across the library.
+ for st in self.tables:
+ st.ensureDecompiled()
+
+ def compile(self, ttFont):
+ self.tables.sort() # sort according to the spec; see CmapSubtable.__lt__()
+ numSubTables = len(self.tables)
+ totalOffset = 4 + 8 * numSubTables
+ data = struct.pack(">HH", self.tableVersion, numSubTables)
+ tableData = b""
+ seen = (
+ {}
+ ) # Some tables are the same object reference. Don't compile them twice.
+ done = (
+ {}
+ ) # Some tables are different objects, but compile to the same data chunk
+ for table in self.tables:
+ offset = seen.get(id(table.cmap))
+ if offset is None:
+ chunk = table.compile(ttFont)
+ offset = done.get(chunk)
+ if offset is None:
+ offset = seen[id(table.cmap)] = done[chunk] = totalOffset + len(
+ tableData
+ )
+ tableData = tableData + chunk
+ data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
+ return data + tableData
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("tableVersion", version=self.tableVersion)
+ writer.newline()
+ for table in self.tables:
+ table.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "tableVersion":
+ self.tableVersion = safeEval(attrs["version"])
+ return
+ if name[:12] != "cmap_format_":
+ return
+ if not hasattr(self, "tables"):
+ self.tables = []
+ format = safeEval(name[12:])
+ table = CmapSubtable.newSubtable(format)
+ table.platformID = safeEval(attrs["platformID"])
+ table.platEncID = safeEval(attrs["platEncID"])
+ table.fromXML(name, attrs, content, ttFont)
+ self.tables.append(table)
+
+
+class CmapSubtable(object):
+ """Base class for all cmap subtable formats.
+
+ Subclasses which handle the individual subtable formats are named
+ ``cmap_format_0``, ``cmap_format_2`` etc. Use :py:meth:`getSubtableClass`
+ to retrieve the concrete subclass, or :py:meth:`newSubtable` to get a
+ new subtable object for a given format.
+
+ The object exposes a ``.cmap`` attribute, which contains a dictionary mapping
+ character codepoints to glyph names.
+ """
+
+ @staticmethod
+ def getSubtableClass(format):
+ """Return the subtable class for a format."""
+ return cmap_classes.get(format, cmap_format_unknown)
+
+ @staticmethod
+ def newSubtable(format):
+ """Return a new instance of a subtable for the given format
+ ."""
+ subtableClass = CmapSubtable.getSubtableClass(format)
+ return subtableClass(format)
+
+ def __init__(self, format):
+ self.format = format
+ self.data = None
+ self.ttFont = None
+ self.platformID = None #: The platform ID of this subtable
+ self.platEncID = None #: The encoding ID of this subtable (interpretation depends on ``platformID``)
+ self.language = (
+ None #: The language ID of this subtable (Macintosh platform only)
+ )
+
+ def ensureDecompiled(self, recurse=False):
+ # The recurse argument is unused, but part of the signature of
+ # ensureDecompiled across the library.
+ if self.data is None:
+ return
+ self.decompile(None, None) # use saved data.
+ self.data = None # Once this table has been decompiled, make sure we don't
+ # just return the original data. Also avoids recursion when
+ # called with an attribute that the cmap subtable doesn't have.
+
+ def __getattr__(self, attr):
+ # allow lazy decompilation of subtables.
+ if attr[:2] == "__": # don't handle requests for member functions like '__lt__'
+ raise AttributeError(attr)
+ if self.data is None:
+ raise AttributeError(attr)
+ self.ensureDecompiled()
+ return getattr(self, attr)
+
+ def decompileHeader(self, data, ttFont):
+ format, length, language = struct.unpack(">HHH", data[:6])
+ assert (
+ len(data) == length
+ ), "corrupt cmap table format %d (data length: %d, header length: %d)" % (
+ format,
+ len(data),
+ length,
+ )
+ self.format = int(format)
+ self.length = int(length)
+ self.language = int(language)
+ self.data = data[6:]
+ self.ttFont = ttFont
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(
+ self.__class__.__name__,
+ [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("language", self.language),
+ ],
+ )
+ writer.newline()
+ codes = sorted(self.cmap.items())
+ self._writeCodes(codes, writer)
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def getEncoding(self, default=None):
+ """Returns the Python encoding name for this cmap subtable based on its platformID,
+ platEncID, and language. If encoding for these values is not known, by default
+ ``None`` is returned. That can be overridden by passing a value to the ``default``
+ argument.
+
+ Note that if you want to choose a "preferred" cmap subtable, most of the time
+ ``self.isUnicode()`` is what you want as that one only returns true for the modern,
+ commonly used, Unicode-compatible triplets, not the legacy ones.
+ """
+ return getEncoding(self.platformID, self.platEncID, self.language, default)
+
+ def isUnicode(self):
+ """Returns true if the characters are interpreted as Unicode codepoints."""
+ return self.platformID == 0 or (
+ self.platformID == 3 and self.platEncID in [0, 1, 10]
+ )
+
+ def isSymbol(self):
+ """Returns true if the subtable is for the Symbol encoding (3,0)"""
+ return self.platformID == 3 and self.platEncID == 0
+
+ def _writeCodes(self, codes, writer):
+ isUnicode = self.isUnicode()
+ for code, name in codes:
+ writer.simpletag("map", code=hex(code), name=name)
+ if isUnicode:
+ writer.comment(Unicode[code])
+ writer.newline()
+
+ def __lt__(self, other):
+ if not isinstance(other, CmapSubtable):
+ return NotImplemented
+
+ # implemented so that list.sort() sorts according to the spec.
+ selfTuple = (
+ getattr(self, "platformID", None),
+ getattr(self, "platEncID", None),
+ getattr(self, "language", None),
+ self.__dict__,
+ )
+ otherTuple = (
+ getattr(other, "platformID", None),
+ getattr(other, "platEncID", None),
+ getattr(other, "language", None),
+ other.__dict__,
+ )
+ return selfTuple < otherTuple
+
+
+class cmap_format_0(CmapSubtable):
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+ data = (
+ self.data
+ ) # decompileHeader assigns the data after the header to self.data
+ assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
+ gids = array.array("B")
+ gids.frombytes(self.data)
+ charCodes = range(len(gids))
+ self.cmap = _make_map(self.ttFont, charCodes, gids)
+
+ def compile(self, ttFont):
+ if self.data:
+ return struct.pack(">HHH", 0, 262, self.language) + self.data
+
+ cmap = self.cmap
+ assert set(cmap.keys()).issubset(range(256))
+ getGlyphID = ttFont.getGlyphID
+ valueList = [getGlyphID(cmap[i]) if i in cmap else 0 for i in range(256)]
+
+ gids = array.array("B", valueList)
+ data = struct.pack(">HHH", 0, 262, self.language) + gids.tobytes()
+ assert len(data) == 262
+ return data
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.language = safeEval(attrs["language"])
+ if not hasattr(self, "cmap"):
+ self.cmap = {}
+ cmap = self.cmap
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "map":
+ continue
+ cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+subHeaderFormat = ">HHhH"
+
+
+class SubHeader(object):
+ def __init__(self):
+ self.firstCode = None
+ self.entryCount = None
+ self.idDelta = None
+ self.idRangeOffset = None
+ self.glyphIndexArray = []
+
+
+class cmap_format_2(CmapSubtable):
+ def setIDDelta(self, subHeader):
+ subHeader.idDelta = 0
+ # find the minGI which is not zero.
+ minGI = subHeader.glyphIndexArray[0]
+ for gid in subHeader.glyphIndexArray:
+ if (gid != 0) and (gid < minGI):
+ minGI = gid
+ # The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
+ # idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K.
+ # We would like to pick an idDelta such that the first glyphArray GID is 1,
+ # so that we are more likely to be able to combine glypharray GID subranges.
+ # This means that we have a problem when minGI is > 32K
+ # Since the final gi is reconstructed from the glyphArray GID by:
+ # (short)finalGID = (gid + idDelta) % 0x10000),
+ # we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
+ # negative number to an unsigned short.
+
+ if minGI > 1:
+ if minGI > 0x7FFF:
+ subHeader.idDelta = -(0x10000 - minGI) - 1
+ else:
+ subHeader.idDelta = minGI - 1
+ idDelta = subHeader.idDelta
+ for i in range(subHeader.entryCount):
+ gid = subHeader.glyphIndexArray[i]
+ if gid > 0:
+ subHeader.glyphIndexArray[i] = gid - idDelta
+
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+
+ data = (
+ self.data
+ ) # decompileHeader assigns the data after the header to self.data
+ subHeaderKeys = []
+ maxSubHeaderindex = 0
+ # get the key array, and determine the number of subHeaders.
+ allKeys = array.array("H")
+ allKeys.frombytes(data[:512])
+ data = data[512:]
+ if sys.byteorder != "big":
+ allKeys.byteswap()
+ subHeaderKeys = [key // 8 for key in allKeys]
+ maxSubHeaderindex = max(subHeaderKeys)
+
+ # Load subHeaders
+ subHeaderList = []
+ pos = 0
+ for i in range(maxSubHeaderindex + 1):
+ subHeader = SubHeader()
+ (
+ subHeader.firstCode,
+ subHeader.entryCount,
+ subHeader.idDelta,
+ subHeader.idRangeOffset,
+ ) = struct.unpack(subHeaderFormat, data[pos : pos + 8])
+ pos += 8
+ giDataPos = pos + subHeader.idRangeOffset - 2
+ giList = array.array("H")
+ giList.frombytes(data[giDataPos : giDataPos + subHeader.entryCount * 2])
+ if sys.byteorder != "big":
+ giList.byteswap()
+ subHeader.glyphIndexArray = giList
+ subHeaderList.append(subHeader)
+ # How this gets processed.
+ # Charcodes may be one or two bytes.
+ # The first byte of a charcode is mapped through the subHeaderKeys, to select
+ # a subHeader. For any subheader but 0, the next byte is then mapped through the
+ # selected subheader. If subheader Index 0 is selected, then the byte itself is
+ # mapped through the subheader, and there is no second byte.
+ # Then assume that the subsequent byte is the first byte of the next charcode,and repeat.
+ #
+ # Each subheader references a range in the glyphIndexArray whose length is entryCount.
+ # The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray
+ # referenced by another subheader.
+ # The only subheader that will be referenced by more than one first-byte value is the subheader
+ # that maps the entire range of glyphID values to glyphIndex 0, e.g notdef:
+ # {firstChar 0, EntryCount 0,idDelta 0,idRangeOffset xx}
+ # A byte being mapped though a subheader is treated as in index into a mapping of array index to font glyphIndex.
+ # A subheader specifies a subrange within (0...256) by the
+ # firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero
+ # (e.g. glyph not in font).
+ # If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar).
+ # The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by
+ # counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the
+ # glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex.
+ # Example for Logocut-Medium
+ # first byte of charcode = 129; selects subheader 1.
+ # subheader 1 = {firstChar 64, EntryCount 108,idDelta 42,idRangeOffset 0252}
+ # second byte of charCode = 66
+ # the index offset = 66-64 = 2.
+ # The subrange of the glyphIndexArray starting at 0x0252 bytes from the idRangeOffset word is:
+ # [glyphIndexArray index], [subrange array index] = glyphIndex
+ # [256], [0]=1 from charcode [129, 64]
+ # [257], [1]=2 from charcode [129, 65]
+ # [258], [2]=3 from charcode [129, 66]
+ # [259], [3]=4 from charcode [129, 67]
+ # So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero,
+ # add it to the glyphID to get the final glyphIndex
+ # value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
+
+ self.data = b""
+ cmap = {}
+ notdefGI = 0
+ for firstByte in range(256):
+ subHeadindex = subHeaderKeys[firstByte]
+ subHeader = subHeaderList[subHeadindex]
+ if subHeadindex == 0:
+ if (firstByte < subHeader.firstCode) or (
+ firstByte >= subHeader.firstCode + subHeader.entryCount
+ ):
+ continue # gi is notdef.
+ else:
+ charCode = firstByte
+ offsetIndex = firstByte - subHeader.firstCode
+ gi = subHeader.glyphIndexArray[offsetIndex]
+ if gi != 0:
+ gi = (gi + subHeader.idDelta) % 0x10000
+ else:
+ continue # gi is notdef.
+ cmap[charCode] = gi
+ else:
+ if subHeader.entryCount:
+ charCodeOffset = firstByte * 256 + subHeader.firstCode
+ for offsetIndex in range(subHeader.entryCount):
+ charCode = charCodeOffset + offsetIndex
+ gi = subHeader.glyphIndexArray[offsetIndex]
+ if gi != 0:
+ gi = (gi + subHeader.idDelta) % 0x10000
+ else:
+ continue
+ cmap[charCode] = gi
+ # If not subHeader.entryCount, then all char codes with this first byte are
+ # mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the
+ # same as mapping it to .notdef.
+
+ gids = list(cmap.values())
+ charCodes = list(cmap.keys())
+ self.cmap = _make_map(self.ttFont, charCodes, gids)
+
+ def compile(self, ttFont):
+ if self.data:
+ return (
+ struct.pack(">HHH", self.format, self.length, self.language) + self.data
+ )
+ kEmptyTwoCharCodeRange = -1
+ notdefGI = 0
+
+ items = sorted(self.cmap.items())
+ charCodes = [item[0] for item in items]
+ names = [item[1] for item in items]
+ nameMap = ttFont.getReverseGlyphMap()
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ # allow virtual GIDs in format 2 tables
+ gids = []
+ for name in names:
+ try:
+ gid = nameMap[name]
+ except KeyError:
+ try:
+ if name[:3] == "gid":
+ gid = int(name[3:])
+ else:
+ gid = ttFont.getGlyphID(name)
+ except:
+ raise KeyError(name)
+
+ gids.append(gid)
+
+ # Process the (char code to gid) item list in char code order.
+ # By definition, all one byte char codes map to subheader 0.
+ # For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
+ # which defines all char codes in its range to map to notdef) unless proven otherwise.
+ # Note that since the char code items are processed in char code order, all the char codes with the
+ # same first byte are in sequential order.
+
+ subHeaderKeys = [
+ kEmptyTwoCharCodeRange for x in range(256)
+ ] # list of indices into subHeaderList.
+ subHeaderList = []
+
+ # We force this subheader entry 0 to exist in the subHeaderList in the case where some one comes up
+ # with a cmap where all the one byte char codes map to notdef,
+ # with the result that the subhead 0 would not get created just by processing the item list.
+ charCode = charCodes[0]
+ if charCode > 255:
+ subHeader = SubHeader()
+ subHeader.firstCode = 0
+ subHeader.entryCount = 0
+ subHeader.idDelta = 0
+ subHeader.idRangeOffset = 0
+ subHeaderList.append(subHeader)
+
+ lastFirstByte = -1
+ items = zip(charCodes, gids)
+ for charCode, gid in items:
+ if gid == 0:
+ continue
+ firstbyte = charCode >> 8
+ secondByte = charCode & 0x00FF
+
+ if (
+ firstbyte != lastFirstByte
+ ): # Need to update the current subhead, and start a new one.
+ if lastFirstByte > -1:
+ # fix GI's and iDelta of current subheader.
+ self.setIDDelta(subHeader)
+
+ # If it was sunheader 0 for one-byte charCodes, then we need to set the subHeaderKeys value to zero
+ # for the indices matching the char codes.
+ if lastFirstByte == 0:
+ for index in range(subHeader.entryCount):
+ charCode = subHeader.firstCode + index
+ subHeaderKeys[charCode] = 0
+
+ assert subHeader.entryCount == len(
+ subHeader.glyphIndexArray
+ ), "Error - subhead entry count does not match len of glyphID subrange."
+ # init new subheader
+ subHeader = SubHeader()
+ subHeader.firstCode = secondByte
+ subHeader.entryCount = 1
+ subHeader.glyphIndexArray.append(gid)
+ subHeaderList.append(subHeader)
+ subHeaderKeys[firstbyte] = len(subHeaderList) - 1
+ lastFirstByte = firstbyte
+ else:
+ # need to fill in with notdefs all the code points between the last charCode and the current charCode.
+ codeDiff = secondByte - (subHeader.firstCode + subHeader.entryCount)
+ for i in range(codeDiff):
+ subHeader.glyphIndexArray.append(notdefGI)
+ subHeader.glyphIndexArray.append(gid)
+ subHeader.entryCount = subHeader.entryCount + codeDiff + 1
+
+ # fix GI's and iDelta of last subheader that we we added to the subheader array.
+ self.setIDDelta(subHeader)
+
+ # Now we add a final subheader for the subHeaderKeys which maps to empty two byte charcode ranges.
+ subHeader = SubHeader()
+ subHeader.firstCode = 0
+ subHeader.entryCount = 0
+ subHeader.idDelta = 0
+ subHeader.idRangeOffset = 2
+ subHeaderList.append(subHeader)
+ emptySubheadIndex = len(subHeaderList) - 1
+ for index in range(256):
+ if subHeaderKeys[index] == kEmptyTwoCharCodeRange:
+ subHeaderKeys[index] = emptySubheadIndex
+ # Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the
+ # idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray,
+ # since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with
+ # charcode 0 and GID 0.
+
+ idRangeOffset = (
+ len(subHeaderList) - 1
+ ) * 8 + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
+ subheadRangeLen = (
+ len(subHeaderList) - 1
+ ) # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2.
+ for index in range(subheadRangeLen):
+ subHeader = subHeaderList[index]
+ subHeader.idRangeOffset = 0
+ for j in range(index):
+ prevSubhead = subHeaderList[j]
+ if (
+ prevSubhead.glyphIndexArray == subHeader.glyphIndexArray
+ ): # use the glyphIndexArray subarray
+ subHeader.idRangeOffset = (
+ prevSubhead.idRangeOffset - (index - j) * 8
+ )
+ subHeader.glyphIndexArray = []
+ break
+ if subHeader.idRangeOffset == 0: # didn't find one.
+ subHeader.idRangeOffset = idRangeOffset
+ idRangeOffset = (
+ idRangeOffset - 8
+ ) + subHeader.entryCount * 2 # one less subheader, one more subArray.
+ else:
+ idRangeOffset = idRangeOffset - 8 # one less subheader
+
+ # Now we can write out the data!
+ length = (
+ 6 + 512 + 8 * len(subHeaderList)
+ ) # header, 256 subHeaderKeys, and subheader array.
+ for subhead in subHeaderList[:-1]:
+ length = (
+ length + len(subhead.glyphIndexArray) * 2
+ ) # We can't use subhead.entryCount, as some of the subhead may share subArrays.
+ dataList = [struct.pack(">HHH", 2, length, self.language)]
+ for index in subHeaderKeys:
+ dataList.append(struct.pack(">H", index * 8))
+ for subhead in subHeaderList:
+ dataList.append(
+ struct.pack(
+ subHeaderFormat,
+ subhead.firstCode,
+ subhead.entryCount,
+ subhead.idDelta,
+ subhead.idRangeOffset,
+ )
+ )
+ for subhead in subHeaderList[:-1]:
+ for gi in subhead.glyphIndexArray:
+ dataList.append(struct.pack(">H", gi))
+ data = bytesjoin(dataList)
+ assert len(data) == length, (
+ "Error: cmap format 2 is not same length as calculated! actual: "
+ + str(len(data))
+ + " calc : "
+ + str(length)
+ )
+ return data
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.language = safeEval(attrs["language"])
+ if not hasattr(self, "cmap"):
+ self.cmap = {}
+ cmap = self.cmap
+
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "map":
+ continue
+ cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+cmap_format_4_format = ">7H"
+
+# uint16 endCode[segCount] # Ending character code for each segment, last = 0xFFFF.
+# uint16 reservedPad # This value should be zero
+# uint16 startCode[segCount] # Starting character code for each segment
+# uint16 idDelta[segCount] # Delta for all character codes in segment
+# uint16 idRangeOffset[segCount] # Offset in bytes to glyph indexArray, or 0
+# uint16 glyphIndexArray[variable] # Glyph index array
+
+
+def splitRange(startCode, endCode, cmap):
+ # Try to split a range of character codes into subranges with consecutive
+ # glyph IDs in such a way that the cmap4 subtable can be stored "most"
+ # efficiently. I can't prove I've got the optimal solution, but it seems
+ # to do well with the fonts I tested: none became bigger, many became smaller.
+ if startCode == endCode:
+ return [], [endCode]
+
+ lastID = cmap[startCode]
+ lastCode = startCode
+ inOrder = None
+ orderedBegin = None
+ subRanges = []
+
+ # Gather subranges in which the glyph IDs are consecutive.
+ for code in range(startCode + 1, endCode + 1):
+ glyphID = cmap[code]
+
+ if glyphID - 1 == lastID:
+ if inOrder is None or not inOrder:
+ inOrder = 1
+ orderedBegin = lastCode
+ else:
+ if inOrder:
+ inOrder = 0
+ subRanges.append((orderedBegin, lastCode))
+ orderedBegin = None
+
+ lastID = glyphID
+ lastCode = code
+
+ if inOrder:
+ subRanges.append((orderedBegin, lastCode))
+ assert lastCode == endCode
+
+ # Now filter out those new subranges that would only make the data bigger.
+ # A new segment cost 8 bytes, not using a new segment costs 2 bytes per
+ # character.
+ newRanges = []
+ for b, e in subRanges:
+ if b == startCode and e == endCode:
+ break # the whole range, we're fine
+ if b == startCode or e == endCode:
+ threshold = 4 # split costs one more segment
+ else:
+ threshold = 8 # split costs two more segments
+ if (e - b + 1) > threshold:
+ newRanges.append((b, e))
+ subRanges = newRanges
+
+ if not subRanges:
+ return [], [endCode]
+
+ if subRanges[0][0] != startCode:
+ subRanges.insert(0, (startCode, subRanges[0][0] - 1))
+ if subRanges[-1][1] != endCode:
+ subRanges.append((subRanges[-1][1] + 1, endCode))
+
+ # Fill the "holes" in the segments list -- those are the segments in which
+ # the glyph IDs are _not_ consecutive.
+ i = 1
+ while i < len(subRanges):
+ if subRanges[i - 1][1] + 1 != subRanges[i][0]:
+ subRanges.insert(i, (subRanges[i - 1][1] + 1, subRanges[i][0] - 1))
+ i = i + 1
+ i = i + 1
+
+ # Transform the ranges into startCode/endCode lists.
+ start = []
+ end = []
+ for b, e in subRanges:
+ start.append(b)
+ end.append(e)
+ start.pop(0)
+
+ assert len(start) + 1 == len(end)
+ return start, end
+
+
+class cmap_format_4(CmapSubtable):
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+
+ data = (
+ self.data
+ ) # decompileHeader assigns the data after the header to self.data
+ (segCountX2, searchRange, entrySelector, rangeShift) = struct.unpack(
+ ">4H", data[:8]
+ )
+ data = data[8:]
+ segCount = segCountX2 // 2
+
+ allCodes = array.array("H")
+ allCodes.frombytes(data)
+ self.data = data = None
+
+ if sys.byteorder != "big":
+ allCodes.byteswap()
+
+ # divide the data
+ endCode = allCodes[:segCount]
+ allCodes = allCodes[segCount + 1 :] # the +1 is skipping the reservedPad field
+ startCode = allCodes[:segCount]
+ allCodes = allCodes[segCount:]
+ idDelta = allCodes[:segCount]
+ allCodes = allCodes[segCount:]
+ idRangeOffset = allCodes[:segCount]
+ glyphIndexArray = allCodes[segCount:]
+ lenGIArray = len(glyphIndexArray)
+
+ # build 2-byte character mapping
+ charCodes = []
+ gids = []
+ for i in range(len(startCode) - 1): # don't do 0xffff!
+ start = startCode[i]
+ delta = idDelta[i]
+ rangeOffset = idRangeOffset[i]
+ partial = rangeOffset // 2 - start + i - len(idRangeOffset)
+
+ rangeCharCodes = list(range(startCode[i], endCode[i] + 1))
+ charCodes.extend(rangeCharCodes)
+ if rangeOffset == 0:
+ gids.extend(
+ [(charCode + delta) & 0xFFFF for charCode in rangeCharCodes]
+ )
+ else:
+ for charCode in rangeCharCodes:
+ index = charCode + partial
+ assert index < lenGIArray, (
+ "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array is not less than the length of the array (%d) !"
+ % (i, index, lenGIArray)
+ )
+ if glyphIndexArray[index] != 0: # if not missing glyph
+ glyphID = glyphIndexArray[index] + delta
+ else:
+ glyphID = 0 # missing glyph
+ gids.append(glyphID & 0xFFFF)
+
+ self.cmap = _make_map(self.ttFont, charCodes, gids)
+
+ def compile(self, ttFont):
+ if self.data:
+ return (
+ struct.pack(">HHH", self.format, self.length, self.language) + self.data
+ )
+
+ charCodes = list(self.cmap.keys())
+ if not charCodes:
+ startCode = [0xFFFF]
+ endCode = [0xFFFF]
+ else:
+ charCodes.sort()
+ names = [self.cmap[code] for code in charCodes]
+ nameMap = ttFont.getReverseGlyphMap()
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ # allow virtual GIDs in format 4 tables
+ gids = []
+ for name in names:
+ try:
+ gid = nameMap[name]
+ except KeyError:
+ try:
+ if name[:3] == "gid":
+ gid = int(name[3:])
+ else:
+ gid = ttFont.getGlyphID(name)
+ except:
+ raise KeyError(name)
+
+ gids.append(gid)
+ cmap = {} # code:glyphID mapping
+ for code, gid in zip(charCodes, gids):
+ cmap[code] = gid
+
+ # Build startCode and endCode lists.
+ # Split the char codes in ranges of consecutive char codes, then split
+ # each range in more ranges of consecutive/not consecutive glyph IDs.
+ # See splitRange().
+ lastCode = charCodes[0]
+ endCode = []
+ startCode = [lastCode]
+ for charCode in charCodes[
+ 1:
+ ]: # skip the first code, it's the first start code
+ if charCode == lastCode + 1:
+ lastCode = charCode
+ continue
+ start, end = splitRange(startCode[-1], lastCode, cmap)
+ startCode.extend(start)
+ endCode.extend(end)
+ startCode.append(charCode)
+ lastCode = charCode
+ start, end = splitRange(startCode[-1], lastCode, cmap)
+ startCode.extend(start)
+ endCode.extend(end)
+ startCode.append(0xFFFF)
+ endCode.append(0xFFFF)
+
+ # build up rest of cruft
+ idDelta = []
+ idRangeOffset = []
+ glyphIndexArray = []
+ for i in range(len(endCode) - 1): # skip the closing codes (0xffff)
+ indices = []
+ for charCode in range(startCode[i], endCode[i] + 1):
+ indices.append(cmap[charCode])
+ if indices == list(range(indices[0], indices[0] + len(indices))):
+ idDelta.append((indices[0] - startCode[i]) % 0x10000)
+ idRangeOffset.append(0)
+ else:
+ idDelta.append(0)
+ idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i))
+ glyphIndexArray.extend(indices)
+ idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
+ idRangeOffset.append(0)
+
+ # Insane.
+ segCount = len(endCode)
+ segCountX2 = segCount * 2
+ searchRange, entrySelector, rangeShift = getSearchRange(segCount, 2)
+
+ charCodeArray = array.array("H", endCode + [0] + startCode)
+ idDeltaArray = array.array("H", idDelta)
+ restArray = array.array("H", idRangeOffset + glyphIndexArray)
+ if sys.byteorder != "big":
+ charCodeArray.byteswap()
+ if sys.byteorder != "big":
+ idDeltaArray.byteswap()
+ if sys.byteorder != "big":
+ restArray.byteswap()
+ data = charCodeArray.tobytes() + idDeltaArray.tobytes() + restArray.tobytes()
+
+ length = struct.calcsize(cmap_format_4_format) + len(data)
+ header = struct.pack(
+ cmap_format_4_format,
+ self.format,
+ length,
+ self.language,
+ segCountX2,
+ searchRange,
+ entrySelector,
+ rangeShift,
+ )
+ return header + data
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.language = safeEval(attrs["language"])
+ if not hasattr(self, "cmap"):
+ self.cmap = {}
+ cmap = self.cmap
+
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ nameMap, attrsMap, dummyContent = element
+ if nameMap != "map":
+ assert 0, "Unrecognized keyword in cmap subtable"
+ cmap[safeEval(attrsMap["code"])] = attrsMap["name"]
+
+
+class cmap_format_6(CmapSubtable):
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+
+ data = (
+ self.data
+ ) # decompileHeader assigns the data after the header to self.data
+ firstCode, entryCount = struct.unpack(">HH", data[:4])
+ firstCode = int(firstCode)
+ data = data[4:]
+ # assert len(data) == 2 * entryCount # XXX not true in Apple's Helvetica!!!
+ gids = array.array("H")
+ gids.frombytes(data[: 2 * int(entryCount)])
+ if sys.byteorder != "big":
+ gids.byteswap()
+ self.data = data = None
+
+ charCodes = list(range(firstCode, firstCode + len(gids)))
+ self.cmap = _make_map(self.ttFont, charCodes, gids)
+
+ def compile(self, ttFont):
+ if self.data:
+ return (
+ struct.pack(">HHH", self.format, self.length, self.language) + self.data
+ )
+ cmap = self.cmap
+ codes = sorted(cmap.keys())
+ if codes: # yes, there are empty cmap tables.
+ codes = list(range(codes[0], codes[-1] + 1))
+ firstCode = codes[0]
+ valueList = [
+ ttFont.getGlyphID(cmap[code]) if code in cmap else 0 for code in codes
+ ]
+ gids = array.array("H", valueList)
+ if sys.byteorder != "big":
+ gids.byteswap()
+ data = gids.tobytes()
+ else:
+ data = b""
+ firstCode = 0
+ header = struct.pack(
+ ">HHHHH", 6, len(data) + 10, self.language, firstCode, len(codes)
+ )
+ return header + data
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.language = safeEval(attrs["language"])
+ if not hasattr(self, "cmap"):
+ self.cmap = {}
+ cmap = self.cmap
+
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "map":
+ continue
+ cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_12_or_13(CmapSubtable):
+ def __init__(self, format):
+ self.format = format
+ self.reserved = 0
+ self.data = None
+ self.ttFont = None
+
+ def decompileHeader(self, data, ttFont):
+ format, reserved, length, language, nGroups = struct.unpack(">HHLLL", data[:16])
+ assert (
+ len(data) == (16 + nGroups * 12) == (length)
+ ), "corrupt cmap table format %d (data length: %d, header length: %d)" % (
+ self.format,
+ len(data),
+ length,
+ )
+ self.format = format
+ self.reserved = reserved
+ self.length = length
+ self.language = language
+ self.nGroups = nGroups
+ self.data = data[16:]
+ self.ttFont = ttFont
+
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+
+ data = (
+ self.data
+ ) # decompileHeader assigns the data after the header to self.data
+ charCodes = []
+ gids = []
+ pos = 0
+ groups = array.array("I", data[: self.nGroups * 12])
+ if sys.byteorder != "big":
+ groups.byteswap()
+ for i in range(self.nGroups):
+ startCharCode = groups[i * 3]
+ endCharCode = groups[i * 3 + 1]
+ glyphID = groups[i * 3 + 2]
+ lenGroup = 1 + endCharCode - startCharCode
+ charCodes.extend(range(startCharCode, endCharCode + 1))
+ gids.extend(self._computeGIDs(glyphID, lenGroup))
+ self.data = data = None
+ self.cmap = _make_map(self.ttFont, charCodes, gids)
+
+ def compile(self, ttFont):
+ if self.data:
+ return (
+ struct.pack(
+ ">HHLLL",
+ self.format,
+ self.reserved,
+ self.length,
+ self.language,
+ self.nGroups,
+ )
+ + self.data
+ )
+ charCodes = list(self.cmap.keys())
+ names = list(self.cmap.values())
+ nameMap = ttFont.getReverseGlyphMap()
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
+ try:
+ gids = [nameMap[name] for name in names]
+ except KeyError:
+ # allow virtual GIDs in format 12 tables
+ gids = []
+ for name in names:
+ try:
+ gid = nameMap[name]
+ except KeyError:
+ try:
+ if name[:3] == "gid":
+ gid = int(name[3:])
+ else:
+ gid = ttFont.getGlyphID(name)
+ except:
+ raise KeyError(name)
+
+ gids.append(gid)
+
+ cmap = {} # code:glyphID mapping
+ for code, gid in zip(charCodes, gids):
+ cmap[code] = gid
+
+ charCodes.sort()
+ index = 0
+ startCharCode = charCodes[0]
+ startGlyphID = cmap[startCharCode]
+ lastGlyphID = startGlyphID - self._format_step
+ lastCharCode = startCharCode - 1
+ nGroups = 0
+ dataList = []
+ maxIndex = len(charCodes)
+ for index in range(maxIndex):
+ charCode = charCodes[index]
+ glyphID = cmap[charCode]
+ if not self._IsInSameRun(glyphID, lastGlyphID, charCode, lastCharCode):
+ dataList.append(
+ struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID)
+ )
+ startCharCode = charCode
+ startGlyphID = glyphID
+ nGroups = nGroups + 1
+ lastGlyphID = glyphID
+ lastCharCode = charCode
+ dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
+ nGroups = nGroups + 1
+ data = bytesjoin(dataList)
+ lengthSubtable = len(data) + 16
+ assert len(data) == (nGroups * 12) == (lengthSubtable - 16)
+ return (
+ struct.pack(
+ ">HHLLL",
+ self.format,
+ self.reserved,
+ lengthSubtable,
+ self.language,
+ nGroups,
+ )
+ + data
+ )
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(
+ self.__class__.__name__,
+ [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("format", self.format),
+ ("reserved", self.reserved),
+ ("length", self.length),
+ ("language", self.language),
+ ("nGroups", self.nGroups),
+ ],
+ )
+ writer.newline()
+ codes = sorted(self.cmap.items())
+ self._writeCodes(codes, writer)
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.format = safeEval(attrs["format"])
+ self.reserved = safeEval(attrs["reserved"])
+ self.length = safeEval(attrs["length"])
+ self.language = safeEval(attrs["language"])
+ self.nGroups = safeEval(attrs["nGroups"])
+ if not hasattr(self, "cmap"):
+ self.cmap = {}
+ cmap = self.cmap
+
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "map":
+ continue
+ cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_12(cmap_format_12_or_13):
+ _format_step = 1
+
+ def __init__(self, format=12):
+ cmap_format_12_or_13.__init__(self, format)
+
+ def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+ return range(startingGlyph, startingGlyph + numberOfGlyphs)
+
+ def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+ return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
+class cmap_format_13(cmap_format_12_or_13):
+ _format_step = 0
+
+ def __init__(self, format=13):
+ cmap_format_12_or_13.__init__(self, format)
+
+ def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+ return [startingGlyph] * numberOfGlyphs
+
+ def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+ return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
+def cvtToUVS(threeByteString):
+ data = b"\0" + threeByteString
+ (val,) = struct.unpack(">L", data)
+ return val
+
+
+def cvtFromUVS(val):
+ assert 0 <= val < 0x1000000
+ fourByteString = struct.pack(">L", val)
+ return fourByteString[1:]
+
+
+class cmap_format_14(CmapSubtable):
+ def decompileHeader(self, data, ttFont):
+ format, length, numVarSelectorRecords = struct.unpack(">HLL", data[:10])
+ self.data = data[10:]
+ self.length = length
+ self.numVarSelectorRecords = numVarSelectorRecords
+ self.ttFont = ttFont
+ self.language = 0xFF # has no language.
+
+ def decompile(self, data, ttFont):
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+ data = self.data
+
+ self.cmap = (
+ {}
+ ) # so that clients that expect this to exist in a cmap table won't fail.
+ uvsDict = {}
+ recOffset = 0
+ for n in range(self.numVarSelectorRecords):
+ uvs, defOVSOffset, nonDefUVSOffset = struct.unpack(
+ ">3sLL", data[recOffset : recOffset + 11]
+ )
+ recOffset += 11
+ varUVS = cvtToUVS(uvs)
+ if defOVSOffset:
+ startOffset = defOVSOffset - 10
+ (numValues,) = struct.unpack(">L", data[startOffset : startOffset + 4])
+ startOffset += 4
+ for r in range(numValues):
+ uv, addtlCnt = struct.unpack(
+ ">3sB", data[startOffset : startOffset + 4]
+ )
+ startOffset += 4
+ firstBaseUV = cvtToUVS(uv)
+ cnt = addtlCnt + 1
+ baseUVList = list(range(firstBaseUV, firstBaseUV + cnt))
+ glyphList = [None] * cnt
+ localUVList = zip(baseUVList, glyphList)
+ try:
+ uvsDict[varUVS].extend(localUVList)
+ except KeyError:
+ uvsDict[varUVS] = list(localUVList)
+
+ if nonDefUVSOffset:
+ startOffset = nonDefUVSOffset - 10
+ (numRecs,) = struct.unpack(">L", data[startOffset : startOffset + 4])
+ startOffset += 4
+ localUVList = []
+ for r in range(numRecs):
+ uv, gid = struct.unpack(">3sH", data[startOffset : startOffset + 5])
+ startOffset += 5
+ uv = cvtToUVS(uv)
+ glyphName = self.ttFont.getGlyphName(gid)
+ localUVList.append((uv, glyphName))
+ try:
+ uvsDict[varUVS].extend(localUVList)
+ except KeyError:
+ uvsDict[varUVS] = localUVList
+
+ self.uvsDict = uvsDict
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(
+ self.__class__.__name__,
+ [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ],
+ )
+ writer.newline()
+ uvsDict = self.uvsDict
+ uvsList = sorted(uvsDict.keys())
+ for uvs in uvsList:
+ uvList = uvsDict[uvs]
+ uvList.sort(key=lambda item: (item[1] is not None, item[0], item[1]))
+ for uv, gname in uvList:
+ attrs = [("uv", hex(uv)), ("uvs", hex(uvs))]
+ if gname is not None:
+ attrs.append(("name", gname))
+ writer.simpletag("map", attrs)
+ writer.newline()
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.language = 0xFF # provide a value so that CmapSubtable.__lt__() won't fail
+ if not hasattr(self, "cmap"):
+ self.cmap = (
+ {}
+ ) # so that clients that expect this to exist in a cmap table won't fail.
+ if not hasattr(self, "uvsDict"):
+ self.uvsDict = {}
+ uvsDict = self.uvsDict
+
+ # For backwards compatibility reasons we accept "None" as an indicator
+ # for "default mapping", unless the font actually has a glyph named
+ # "None".
+ _hasGlyphNamedNone = None
+
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "map":
+ continue
+ uvs = safeEval(attrs["uvs"])
+ uv = safeEval(attrs["uv"])
+ gname = attrs.get("name")
+ if gname == "None":
+ if _hasGlyphNamedNone is None:
+ _hasGlyphNamedNone = "None" in ttFont.getGlyphOrder()
+ if not _hasGlyphNamedNone:
+ gname = None
+ try:
+ uvsDict[uvs].append((uv, gname))
+ except KeyError:
+ uvsDict[uvs] = [(uv, gname)]
+
+ def compile(self, ttFont):
+ if self.data:
+ return (
+ struct.pack(
+ ">HLL", self.format, self.length, self.numVarSelectorRecords
+ )
+ + self.data
+ )
+
+ uvsDict = self.uvsDict
+ uvsList = sorted(uvsDict.keys())
+ self.numVarSelectorRecords = len(uvsList)
+ offset = (
+ 10 + self.numVarSelectorRecords * 11
+ ) # current value is end of VarSelectorRecords block.
+ data = []
+ varSelectorRecords = []
+ for uvs in uvsList:
+ entryList = uvsDict[uvs]
+
+ defList = [entry for entry in entryList if entry[1] is None]
+ if defList:
+ defList = [entry[0] for entry in defList]
+ defOVSOffset = offset
+ defList.sort()
+
+ lastUV = defList[0]
+ cnt = -1
+ defRecs = []
+ for defEntry in defList:
+ cnt += 1
+ if (lastUV + cnt) != defEntry:
+ rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt - 1)
+ lastUV = defEntry
+ defRecs.append(rec)
+ cnt = 0
+
+ rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt)
+ defRecs.append(rec)
+
+ numDefRecs = len(defRecs)
+ data.append(struct.pack(">L", numDefRecs))
+ data.extend(defRecs)
+ offset += 4 + numDefRecs * 4
+ else:
+ defOVSOffset = 0
+
+ ndefList = [entry for entry in entryList if entry[1] is not None]
+ if ndefList:
+ nonDefUVSOffset = offset
+ ndefList.sort()
+ numNonDefRecs = len(ndefList)
+ data.append(struct.pack(">L", numNonDefRecs))
+ offset += 4 + numNonDefRecs * 5
+
+ for uv, gname in ndefList:
+ gid = ttFont.getGlyphID(gname)
+ ndrec = struct.pack(">3sH", cvtFromUVS(uv), gid)
+ data.append(ndrec)
+ else:
+ nonDefUVSOffset = 0
+
+ vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
+ varSelectorRecords.append(vrec)
+
+ data = bytesjoin(varSelectorRecords) + bytesjoin(data)
+ self.length = 10 + len(data)
+ headerdata = struct.pack(
+ ">HLL", self.format, self.length, self.numVarSelectorRecords
+ )
+
+ return headerdata + data
+
+
+class cmap_format_unknown(CmapSubtable):
+ def toXML(self, writer, ttFont):
+ cmapName = self.__class__.__name__[:12] + str(self.format)
+ writer.begintag(
+ cmapName,
+ [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ],
+ )
+ writer.newline()
+ writer.dumphex(self.data)
+ writer.endtag(cmapName)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.data = readHex(content)
+ self.cmap = {}
+
+ def decompileHeader(self, data, ttFont):
+ self.language = 0 # dummy value
+ self.data = data
+
+ def decompile(self, data, ttFont):
+ # we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
+ # If not, someone is calling the subtable decompile() directly, and must provide both args.
+ if data is not None and ttFont is not None:
+ self.decompileHeader(data, ttFont)
+ else:
+ assert (
+ data is None and ttFont is None
+ ), "Need both data and ttFont arguments"
+
+ def compile(self, ttFont):
+ if self.data:
+ return self.data
+ else:
+ return None
+
+
+cmap_classes = {
+ 0: cmap_format_0,
+ 2: cmap_format_2,
+ 4: cmap_format_4,
+ 6: cmap_format_6,
+ 12: cmap_format_12,
+ 13: cmap_format_13,
+ 14: cmap_format_14,
+}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_a_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_a_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..872710c8f0c93ade86e8be8640205a91b7b397af
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_a_r.py
@@ -0,0 +1,94 @@
+from . import DefaultTable
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytesjoin
+from fontTools.ttLib.tables.TupleVariation import (
+ compileTupleVariationStore,
+ decompileTupleVariationStore,
+ TupleVariation,
+)
+
+
+# https://www.microsoft.com/typography/otspec/cvar.htm
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cvar.html
+
+CVAR_HEADER_FORMAT = """
+ > # big endian
+ majorVersion: H
+ minorVersion: H
+ tupleVariationCount: H
+ offsetToData: H
+"""
+
+CVAR_HEADER_SIZE = sstruct.calcsize(CVAR_HEADER_FORMAT)
+
+
+class table__c_v_a_r(DefaultTable.DefaultTable):
+ """Control Value Table (CVT) variations table
+
+ The ``cvar`` table contains variations for the values in a ``cvt``
+ table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cvar
+ """
+
+ dependencies = ["cvt ", "fvar"]
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.majorVersion, self.minorVersion = 1, 0
+ self.variations = []
+
+ def compile(self, ttFont, useSharedPoints=False):
+ tupleVariationCount, tuples, data = compileTupleVariationStore(
+ variations=[v for v in self.variations if v.hasImpact()],
+ pointCount=len(ttFont["cvt "].values),
+ axisTags=[axis.axisTag for axis in ttFont["fvar"].axes],
+ sharedTupleIndices={},
+ useSharedPoints=useSharedPoints,
+ )
+ header = {
+ "majorVersion": self.majorVersion,
+ "minorVersion": self.minorVersion,
+ "tupleVariationCount": tupleVariationCount,
+ "offsetToData": CVAR_HEADER_SIZE + len(tuples),
+ }
+ return b"".join([sstruct.pack(CVAR_HEADER_FORMAT, header), tuples, data])
+
+ def decompile(self, data, ttFont):
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ header = {}
+ sstruct.unpack(CVAR_HEADER_FORMAT, data[0:CVAR_HEADER_SIZE], header)
+ self.majorVersion = header["majorVersion"]
+ self.minorVersion = header["minorVersion"]
+ assert self.majorVersion == 1, self.majorVersion
+ self.variations = decompileTupleVariationStore(
+ tableTag=self.tableTag,
+ axisTags=axisTags,
+ tupleVariationCount=header["tupleVariationCount"],
+ pointCount=len(ttFont["cvt "].values),
+ sharedTuples=None,
+ data=data,
+ pos=CVAR_HEADER_SIZE,
+ dataPos=header["offsetToData"],
+ )
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.majorVersion = int(attrs.get("major", "1"))
+ self.minorVersion = int(attrs.get("minor", "0"))
+ elif name == "tuple":
+ valueCount = len(ttFont["cvt "].values)
+ var = TupleVariation({}, [None] * valueCount)
+ self.variations.append(var)
+ for tupleElement in content:
+ if isinstance(tupleElement, tuple):
+ tupleName, tupleAttrs, tupleContent = tupleElement
+ var.fromXML(tupleName, tupleAttrs, tupleContent)
+
+ def toXML(self, writer, ttFont):
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ writer.simpletag("version", major=self.majorVersion, minor=self.minorVersion)
+ writer.newline()
+ for var in self.variations:
+ var.toXML(writer, axisTags)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..c89fe2c239a0ab72fe763a1c0f700540d6b4126e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_c_v_t.py
@@ -0,0 +1,56 @@
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
+
+
+class table__c_v_t(DefaultTable.DefaultTable):
+ """Control Value Table
+
+ The Control Value Table holds a list of values that can be referenced
+ by TrueType font instructions.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/cvt
+ """
+
+ def decompile(self, data, ttFont):
+ values = array.array("h")
+ values.frombytes(data)
+ if sys.byteorder != "big":
+ values.byteswap()
+ self.values = values
+
+ def compile(self, ttFont):
+ if not hasattr(self, "values"):
+ return b""
+ values = self.values[:]
+ if sys.byteorder != "big":
+ values.byteswap()
+ return values.tobytes()
+
+ def toXML(self, writer, ttFont):
+ for i, value in enumerate(self.values):
+ writer.simpletag("cv", value=value, index=i)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "values"):
+ self.values = array.array("h")
+ if name == "cv":
+ index = safeEval(attrs["index"])
+ value = safeEval(attrs["value"])
+ for i in range(1 + index - len(self.values)):
+ self.values.append(0)
+ self.values[index] = value
+
+ def __len__(self):
+ return len(self.values)
+
+ def __getitem__(self, index):
+ return self.values[index]
+
+ def __setitem__(self, index, value):
+ self.values[index] = value
+
+ def __delitem__(self, index):
+ del self.values[index]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_e_a_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_e_a_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e279db0059613d5c0e0ac061dc9b2b62aefe73b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_e_a_t.py
@@ -0,0 +1,15 @@
+from .otBase import BaseTTXConverter
+
+
+class table__f_e_a_t(BaseTTXConverter):
+ """Feature name table
+
+ The feature name table is an AAT (Apple Advanced Typography) table for
+ storing font features, settings, and their human-readable names. It should
+ not be confused with the ``Feat`` table or the OpenType Layout ``GSUB``/``GPOS``
+ tables.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_p_g_m.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_p_g_m.py
new file mode 100644
index 0000000000000000000000000000000000000000..c21a9d4b6817d33bc68aabb8d866708657d1e068
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_p_g_m.py
@@ -0,0 +1,62 @@
+from . import DefaultTable
+from . import ttProgram
+
+
+class table__f_p_g_m(DefaultTable.DefaultTable):
+ """Font Program table
+
+ The ``fpgm`` table typically contains function defintions that are
+ used by font instructions. This Font Program is similar to the Control
+ Value Program that is stored in the ``prep`` table, but
+ the ``fpgm`` table is only executed one time, when the font is first
+ used.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/fpgm
+ """
+
+ def decompile(self, data, ttFont):
+ program = ttProgram.Program()
+ program.fromBytecode(data)
+ self.program = program
+
+ def compile(self, ttFont):
+ if hasattr(self, "program"):
+ return self.program.getBytecode()
+ return b""
+
+ def toXML(self, writer, ttFont):
+ self.program.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ program = ttProgram.Program()
+ program.fromXML(name, attrs, content, ttFont)
+ self.program = program
+
+ def __bool__(self):
+ """
+ >>> fpgm = table__f_p_g_m()
+ >>> bool(fpgm)
+ False
+ >>> p = ttProgram.Program()
+ >>> fpgm.program = p
+ >>> bool(fpgm)
+ False
+ >>> bc = bytearray([0])
+ >>> p.fromBytecode(bc)
+ >>> bool(fpgm)
+ True
+ >>> p.bytecode.pop()
+ 0
+ >>> bool(fpgm)
+ False
+ """
+ return hasattr(self, "program") and bool(self.program)
+
+ __nonzero__ = __bool__
+
+
+if __name__ == "__main__":
+ import sys
+ import doctest
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_v_a_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_v_a_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2536cb288a9140f59b298246daa2aea9b6d356f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_f_v_a_r.py
@@ -0,0 +1,261 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+)
+from fontTools.misc.textTools import Tag, bytesjoin, safeEval
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import struct
+
+
+# Apple's documentation of 'fvar':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html
+
+FVAR_HEADER_FORMAT = """
+ > # big endian
+ version: L
+ offsetToData: H
+ countSizePairs: H
+ axisCount: H
+ axisSize: H
+ instanceCount: H
+ instanceSize: H
+"""
+
+FVAR_AXIS_FORMAT = """
+ > # big endian
+ axisTag: 4s
+ minValue: 16.16F
+ defaultValue: 16.16F
+ maxValue: 16.16F
+ flags: H
+ axisNameID: H
+"""
+
+FVAR_INSTANCE_FORMAT = """
+ > # big endian
+ subfamilyNameID: H
+ flags: H
+"""
+
+
+class table__f_v_a_r(DefaultTable.DefaultTable):
+ """FonT Variations table
+
+ The ``fvar`` table contains records of the variation axes and of the
+ named instances in a variable font.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html
+ """
+
+ dependencies = ["name"]
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.axes = []
+ self.instances = []
+
+ def compile(self, ttFont):
+ instanceSize = sstruct.calcsize(FVAR_INSTANCE_FORMAT) + (len(self.axes) * 4)
+ includePostScriptNames = any(
+ instance.postscriptNameID != 0xFFFF for instance in self.instances
+ )
+ if includePostScriptNames:
+ instanceSize += 2
+ header = {
+ "version": 0x00010000,
+ "offsetToData": sstruct.calcsize(FVAR_HEADER_FORMAT),
+ "countSizePairs": 2,
+ "axisCount": len(self.axes),
+ "axisSize": sstruct.calcsize(FVAR_AXIS_FORMAT),
+ "instanceCount": len(self.instances),
+ "instanceSize": instanceSize,
+ }
+ result = [sstruct.pack(FVAR_HEADER_FORMAT, header)]
+ result.extend([axis.compile() for axis in self.axes])
+ axisTags = [axis.axisTag for axis in self.axes]
+ for instance in self.instances:
+ result.append(instance.compile(axisTags, includePostScriptNames))
+ return bytesjoin(result)
+
+ def decompile(self, data, ttFont):
+ header = {}
+ headerSize = sstruct.calcsize(FVAR_HEADER_FORMAT)
+ header = sstruct.unpack(FVAR_HEADER_FORMAT, data[0:headerSize])
+ if header["version"] != 0x00010000:
+ raise TTLibError("unsupported 'fvar' version %04x" % header["version"])
+ pos = header["offsetToData"]
+ axisSize = header["axisSize"]
+ for _ in range(header["axisCount"]):
+ axis = Axis()
+ axis.decompile(data[pos : pos + axisSize])
+ self.axes.append(axis)
+ pos += axisSize
+ instanceSize = header["instanceSize"]
+ axisTags = [axis.axisTag for axis in self.axes]
+ for _ in range(header["instanceCount"]):
+ instance = NamedInstance()
+ instance.decompile(data[pos : pos + instanceSize], axisTags)
+ self.instances.append(instance)
+ pos += instanceSize
+
+ def toXML(self, writer, ttFont):
+ for axis in self.axes:
+ axis.toXML(writer, ttFont)
+ for instance in self.instances:
+ instance.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "Axis":
+ axis = Axis()
+ axis.fromXML(name, attrs, content, ttFont)
+ self.axes.append(axis)
+ elif name == "NamedInstance":
+ instance = NamedInstance()
+ instance.fromXML(name, attrs, content, ttFont)
+ self.instances.append(instance)
+
+ def getAxes(self):
+ return {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in self.axes}
+
+
+class Axis(object):
+ def __init__(self):
+ self.axisTag = None
+ self.axisNameID = 0
+ self.flags = 0
+ self.minValue = -1.0
+ self.defaultValue = 0.0
+ self.maxValue = 1.0
+
+ def compile(self):
+ return sstruct.pack(FVAR_AXIS_FORMAT, self)
+
+ def decompile(self, data):
+ sstruct.unpack2(FVAR_AXIS_FORMAT, data, self)
+
+ def toXML(self, writer, ttFont):
+ name = (
+ ttFont["name"].getDebugName(self.axisNameID) if "name" in ttFont else None
+ )
+ if name is not None:
+ writer.newline()
+ writer.comment(name)
+ writer.newline()
+ writer.begintag("Axis")
+ writer.newline()
+ for tag, value in [
+ ("AxisTag", self.axisTag),
+ ("Flags", "0x%X" % self.flags),
+ ("MinValue", fl2str(self.minValue, 16)),
+ ("DefaultValue", fl2str(self.defaultValue, 16)),
+ ("MaxValue", fl2str(self.maxValue, 16)),
+ ("AxisNameID", str(self.axisNameID)),
+ ]:
+ writer.begintag(tag)
+ writer.write(value)
+ writer.endtag(tag)
+ writer.newline()
+ writer.endtag("Axis")
+ writer.newline()
+
+ def fromXML(self, name, _attrs, content, ttFont):
+ assert name == "Axis"
+ for tag, _, value in filter(lambda t: type(t) is tuple, content):
+ value = "".join(value)
+ if tag == "AxisTag":
+ self.axisTag = Tag(value)
+ elif tag in {"Flags", "MinValue", "DefaultValue", "MaxValue", "AxisNameID"}:
+ setattr(
+ self,
+ tag[0].lower() + tag[1:],
+ str2fl(value, 16) if tag.endswith("Value") else safeEval(value),
+ )
+
+
+class NamedInstance(object):
+ def __init__(self):
+ self.subfamilyNameID = 0
+ self.postscriptNameID = 0xFFFF
+ self.flags = 0
+ self.coordinates = {}
+
+ def compile(self, axisTags, includePostScriptName):
+ result = [sstruct.pack(FVAR_INSTANCE_FORMAT, self)]
+ for axis in axisTags:
+ fixedCoord = fl2fi(self.coordinates[axis], 16)
+ result.append(struct.pack(">l", fixedCoord))
+ if includePostScriptName:
+ result.append(struct.pack(">H", self.postscriptNameID))
+ return bytesjoin(result)
+
+ def decompile(self, data, axisTags):
+ sstruct.unpack2(FVAR_INSTANCE_FORMAT, data, self)
+ pos = sstruct.calcsize(FVAR_INSTANCE_FORMAT)
+ for axis in axisTags:
+ value = struct.unpack(">l", data[pos : pos + 4])[0]
+ self.coordinates[axis] = fi2fl(value, 16)
+ pos += 4
+ if pos + 2 <= len(data):
+ self.postscriptNameID = struct.unpack(">H", data[pos : pos + 2])[0]
+ else:
+ self.postscriptNameID = 0xFFFF
+
+ def toXML(self, writer, ttFont):
+ name = (
+ ttFont["name"].getDebugName(self.subfamilyNameID)
+ if "name" in ttFont
+ else None
+ )
+ if name is not None:
+ writer.newline()
+ writer.comment(name)
+ writer.newline()
+ psname = (
+ ttFont["name"].getDebugName(self.postscriptNameID)
+ if "name" in ttFont
+ else None
+ )
+ if psname is not None:
+ writer.comment("PostScript: " + psname)
+ writer.newline()
+ if self.postscriptNameID == 0xFFFF:
+ writer.begintag(
+ "NamedInstance",
+ flags=("0x%X" % self.flags),
+ subfamilyNameID=self.subfamilyNameID,
+ )
+ else:
+ writer.begintag(
+ "NamedInstance",
+ flags=("0x%X" % self.flags),
+ subfamilyNameID=self.subfamilyNameID,
+ postscriptNameID=self.postscriptNameID,
+ )
+ writer.newline()
+ for axis in ttFont["fvar"].axes:
+ writer.simpletag(
+ "coord",
+ axis=axis.axisTag,
+ value=fl2str(self.coordinates[axis.axisTag], 16),
+ )
+ writer.newline()
+ writer.endtag("NamedInstance")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ assert name == "NamedInstance"
+ self.subfamilyNameID = safeEval(attrs["subfamilyNameID"])
+ self.flags = safeEval(attrs.get("flags", "0"))
+ if "postscriptNameID" in attrs:
+ self.postscriptNameID = safeEval(attrs["postscriptNameID"])
+ else:
+ self.postscriptNameID = 0xFFFF
+
+ for tag, elementAttrs, _ in filter(lambda t: type(t) is tuple, content):
+ if tag == "coord":
+ value = str2fl(elementAttrs["value"], 16)
+ self.coordinates[elementAttrs["axis"]] = value
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_a_s_p.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_a_s_p.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f605771e9b7d98da4384e0d6a5f692646f6c07d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_a_s_p.py
@@ -0,0 +1,63 @@
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
+
+
+GASP_SYMMETRIC_GRIDFIT = 0x0004
+GASP_SYMMETRIC_SMOOTHING = 0x0008
+GASP_DOGRAY = 0x0002
+GASP_GRIDFIT = 0x0001
+
+
+class table__g_a_s_p(DefaultTable.DefaultTable):
+ """Grid-fitting and Scan-conversion Procedure table
+
+ The ``gasp`` table defines the preferred rasterization settings for
+ the font when rendered on monochrome and greyscale output devices.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/gasp
+ """
+
+ def decompile(self, data, ttFont):
+ self.version, numRanges = struct.unpack(">HH", data[:4])
+ assert 0 <= self.version <= 1, "unknown 'gasp' format: %s" % self.version
+ data = data[4:]
+ self.gaspRange = {}
+ for i in range(numRanges):
+ rangeMaxPPEM, rangeGaspBehavior = struct.unpack(">HH", data[:4])
+ self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior)
+ data = data[4:]
+ assert not data, "too much data"
+
+ def compile(self, ttFont):
+ version = 0 # ignore self.version
+ numRanges = len(self.gaspRange)
+ data = b""
+ items = sorted(self.gaspRange.items())
+ for rangeMaxPPEM, rangeGaspBehavior in items:
+ data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior)
+ if rangeGaspBehavior & ~(GASP_GRIDFIT | GASP_DOGRAY):
+ version = 1
+ data = struct.pack(">HH", version, numRanges) + data
+ return data
+
+ def toXML(self, writer, ttFont):
+ items = sorted(self.gaspRange.items())
+ for rangeMaxPPEM, rangeGaspBehavior in items:
+ writer.simpletag(
+ "gaspRange",
+ [
+ ("rangeMaxPPEM", rangeMaxPPEM),
+ ("rangeGaspBehavior", rangeGaspBehavior),
+ ],
+ )
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "gaspRange":
+ return
+ if not hasattr(self, "gaspRange"):
+ self.gaspRange = {}
+ self.gaspRange[safeEval(attrs["rangeMaxPPEM"])] = safeEval(
+ attrs["rangeGaspBehavior"]
+ )
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_c_i_d.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_c_i_d.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d6d6ae425384b47613c146d567847c00660a5cc
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_c_i_d.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
+class table__g_c_i_d(BaseTTXConverter):
+ """Glyph ID to CID table
+
+ The AAT ``gcid`` table stores glyphID-to-CID mappings.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_l_y_f.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_l_y_f.py
new file mode 100644
index 0000000000000000000000000000000000000000..3dea653baa0673a0327b8c7080e7b62c33e5a726
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_l_y_f.py
@@ -0,0 +1,2311 @@
+"""_g_l_y_f.py -- Converter classes for the 'glyf' table."""
+
+from collections import namedtuple
+from fontTools.misc import sstruct
+from fontTools import ttLib
+from fontTools import version
+from fontTools.misc.transform import DecomposedTransform
+from fontTools.misc.textTools import tostr, safeEval, pad
+from fontTools.misc.arrayTools import updateBounds, pointInRect
+from fontTools.misc.bezierTools import calcQuadraticBounds
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+)
+from fontTools.misc.roundTools import noRound, otRound
+from fontTools.misc.vector import Vector
+from numbers import Number
+from . import DefaultTable
+from . import ttProgram
+import sys
+import struct
+import array
+import logging
+import math
+import os
+from fontTools.misc import xmlWriter
+from fontTools.misc.filenames import userNameToFileName
+from fontTools.misc.loggingTools import deprecateFunction
+from enum import IntFlag
+from functools import partial
+from types import SimpleNamespace
+from typing import Set
+
+log = logging.getLogger(__name__)
+
+# We compute the version the same as is computed in ttlib/__init__
+# so that we can write 'ttLibVersion' attribute of the glyf TTX files
+# when glyf is written to separate files.
+version = ".".join(version.split(".")[:2])
+
+#
+# The Apple and MS rasterizers behave differently for
+# scaled composite components: one does scale first and then translate
+# and the other does it vice versa. MS defined some flags to indicate
+# the difference, but it seems nobody actually _sets_ those flags.
+#
+# Funny thing: Apple seems to _only_ do their thing in the
+# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE
+# (eg. Charcoal)...
+#
+SCALE_COMPONENT_OFFSET_DEFAULT = 0 # 0 == MS, 1 == Apple
+
+
+class table__g_l_y_f(DefaultTable.DefaultTable):
+ """Glyph Data table
+
+ This class represents the `glyf `_
+ table, which contains outlines for glyphs in TrueType format. In many cases,
+ it is easier to access and manipulate glyph outlines through the ``GlyphSet``
+ object returned from :py:meth:`fontTools.ttLib.ttFont.getGlyphSet`::
+
+ >> from fontTools.pens.boundsPen import BoundsPen
+ >> glyphset = font.getGlyphSet()
+ >> bp = BoundsPen(glyphset)
+ >> glyphset["A"].draw(bp)
+ >> bp.bounds
+ (19, 0, 633, 716)
+
+ However, this class can be used for low-level access to the ``glyf`` table data.
+ Objects of this class support dictionary-like access, mapping glyph names to
+ :py:class:`Glyph` objects::
+
+ >> glyf = font["glyf"]
+ >> len(glyf["Aacute"].components)
+ 2
+
+ Note that when adding glyphs to the font via low-level access to the ``glyf``
+ table, the new glyphs must also be added to the ``hmtx``/``vmtx`` table::
+
+ >> font["glyf"]["divisionslash"] = Glyph()
+ >> font["hmtx"]["divisionslash"] = (640, 0)
+
+ """
+
+ dependencies = ["fvar"]
+
+ # this attribute controls the amount of padding applied to glyph data upon compile.
+ # Glyph lenghts are aligned to multiples of the specified value.
+ # Allowed values are (0, 1, 2, 4). '0' means no padding; '1' (default) also means
+ # no padding, except for when padding would allow to use short loca offsets.
+ padding = 1
+
+ def decompile(self, data, ttFont):
+ self.axisTags = (
+ [axis.axisTag for axis in ttFont["fvar"].axes] if "fvar" in ttFont else []
+ )
+ loca = ttFont["loca"]
+ pos = int(loca[0])
+ nextPos = 0
+ noname = 0
+ self.glyphs = {}
+ self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
+ self._reverseGlyphOrder = {}
+ for i in range(0, len(loca) - 1):
+ try:
+ glyphName = glyphOrder[i]
+ except IndexError:
+ noname = noname + 1
+ glyphName = "ttxautoglyph%s" % i
+ nextPos = int(loca[i + 1])
+ glyphdata = data[pos:nextPos]
+ if len(glyphdata) != (nextPos - pos):
+ raise ttLib.TTLibError("not enough 'glyf' table data")
+ glyph = Glyph(glyphdata)
+ self.glyphs[glyphName] = glyph
+ pos = nextPos
+ if len(data) - nextPos >= 4:
+ log.warning(
+ "too much 'glyf' table data: expected %d, received %d bytes",
+ nextPos,
+ len(data),
+ )
+ if noname:
+ log.warning("%s glyphs have no name", noname)
+ if ttFont.lazy is False: # Be lazy for None and True
+ self.ensureDecompiled()
+
+ def ensureDecompiled(self, recurse=False):
+ # The recurse argument is unused, but part of the signature of
+ # ensureDecompiled across the library.
+ for glyph in self.glyphs.values():
+ glyph.expand(self)
+
+ def compile(self, ttFont):
+ optimizeSpeed = ttFont.cfg[ttLib.OPTIMIZE_FONT_SPEED]
+
+ self.axisTags = (
+ [axis.axisTag for axis in ttFont["fvar"].axes] if "fvar" in ttFont else []
+ )
+ if not hasattr(self, "glyphOrder"):
+ self.glyphOrder = ttFont.getGlyphOrder()
+ padding = self.padding
+ assert padding in (0, 1, 2, 4)
+ locations = []
+ currentLocation = 0
+ dataList = []
+ recalcBBoxes = ttFont.recalcBBoxes
+ boundsDone = set()
+ for glyphName in self.glyphOrder:
+ glyph = self.glyphs[glyphName]
+ glyphData = glyph.compile(
+ self,
+ recalcBBoxes,
+ boundsDone=boundsDone,
+ optimizeSize=not optimizeSpeed,
+ )
+ if padding > 1:
+ glyphData = pad(glyphData, size=padding)
+ locations.append(currentLocation)
+ currentLocation = currentLocation + len(glyphData)
+ dataList.append(glyphData)
+ locations.append(currentLocation)
+
+ if padding == 1 and currentLocation < 0x20000:
+ # See if we can pad any odd-lengthed glyphs to allow loca
+ # table to use the short offsets.
+ indices = [
+ i for i, glyphData in enumerate(dataList) if len(glyphData) % 2 == 1
+ ]
+ if indices and currentLocation + len(indices) < 0x20000:
+ # It fits. Do it.
+ for i in indices:
+ dataList[i] += b"\0"
+ currentLocation = 0
+ for i, glyphData in enumerate(dataList):
+ locations[i] = currentLocation
+ currentLocation += len(glyphData)
+ locations[len(dataList)] = currentLocation
+
+ data = b"".join(dataList)
+ if "loca" in ttFont:
+ ttFont["loca"].set(locations)
+ if "maxp" in ttFont:
+ ttFont["maxp"].numGlyphs = len(self.glyphs)
+ if not data:
+ # As a special case when all glyph in the font are empty, add a zero byte
+ # to the table, so that OTS doesn’t reject it, and to make the table work
+ # on Windows as well.
+ # See https://github.com/khaledhosny/ots/issues/52
+ data = b"\0"
+ return data
+
+ def toXML(self, writer, ttFont, splitGlyphs=False):
+ notice = (
+ "The xMin, yMin, xMax and yMax values\n"
+ "will be recalculated by the compiler."
+ )
+ glyphNames = ttFont.getGlyphNames()
+ if not splitGlyphs:
+ writer.newline()
+ writer.comment(notice)
+ writer.newline()
+ writer.newline()
+ numGlyphs = len(glyphNames)
+ if splitGlyphs:
+ path, ext = os.path.splitext(writer.file.name)
+ existingGlyphFiles = set()
+ for glyphName in glyphNames:
+ glyph = self.get(glyphName)
+ if glyph is None:
+ log.warning("glyph '%s' does not exist in glyf table", glyphName)
+ continue
+ if glyph.numberOfContours:
+ if splitGlyphs:
+ glyphPath = userNameToFileName(
+ tostr(glyphName, "utf-8"),
+ existingGlyphFiles,
+ prefix=path + ".",
+ suffix=ext,
+ )
+ existingGlyphFiles.add(glyphPath.lower())
+ glyphWriter = xmlWriter.XMLWriter(
+ glyphPath,
+ idlefunc=writer.idlefunc,
+ newlinestr=writer.newlinestr,
+ )
+ glyphWriter.begintag("ttFont", ttLibVersion=version)
+ glyphWriter.newline()
+ glyphWriter.begintag("glyf")
+ glyphWriter.newline()
+ glyphWriter.comment(notice)
+ glyphWriter.newline()
+ writer.simpletag("TTGlyph", src=os.path.basename(glyphPath))
+ else:
+ glyphWriter = writer
+ glyphWriter.begintag(
+ "TTGlyph",
+ [
+ ("name", glyphName),
+ ("xMin", glyph.xMin),
+ ("yMin", glyph.yMin),
+ ("xMax", glyph.xMax),
+ ("yMax", glyph.yMax),
+ ],
+ )
+ glyphWriter.newline()
+ glyph.toXML(glyphWriter, ttFont)
+ glyphWriter.endtag("TTGlyph")
+ glyphWriter.newline()
+ if splitGlyphs:
+ glyphWriter.endtag("glyf")
+ glyphWriter.newline()
+ glyphWriter.endtag("ttFont")
+ glyphWriter.newline()
+ glyphWriter.close()
+ else:
+ writer.simpletag("TTGlyph", name=glyphName)
+ writer.comment("contains no outline data")
+ if not splitGlyphs:
+ writer.newline()
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "TTGlyph":
+ return
+ if not hasattr(self, "glyphs"):
+ self.glyphs = {}
+ if not hasattr(self, "glyphOrder"):
+ self.glyphOrder = ttFont.getGlyphOrder()
+ glyphName = attrs["name"]
+ log.debug("unpacking glyph '%s'", glyphName)
+ glyph = Glyph()
+ for attr in ["xMin", "yMin", "xMax", "yMax"]:
+ setattr(glyph, attr, safeEval(attrs.get(attr, "0")))
+ self.glyphs[glyphName] = glyph
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ glyph.fromXML(name, attrs, content, ttFont)
+ if not ttFont.recalcBBoxes:
+ glyph.compact(self, 0)
+
+ def setGlyphOrder(self, glyphOrder):
+ """Sets the glyph order
+
+ Args:
+ glyphOrder ([str]): List of glyph names in order.
+ """
+ self.glyphOrder = glyphOrder
+ self._reverseGlyphOrder = {}
+
+ def getGlyphName(self, glyphID):
+ """Returns the name for the glyph with the given ID.
+
+ Raises a ``KeyError`` if the glyph name is not found in the font.
+ """
+ return self.glyphOrder[glyphID]
+
+ def _buildReverseGlyphOrderDict(self):
+ self._reverseGlyphOrder = d = {}
+ for glyphID, glyphName in enumerate(self.glyphOrder):
+ d[glyphName] = glyphID
+
+ def getGlyphID(self, glyphName):
+ """Returns the ID of the glyph with the given name.
+
+ Raises a ``ValueError`` if the glyph is not found in the font.
+ """
+ glyphOrder = self.glyphOrder
+ id = getattr(self, "_reverseGlyphOrder", {}).get(glyphName)
+ if id is None or id >= len(glyphOrder) or glyphOrder[id] != glyphName:
+ self._buildReverseGlyphOrderDict()
+ id = self._reverseGlyphOrder.get(glyphName)
+ if id is None:
+ raise ValueError(glyphName)
+ return id
+
+ def removeHinting(self):
+ """Removes TrueType hints from all glyphs in the glyphset.
+
+ See :py:meth:`Glyph.removeHinting`.
+ """
+ for glyph in self.glyphs.values():
+ glyph.removeHinting()
+
+ def keys(self):
+ return self.glyphs.keys()
+
+ def has_key(self, glyphName):
+ return glyphName in self.glyphs
+
+ __contains__ = has_key
+
+ def get(self, glyphName, default=None):
+ glyph = self.glyphs.get(glyphName, default)
+ if glyph is not None:
+ glyph.expand(self)
+ return glyph
+
+ def __getitem__(self, glyphName):
+ glyph = self.glyphs[glyphName]
+ glyph.expand(self)
+ return glyph
+
+ def __setitem__(self, glyphName, glyph):
+ self.glyphs[glyphName] = glyph
+ if glyphName not in self.glyphOrder:
+ self.glyphOrder.append(glyphName)
+
+ def __delitem__(self, glyphName):
+ del self.glyphs[glyphName]
+ self.glyphOrder.remove(glyphName)
+
+ def __len__(self):
+ assert len(self.glyphOrder) == len(self.glyphs)
+ return len(self.glyphs)
+
+ def _getPhantomPoints(self, glyphName, hMetrics, vMetrics=None):
+ """Compute the four "phantom points" for the given glyph from its bounding box
+ and the horizontal and vertical advance widths and sidebearings stored in the
+ ttFont's "hmtx" and "vmtx" tables.
+
+ 'hMetrics' should be ttFont['hmtx'].metrics.
+
+ 'vMetrics' should be ttFont['vmtx'].metrics if there is "vmtx" or None otherwise.
+ If there is no vMetrics passed in, vertical phantom points are set to the zero coordinate.
+
+ https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructing_glyphs#phantoms
+ """
+ glyph = self[glyphName]
+ if not hasattr(glyph, "xMin"):
+ glyph.recalcBounds(self)
+
+ horizontalAdvanceWidth, leftSideBearing = hMetrics[glyphName]
+ leftSideX = glyph.xMin - leftSideBearing
+ rightSideX = leftSideX + horizontalAdvanceWidth
+
+ if vMetrics:
+ verticalAdvanceWidth, topSideBearing = vMetrics[glyphName]
+ topSideY = topSideBearing + glyph.yMax
+ bottomSideY = topSideY - verticalAdvanceWidth
+ else:
+ bottomSideY = topSideY = 0
+
+ return [
+ (leftSideX, 0),
+ (rightSideX, 0),
+ (0, topSideY),
+ (0, bottomSideY),
+ ]
+
+ def _getCoordinatesAndControls(
+ self, glyphName, hMetrics, vMetrics=None, *, round=otRound
+ ):
+ """Return glyph coordinates and controls as expected by "gvar" table.
+
+ The coordinates includes four "phantom points" for the glyph metrics,
+ as mandated by the "gvar" spec.
+
+ The glyph controls is a namedtuple with the following attributes:
+ - numberOfContours: -1 for composite glyphs.
+ - endPts: list of indices of end points for each contour in simple
+ glyphs, or component indices in composite glyphs (used for IUP
+ optimization).
+ - flags: array of contour point flags for simple glyphs (None for
+ composite glyphs).
+ - components: list of base glyph names (str) for each component in
+ composite glyphs (None for simple glyphs).
+
+ The "hMetrics" and vMetrics are used to compute the "phantom points" (see
+ the "_getPhantomPoints" method).
+
+ Return None if the requested glyphName is not present.
+ """
+ glyph = self.get(glyphName)
+ if glyph is None:
+ return None
+ if glyph.isComposite():
+ coords = GlyphCoordinates(
+ [(getattr(c, "x", 0), getattr(c, "y", 0)) for c in glyph.components]
+ )
+ controls = _GlyphControls(
+ numberOfContours=glyph.numberOfContours,
+ endPts=list(range(len(glyph.components))),
+ flags=None,
+ components=[
+ (c.glyphName, getattr(c, "transform", None))
+ for c in glyph.components
+ ],
+ )
+ else:
+ coords, endPts, flags = glyph.getCoordinates(self)
+ coords = coords.copy()
+ controls = _GlyphControls(
+ numberOfContours=glyph.numberOfContours,
+ endPts=endPts,
+ flags=flags,
+ components=None,
+ )
+ # Add phantom points for (left, right, top, bottom) positions.
+ phantomPoints = self._getPhantomPoints(glyphName, hMetrics, vMetrics)
+ coords.extend(phantomPoints)
+ coords.toInt(round=round)
+ return coords, controls
+
+ def _setCoordinates(self, glyphName, coord, hMetrics, vMetrics=None):
+ """Set coordinates and metrics for the given glyph.
+
+ "coord" is an array of GlyphCoordinates which must include the "phantom
+ points" as the last four coordinates.
+
+ Both the horizontal/vertical advances and left/top sidebearings in "hmtx"
+ and "vmtx" tables (if any) are updated from four phantom points and
+ the glyph's bounding boxes.
+
+ The "hMetrics" and vMetrics are used to propagate "phantom points"
+ into "hmtx" and "vmtx" tables if desired. (see the "_getPhantomPoints"
+ method).
+ """
+ glyph = self[glyphName]
+
+ # Handle phantom points for (left, right, top, bottom) positions.
+ assert len(coord) >= 4
+ leftSideX = coord[-4][0]
+ rightSideX = coord[-3][0]
+ topSideY = coord[-2][1]
+ bottomSideY = coord[-1][1]
+
+ coord = coord[:-4]
+
+ if glyph.isComposite():
+ assert len(coord) == len(glyph.components)
+ for p, comp in zip(coord, glyph.components):
+ if hasattr(comp, "x"):
+ comp.x, comp.y = p
+ elif glyph.numberOfContours == 0:
+ assert len(coord) == 0
+ else:
+ assert len(coord) == len(glyph.coordinates)
+ glyph.coordinates = GlyphCoordinates(coord)
+
+ glyph.recalcBounds(self, boundsDone=set())
+
+ horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
+ if horizontalAdvanceWidth < 0:
+ # unlikely, but it can happen, see:
+ # https://github.com/fonttools/fonttools/pull/1198
+ horizontalAdvanceWidth = 0
+ leftSideBearing = otRound(glyph.xMin - leftSideX)
+ hMetrics[glyphName] = horizontalAdvanceWidth, leftSideBearing
+
+ if vMetrics is not None:
+ verticalAdvanceWidth = otRound(topSideY - bottomSideY)
+ if verticalAdvanceWidth < 0: # unlikely but do the same as horizontal
+ verticalAdvanceWidth = 0
+ topSideBearing = otRound(topSideY - glyph.yMax)
+ vMetrics[glyphName] = verticalAdvanceWidth, topSideBearing
+
+ # Deprecated
+
+ def _synthesizeVMetrics(self, glyphName, ttFont, defaultVerticalOrigin):
+ """This method is wrong and deprecated.
+ For rationale see:
+ https://github.com/fonttools/fonttools/pull/2266/files#r613569473
+ """
+ vMetrics = getattr(ttFont.get("vmtx"), "metrics", None)
+ if vMetrics is None:
+ verticalAdvanceWidth = ttFont["head"].unitsPerEm
+ topSideY = getattr(ttFont.get("hhea"), "ascent", None)
+ if topSideY is None:
+ if defaultVerticalOrigin is not None:
+ topSideY = defaultVerticalOrigin
+ else:
+ topSideY = verticalAdvanceWidth
+ glyph = self[glyphName]
+ glyph.recalcBounds(self)
+ topSideBearing = otRound(topSideY - glyph.yMax)
+ vMetrics = {glyphName: (verticalAdvanceWidth, topSideBearing)}
+ return vMetrics
+
+ @deprecateFunction("use '_getPhantomPoints' instead", category=DeprecationWarning)
+ def getPhantomPoints(self, glyphName, ttFont, defaultVerticalOrigin=None):
+ """Old public name for self._getPhantomPoints().
+ See: https://github.com/fonttools/fonttools/pull/2266"""
+ hMetrics = ttFont["hmtx"].metrics
+ vMetrics = self._synthesizeVMetrics(glyphName, ttFont, defaultVerticalOrigin)
+ return self._getPhantomPoints(glyphName, hMetrics, vMetrics)
+
+ @deprecateFunction(
+ "use '_getCoordinatesAndControls' instead", category=DeprecationWarning
+ )
+ def getCoordinatesAndControls(self, glyphName, ttFont, defaultVerticalOrigin=None):
+ """Old public name for self._getCoordinatesAndControls().
+ See: https://github.com/fonttools/fonttools/pull/2266"""
+ hMetrics = ttFont["hmtx"].metrics
+ vMetrics = self._synthesizeVMetrics(glyphName, ttFont, defaultVerticalOrigin)
+ return self._getCoordinatesAndControls(glyphName, hMetrics, vMetrics)
+
+ @deprecateFunction("use '_setCoordinates' instead", category=DeprecationWarning)
+ def setCoordinates(self, glyphName, ttFont):
+ """Old public name for self._setCoordinates().
+ See: https://github.com/fonttools/fonttools/pull/2266"""
+ hMetrics = ttFont["hmtx"].metrics
+ vMetrics = getattr(ttFont.get("vmtx"), "metrics", None)
+ self._setCoordinates(glyphName, hMetrics, vMetrics)
+
+
+_GlyphControls = namedtuple(
+ "_GlyphControls", "numberOfContours endPts flags components"
+)
+
+
+glyphHeaderFormat = """
+ > # big endian
+ numberOfContours: h
+ xMin: h
+ yMin: h
+ xMax: h
+ yMax: h
+"""
+
+# flags
+flagOnCurve = 0x01
+flagXShort = 0x02
+flagYShort = 0x04
+flagRepeat = 0x08
+flagXsame = 0x10
+flagYsame = 0x20
+flagOverlapSimple = 0x40
+flagCubic = 0x80
+
+# These flags are kept for XML output after decompiling the coordinates
+keepFlags = flagOnCurve + flagOverlapSimple + flagCubic
+
+_flagSignBytes = {
+ 0: 2,
+ flagXsame: 0,
+ flagXShort | flagXsame: +1,
+ flagXShort: -1,
+ flagYsame: 0,
+ flagYShort | flagYsame: +1,
+ flagYShort: -1,
+}
+
+
+def flagBest(x, y, onCurve):
+ """For a given x,y delta pair, returns the flag that packs this pair
+ most efficiently, as well as the number of byte cost of such flag."""
+
+ flag = flagOnCurve if onCurve else 0
+ cost = 0
+ # do x
+ if x == 0:
+ flag = flag | flagXsame
+ elif -255 <= x <= 255:
+ flag = flag | flagXShort
+ if x > 0:
+ flag = flag | flagXsame
+ cost += 1
+ else:
+ cost += 2
+ # do y
+ if y == 0:
+ flag = flag | flagYsame
+ elif -255 <= y <= 255:
+ flag = flag | flagYShort
+ if y > 0:
+ flag = flag | flagYsame
+ cost += 1
+ else:
+ cost += 2
+ return flag, cost
+
+
+def flagFits(newFlag, oldFlag, mask):
+ newBytes = _flagSignBytes[newFlag & mask]
+ oldBytes = _flagSignBytes[oldFlag & mask]
+ return newBytes == oldBytes or abs(newBytes) > abs(oldBytes)
+
+
+def flagSupports(newFlag, oldFlag):
+ return (
+ (oldFlag & flagOnCurve) == (newFlag & flagOnCurve)
+ and flagFits(newFlag, oldFlag, flagXsame | flagXShort)
+ and flagFits(newFlag, oldFlag, flagYsame | flagYShort)
+ )
+
+
+def flagEncodeCoord(flag, mask, coord, coordBytes):
+ byteCount = _flagSignBytes[flag & mask]
+ if byteCount == 1:
+ coordBytes.append(coord)
+ elif byteCount == -1:
+ coordBytes.append(-coord)
+ elif byteCount == 2:
+ coordBytes.extend(struct.pack(">h", coord))
+
+
+def flagEncodeCoords(flag, x, y, xBytes, yBytes):
+ flagEncodeCoord(flag, flagXsame | flagXShort, x, xBytes)
+ flagEncodeCoord(flag, flagYsame | flagYShort, y, yBytes)
+
+
+ARG_1_AND_2_ARE_WORDS = 0x0001 # if set args are words otherwise they are bytes
+ARGS_ARE_XY_VALUES = 0x0002 # if set args are xy values, otherwise they are points
+ROUND_XY_TO_GRID = 0x0004 # for the xy values if above is true
+WE_HAVE_A_SCALE = 0x0008 # Sx = Sy, otherwise scale == 1.0
+NON_OVERLAPPING = 0x0010 # set to same value for all components (obsolete!)
+MORE_COMPONENTS = 0x0020 # indicates at least one more glyph after this one
+WE_HAVE_AN_X_AND_Y_SCALE = 0x0040 # Sx, Sy
+WE_HAVE_A_TWO_BY_TWO = 0x0080 # t00, t01, t10, t11
+WE_HAVE_INSTRUCTIONS = 0x0100 # instructions follow
+USE_MY_METRICS = 0x0200 # apply these metrics to parent glyph
+OVERLAP_COMPOUND = 0x0400 # used by Apple in GX fonts
+SCALED_COMPONENT_OFFSET = 0x0800 # composite designed to have the component offset scaled (designed for Apple)
+UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS)
+
+
+CompositeMaxpValues = namedtuple(
+ "CompositeMaxpValues", ["nPoints", "nContours", "maxComponentDepth"]
+)
+
+
+class Glyph(object):
+ """This class represents an individual TrueType glyph.
+
+ TrueType glyph objects come in two flavours: simple and composite. Simple
+ glyph objects contain contours, represented via the ``.coordinates``,
+ ``.flags``, ``.numberOfContours``, and ``.endPtsOfContours`` attributes;
+ composite glyphs contain components, available through the ``.components``
+ attributes.
+
+ Because the ``.coordinates`` attribute (and other simple glyph attributes mentioned
+ above) is only set on simple glyphs and the ``.components`` attribute is only
+ set on composite glyphs, it is necessary to use the :py:meth:`isComposite`
+ method to test whether a glyph is simple or composite before attempting to
+ access its data.
+
+ For a composite glyph, the components can also be accessed via array-like access::
+
+ >> assert(font["glyf"]["Aacute"].isComposite())
+ >> font["glyf"]["Aacute"][0]
+
+
+ """
+
+ def __init__(self, data=b""):
+ if not data:
+ # empty char
+ self.numberOfContours = 0
+ return
+ self.data = data
+
+ def compact(self, glyfTable, recalcBBoxes=True):
+ data = self.compile(glyfTable, recalcBBoxes)
+ self.__dict__.clear()
+ self.data = data
+
+ def expand(self, glyfTable):
+ if not hasattr(self, "data"):
+ # already unpacked
+ return
+ if not self.data:
+ # empty char
+ del self.data
+ self.numberOfContours = 0
+ return
+ dummy, data = sstruct.unpack2(glyphHeaderFormat, self.data, self)
+ del self.data
+ # Some fonts (eg. Neirizi.ttf) have a 0 for numberOfContours in
+ # some glyphs; decompileCoordinates assumes that there's at least
+ # one, so short-circuit here.
+ if self.numberOfContours == 0:
+ return
+ if self.isComposite():
+ self.decompileComponents(data, glyfTable)
+ else:
+ self.decompileCoordinates(data)
+
+ def compile(
+ self, glyfTable, recalcBBoxes=True, *, boundsDone=None, optimizeSize=True
+ ):
+ if hasattr(self, "data"):
+ if recalcBBoxes:
+ # must unpack glyph in order to recalculate bounding box
+ self.expand(glyfTable)
+ else:
+ return self.data
+ if self.numberOfContours == 0:
+ return b""
+
+ if recalcBBoxes:
+ self.recalcBounds(glyfTable, boundsDone=boundsDone)
+
+ data = sstruct.pack(glyphHeaderFormat, self)
+ if self.isComposite():
+ data = data + self.compileComponents(glyfTable)
+ else:
+ data = data + self.compileCoordinates(optimizeSize=optimizeSize)
+ return data
+
+ def toXML(self, writer, ttFont):
+ if self.isComposite():
+ for compo in self.components:
+ compo.toXML(writer, ttFont)
+ haveInstructions = hasattr(self, "program")
+ else:
+ last = 0
+ for i in range(self.numberOfContours):
+ writer.begintag("contour")
+ writer.newline()
+ for j in range(last, self.endPtsOfContours[i] + 1):
+ attrs = [
+ ("x", self.coordinates[j][0]),
+ ("y", self.coordinates[j][1]),
+ ("on", self.flags[j] & flagOnCurve),
+ ]
+ if self.flags[j] & flagOverlapSimple:
+ # Apple's rasterizer uses flagOverlapSimple in the first contour/first pt to flag glyphs that contain overlapping contours
+ attrs.append(("overlap", 1))
+ if self.flags[j] & flagCubic:
+ attrs.append(("cubic", 1))
+ writer.simpletag("pt", attrs)
+ writer.newline()
+ last = self.endPtsOfContours[i] + 1
+ writer.endtag("contour")
+ writer.newline()
+ haveInstructions = self.numberOfContours > 0
+ if haveInstructions:
+ if self.program:
+ writer.begintag("instructions")
+ writer.newline()
+ self.program.toXML(writer, ttFont)
+ writer.endtag("instructions")
+ else:
+ writer.simpletag("instructions")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "contour":
+ if self.numberOfContours < 0:
+ raise ttLib.TTLibError("can't mix composites and contours in glyph")
+ self.numberOfContours = self.numberOfContours + 1
+ coordinates = GlyphCoordinates()
+ flags = bytearray()
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name != "pt":
+ continue # ignore anything but "pt"
+ coordinates.append((safeEval(attrs["x"]), safeEval(attrs["y"])))
+ flag = bool(safeEval(attrs["on"]))
+ if "overlap" in attrs and bool(safeEval(attrs["overlap"])):
+ flag |= flagOverlapSimple
+ if "cubic" in attrs and bool(safeEval(attrs["cubic"])):
+ flag |= flagCubic
+ flags.append(flag)
+ if not hasattr(self, "coordinates"):
+ self.coordinates = coordinates
+ self.flags = flags
+ self.endPtsOfContours = [len(coordinates) - 1]
+ else:
+ self.coordinates.extend(coordinates)
+ self.flags.extend(flags)
+ self.endPtsOfContours.append(len(self.coordinates) - 1)
+ elif name == "component":
+ if self.numberOfContours > 0:
+ raise ttLib.TTLibError("can't mix composites and contours in glyph")
+ self.numberOfContours = -1
+ if not hasattr(self, "components"):
+ self.components = []
+ component = GlyphComponent()
+ self.components.append(component)
+ component.fromXML(name, attrs, content, ttFont)
+ elif name == "instructions":
+ self.program = ttProgram.Program()
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ self.program.fromXML(name, attrs, content, ttFont)
+
+ def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
+ assert self.isComposite()
+ nContours = 0
+ nPoints = 0
+ initialMaxComponentDepth = maxComponentDepth
+ for compo in self.components:
+ baseGlyph = glyfTable[compo.glyphName]
+ if baseGlyph.numberOfContours == 0:
+ continue
+ elif baseGlyph.numberOfContours > 0:
+ nP, nC = baseGlyph.getMaxpValues()
+ else:
+ nP, nC, componentDepth = baseGlyph.getCompositeMaxpValues(
+ glyfTable, initialMaxComponentDepth + 1
+ )
+ maxComponentDepth = max(maxComponentDepth, componentDepth)
+ nPoints = nPoints + nP
+ nContours = nContours + nC
+ return CompositeMaxpValues(nPoints, nContours, maxComponentDepth)
+
+ def getMaxpValues(self):
+ assert self.numberOfContours > 0
+ return len(self.coordinates), len(self.endPtsOfContours)
+
+ def decompileComponents(self, data, glyfTable):
+ self.components = []
+ more = 1
+ haveInstructions = 0
+ while more:
+ component = GlyphComponent()
+ more, haveInstr, data = component.decompile(data, glyfTable)
+ haveInstructions = haveInstructions | haveInstr
+ self.components.append(component)
+ if haveInstructions:
+ (numInstructions,) = struct.unpack(">h", data[:2])
+ data = data[2:]
+ self.program = ttProgram.Program()
+ self.program.fromBytecode(data[:numInstructions])
+ data = data[numInstructions:]
+ if len(data) >= 4:
+ log.warning(
+ "too much glyph data at the end of composite glyph: %d excess bytes",
+ len(data),
+ )
+
+ def decompileCoordinates(self, data):
+ endPtsOfContours = array.array("H")
+ endPtsOfContours.frombytes(data[: 2 * self.numberOfContours])
+ if sys.byteorder != "big":
+ endPtsOfContours.byteswap()
+ self.endPtsOfContours = endPtsOfContours.tolist()
+
+ pos = 2 * self.numberOfContours
+ (instructionLength,) = struct.unpack(">h", data[pos : pos + 2])
+ self.program = ttProgram.Program()
+ self.program.fromBytecode(data[pos + 2 : pos + 2 + instructionLength])
+ pos += 2 + instructionLength
+ nCoordinates = self.endPtsOfContours[-1] + 1
+ flags, xCoordinates, yCoordinates = self.decompileCoordinatesRaw(
+ nCoordinates, data, pos
+ )
+
+ # fill in repetitions and apply signs
+ self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates)
+ xIndex = 0
+ yIndex = 0
+ for i in range(nCoordinates):
+ flag = flags[i]
+ # x coordinate
+ if flag & flagXShort:
+ if flag & flagXsame:
+ x = xCoordinates[xIndex]
+ else:
+ x = -xCoordinates[xIndex]
+ xIndex = xIndex + 1
+ elif flag & flagXsame:
+ x = 0
+ else:
+ x = xCoordinates[xIndex]
+ xIndex = xIndex + 1
+ # y coordinate
+ if flag & flagYShort:
+ if flag & flagYsame:
+ y = yCoordinates[yIndex]
+ else:
+ y = -yCoordinates[yIndex]
+ yIndex = yIndex + 1
+ elif flag & flagYsame:
+ y = 0
+ else:
+ y = yCoordinates[yIndex]
+ yIndex = yIndex + 1
+ coordinates[i] = (x, y)
+ assert xIndex == len(xCoordinates)
+ assert yIndex == len(yCoordinates)
+ coordinates.relativeToAbsolute()
+ # discard all flags except "keepFlags"
+ for i in range(len(flags)):
+ flags[i] &= keepFlags
+ self.flags = flags
+
+ def decompileCoordinatesRaw(self, nCoordinates, data, pos=0):
+ # unpack flags and prepare unpacking of coordinates
+ flags = bytearray(nCoordinates)
+ # Warning: deep Python trickery going on. We use the struct module to unpack
+ # the coordinates. We build a format string based on the flags, so we can
+ # unpack the coordinates in one struct.unpack() call.
+ xFormat = ">" # big endian
+ yFormat = ">" # big endian
+ j = 0
+ while True:
+ flag = data[pos]
+ pos += 1
+ repeat = 1
+ if flag & flagRepeat:
+ repeat = data[pos] + 1
+ pos += 1
+ for k in range(repeat):
+ if flag & flagXShort:
+ xFormat = xFormat + "B"
+ elif not (flag & flagXsame):
+ xFormat = xFormat + "h"
+ if flag & flagYShort:
+ yFormat = yFormat + "B"
+ elif not (flag & flagYsame):
+ yFormat = yFormat + "h"
+ flags[j] = flag
+ j = j + 1
+ if j >= nCoordinates:
+ break
+ assert j == nCoordinates, "bad glyph flags"
+ # unpack raw coordinates, krrrrrr-tching!
+ xDataLen = struct.calcsize(xFormat)
+ yDataLen = struct.calcsize(yFormat)
+ if len(data) - pos - (xDataLen + yDataLen) >= 4:
+ log.warning(
+ "too much glyph data: %d excess bytes",
+ len(data) - pos - (xDataLen + yDataLen),
+ )
+ xCoordinates = struct.unpack(xFormat, data[pos : pos + xDataLen])
+ yCoordinates = struct.unpack(
+ yFormat, data[pos + xDataLen : pos + xDataLen + yDataLen]
+ )
+ return flags, xCoordinates, yCoordinates
+
+ def compileComponents(self, glyfTable):
+ data = b""
+ lastcomponent = len(self.components) - 1
+ more = 1
+ haveInstructions = 0
+ for i, compo in enumerate(self.components):
+ if i == lastcomponent:
+ haveInstructions = hasattr(self, "program")
+ more = 0
+ data = data + compo.compile(more, haveInstructions, glyfTable)
+ if haveInstructions:
+ instructions = self.program.getBytecode()
+ data = data + struct.pack(">h", len(instructions)) + instructions
+ return data
+
+ def compileCoordinates(self, *, optimizeSize=True):
+ assert len(self.coordinates) == len(self.flags)
+ data = []
+ endPtsOfContours = array.array("H", self.endPtsOfContours)
+ if sys.byteorder != "big":
+ endPtsOfContours.byteswap()
+ data.append(endPtsOfContours.tobytes())
+ instructions = self.program.getBytecode()
+ data.append(struct.pack(">h", len(instructions)))
+ data.append(instructions)
+
+ deltas = self.coordinates.copy()
+ deltas.toInt()
+ deltas.absoluteToRelative()
+
+ if optimizeSize:
+ # TODO(behdad): Add a configuration option for this?
+ deltas = self.compileDeltasGreedy(self.flags, deltas)
+ # deltas = self.compileDeltasOptimal(self.flags, deltas)
+ else:
+ deltas = self.compileDeltasForSpeed(self.flags, deltas)
+
+ data.extend(deltas)
+ return b"".join(data)
+
+ def compileDeltasGreedy(self, flags, deltas):
+ # Implements greedy algorithm for packing coordinate deltas:
+ # uses shortest representation one coordinate at a time.
+ compressedFlags = bytearray()
+ compressedXs = bytearray()
+ compressedYs = bytearray()
+ lastflag = None
+ repeat = 0
+ for flag, (x, y) in zip(flags, deltas):
+ # Oh, the horrors of TrueType
+ # do x
+ if x == 0:
+ flag = flag | flagXsame
+ elif -255 <= x <= 255:
+ flag = flag | flagXShort
+ if x > 0:
+ flag = flag | flagXsame
+ else:
+ x = -x
+ compressedXs.append(x)
+ else:
+ compressedXs.extend(struct.pack(">h", x))
+ # do y
+ if y == 0:
+ flag = flag | flagYsame
+ elif -255 <= y <= 255:
+ flag = flag | flagYShort
+ if y > 0:
+ flag = flag | flagYsame
+ else:
+ y = -y
+ compressedYs.append(y)
+ else:
+ compressedYs.extend(struct.pack(">h", y))
+ # handle repeating flags
+ if flag == lastflag and repeat != 255:
+ repeat = repeat + 1
+ if repeat == 1:
+ compressedFlags.append(flag)
+ else:
+ compressedFlags[-2] = flag | flagRepeat
+ compressedFlags[-1] = repeat
+ else:
+ repeat = 0
+ compressedFlags.append(flag)
+ lastflag = flag
+ return (compressedFlags, compressedXs, compressedYs)
+
+ def compileDeltasOptimal(self, flags, deltas):
+ # Implements optimal, dynaic-programming, algorithm for packing coordinate
+ # deltas. The savings are negligible :(.
+ candidates = []
+ bestTuple = None
+ bestCost = 0
+ repeat = 0
+ for flag, (x, y) in zip(flags, deltas):
+ # Oh, the horrors of TrueType
+ flag, coordBytes = flagBest(x, y, flag)
+ bestCost += 1 + coordBytes
+ newCandidates = [
+ (bestCost, bestTuple, flag, coordBytes),
+ (bestCost + 1, bestTuple, (flag | flagRepeat), coordBytes),
+ ]
+ for lastCost, lastTuple, lastFlag, coordBytes in candidates:
+ if (
+ lastCost + coordBytes <= bestCost + 1
+ and (lastFlag & flagRepeat)
+ and (lastFlag < 0xFF00)
+ and flagSupports(lastFlag, flag)
+ ):
+ if (lastFlag & 0xFF) == (
+ flag | flagRepeat
+ ) and lastCost == bestCost + 1:
+ continue
+ newCandidates.append(
+ (lastCost + coordBytes, lastTuple, lastFlag + 256, coordBytes)
+ )
+ candidates = newCandidates
+ bestTuple = min(candidates, key=lambda t: t[0])
+ bestCost = bestTuple[0]
+
+ flags = []
+ while bestTuple:
+ cost, bestTuple, flag, coordBytes = bestTuple
+ flags.append(flag)
+ flags.reverse()
+
+ compressedFlags = bytearray()
+ compressedXs = bytearray()
+ compressedYs = bytearray()
+ coords = iter(deltas)
+ ff = []
+ for flag in flags:
+ repeatCount, flag = flag >> 8, flag & 0xFF
+ compressedFlags.append(flag)
+ if flag & flagRepeat:
+ assert repeatCount > 0
+ compressedFlags.append(repeatCount)
+ else:
+ assert repeatCount == 0
+ for i in range(1 + repeatCount):
+ x, y = next(coords)
+ flagEncodeCoords(flag, x, y, compressedXs, compressedYs)
+ ff.append(flag)
+ try:
+ next(coords)
+ raise Exception("internal error")
+ except StopIteration:
+ pass
+
+ return (compressedFlags, compressedXs, compressedYs)
+
+ def compileDeltasForSpeed(self, flags, deltas):
+ # uses widest representation needed, for all deltas.
+ compressedFlags = bytearray()
+ compressedXs = bytearray()
+ compressedYs = bytearray()
+
+ # Compute the necessary width for each axis
+ xs = [d[0] for d in deltas]
+ ys = [d[1] for d in deltas]
+ minX, minY, maxX, maxY = min(xs), min(ys), max(xs), max(ys)
+ xZero = minX == 0 and maxX == 0
+ yZero = minY == 0 and maxY == 0
+ xShort = -255 <= minX <= maxX <= 255
+ yShort = -255 <= minY <= maxY <= 255
+
+ lastflag = None
+ repeat = 0
+ for flag, (x, y) in zip(flags, deltas):
+ # Oh, the horrors of TrueType
+ # do x
+ if xZero:
+ flag = flag | flagXsame
+ elif xShort:
+ flag = flag | flagXShort
+ if x > 0:
+ flag = flag | flagXsame
+ else:
+ x = -x
+ compressedXs.append(x)
+ else:
+ compressedXs.extend(struct.pack(">h", x))
+ # do y
+ if yZero:
+ flag = flag | flagYsame
+ elif yShort:
+ flag = flag | flagYShort
+ if y > 0:
+ flag = flag | flagYsame
+ else:
+ y = -y
+ compressedYs.append(y)
+ else:
+ compressedYs.extend(struct.pack(">h", y))
+ # handle repeating flags
+ if flag == lastflag and repeat != 255:
+ repeat = repeat + 1
+ if repeat == 1:
+ compressedFlags.append(flag)
+ else:
+ compressedFlags[-2] = flag | flagRepeat
+ compressedFlags[-1] = repeat
+ else:
+ repeat = 0
+ compressedFlags.append(flag)
+ lastflag = flag
+ return (compressedFlags, compressedXs, compressedYs)
+
+ def recalcBounds(self, glyfTable, *, boundsDone=None):
+ """Recalculates the bounds of the glyph.
+
+ Each glyph object stores its bounding box in the
+ ``xMin``/``yMin``/``xMax``/``yMax`` attributes. These bounds must be
+ recomputed when the ``coordinates`` change. The ``table__g_l_y_f`` bounds
+ must be provided to resolve component bounds.
+ """
+ if self.isComposite() and self.tryRecalcBoundsComposite(
+ glyfTable, boundsDone=boundsDone
+ ):
+ return
+ try:
+ coords, endPts, flags = self.getCoordinates(glyfTable, round=otRound)
+ self.xMin, self.yMin, self.xMax, self.yMax = coords.calcIntBounds()
+ except NotImplementedError:
+ pass
+
+ def tryRecalcBoundsComposite(self, glyfTable, *, boundsDone=None):
+ """Try recalculating the bounds of a composite glyph that has
+ certain constrained properties. Namely, none of the components
+ have a transform other than an integer translate, and none
+ uses the anchor points.
+
+ Each glyph object stores its bounding box in the
+ ``xMin``/``yMin``/``xMax``/``yMax`` attributes. These bounds must be
+ recomputed when the ``coordinates`` change. The ``table__g_l_y_f`` bounds
+ must be provided to resolve component bounds.
+
+ Return True if bounds were calculated, False otherwise.
+ """
+ for compo in self.components:
+ if not compo._hasOnlyIntegerTranslate():
+ return False
+
+ # All components are untransformed and have an integer x/y translate
+ bounds = None
+ for compo in self.components:
+ glyphName = compo.glyphName
+ g = glyfTable[glyphName]
+
+ if boundsDone is None or glyphName not in boundsDone:
+ g.recalcBounds(glyfTable, boundsDone=boundsDone)
+ if boundsDone is not None:
+ boundsDone.add(glyphName)
+ # empty components shouldn't update the bounds of the parent glyph
+ if g.yMin == g.yMax and g.xMin == g.xMax:
+ continue
+
+ x, y = compo.x, compo.y
+ bounds = updateBounds(bounds, (g.xMin + x, g.yMin + y))
+ bounds = updateBounds(bounds, (g.xMax + x, g.yMax + y))
+
+ if bounds is None:
+ bounds = (0, 0, 0, 0)
+ self.xMin, self.yMin, self.xMax, self.yMax = bounds
+ return True
+
+ def isComposite(self):
+ """Test whether a glyph has components"""
+ if hasattr(self, "data"):
+ return struct.unpack(">h", self.data[:2])[0] == -1 if self.data else False
+ else:
+ return self.numberOfContours == -1
+
+ def getCoordinates(self, glyfTable, *, round=noRound):
+ """Return the coordinates, end points and flags
+
+ This method returns three values: A :py:class:`GlyphCoordinates` object,
+ a list of the indexes of the final points of each contour (allowing you
+ to split up the coordinates list into contours) and a list of flags.
+
+ On simple glyphs, this method returns information from the glyph's own
+ contours; on composite glyphs, it "flattens" all components recursively
+ to return a list of coordinates representing all the components involved
+ in the glyph.
+
+ To interpret the flags for each point, see the "Simple Glyph Flags"
+ section of the `glyf table specification `.
+ """
+
+ if self.numberOfContours > 0:
+ return self.coordinates, self.endPtsOfContours, self.flags
+ elif self.isComposite():
+ # it's a composite
+ allCoords = GlyphCoordinates()
+ allFlags = bytearray()
+ allEndPts = []
+ for compo in self.components:
+ g = glyfTable[compo.glyphName]
+ try:
+ coordinates, endPts, flags = g.getCoordinates(
+ glyfTable, round=round
+ )
+ except RecursionError:
+ raise ttLib.TTLibError(
+ "glyph '%s' contains a recursive component reference"
+ % compo.glyphName
+ )
+ coordinates = GlyphCoordinates(coordinates)
+ # if asked to round e.g. while computing bboxes, it's important we
+ # do it immediately before a component transform is applied to a
+ # simple glyph's coordinates in case these might still contain floats;
+ # however, if the referenced component glyph is another composite, we
+ # must not round here but only at the end, after all the nested
+ # transforms have been applied, or else rounding errors will compound.
+ if round is not noRound and g.numberOfContours > 0:
+ coordinates.toInt(round=round)
+ if hasattr(compo, "firstPt"):
+ # component uses two reference points: we apply the transform _before_
+ # computing the offset between the points
+ if hasattr(compo, "transform"):
+ coordinates.transform(compo.transform)
+ x1, y1 = allCoords[compo.firstPt]
+ x2, y2 = coordinates[compo.secondPt]
+ move = x1 - x2, y1 - y2
+ coordinates.translate(move)
+ else:
+ # component uses XY offsets
+ move = compo.x, compo.y
+ if not hasattr(compo, "transform"):
+ coordinates.translate(move)
+ else:
+ apple_way = compo.flags & SCALED_COMPONENT_OFFSET
+ ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
+ assert not (apple_way and ms_way)
+ if not (apple_way or ms_way):
+ scale_component_offset = (
+ SCALE_COMPONENT_OFFSET_DEFAULT # see top of this file
+ )
+ else:
+ scale_component_offset = apple_way
+ if scale_component_offset:
+ # the Apple way: first move, then scale (ie. scale the component offset)
+ coordinates.translate(move)
+ coordinates.transform(compo.transform)
+ else:
+ # the MS way: first scale, then move
+ coordinates.transform(compo.transform)
+ coordinates.translate(move)
+ offset = len(allCoords)
+ allEndPts.extend(e + offset for e in endPts)
+ allCoords.extend(coordinates)
+ allFlags.extend(flags)
+ return allCoords, allEndPts, allFlags
+ else:
+ return GlyphCoordinates(), [], bytearray()
+
+ def getComponentNames(self, glyfTable):
+ """Returns a list of names of component glyphs used in this glyph
+
+ This method can be used on simple glyphs (in which case it returns an
+ empty list) or composite glyphs.
+ """
+ if not hasattr(self, "data"):
+ if self.isComposite():
+ return [c.glyphName for c in self.components]
+ else:
+ return []
+
+ # Extract components without expanding glyph
+
+ if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
+ return [] # Not composite
+
+ data = self.data
+ i = 10
+ components = []
+ more = 1
+ while more:
+ flags, glyphID = struct.unpack(">HH", data[i : i + 4])
+ i += 4
+ flags = int(flags)
+ components.append(glyfTable.getGlyphName(int(glyphID)))
+
+ if flags & ARG_1_AND_2_ARE_WORDS:
+ i += 4
+ else:
+ i += 2
+ if flags & WE_HAVE_A_SCALE:
+ i += 2
+ elif flags & WE_HAVE_AN_X_AND_Y_SCALE:
+ i += 4
+ elif flags & WE_HAVE_A_TWO_BY_TWO:
+ i += 8
+ more = flags & MORE_COMPONENTS
+
+ return components
+
+ def trim(self, remove_hinting=False):
+ """Remove padding and, if requested, hinting, from a glyph.
+ This works on both expanded and compacted glyphs, without
+ expanding it."""
+ if not hasattr(self, "data"):
+ if remove_hinting:
+ if self.isComposite():
+ if hasattr(self, "program"):
+ del self.program
+ else:
+ self.program = ttProgram.Program()
+ self.program.fromBytecode([])
+ # No padding to trim.
+ return
+ if not self.data:
+ return
+ numContours = struct.unpack(">h", self.data[:2])[0]
+ data = bytearray(self.data)
+ i = 10
+ if numContours >= 0:
+ i += 2 * numContours # endPtsOfContours
+ nCoordinates = ((data[i - 2] << 8) | data[i - 1]) + 1
+ instructionLen = (data[i] << 8) | data[i + 1]
+ if remove_hinting:
+ # Zero instruction length
+ data[i] = data[i + 1] = 0
+ i += 2
+ if instructionLen:
+ # Splice it out
+ data = data[:i] + data[i + instructionLen :]
+ instructionLen = 0
+ else:
+ i += 2 + instructionLen
+
+ coordBytes = 0
+ j = 0
+ while True:
+ flag = data[i]
+ i = i + 1
+ repeat = 1
+ if flag & flagRepeat:
+ repeat = data[i] + 1
+ i = i + 1
+ xBytes = yBytes = 0
+ if flag & flagXShort:
+ xBytes = 1
+ elif not (flag & flagXsame):
+ xBytes = 2
+ if flag & flagYShort:
+ yBytes = 1
+ elif not (flag & flagYsame):
+ yBytes = 2
+ coordBytes += (xBytes + yBytes) * repeat
+ j += repeat
+ if j >= nCoordinates:
+ break
+ assert j == nCoordinates, "bad glyph flags"
+ i += coordBytes
+ # Remove padding
+ data = data[:i]
+ elif self.isComposite():
+ more = 1
+ we_have_instructions = False
+ while more:
+ flags = (data[i] << 8) | data[i + 1]
+ if remove_hinting:
+ flags &= ~WE_HAVE_INSTRUCTIONS
+ if flags & WE_HAVE_INSTRUCTIONS:
+ we_have_instructions = True
+ data[i + 0] = flags >> 8
+ data[i + 1] = flags & 0xFF
+ i += 4
+ flags = int(flags)
+
+ if flags & ARG_1_AND_2_ARE_WORDS:
+ i += 4
+ else:
+ i += 2
+ if flags & WE_HAVE_A_SCALE:
+ i += 2
+ elif flags & WE_HAVE_AN_X_AND_Y_SCALE:
+ i += 4
+ elif flags & WE_HAVE_A_TWO_BY_TWO:
+ i += 8
+ more = flags & MORE_COMPONENTS
+ if we_have_instructions:
+ instructionLen = (data[i] << 8) | data[i + 1]
+ i += 2 + instructionLen
+ # Remove padding
+ data = data[:i]
+
+ self.data = data
+
+ def removeHinting(self):
+ """Removes TrueType hinting instructions from the glyph."""
+ self.trim(remove_hinting=True)
+
+ def draw(self, pen, glyfTable, offset=0):
+ """Draws the glyph using the supplied pen object.
+
+ Arguments:
+ pen: An object conforming to the pen protocol.
+ glyfTable: A :py:class:`table__g_l_y_f` object, to resolve components.
+ offset (int): A horizontal offset. If provided, all coordinates are
+ translated by this offset.
+ """
+
+ if self.isComposite():
+ for component in self.components:
+ glyphName, transform = component.getComponentInfo()
+ pen.addComponent(glyphName, transform)
+ return
+
+ self.expand(glyfTable)
+ coordinates, endPts, flags = self.getCoordinates(glyfTable)
+ if offset:
+ coordinates = coordinates.copy()
+ coordinates.translate((offset, 0))
+ start = 0
+ maybeInt = lambda v: int(v) if v == int(v) else v
+ for end in endPts:
+ end = end + 1
+ contour = coordinates[start:end]
+ cFlags = [flagOnCurve & f for f in flags[start:end]]
+ cuFlags = [flagCubic & f for f in flags[start:end]]
+ start = end
+ if 1 not in cFlags:
+ assert all(cuFlags) or not any(cuFlags)
+ cubic = all(cuFlags)
+ if cubic:
+ count = len(contour)
+ assert count % 2 == 0, "Odd number of cubic off-curves undefined"
+ l = contour[-1]
+ f = contour[0]
+ p0 = (maybeInt((l[0] + f[0]) * 0.5), maybeInt((l[1] + f[1]) * 0.5))
+ pen.moveTo(p0)
+ for i in range(0, count, 2):
+ p1 = contour[i]
+ p2 = contour[i + 1]
+ p4 = contour[i + 2 if i + 2 < count else 0]
+ p3 = (
+ maybeInt((p2[0] + p4[0]) * 0.5),
+ maybeInt((p2[1] + p4[1]) * 0.5),
+ )
+ pen.curveTo(p1, p2, p3)
+ else:
+ # There is not a single on-curve point on the curve,
+ # use pen.qCurveTo's special case by specifying None
+ # as the on-curve point.
+ contour.append(None)
+ pen.qCurveTo(*contour)
+ else:
+ # Shuffle the points so that the contour is guaranteed
+ # to *end* in an on-curve point, which we'll use for
+ # the moveTo.
+ firstOnCurve = cFlags.index(1) + 1
+ contour = contour[firstOnCurve:] + contour[:firstOnCurve]
+ cFlags = cFlags[firstOnCurve:] + cFlags[:firstOnCurve]
+ cuFlags = cuFlags[firstOnCurve:] + cuFlags[:firstOnCurve]
+ pen.moveTo(contour[-1])
+ while contour:
+ nextOnCurve = cFlags.index(1) + 1
+ if nextOnCurve == 1:
+ # Skip a final lineTo(), as it is implied by
+ # pen.closePath()
+ if len(contour) > 1:
+ pen.lineTo(contour[0])
+ else:
+ cubicFlags = [f for f in cuFlags[: nextOnCurve - 1]]
+ assert all(cubicFlags) or not any(cubicFlags)
+ cubic = any(cubicFlags)
+ if cubic:
+ assert all(
+ cubicFlags
+ ), "Mixed cubic and quadratic segment undefined"
+
+ count = nextOnCurve
+ assert (
+ count >= 3
+ ), "At least two cubic off-curve points required"
+ assert (
+ count - 1
+ ) % 2 == 0, "Odd number of cubic off-curves undefined"
+ for i in range(0, count - 3, 2):
+ p1 = contour[i]
+ p2 = contour[i + 1]
+ p4 = contour[i + 2]
+ p3 = (
+ maybeInt((p2[0] + p4[0]) * 0.5),
+ maybeInt((p2[1] + p4[1]) * 0.5),
+ )
+ lastOnCurve = p3
+ pen.curveTo(p1, p2, p3)
+ pen.curveTo(*contour[count - 3 : count])
+ else:
+ pen.qCurveTo(*contour[:nextOnCurve])
+ contour = contour[nextOnCurve:]
+ cFlags = cFlags[nextOnCurve:]
+ cuFlags = cuFlags[nextOnCurve:]
+ pen.closePath()
+
+ def drawPoints(self, pen, glyfTable, offset=0):
+ """Draw the glyph using the supplied pointPen. As opposed to Glyph.draw(),
+ this will not change the point indices.
+ """
+
+ if self.isComposite():
+ for component in self.components:
+ glyphName, transform = component.getComponentInfo()
+ pen.addComponent(glyphName, transform)
+ return
+
+ coordinates, endPts, flags = self.getCoordinates(glyfTable)
+ if offset:
+ coordinates = coordinates.copy()
+ coordinates.translate((offset, 0))
+ start = 0
+ for end in endPts:
+ end = end + 1
+ contour = coordinates[start:end]
+ cFlags = flags[start:end]
+ start = end
+ pen.beginPath()
+ # Start with the appropriate segment type based on the final segment
+
+ if cFlags[-1] & flagOnCurve:
+ segmentType = "line"
+ elif cFlags[-1] & flagCubic:
+ segmentType = "curve"
+ else:
+ segmentType = "qcurve"
+ for i, pt in enumerate(contour):
+ if cFlags[i] & flagOnCurve:
+ pen.addPoint(pt, segmentType=segmentType)
+ segmentType = "line"
+ else:
+ pen.addPoint(pt)
+ segmentType = "curve" if cFlags[i] & flagCubic else "qcurve"
+ pen.endPath()
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+
+# Vector.__round__ uses the built-in (Banker's) `round` but we want
+# to use otRound below
+_roundv = partial(Vector.__round__, round=otRound)
+
+
+def _is_mid_point(p0: tuple, p1: tuple, p2: tuple) -> bool:
+ # True if p1 is in the middle of p0 and p2, either before or after rounding
+ p0 = Vector(p0)
+ p1 = Vector(p1)
+ p2 = Vector(p2)
+ return ((p0 + p2) * 0.5).isclose(p1) or _roundv(p0) + _roundv(p2) == _roundv(p1) * 2
+
+
+def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
+ """Drop impliable on-curve points from the (simple) glyph or glyphs.
+
+ In TrueType glyf outlines, on-curve points can be implied when they are located at
+ the midpoint of the line connecting two consecutive off-curve points.
+
+ If more than one glyphs are passed, these are assumed to be interpolatable masters
+ of the same glyph impliable, and thus only the on-curve points that are impliable
+ for all of them will actually be implied.
+ Composite glyphs or empty glyphs are skipped, only simple glyphs with 1 or more
+ contours are considered.
+ The input glyph(s) is/are modified in-place.
+
+ Args:
+ interpolatable_glyphs: The glyph or glyphs to modify in-place.
+
+ Returns:
+ The set of point indices that were dropped if any.
+
+ Raises:
+ ValueError if simple glyphs are not in fact interpolatable because they have
+ different point flags or number of contours.
+
+ Reference:
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
+ """
+ staticAttributes = SimpleNamespace(
+ numberOfContours=None, flags=None, endPtsOfContours=None
+ )
+ drop = None
+ simple_glyphs = []
+ for i, glyph in enumerate(interpolatable_glyphs):
+ if glyph.numberOfContours < 1:
+ # ignore composite or empty glyphs
+ continue
+
+ for attr in staticAttributes.__dict__:
+ expected = getattr(staticAttributes, attr)
+ found = getattr(glyph, attr)
+ if expected is None:
+ setattr(staticAttributes, attr, found)
+ elif expected != found:
+ raise ValueError(
+ f"Incompatible {attr} for glyph at master index {i}: "
+ f"expected {expected}, found {found}"
+ )
+
+ may_drop = set()
+ start = 0
+ coords = glyph.coordinates
+ flags = staticAttributes.flags
+ endPtsOfContours = staticAttributes.endPtsOfContours
+ for last in endPtsOfContours:
+ for i in range(start, last + 1):
+ if not (flags[i] & flagOnCurve):
+ continue
+ prv = i - 1 if i > start else last
+ nxt = i + 1 if i < last else start
+ if (flags[prv] & flagOnCurve) or flags[prv] != flags[nxt]:
+ continue
+ # we may drop the ith on-curve if halfway between previous/next off-curves
+ if not _is_mid_point(coords[prv], coords[i], coords[nxt]):
+ continue
+
+ may_drop.add(i)
+ start = last + 1
+ # we only want to drop if ALL interpolatable glyphs have the same implied oncurves
+ if drop is None:
+ drop = may_drop
+ else:
+ drop.intersection_update(may_drop)
+
+ simple_glyphs.append(glyph)
+
+ if drop:
+ # Do the actual dropping
+ flags = staticAttributes.flags
+ assert flags is not None
+ newFlags = array.array(
+ "B", (flags[i] for i in range(len(flags)) if i not in drop)
+ )
+
+ endPts = staticAttributes.endPtsOfContours
+ assert endPts is not None
+ newEndPts = []
+ i = 0
+ delta = 0
+ for d in sorted(drop):
+ while d > endPts[i]:
+ newEndPts.append(endPts[i] - delta)
+ i += 1
+ delta += 1
+ while i < len(endPts):
+ newEndPts.append(endPts[i] - delta)
+ i += 1
+
+ for glyph in simple_glyphs:
+ coords = glyph.coordinates
+ glyph.coordinates = GlyphCoordinates(
+ coords[i] for i in range(len(coords)) if i not in drop
+ )
+ glyph.flags = newFlags
+ glyph.endPtsOfContours = newEndPts
+
+ return drop if drop is not None else set()
+
+
+class GlyphComponent(object):
+ """Represents a component within a composite glyph.
+
+ The component is represented internally with four attributes: ``glyphName``,
+ ``x``, ``y`` and ``transform``. If there is no "two-by-two" matrix (i.e
+ no scaling, reflection, or rotation; only translation), the ``transform``
+ attribute is not present.
+ """
+
+ # The above documentation is not *completely* true, but is *true enough* because
+ # the rare firstPt/lastPt attributes are not totally supported and nobody seems to
+ # mind - see below.
+
+ def __init__(self):
+ pass
+
+ def getComponentInfo(self):
+ """Return information about the component
+
+ This method returns a tuple of two values: the glyph name of the component's
+ base glyph, and a transformation matrix. As opposed to accessing the attributes
+ directly, ``getComponentInfo`` always returns a six-element tuple of the
+ component's transformation matrix, even when the two-by-two ``.transform``
+ matrix is not present.
+ """
+ # XXX Ignoring self.firstPt & self.lastpt for now: I need to implement
+ # something equivalent in fontTools.objects.glyph (I'd rather not
+ # convert it to an absolute offset, since it is valuable information).
+ # This method will now raise "AttributeError: x" on glyphs that use
+ # this TT feature.
+ if hasattr(self, "transform"):
+ [[xx, xy], [yx, yy]] = self.transform
+ trans = (xx, xy, yx, yy, self.x, self.y)
+ else:
+ trans = (1, 0, 0, 1, self.x, self.y)
+ return self.glyphName, trans
+
+ def decompile(self, data, glyfTable):
+ flags, glyphID = struct.unpack(">HH", data[:4])
+ self.flags = int(flags)
+ glyphID = int(glyphID)
+ self.glyphName = glyfTable.getGlyphName(int(glyphID))
+ data = data[4:]
+
+ if self.flags & ARG_1_AND_2_ARE_WORDS:
+ if self.flags & ARGS_ARE_XY_VALUES:
+ self.x, self.y = struct.unpack(">hh", data[:4])
+ else:
+ x, y = struct.unpack(">HH", data[:4])
+ self.firstPt, self.secondPt = int(x), int(y)
+ data = data[4:]
+ else:
+ if self.flags & ARGS_ARE_XY_VALUES:
+ self.x, self.y = struct.unpack(">bb", data[:2])
+ else:
+ x, y = struct.unpack(">BB", data[:2])
+ self.firstPt, self.secondPt = int(x), int(y)
+ data = data[2:]
+
+ if self.flags & WE_HAVE_A_SCALE:
+ (scale,) = struct.unpack(">h", data[:2])
+ self.transform = [
+ [fi2fl(scale, 14), 0],
+ [0, fi2fl(scale, 14)],
+ ] # fixed 2.14
+ data = data[2:]
+ elif self.flags & WE_HAVE_AN_X_AND_Y_SCALE:
+ xscale, yscale = struct.unpack(">hh", data[:4])
+ self.transform = [
+ [fi2fl(xscale, 14), 0],
+ [0, fi2fl(yscale, 14)],
+ ] # fixed 2.14
+ data = data[4:]
+ elif self.flags & WE_HAVE_A_TWO_BY_TWO:
+ (xscale, scale01, scale10, yscale) = struct.unpack(">hhhh", data[:8])
+ self.transform = [
+ [fi2fl(xscale, 14), fi2fl(scale01, 14)],
+ [fi2fl(scale10, 14), fi2fl(yscale, 14)],
+ ] # fixed 2.14
+ data = data[8:]
+ more = self.flags & MORE_COMPONENTS
+ haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
+ self.flags = self.flags & (
+ ROUND_XY_TO_GRID
+ | USE_MY_METRICS
+ | SCALED_COMPONENT_OFFSET
+ | UNSCALED_COMPONENT_OFFSET
+ | NON_OVERLAPPING
+ | OVERLAP_COMPOUND
+ )
+ return more, haveInstructions, data
+
+ def compile(self, more, haveInstructions, glyfTable):
+ data = b""
+
+ # reset all flags we will calculate ourselves
+ flags = self.flags & (
+ ROUND_XY_TO_GRID
+ | USE_MY_METRICS
+ | SCALED_COMPONENT_OFFSET
+ | UNSCALED_COMPONENT_OFFSET
+ | NON_OVERLAPPING
+ | OVERLAP_COMPOUND
+ )
+ if more:
+ flags = flags | MORE_COMPONENTS
+ if haveInstructions:
+ flags = flags | WE_HAVE_INSTRUCTIONS
+
+ if hasattr(self, "firstPt"):
+ if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255):
+ data = data + struct.pack(">BB", self.firstPt, self.secondPt)
+ else:
+ data = data + struct.pack(">HH", self.firstPt, self.secondPt)
+ flags = flags | ARG_1_AND_2_ARE_WORDS
+ else:
+ x = otRound(self.x)
+ y = otRound(self.y)
+ flags = flags | ARGS_ARE_XY_VALUES
+ if (-128 <= x <= 127) and (-128 <= y <= 127):
+ data = data + struct.pack(">bb", x, y)
+ else:
+ data = data + struct.pack(">hh", x, y)
+ flags = flags | ARG_1_AND_2_ARE_WORDS
+
+ if hasattr(self, "transform"):
+ transform = [[fl2fi(x, 14) for x in row] for row in self.transform]
+ if transform[0][1] or transform[1][0]:
+ flags = flags | WE_HAVE_A_TWO_BY_TWO
+ data = data + struct.pack(
+ ">hhhh",
+ transform[0][0],
+ transform[0][1],
+ transform[1][0],
+ transform[1][1],
+ )
+ elif transform[0][0] != transform[1][1]:
+ flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
+ data = data + struct.pack(">hh", transform[0][0], transform[1][1])
+ else:
+ flags = flags | WE_HAVE_A_SCALE
+ data = data + struct.pack(">h", transform[0][0])
+
+ glyphID = glyfTable.getGlyphID(self.glyphName)
+ return struct.pack(">HH", flags, glyphID) + data
+
+ def toXML(self, writer, ttFont):
+ attrs = [("glyphName", self.glyphName)]
+ if not hasattr(self, "firstPt"):
+ attrs = attrs + [("x", self.x), ("y", self.y)]
+ else:
+ attrs = attrs + [("firstPt", self.firstPt), ("secondPt", self.secondPt)]
+
+ if hasattr(self, "transform"):
+ transform = self.transform
+ if transform[0][1] or transform[1][0]:
+ attrs = attrs + [
+ ("scalex", fl2str(transform[0][0], 14)),
+ ("scale01", fl2str(transform[0][1], 14)),
+ ("scale10", fl2str(transform[1][0], 14)),
+ ("scaley", fl2str(transform[1][1], 14)),
+ ]
+ elif transform[0][0] != transform[1][1]:
+ attrs = attrs + [
+ ("scalex", fl2str(transform[0][0], 14)),
+ ("scaley", fl2str(transform[1][1], 14)),
+ ]
+ else:
+ attrs = attrs + [("scale", fl2str(transform[0][0], 14))]
+ attrs = attrs + [("flags", hex(self.flags))]
+ writer.simpletag("component", attrs)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.glyphName = attrs["glyphName"]
+ if "firstPt" in attrs:
+ self.firstPt = safeEval(attrs["firstPt"])
+ self.secondPt = safeEval(attrs["secondPt"])
+ else:
+ self.x = safeEval(attrs["x"])
+ self.y = safeEval(attrs["y"])
+ if "scale01" in attrs:
+ scalex = str2fl(attrs["scalex"], 14)
+ scale01 = str2fl(attrs["scale01"], 14)
+ scale10 = str2fl(attrs["scale10"], 14)
+ scaley = str2fl(attrs["scaley"], 14)
+ self.transform = [[scalex, scale01], [scale10, scaley]]
+ elif "scalex" in attrs:
+ scalex = str2fl(attrs["scalex"], 14)
+ scaley = str2fl(attrs["scaley"], 14)
+ self.transform = [[scalex, 0], [0, scaley]]
+ elif "scale" in attrs:
+ scale = str2fl(attrs["scale"], 14)
+ self.transform = [[scale, 0], [0, scale]]
+ self.flags = safeEval(attrs["flags"])
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+ def _hasOnlyIntegerTranslate(self):
+ """Return True if it's a 'simple' component.
+
+ That is, it has no anchor points and no transform other than integer translate.
+ """
+ return (
+ not hasattr(self, "firstPt")
+ and not hasattr(self, "transform")
+ and float(self.x).is_integer()
+ and float(self.y).is_integer()
+ )
+
+
+class GlyphCoordinates(object):
+ """A list of glyph coordinates.
+
+ Unlike an ordinary list, this is a numpy-like matrix object which supports
+ matrix addition, scalar multiplication and other operations described below.
+ """
+
+ def __init__(self, iterable=[]):
+ self._a = array.array("d")
+ self.extend(iterable)
+
+ @property
+ def array(self):
+ """Returns the underlying array of coordinates"""
+ return self._a
+
+ @staticmethod
+ def zeros(count):
+ """Creates a new ``GlyphCoordinates`` object with all coordinates set to (0,0)"""
+ g = GlyphCoordinates()
+ g._a.frombytes(bytes(count * 2 * g._a.itemsize))
+ return g
+
+ def copy(self):
+ """Creates a new ``GlyphCoordinates`` object which is a copy of the current one."""
+ c = GlyphCoordinates()
+ c._a.extend(self._a)
+ return c
+
+ def __len__(self):
+ """Returns the number of coordinates in the array."""
+ return len(self._a) // 2
+
+ def __getitem__(self, k):
+ """Returns a two element tuple (x,y)"""
+ a = self._a
+ if isinstance(k, slice):
+ indices = range(*k.indices(len(self)))
+ # Instead of calling ourselves recursively, duplicate code; faster
+ ret = []
+ for k in indices:
+ x = a[2 * k]
+ y = a[2 * k + 1]
+ ret.append(
+ (int(x) if x.is_integer() else x, int(y) if y.is_integer() else y)
+ )
+ return ret
+ x = a[2 * k]
+ y = a[2 * k + 1]
+ return (int(x) if x.is_integer() else x, int(y) if y.is_integer() else y)
+
+ def __setitem__(self, k, v):
+ """Sets a point's coordinates to a two element tuple (x,y)"""
+ if isinstance(k, slice):
+ indices = range(*k.indices(len(self)))
+ # XXX This only works if len(v) == len(indices)
+ for j, i in enumerate(indices):
+ self[i] = v[j]
+ return
+ self._a[2 * k], self._a[2 * k + 1] = v
+
+ def __delitem__(self, i):
+ """Removes a point from the list"""
+ i = (2 * i) % len(self._a)
+ del self._a[i]
+ del self._a[i]
+
+ def __repr__(self):
+ return "GlyphCoordinates([" + ",".join(str(c) for c in self) + "])"
+
+ def append(self, p):
+ self._a.extend(tuple(p))
+
+ def extend(self, iterable):
+ for p in iterable:
+ self._a.extend(p)
+
+ def toInt(self, *, round=otRound):
+ if round is noRound:
+ return
+ a = self._a
+ for i, value in enumerate(a):
+ a[i] = round(value)
+
+ def calcBounds(self):
+ a = self._a
+ if not a:
+ return 0, 0, 0, 0
+ xs = a[0::2]
+ ys = a[1::2]
+ return min(xs), min(ys), max(xs), max(ys)
+
+ def calcIntBounds(self, round=otRound):
+ return tuple(round(v) for v in self.calcBounds())
+
+ def relativeToAbsolute(self):
+ a = self._a
+ x, y = 0, 0
+ for i in range(0, len(a), 2):
+ a[i] = x = a[i] + x
+ a[i + 1] = y = a[i + 1] + y
+
+ def absoluteToRelative(self):
+ a = self._a
+ x, y = 0, 0
+ for i in range(0, len(a), 2):
+ nx = a[i]
+ ny = a[i + 1]
+ a[i] = nx - x
+ a[i + 1] = ny - y
+ x = nx
+ y = ny
+
+ def translate(self, p):
+ """
+ >>> GlyphCoordinates([(1,2)]).translate((.5,0))
+ """
+ x, y = p
+ if x == 0 and y == 0:
+ return
+ a = self._a
+ for i in range(0, len(a), 2):
+ a[i] += x
+ a[i + 1] += y
+
+ def scale(self, p):
+ """
+ >>> GlyphCoordinates([(1,2)]).scale((.5,0))
+ """
+ x, y = p
+ if x == 1 and y == 1:
+ return
+ a = self._a
+ for i in range(0, len(a), 2):
+ a[i] *= x
+ a[i + 1] *= y
+
+ def transform(self, t):
+ """
+ >>> GlyphCoordinates([(1,2)]).transform(((.5,0),(.2,.5)))
+ """
+ a = self._a
+ for i in range(0, len(a), 2):
+ x = a[i]
+ y = a[i + 1]
+ px = x * t[0][0] + y * t[1][0]
+ py = x * t[0][1] + y * t[1][1]
+ a[i] = px
+ a[i + 1] = py
+
+ def __eq__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g2 = GlyphCoordinates([(1.0,2)])
+ >>> g3 = GlyphCoordinates([(1.5,2)])
+ >>> g == g2
+ True
+ >>> g == g3
+ False
+ >>> g2 == g3
+ False
+ """
+ if type(self) != type(other):
+ return NotImplemented
+ return self._a == other._a
+
+ def __ne__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g2 = GlyphCoordinates([(1.0,2)])
+ >>> g3 = GlyphCoordinates([(1.5,2)])
+ >>> g != g2
+ False
+ >>> g != g3
+ True
+ >>> g2 != g3
+ True
+ """
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+ # Math operations
+
+ def __pos__(self):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g
+ GlyphCoordinates([(1, 2)])
+ >>> g2 = +g
+ >>> g2
+ GlyphCoordinates([(1, 2)])
+ >>> g2.translate((1,0))
+ >>> g2
+ GlyphCoordinates([(2, 2)])
+ >>> g
+ GlyphCoordinates([(1, 2)])
+ """
+ return self.copy()
+
+ def __neg__(self):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g
+ GlyphCoordinates([(1, 2)])
+ >>> g2 = -g
+ >>> g2
+ GlyphCoordinates([(-1, -2)])
+ >>> g
+ GlyphCoordinates([(1, 2)])
+ """
+ r = self.copy()
+ a = r._a
+ for i, value in enumerate(a):
+ a[i] = -value
+ return r
+
+ def __round__(self, *, round=otRound):
+ r = self.copy()
+ r.toInt(round=round)
+ return r
+
+ def __add__(self, other):
+ return self.copy().__iadd__(other)
+
+ def __sub__(self, other):
+ return self.copy().__isub__(other)
+
+ def __mul__(self, other):
+ return self.copy().__imul__(other)
+
+ def __truediv__(self, other):
+ return self.copy().__itruediv__(other)
+
+ __radd__ = __add__
+ __rmul__ = __mul__
+
+ def __rsub__(self, other):
+ return other + (-self)
+
+ def __iadd__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g += (.5,0)
+ >>> g
+ GlyphCoordinates([(1.5, 2)])
+ >>> g2 = GlyphCoordinates([(3,4)])
+ >>> g += g2
+ >>> g
+ GlyphCoordinates([(4.5, 6)])
+ """
+ if isinstance(other, tuple):
+ assert len(other) == 2
+ self.translate(other)
+ return self
+ if isinstance(other, GlyphCoordinates):
+ other = other._a
+ a = self._a
+ assert len(a) == len(other)
+ for i, value in enumerate(other):
+ a[i] += value
+ return self
+ return NotImplemented
+
+ def __isub__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g -= (.5,0)
+ >>> g
+ GlyphCoordinates([(0.5, 2)])
+ >>> g2 = GlyphCoordinates([(3,4)])
+ >>> g -= g2
+ >>> g
+ GlyphCoordinates([(-2.5, -2)])
+ """
+ if isinstance(other, tuple):
+ assert len(other) == 2
+ self.translate((-other[0], -other[1]))
+ return self
+ if isinstance(other, GlyphCoordinates):
+ other = other._a
+ a = self._a
+ assert len(a) == len(other)
+ for i, value in enumerate(other):
+ a[i] -= value
+ return self
+ return NotImplemented
+
+ def __imul__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g *= (2,.5)
+ >>> g *= 2
+ >>> g
+ GlyphCoordinates([(4, 2)])
+ >>> g = GlyphCoordinates([(1,2)])
+ >>> g *= 2
+ >>> g
+ GlyphCoordinates([(2, 4)])
+ """
+ if isinstance(other, tuple):
+ assert len(other) == 2
+ self.scale(other)
+ return self
+ if isinstance(other, Number):
+ if other == 1:
+ return self
+ a = self._a
+ for i in range(len(a)):
+ a[i] *= other
+ return self
+ return NotImplemented
+
+ def __itruediv__(self, other):
+ """
+ >>> g = GlyphCoordinates([(1,3)])
+ >>> g /= (.5,1.5)
+ >>> g /= 2
+ >>> g
+ GlyphCoordinates([(1, 1)])
+ """
+ if isinstance(other, Number):
+ other = (other, other)
+ if isinstance(other, tuple):
+ if other == (1, 1):
+ return self
+ assert len(other) == 2
+ self.scale((1.0 / other[0], 1.0 / other[1]))
+ return self
+ return NotImplemented
+
+ def __bool__(self):
+ """
+ >>> g = GlyphCoordinates([])
+ >>> bool(g)
+ False
+ >>> g = GlyphCoordinates([(0,0), (0.,0)])
+ >>> bool(g)
+ True
+ >>> g = GlyphCoordinates([(0,0), (1,0)])
+ >>> bool(g)
+ True
+ >>> g = GlyphCoordinates([(0,.5), (0,0)])
+ >>> bool(g)
+ True
+ """
+ return bool(self._a)
+
+ __nonzero__ = __bool__
+
+
+if __name__ == "__main__":
+ import doctest, sys
+
+ sys.exit(doctest.testmod().failed)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_v_a_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_v_a_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..15cc6fab5bd1b92fa0f778eb6d9952b195843786
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_g_v_a_r.py
@@ -0,0 +1,340 @@
+from collections import deque
+from functools import partial
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.lazyTools import LazyDict
+from fontTools.ttLib import OPTIMIZE_FONT_SPEED
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from . import DefaultTable
+import array
+import itertools
+import logging
+import struct
+import sys
+import fontTools.ttLib.tables.TupleVariation as tv
+
+log = logging.getLogger(__name__)
+
+# https://www.microsoft.com/typography/otspec/gvar.htm
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+#
+# Apple's documentation of 'gvar':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
+#
+# FreeType2 source code for parsing 'gvar':
+# http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
+
+GVAR_HEADER_FORMAT_HEAD = """
+ > # big endian
+ version: H
+ reserved: H
+ axisCount: H
+ sharedTupleCount: H
+ offsetToSharedTuples: I
+"""
+# In between the HEAD and TAIL lies the glyphCount, which is
+# of different size: 2 bytes for gvar, and 3 bytes for GVAR.
+GVAR_HEADER_FORMAT_TAIL = """
+ > # big endian
+ flags: H
+ offsetToGlyphVariationData: I
+"""
+
+GVAR_HEADER_SIZE_HEAD = sstruct.calcsize(GVAR_HEADER_FORMAT_HEAD)
+GVAR_HEADER_SIZE_TAIL = sstruct.calcsize(GVAR_HEADER_FORMAT_TAIL)
+
+
+class table__g_v_a_r(DefaultTable.DefaultTable):
+ """Glyph Variations table
+
+ The ``gvar`` table provides the per-glyph variation data that
+ describe how glyph outlines in the ``glyf`` table change across
+ the variation space that is defined for the font in the ``fvar``
+ table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/gvar
+ """
+
+ dependencies = ["fvar", "glyf"]
+ gid_size = 2
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.version, self.reserved = 1, 0
+ self.variations = {}
+
+ def compile(self, ttFont):
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ sharedTuples = tv.compileSharedTuples(
+ axisTags, itertools.chain(*self.variations.values())
+ )
+ sharedTupleIndices = {coord: i for i, coord in enumerate(sharedTuples)}
+ sharedTupleSize = sum([len(c) for c in sharedTuples])
+ compiledGlyphs = self.compileGlyphs_(ttFont, axisTags, sharedTupleIndices)
+ offset = 0
+ offsets = []
+ for glyph in compiledGlyphs:
+ offsets.append(offset)
+ offset += len(glyph)
+ offsets.append(offset)
+ compiledOffsets, tableFormat = self.compileOffsets_(offsets)
+
+ GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
+ header = {}
+ header["version"] = self.version
+ header["reserved"] = self.reserved
+ header["axisCount"] = len(axisTags)
+ header["sharedTupleCount"] = len(sharedTuples)
+ header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
+ header["flags"] = tableFormat
+ header["offsetToGlyphVariationData"] = (
+ header["offsetToSharedTuples"] + sharedTupleSize
+ )
+
+ result = [
+ sstruct.pack(GVAR_HEADER_FORMAT_HEAD, header),
+ len(compiledGlyphs).to_bytes(self.gid_size, "big"),
+ sstruct.pack(GVAR_HEADER_FORMAT_TAIL, header),
+ ]
+
+ result.append(compiledOffsets)
+ result.extend(sharedTuples)
+ result.extend(compiledGlyphs)
+ return b"".join(result)
+
+ def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
+ optimizeSpeed = ttFont.cfg[OPTIMIZE_FONT_SPEED]
+ result = []
+ glyf = ttFont["glyf"]
+ for glyphName in ttFont.getGlyphOrder():
+ variations = self.variations.get(glyphName, [])
+ if not variations:
+ result.append(b"")
+ continue
+ pointCountUnused = 0 # pointCount is actually unused by compileGlyph
+ result.append(
+ compileGlyph_(
+ self.gid_size,
+ variations,
+ pointCountUnused,
+ axisTags,
+ sharedCoordIndices,
+ optimizeSize=not optimizeSpeed,
+ )
+ )
+ return result
+
+ def decompile(self, data, ttFont):
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ glyphs = ttFont.getGlyphOrder()
+
+ # Parse the header
+ GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
+ sstruct.unpack(GVAR_HEADER_FORMAT_HEAD, data[:GVAR_HEADER_SIZE_HEAD], self)
+ self.glyphCount = int.from_bytes(
+ data[GVAR_HEADER_SIZE_HEAD : GVAR_HEADER_SIZE_HEAD + self.gid_size], "big"
+ )
+ sstruct.unpack(
+ GVAR_HEADER_FORMAT_TAIL,
+ data[GVAR_HEADER_SIZE_HEAD + self.gid_size : GVAR_HEADER_SIZE],
+ self,
+ )
+
+ assert len(glyphs) == self.glyphCount, (len(glyphs), self.glyphCount)
+ assert len(axisTags) == self.axisCount, (
+ len(axisTags),
+ self.axisCount,
+ axisTags,
+ )
+ sharedCoords = tv.decompileSharedTuples(
+ axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples
+ )
+ variations = {}
+ offsetToData = self.offsetToGlyphVariationData
+ glyf = ttFont["glyf"]
+
+ def get_read_item():
+ reverseGlyphMap = ttFont.getReverseGlyphMap()
+ tableFormat = self.flags & 1
+
+ def read_item(glyphName):
+ gid = reverseGlyphMap[glyphName]
+ offsetSize = 2 if tableFormat == 0 else 4
+ startOffset = GVAR_HEADER_SIZE + offsetSize * gid
+ endOffset = startOffset + offsetSize * 2
+ offsets = table__g_v_a_r.decompileOffsets_(
+ data[startOffset:endOffset],
+ tableFormat=tableFormat,
+ glyphCount=1,
+ )
+ gvarData = data[offsetToData + offsets[0] : offsetToData + offsets[1]]
+ if not gvarData:
+ return []
+ glyph = glyf[glyphName]
+ numPointsInGlyph = self.getNumPoints_(glyph)
+ return decompileGlyph_(
+ self.gid_size, numPointsInGlyph, sharedCoords, axisTags, gvarData
+ )
+
+ return read_item
+
+ read_item = get_read_item()
+ l = LazyDict({glyphs[gid]: read_item for gid in range(self.glyphCount)})
+
+ self.variations = l
+
+ if ttFont.lazy is False: # Be lazy for None and True
+ self.ensureDecompiled()
+
+ def ensureDecompiled(self, recurse=False):
+ # The recurse argument is unused, but part of the signature of
+ # ensureDecompiled across the library.
+ # Use a zero-length deque to consume the lazy dict
+ deque(self.variations.values(), maxlen=0)
+
+ @staticmethod
+ def decompileOffsets_(data, tableFormat, glyphCount):
+ if tableFormat == 0:
+ # Short format: array of UInt16
+ offsets = array.array("H")
+ offsetsSize = (glyphCount + 1) * 2
+ else:
+ # Long format: array of UInt32
+ offsets = array.array("I")
+ offsetsSize = (glyphCount + 1) * 4
+ offsets.frombytes(data[0:offsetsSize])
+ if sys.byteorder != "big":
+ offsets.byteswap()
+
+ # In the short format, offsets need to be multiplied by 2.
+ # This is not documented in Apple's TrueType specification,
+ # but can be inferred from the FreeType implementation, and
+ # we could verify it with two sample GX fonts.
+ if tableFormat == 0:
+ offsets = [off * 2 for off in offsets]
+
+ return offsets
+
+ @staticmethod
+ def compileOffsets_(offsets):
+ """Packs a list of offsets into a 'gvar' offset table.
+
+ Returns a pair (bytestring, tableFormat). Bytestring is the
+ packed offset table. Format indicates whether the table
+ uses short (tableFormat=0) or long (tableFormat=1) integers.
+ The returned tableFormat should get packed into the flags field
+ of the 'gvar' header.
+ """
+ assert len(offsets) >= 2
+ for i in range(1, len(offsets)):
+ assert offsets[i - 1] <= offsets[i]
+ if max(offsets) <= 0xFFFF * 2:
+ packed = array.array("H", [n >> 1 for n in offsets])
+ tableFormat = 0
+ else:
+ packed = array.array("I", offsets)
+ tableFormat = 1
+ if sys.byteorder != "big":
+ packed.byteswap()
+ return (packed.tobytes(), tableFormat)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ writer.simpletag("reserved", value=self.reserved)
+ writer.newline()
+ axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+ for glyphName in ttFont.getGlyphNames():
+ variations = self.variations.get(glyphName)
+ if not variations:
+ continue
+ writer.begintag("glyphVariations", glyph=glyphName)
+ writer.newline()
+ for gvar in variations:
+ gvar.toXML(writer, axisTags)
+ writer.endtag("glyphVariations")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = safeEval(attrs["value"])
+ elif name == "reserved":
+ self.reserved = safeEval(attrs["value"])
+ elif name == "glyphVariations":
+ if not hasattr(self, "variations"):
+ self.variations = {}
+ glyphName = attrs["glyph"]
+ glyph = ttFont["glyf"][glyphName]
+ numPointsInGlyph = self.getNumPoints_(glyph)
+ glyphVariations = []
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ if name == "tuple":
+ gvar = TupleVariation({}, [None] * numPointsInGlyph)
+ glyphVariations.append(gvar)
+ for tupleElement in content:
+ if isinstance(tupleElement, tuple):
+ tupleName, tupleAttrs, tupleContent = tupleElement
+ gvar.fromXML(tupleName, tupleAttrs, tupleContent)
+ self.variations[glyphName] = glyphVariations
+
+ @staticmethod
+ def getNumPoints_(glyph):
+ NUM_PHANTOM_POINTS = 4
+
+ if glyph.isComposite():
+ return len(glyph.components) + NUM_PHANTOM_POINTS
+ else:
+ # Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute.
+ return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS
+
+
+def compileGlyph_(
+ dataOffsetSize,
+ variations,
+ pointCount,
+ axisTags,
+ sharedCoordIndices,
+ *,
+ optimizeSize=True,
+):
+ assert dataOffsetSize in (2, 3)
+ tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
+ variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize
+ )
+ if tupleVariationCount == 0:
+ return b""
+
+ offsetToData = 2 + dataOffsetSize + len(tuples)
+
+ result = [
+ tupleVariationCount.to_bytes(2, "big"),
+ offsetToData.to_bytes(dataOffsetSize, "big"),
+ tuples,
+ data,
+ ]
+ if (offsetToData + len(data)) % 2 != 0:
+ result.append(b"\0") # padding
+ return b"".join(result)
+
+
+def decompileGlyph_(dataOffsetSize, pointCount, sharedTuples, axisTags, data):
+ assert dataOffsetSize in (2, 3)
+ if len(data) < 2 + dataOffsetSize:
+ return []
+
+ tupleVariationCount = int.from_bytes(data[:2], "big")
+ offsetToData = int.from_bytes(data[2 : 2 + dataOffsetSize], "big")
+
+ dataPos = offsetToData
+ return tv.decompileTupleVariationStore(
+ "gvar",
+ axisTags,
+ tupleVariationCount,
+ pointCount,
+ sharedTuples,
+ data,
+ 2 + dataOffsetSize,
+ offsetToData,
+ )
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_d_m_x.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_d_m_x.py
new file mode 100644
index 0000000000000000000000000000000000000000..07c4566a98dcd36e9b3e2cdc9513793f902d1dd9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_d_m_x.py
@@ -0,0 +1,127 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytechr, byteord, strjoin
+from . import DefaultTable
+import array
+from collections.abc import Mapping
+
+hdmxHeaderFormat = """
+ > # big endian!
+ version: H
+ numRecords: H
+ recordSize: l
+"""
+
+
+class _GlyphnamedList(Mapping):
+ def __init__(self, reverseGlyphOrder, data):
+ self._array = data
+ self._map = dict(reverseGlyphOrder)
+
+ def __getitem__(self, k):
+ return self._array[self._map[k]]
+
+ def __len__(self):
+ return len(self._map)
+
+ def __iter__(self):
+ return iter(self._map)
+
+ def keys(self):
+ return self._map.keys()
+
+
+class table__h_d_m_x(DefaultTable.DefaultTable):
+ """Horizontal Device Metrics table
+
+ The ``hdmx`` table is an optional table that stores advance widths for
+ glyph outlines at specified pixel sizes.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/hdmx
+ """
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont["maxp"].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
+ self.hdmx = {}
+ for i in range(self.numRecords):
+ ppem = byteord(data[0])
+ maxSize = byteord(data[1])
+ widths = _GlyphnamedList(
+ ttFont.getReverseGlyphMap(), array.array("B", data[2 : 2 + numGlyphs])
+ )
+ self.hdmx[ppem] = widths
+ data = data[self.recordSize :]
+ assert len(data) == 0, "too much hdmx data"
+
+ def compile(self, ttFont):
+ self.version = 0
+ numGlyphs = ttFont["maxp"].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
+ pad = (self.recordSize - 2 - numGlyphs) * b"\0"
+ self.numRecords = len(self.hdmx)
+ data = sstruct.pack(hdmxHeaderFormat, self)
+ items = sorted(self.hdmx.items())
+ for ppem, widths in items:
+ data = data + bytechr(ppem) + bytechr(max(widths.values()))
+ for glyphName in glyphOrder:
+ width = widths[glyphName]
+ data = data + bytechr(width)
+ data = data + pad
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("hdmxData")
+ writer.newline()
+ ppems = sorted(self.hdmx.keys())
+ records = []
+ format = ""
+ for ppem in ppems:
+ widths = self.hdmx[ppem]
+ records.append(widths)
+ format = format + "%4d"
+ glyphNames = ttFont.getGlyphOrder()[:]
+ glyphNames.sort()
+ maxNameLen = max(map(len, glyphNames))
+ format = "%" + repr(maxNameLen) + "s:" + format + " ;"
+ writer.write(format % (("ppem",) + tuple(ppems)))
+ writer.newline()
+ writer.newline()
+ for glyphName in glyphNames:
+ row = []
+ for ppem in ppems:
+ widths = self.hdmx[ppem]
+ row.append(widths[glyphName])
+ if ";" in glyphName:
+ glyphName = "\\x3b".join(glyphName.split(";"))
+ writer.write(format % ((glyphName,) + tuple(row)))
+ writer.newline()
+ writer.endtag("hdmxData")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "hdmxData":
+ return
+ content = strjoin(content)
+ lines = content.split(";")
+ topRow = lines[0].split()
+ assert topRow[0] == "ppem:", "illegal hdmx format"
+ ppems = list(map(int, topRow[1:]))
+ self.hdmx = hdmx = {}
+ for ppem in ppems:
+ hdmx[ppem] = {}
+ lines = (line.split() for line in lines[1:])
+ for line in lines:
+ if not line:
+ continue
+ assert line[0][-1] == ":", "illegal hdmx format"
+ glyphName = line[0][:-1]
+ if "\\" in glyphName:
+ from fontTools.misc.textTools import safeEval
+
+ glyphName = safeEval('"""' + glyphName + '"""')
+ line = list(map(int, line[1:]))
+ assert len(line) == len(ppems), "illegal hdmx format"
+ for i, ppem in enumerate(ppems):
+ hdmx[ppem][glyphName] = line[i]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_e_a_d.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_e_a_d.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcadf68a275ce25748a50a3d257d50d849cb24fc
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_e_a_d.py
@@ -0,0 +1,130 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr, strToFixedToFloat
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.timeTools import (
+ timestampFromString,
+ timestampToString,
+ timestampNow,
+)
+from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff # For backward compat
+from fontTools.misc.arrayTools import intRect, unionRect
+from . import DefaultTable
+import logging
+
+
+log = logging.getLogger(__name__)
+
+headFormat = """
+ > # big endian
+ tableVersion: 16.16F
+ fontRevision: 16.16F
+ checkSumAdjustment: I
+ magicNumber: I
+ flags: H
+ unitsPerEm: H
+ created: Q
+ modified: Q
+ xMin: h
+ yMin: h
+ xMax: h
+ yMax: h
+ macStyle: H
+ lowestRecPPEM: H
+ fontDirectionHint: h
+ indexToLocFormat: h
+ glyphDataFormat: h
+"""
+
+
+class table__h_e_a_d(DefaultTable.DefaultTable):
+ """Font Header table
+
+ The ``head`` table contains a variety of font-wide information.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/head
+ """
+
+ dependencies = ["maxp", "loca", "CFF ", "CFF2"]
+
+ def decompile(self, data, ttFont):
+ dummy, rest = sstruct.unpack2(headFormat, data, self)
+ if rest:
+ # this is quite illegal, but there seem to be fonts out there that do this
+ log.warning("extra bytes at the end of 'head' table")
+ assert rest == b"\0\0"
+
+ # For timestamp fields, ignore the top four bytes. Some fonts have
+ # bogus values there. Since till 2038 those bytes only can be zero,
+ # ignore them.
+ #
+ # https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
+ for stamp in "created", "modified":
+ value = getattr(self, stamp)
+ if value > 0xFFFFFFFF:
+ log.warning("'%s' timestamp out of range; ignoring top bytes", stamp)
+ value &= 0xFFFFFFFF
+ setattr(self, stamp, value)
+ if value < 0x7C259DC0: # January 1, 1970 00:00:00
+ log.warning(
+ "'%s' timestamp seems very low; regarding as unix timestamp", stamp
+ )
+ value += 0x7C259DC0
+ setattr(self, stamp, value)
+
+ def compile(self, ttFont):
+ if ttFont.recalcBBoxes:
+ # For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc().
+ if "CFF " in ttFont:
+ topDict = ttFont["CFF "].cff.topDictIndex[0]
+ self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox)
+ elif "CFF2" in ttFont:
+ topDict = ttFont["CFF2"].cff.topDictIndex[0]
+ charStrings = topDict.CharStrings
+ fontBBox = None
+ for charString in charStrings.values():
+ bounds = charString.calcBounds(charStrings)
+ if bounds is not None:
+ if fontBBox is not None:
+ fontBBox = unionRect(fontBBox, bounds)
+ else:
+ fontBBox = bounds
+ if fontBBox is not None:
+ self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox)
+ if ttFont.recalcTimestamp:
+ self.modified = timestampNow()
+ data = sstruct.pack(headFormat, self)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ _, names, fixes = sstruct.getformat(headFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name in fixes:
+ value = floatToFixedToStr(value, precisionBits=fixes[name])
+ elif name in ("created", "modified"):
+ value = timestampToString(value)
+ elif name in ("magicNumber", "checkSumAdjustment"):
+ if value < 0:
+ value = value + 0x100000000
+ value = hex(value)
+ if value[-1:] == "L":
+ value = value[:-1]
+ elif name in ("macStyle", "flags"):
+ value = num2binary(value, 16)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ fixes = sstruct.getformat(headFormat)[2]
+ if name in fixes:
+ value = strToFixedToFloat(value, precisionBits=fixes[name])
+ elif name in ("created", "modified"):
+ value = timestampFromString(value)
+ elif name in ("macStyle", "flags"):
+ value = binary2num(value)
+ else:
+ value = safeEval(value)
+ setattr(self, name, value)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_h_e_a.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_h_e_a.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f051e499ceb223e9260da5b7bb76ad47ae174dd
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_h_e_a.py
@@ -0,0 +1,147 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import (
+ ensureVersionIsLong as fi2ve,
+ versionToFixed as ve2fi,
+)
+from . import DefaultTable
+import math
+
+
+hheaFormat = """
+ > # big endian
+ tableVersion: L
+ ascent: h
+ descent: h
+ lineGap: h
+ advanceWidthMax: H
+ minLeftSideBearing: h
+ minRightSideBearing: h
+ xMaxExtent: h
+ caretSlopeRise: h
+ caretSlopeRun: h
+ caretOffset: h
+ reserved0: h
+ reserved1: h
+ reserved2: h
+ reserved3: h
+ metricDataFormat: h
+ numberOfHMetrics: H
+"""
+
+
+class table__h_h_e_a(DefaultTable.DefaultTable):
+ """Horizontal Header table
+
+ The ``hhea`` table contains information needed during horizontal
+ text layout.
+
+ .. note::
+ This converter class is kept in sync with the :class:`._v_h_e_a.table__v_h_e_a`
+ table constructor.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/hhea
+ """
+
+ # Note: Keep in sync with table__v_h_e_a
+
+ dependencies = ["hmtx", "glyf", "CFF ", "CFF2"]
+
+ # OpenType spec renamed these, add aliases for compatibility
+ @property
+ def ascender(self):
+ return self.ascent
+
+ @ascender.setter
+ def ascender(self, value):
+ self.ascent = value
+
+ @property
+ def descender(self):
+ return self.descent
+
+ @descender.setter
+ def descender(self, value):
+ self.descent = value
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(hheaFormat, data, self)
+
+ def compile(self, ttFont):
+ if ttFont.recalcBBoxes and (
+ ttFont.isLoaded("glyf")
+ or ttFont.isLoaded("CFF ")
+ or ttFont.isLoaded("CFF2")
+ ):
+ self.recalc(ttFont)
+ self.tableVersion = fi2ve(self.tableVersion)
+ return sstruct.pack(hheaFormat, self)
+
+ def recalc(self, ttFont):
+ if "hmtx" not in ttFont:
+ return
+
+ hmtxTable = ttFont["hmtx"]
+ self.advanceWidthMax = max(adv for adv, _ in hmtxTable.metrics.values())
+
+ boundsWidthDict = {}
+ if "glyf" in ttFont:
+ glyfTable = ttFont["glyf"]
+ for name in ttFont.getGlyphOrder():
+ g = glyfTable[name]
+ if g.numberOfContours == 0:
+ continue
+ if g.numberOfContours < 0 and not hasattr(g, "xMax"):
+ # Composite glyph without extents set.
+ # Calculate those.
+ g.recalcBounds(glyfTable)
+ boundsWidthDict[name] = g.xMax - g.xMin
+ elif "CFF " in ttFont or "CFF2" in ttFont:
+ if "CFF " in ttFont:
+ topDict = ttFont["CFF "].cff.topDictIndex[0]
+ else:
+ topDict = ttFont["CFF2"].cff.topDictIndex[0]
+ charStrings = topDict.CharStrings
+ for name in ttFont.getGlyphOrder():
+ cs = charStrings[name]
+ bounds = cs.calcBounds(charStrings)
+ if bounds is not None:
+ boundsWidthDict[name] = int(
+ math.ceil(bounds[2]) - math.floor(bounds[0])
+ )
+
+ if boundsWidthDict:
+ minLeftSideBearing = float("inf")
+ minRightSideBearing = float("inf")
+ xMaxExtent = -float("inf")
+ for name, boundsWidth in boundsWidthDict.items():
+ advanceWidth, lsb = hmtxTable[name]
+ rsb = advanceWidth - lsb - boundsWidth
+ extent = lsb + boundsWidth
+ minLeftSideBearing = min(minLeftSideBearing, lsb)
+ minRightSideBearing = min(minRightSideBearing, rsb)
+ xMaxExtent = max(xMaxExtent, extent)
+ self.minLeftSideBearing = minLeftSideBearing
+ self.minRightSideBearing = minRightSideBearing
+ self.xMaxExtent = xMaxExtent
+
+ else: # No glyph has outlines.
+ self.minLeftSideBearing = 0
+ self.minRightSideBearing = 0
+ self.xMaxExtent = 0
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(hheaFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name == "tableVersion":
+ value = fi2ve(value)
+ value = "0x%08x" % value
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "tableVersion":
+ setattr(self, name, ve2fi(attrs["value"]))
+ return
+ setattr(self, name, safeEval(attrs["value"]))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_m_t_x.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_m_t_x.py
new file mode 100644
index 0000000000000000000000000000000000000000..0dc5077588b2bfd8892832256801af7cc83d83c0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_h_m_t_x.py
@@ -0,0 +1,164 @@
+from fontTools.misc.roundTools import otRound
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import struct
+import array
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+class table__h_m_t_x(DefaultTable.DefaultTable):
+ """Horizontal Metrics table
+
+ The ``hmtx`` table contains per-glyph metrics for the glyphs in a
+ ``glyf``, ``CFF ``, or ``CFF2`` table, as needed for horizontal text
+ layout.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
+ """
+
+ headerTag = "hhea"
+ advanceName = "width"
+ sideBearingName = "lsb"
+ numberOfMetricsName = "numberOfHMetrics"
+ longMetricFormat = "Hh"
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont["maxp"].numGlyphs
+ headerTable = ttFont.get(self.headerTag)
+ if headerTable is not None:
+ numberOfMetrics = int(getattr(headerTable, self.numberOfMetricsName))
+ else:
+ numberOfMetrics = numGlyphs
+ if numberOfMetrics > numGlyphs:
+ log.warning(
+ "The %s.%s exceeds the maxp.numGlyphs"
+ % (self.headerTag, self.numberOfMetricsName)
+ )
+ numberOfMetrics = numGlyphs
+ numberOfSideBearings = numGlyphs - numberOfMetrics
+ tableSize = 4 * numberOfMetrics + 2 * numberOfSideBearings
+ if len(data) < tableSize:
+ raise ttLib.TTLibError(
+ f"not enough '{self.tableTag}' table data: "
+ f"expected {tableSize} bytes, got {len(data)}"
+ )
+ # Note: advanceWidth is unsigned, but some font editors might
+ # read/write as signed. We can't be sure whether it was a mistake
+ # or not, so we read as unsigned but also issue a warning...
+ metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
+ metrics = struct.unpack(metricsFmt, data[: 4 * numberOfMetrics])
+ data = data[4 * numberOfMetrics :]
+ sideBearings = array.array("h", data[: 2 * numberOfSideBearings])
+ data = data[2 * numberOfSideBearings :]
+
+ if sys.byteorder != "big":
+ sideBearings.byteswap()
+ if data:
+ log.warning("too much '%s' table data" % self.tableTag)
+ self.metrics = {}
+ glyphOrder = ttFont.getGlyphOrder()
+ for i in range(numberOfMetrics):
+ glyphName = glyphOrder[i]
+ advanceWidth, lsb = metrics[i * 2 : i * 2 + 2]
+ if advanceWidth > 32767:
+ log.warning(
+ "Glyph %r has a huge advance %s (%d); is it intentional or "
+ "an (invalid) negative value?",
+ glyphName,
+ self.advanceName,
+ advanceWidth,
+ )
+ self.metrics[glyphName] = (advanceWidth, lsb)
+ lastAdvance = metrics[-2]
+ for i in range(numberOfSideBearings):
+ glyphName = glyphOrder[i + numberOfMetrics]
+ self.metrics[glyphName] = (lastAdvance, sideBearings[i])
+
+ def compile(self, ttFont):
+ metrics = []
+ hasNegativeAdvances = False
+ for glyphName in ttFont.getGlyphOrder():
+ advanceWidth, sideBearing = self.metrics[glyphName]
+ if advanceWidth < 0:
+ log.error(
+ "Glyph %r has negative advance %s" % (glyphName, self.advanceName)
+ )
+ hasNegativeAdvances = True
+ metrics.append([advanceWidth, sideBearing])
+
+ headerTable = ttFont.get(self.headerTag)
+ if headerTable is not None:
+ lastAdvance = metrics[-1][0]
+ lastIndex = len(metrics)
+ while metrics[lastIndex - 2][0] == lastAdvance:
+ lastIndex -= 1
+ if lastIndex <= 1:
+ # all advances are equal
+ lastIndex = 1
+ break
+ additionalMetrics = metrics[lastIndex:]
+ additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
+ metrics = metrics[:lastIndex]
+ numberOfMetrics = len(metrics)
+ setattr(headerTable, self.numberOfMetricsName, numberOfMetrics)
+ else:
+ # no hhea/vhea, can't store numberOfMetrics; assume == numGlyphs
+ numberOfMetrics = ttFont["maxp"].numGlyphs
+ additionalMetrics = []
+
+ allMetrics = []
+ for advance, sb in metrics:
+ allMetrics.extend([otRound(advance), otRound(sb)])
+ metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
+ try:
+ data = struct.pack(metricsFmt, *allMetrics)
+ except struct.error as e:
+ if "out of range" in str(e) and hasNegativeAdvances:
+ raise ttLib.TTLibError(
+ "'%s' table can't contain negative advance %ss"
+ % (self.tableTag, self.advanceName)
+ )
+ else:
+ raise
+ additionalMetrics = array.array("h", additionalMetrics)
+ if sys.byteorder != "big":
+ additionalMetrics.byteswap()
+ data = data + additionalMetrics.tobytes()
+ return data
+
+ def toXML(self, writer, ttFont):
+ names = sorted(self.metrics.keys())
+ for glyphName in names:
+ advance, sb = self.metrics[glyphName]
+ writer.simpletag(
+ "mtx",
+ [
+ ("name", glyphName),
+ (self.advanceName, advance),
+ (self.sideBearingName, sb),
+ ],
+ )
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "metrics"):
+ self.metrics = {}
+ if name == "mtx":
+ self.metrics[attrs["name"]] = (
+ safeEval(attrs[self.advanceName]),
+ safeEval(attrs[self.sideBearingName]),
+ )
+
+ def __delitem__(self, glyphName):
+ del self.metrics[glyphName]
+
+ def __getitem__(self, glyphName):
+ return self.metrics[glyphName]
+
+ def __setitem__(self, glyphName, advance_sb_pair):
+ self.metrics[glyphName] = tuple(advance_sb_pair)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_k_e_r_n.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_k_e_r_n.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdbaebedc63db51ecfb06601c9673f08f74a445b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_k_e_r_n.py
@@ -0,0 +1,289 @@
+from fontTools.ttLib import getSearchRange
+from fontTools.misc.textTools import safeEval, readHex
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from . import DefaultTable
+import struct
+import sys
+import array
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+class table__k_e_r_n(DefaultTable.DefaultTable):
+ """Kerning table
+
+ The ``kern`` table contains values that contextually adjust the inter-glyph
+ spacing for the glyphs in a ``glyf`` table.
+
+ Note that similar contextual spacing adjustments can also be stored
+ in the "kern" feature of a ``GPOS`` table.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/kern
+ """
+
+ def getkern(self, format):
+ for subtable in self.kernTables:
+ if subtable.format == format:
+ return subtable
+ return None # not found
+
+ def decompile(self, data, ttFont):
+ version, nTables = struct.unpack(">HH", data[:4])
+ apple = False
+ if (len(data) >= 8) and (version == 1):
+ # AAT Apple's "new" format. Hm.
+ version, nTables = struct.unpack(">LL", data[:8])
+ self.version = fi2fl(version, 16)
+ data = data[8:]
+ apple = True
+ else:
+ self.version = version
+ data = data[4:]
+ self.kernTables = []
+ for i in range(nTables):
+ if self.version == 1.0:
+ # Apple
+ length, coverage, subtableFormat = struct.unpack(">LBB", data[:6])
+ else:
+ # in OpenType spec the "version" field refers to the common
+ # subtable header; the actual subtable format is stored in
+ # the 8-15 mask bits of "coverage" field.
+ # This "version" is always 0 so we ignore it here
+ _, length, subtableFormat, coverage = struct.unpack(">HHBB", data[:6])
+ if nTables == 1 and subtableFormat == 0:
+ # The "length" value is ignored since some fonts
+ # (like OpenSans and Calibri) have a subtable larger than
+ # its value.
+ (nPairs,) = struct.unpack(">H", data[6:8])
+ calculated_length = (nPairs * 6) + 14
+ if length != calculated_length:
+ log.warning(
+ "'kern' subtable longer than defined: "
+ "%d bytes instead of %d bytes" % (calculated_length, length)
+ )
+ length = calculated_length
+ if subtableFormat not in kern_classes:
+ subtable = KernTable_format_unkown(subtableFormat)
+ else:
+ subtable = kern_classes[subtableFormat](apple)
+ subtable.decompile(data[:length], ttFont)
+ self.kernTables.append(subtable)
+ data = data[length:]
+
+ def compile(self, ttFont):
+ if hasattr(self, "kernTables"):
+ nTables = len(self.kernTables)
+ else:
+ nTables = 0
+ if self.version == 1.0:
+ # AAT Apple's "new" format.
+ data = struct.pack(">LL", fl2fi(self.version, 16), nTables)
+ else:
+ data = struct.pack(">HH", self.version, nTables)
+ if hasattr(self, "kernTables"):
+ for subtable in self.kernTables:
+ data = data + subtable.compile(ttFont)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ for subtable in self.kernTables:
+ subtable.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = safeEval(attrs["value"])
+ return
+ if name != "kernsubtable":
+ return
+ if not hasattr(self, "kernTables"):
+ self.kernTables = []
+ format = safeEval(attrs["format"])
+ if format not in kern_classes:
+ subtable = KernTable_format_unkown(format)
+ else:
+ apple = self.version == 1.0
+ subtable = kern_classes[format](apple)
+ self.kernTables.append(subtable)
+ subtable.fromXML(name, attrs, content, ttFont)
+
+
+class KernTable_format_0(object):
+ # 'version' is kept for backward compatibility
+ version = format = 0
+
+ def __init__(self, apple=False):
+ self.apple = apple
+
+ def decompile(self, data, ttFont):
+ if not self.apple:
+ version, length, subtableFormat, coverage = struct.unpack(">HHBB", data[:6])
+ if version != 0:
+ from fontTools.ttLib import TTLibError
+
+ raise TTLibError("unsupported kern subtable version: %d" % version)
+ tupleIndex = None
+ # Should we also assert length == len(data)?
+ data = data[6:]
+ else:
+ length, coverage, subtableFormat, tupleIndex = struct.unpack(
+ ">LBBH", data[:8]
+ )
+ data = data[8:]
+ assert self.format == subtableFormat, "unsupported format"
+ self.coverage = coverage
+ self.tupleIndex = tupleIndex
+
+ self.kernTable = kernTable = {}
+
+ nPairs, searchRange, entrySelector, rangeShift = struct.unpack(
+ ">HHHH", data[:8]
+ )
+ data = data[8:]
+
+ datas = array.array("H", data[: 6 * nPairs])
+ if sys.byteorder != "big":
+ datas.byteswap()
+ it = iter(datas)
+ glyphOrder = ttFont.getGlyphOrder()
+ for k in range(nPairs):
+ left, right, value = next(it), next(it), next(it)
+ if value >= 32768:
+ value -= 65536
+ try:
+ kernTable[(glyphOrder[left], glyphOrder[right])] = value
+ except IndexError:
+ # Slower, but will not throw an IndexError on an invalid
+ # glyph id.
+ kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = (
+ value
+ )
+ if len(data) > 6 * nPairs + 4: # Ignore up to 4 bytes excess
+ log.warning(
+ "excess data in 'kern' subtable: %d bytes", len(data) - 6 * nPairs
+ )
+
+ def compile(self, ttFont):
+ nPairs = min(len(self.kernTable), 0xFFFF)
+ searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
+ searchRange &= 0xFFFF
+ entrySelector = min(entrySelector, 0xFFFF)
+ rangeShift = min(rangeShift, 0xFFFF)
+ data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
+
+ # yeehee! (I mean, turn names into indices)
+ try:
+ reverseOrder = ttFont.getReverseGlyphMap()
+ kernTable = sorted(
+ (reverseOrder[left], reverseOrder[right], value)
+ for ((left, right), value) in self.kernTable.items()
+ )
+ except KeyError:
+ # Slower, but will not throw KeyError on invalid glyph id.
+ getGlyphID = ttFont.getGlyphID
+ kernTable = sorted(
+ (getGlyphID(left), getGlyphID(right), value)
+ for ((left, right), value) in self.kernTable.items()
+ )
+
+ for left, right, value in kernTable:
+ data = data + struct.pack(">HHh", left, right, value)
+
+ if not self.apple:
+ version = 0
+ length = len(data) + 6
+ if length >= 0x10000:
+ log.warning(
+ '"kern" subtable overflow, '
+ "truncating length value while preserving pairs."
+ )
+ length &= 0xFFFF
+ header = struct.pack(">HHBB", version, length, self.format, self.coverage)
+ else:
+ if self.tupleIndex is None:
+ # sensible default when compiling a TTX from an old fonttools
+ # or when inserting a Windows-style format 0 subtable into an
+ # Apple version=1.0 kern table
+ log.warning("'tupleIndex' is None; default to 0")
+ self.tupleIndex = 0
+ length = len(data) + 8
+ header = struct.pack(
+ ">LBBH", length, self.coverage, self.format, self.tupleIndex
+ )
+ return header + data
+
+ def toXML(self, writer, ttFont):
+ attrs = dict(coverage=self.coverage, format=self.format)
+ if self.apple:
+ if self.tupleIndex is None:
+ log.warning("'tupleIndex' is None; default to 0")
+ attrs["tupleIndex"] = 0
+ else:
+ attrs["tupleIndex"] = self.tupleIndex
+ writer.begintag("kernsubtable", **attrs)
+ writer.newline()
+ items = sorted(self.kernTable.items())
+ for (left, right), value in items:
+ writer.simpletag("pair", [("l", left), ("r", right), ("v", value)])
+ writer.newline()
+ writer.endtag("kernsubtable")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.coverage = safeEval(attrs["coverage"])
+ subtableFormat = safeEval(attrs["format"])
+ if self.apple:
+ if "tupleIndex" in attrs:
+ self.tupleIndex = safeEval(attrs["tupleIndex"])
+ else:
+ # previous fontTools versions didn't export tupleIndex
+ log.warning("Apple kern subtable is missing 'tupleIndex' attribute")
+ self.tupleIndex = None
+ else:
+ self.tupleIndex = None
+ assert subtableFormat == self.format, "unsupported format"
+ if not hasattr(self, "kernTable"):
+ self.kernTable = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
+
+ def __getitem__(self, pair):
+ return self.kernTable[pair]
+
+ def __setitem__(self, pair, value):
+ self.kernTable[pair] = value
+
+ def __delitem__(self, pair):
+ del self.kernTable[pair]
+
+
+class KernTable_format_unkown(object):
+ def __init__(self, format):
+ self.format = format
+
+ def decompile(self, data, ttFont):
+ self.data = data
+
+ def compile(self, ttFont):
+ return self.data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("kernsubtable", format=self.format)
+ writer.newline()
+ writer.comment("unknown 'kern' subtable format")
+ writer.newline()
+ writer.dumphex(self.data)
+ writer.endtag("kernsubtable")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.decompile(readHex(content), ttFont)
+
+
+kern_classes = {0: KernTable_format_0}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_c_a_r.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_c_a_r.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbb32d23d9d636361117e068fb7846d319f1b52c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_c_a_r.py
@@ -0,0 +1,13 @@
+from .otBase import BaseTTXConverter
+
+
+class table__l_c_a_r(BaseTTXConverter):
+ """Ligature Caret table
+
+ The AAT ``lcar`` table stores division points within ligatures, which applications
+ can use to position carets properly between the logical parts of the ligature.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_o_c_a.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_o_c_a.py
new file mode 100644
index 0000000000000000000000000000000000000000..f0b12fe57ee7ba2aafa1ea4d5aa503e14134b9af
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_o_c_a.py
@@ -0,0 +1,70 @@
+from . import DefaultTable
+import sys
+import array
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+class table__l_o_c_a(DefaultTable.DefaultTable):
+ """Index to Location table
+
+ The ``loca`` table stores the offsets in the ``glyf`` table that correspond
+ to the descriptions of each glyph. The glyphs are references by Glyph ID.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/loca
+ """
+
+ dependencies = ["glyf"]
+
+ def decompile(self, data, ttFont):
+ longFormat = ttFont["head"].indexToLocFormat
+ if longFormat:
+ format = "I"
+ else:
+ format = "H"
+ locations = array.array(format)
+ locations.frombytes(data)
+ if sys.byteorder != "big":
+ locations.byteswap()
+ if not longFormat:
+ locations = array.array("I", (2 * l for l in locations))
+ if len(locations) < (ttFont["maxp"].numGlyphs + 1):
+ log.warning(
+ "corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d",
+ len(locations) - 1,
+ ttFont["maxp"].numGlyphs,
+ )
+ self.locations = locations
+
+ def compile(self, ttFont):
+ try:
+ max_location = max(self.locations)
+ except AttributeError:
+ self.set([])
+ max_location = 0
+ if max_location < 0x20000 and all(l % 2 == 0 for l in self.locations):
+ locations = array.array("H")
+ for location in self.locations:
+ locations.append(location // 2)
+ ttFont["head"].indexToLocFormat = 0
+ else:
+ locations = array.array("I", self.locations)
+ ttFont["head"].indexToLocFormat = 1
+ if sys.byteorder != "big":
+ locations.byteswap()
+ return locations.tobytes()
+
+ def set(self, locations):
+ self.locations = array.array("I", locations)
+
+ def toXML(self, writer, ttFont):
+ writer.comment("The 'loca' table will be calculated by the compiler")
+ writer.newline()
+
+ def __getitem__(self, index):
+ return self.locations[index]
+
+ def __len__(self):
+ return len(self.locations)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_t_a_g.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_t_a_g.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc47cd648bc7b6be6b4ca3011c5125cc2e1d897e
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_l_t_a_g.py
@@ -0,0 +1,72 @@
+from fontTools.misc.textTools import bytesjoin, tobytes, safeEval
+from . import DefaultTable
+import struct
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html
+
+
+class table__l_t_a_g(DefaultTable.DefaultTable):
+ """Language Tag table
+
+ The AAT ``ltag`` table contains mappings between the numeric codes used
+ in the language field of the ``name`` table and IETF language tags.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.version, self.flags = 1, 0
+ self.tags = []
+
+ def addTag(self, tag):
+ """Add 'tag' to the list of langauge tags if not already there.
+
+ Returns the integer index of 'tag' in the list of all tags.
+ """
+ try:
+ return self.tags.index(tag)
+ except ValueError:
+ self.tags.append(tag)
+ return len(self.tags) - 1
+
+ def decompile(self, data, ttFont):
+ self.version, self.flags, numTags = struct.unpack(">LLL", data[:12])
+ assert self.version == 1
+ self.tags = []
+ for i in range(numTags):
+ pos = 12 + i * 4
+ offset, length = struct.unpack(">HH", data[pos : pos + 4])
+ tag = data[offset : offset + length].decode("ascii")
+ self.tags.append(tag)
+
+ def compile(self, ttFont):
+ dataList = [struct.pack(">LLL", self.version, self.flags, len(self.tags))]
+ stringPool = ""
+ for tag in self.tags:
+ offset = stringPool.find(tag)
+ if offset < 0:
+ offset = len(stringPool)
+ stringPool = stringPool + tag
+ offset = offset + 12 + len(self.tags) * 4
+ dataList.append(struct.pack(">HH", offset, len(tag)))
+ dataList.append(tobytes(stringPool))
+ return bytesjoin(dataList)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ writer.simpletag("flags", value=self.flags)
+ writer.newline()
+ for tag in self.tags:
+ writer.simpletag("LanguageTag", tag=tag)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if not hasattr(self, "tags"):
+ self.tags = []
+ if name == "LanguageTag":
+ self.tags.append(attrs["tag"])
+ elif "value" in attrs:
+ value = safeEval(attrs["value"])
+ setattr(self, name, value)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_a_x_p.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_a_x_p.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1576a578f22c375ad4c5c4534b38162329aadde
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_a_x_p.py
@@ -0,0 +1,147 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+
+maxpFormat_0_5 = """
+ > # big endian
+ tableVersion: i
+ numGlyphs: H
+"""
+
+maxpFormat_1_0_add = """
+ > # big endian
+ maxPoints: H
+ maxContours: H
+ maxCompositePoints: H
+ maxCompositeContours: H
+ maxZones: H
+ maxTwilightPoints: H
+ maxStorage: H
+ maxFunctionDefs: H
+ maxInstructionDefs: H
+ maxStackElements: H
+ maxSizeOfInstructions: H
+ maxComponentElements: H
+ maxComponentDepth: H
+"""
+
+
+class table__m_a_x_p(DefaultTable.DefaultTable):
+ """Maximum Profile table
+
+ The ``maxp`` table contains the memory requirements for the data in
+ the font.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/maxp
+ """
+
+ dependencies = ["glyf"]
+
+ def decompile(self, data, ttFont):
+ dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self)
+ self.numGlyphs = int(self.numGlyphs)
+ if self.tableVersion != 0x00005000:
+ dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self)
+ assert len(data) == 0
+
+ def compile(self, ttFont):
+ if "glyf" in ttFont:
+ if ttFont.isLoaded("glyf") and ttFont.recalcBBoxes:
+ self.recalc(ttFont)
+ else:
+ pass # CFF
+ self.numGlyphs = len(ttFont.getGlyphOrder())
+ if self.tableVersion != 0x00005000:
+ self.tableVersion = 0x00010000
+ data = sstruct.pack(maxpFormat_0_5, self)
+ if self.tableVersion == 0x00010000:
+ data = data + sstruct.pack(maxpFormat_1_0_add, self)
+ return data
+
+ def recalc(self, ttFont):
+ """Recalculate the font bounding box, and most other maxp values except
+ for the TT instructions values. Also recalculate the value of bit 1
+ of the flags field and the font bounding box of the 'head' table.
+ """
+ glyfTable = ttFont["glyf"]
+ hmtxTable = ttFont["hmtx"]
+ headTable = ttFont["head"]
+ self.numGlyphs = len(glyfTable)
+ INFINITY = 100000
+ xMin = +INFINITY
+ yMin = +INFINITY
+ xMax = -INFINITY
+ yMax = -INFINITY
+ maxPoints = 0
+ maxContours = 0
+ maxCompositePoints = 0
+ maxCompositeContours = 0
+ maxComponentElements = 0
+ maxComponentDepth = 0
+ allXMinIsLsb = 1
+ for glyphName in ttFont.getGlyphOrder():
+ g = glyfTable[glyphName]
+ if g.numberOfContours:
+ if hmtxTable[glyphName][1] != g.xMin:
+ allXMinIsLsb = 0
+ xMin = min(xMin, g.xMin)
+ yMin = min(yMin, g.yMin)
+ xMax = max(xMax, g.xMax)
+ yMax = max(yMax, g.yMax)
+ if g.numberOfContours > 0:
+ nPoints, nContours = g.getMaxpValues()
+ maxPoints = max(maxPoints, nPoints)
+ maxContours = max(maxContours, nContours)
+ elif g.isComposite():
+ nPoints, nContours, componentDepth = g.getCompositeMaxpValues(
+ glyfTable
+ )
+ maxCompositePoints = max(maxCompositePoints, nPoints)
+ maxCompositeContours = max(maxCompositeContours, nContours)
+ maxComponentElements = max(maxComponentElements, len(g.components))
+ maxComponentDepth = max(maxComponentDepth, componentDepth)
+ if xMin == +INFINITY:
+ headTable.xMin = 0
+ headTable.yMin = 0
+ headTable.xMax = 0
+ headTable.yMax = 0
+ else:
+ headTable.xMin = xMin
+ headTable.yMin = yMin
+ headTable.xMax = xMax
+ headTable.yMax = yMax
+ self.maxPoints = maxPoints
+ self.maxContours = maxContours
+ self.maxCompositePoints = maxCompositePoints
+ self.maxCompositeContours = maxCompositeContours
+ self.maxComponentElements = maxComponentElements
+ self.maxComponentDepth = maxComponentDepth
+ if allXMinIsLsb:
+ headTable.flags = headTable.flags | 0x2
+ else:
+ headTable.flags = headTable.flags & ~0x2
+
+ def testrepr(self):
+ items = sorted(self.__dict__.items())
+ print(". . . . . . . . .")
+ for combo in items:
+ print(" %s: %s" % combo)
+ print(". . . . . . . . .")
+
+ def toXML(self, writer, ttFont):
+ if self.tableVersion != 0x00005000:
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(maxpFormat_0_5)
+ if self.tableVersion != 0x00005000:
+ formatstring, names_1_0, fixes = sstruct.getformat(maxpFormat_1_0_add)
+ names = {**names, **names_1_0}
+ for name in names:
+ value = getattr(self, name)
+ if name == "tableVersion":
+ value = hex(value)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_e_t_a.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_e_t_a.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5cea1b993e8e15968065f9a50b27784fcf4eb8c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_e_t_a.py
@@ -0,0 +1,112 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytesjoin, strjoin, readHex
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+
+# Apple's documentation of 'meta':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html
+
+META_HEADER_FORMAT = """
+ > # big endian
+ version: L
+ flags: L
+ dataOffset: L
+ numDataMaps: L
+"""
+
+
+DATA_MAP_FORMAT = """
+ > # big endian
+ tag: 4s
+ dataOffset: L
+ dataLength: L
+"""
+
+
+class table__m_e_t_a(DefaultTable.DefaultTable):
+ """Metadata table
+
+ The ``meta`` table contains various metadata values for the font. Each
+ category of metadata in the table is identified by a four-character tag.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/meta
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.data = {}
+
+ def decompile(self, data, ttFont):
+ headerSize = sstruct.calcsize(META_HEADER_FORMAT)
+ header = sstruct.unpack(META_HEADER_FORMAT, data[0:headerSize])
+ if header["version"] != 1:
+ raise TTLibError("unsupported 'meta' version %d" % header["version"])
+ dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT)
+ for i in range(header["numDataMaps"]):
+ dataMapOffset = headerSize + i * dataMapSize
+ dataMap = sstruct.unpack(
+ DATA_MAP_FORMAT, data[dataMapOffset : dataMapOffset + dataMapSize]
+ )
+ tag = dataMap["tag"]
+ offset = dataMap["dataOffset"]
+ self.data[tag] = data[offset : offset + dataMap["dataLength"]]
+ if tag in ["dlng", "slng"]:
+ self.data[tag] = self.data[tag].decode("utf-8")
+
+ def compile(self, ttFont):
+ keys = sorted(self.data.keys())
+ headerSize = sstruct.calcsize(META_HEADER_FORMAT)
+ dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT)
+ header = sstruct.pack(
+ META_HEADER_FORMAT,
+ {
+ "version": 1,
+ "flags": 0,
+ "dataOffset": dataOffset,
+ "numDataMaps": len(keys),
+ },
+ )
+ dataMaps = []
+ dataBlocks = []
+ for tag in keys:
+ if tag in ["dlng", "slng"]:
+ data = self.data[tag].encode("utf-8")
+ else:
+ data = self.data[tag]
+ dataMaps.append(
+ sstruct.pack(
+ DATA_MAP_FORMAT,
+ {"tag": tag, "dataOffset": dataOffset, "dataLength": len(data)},
+ )
+ )
+ dataBlocks.append(data)
+ dataOffset += len(data)
+ return bytesjoin([header] + dataMaps + dataBlocks)
+
+ def toXML(self, writer, ttFont):
+ for tag in sorted(self.data.keys()):
+ if tag in ["dlng", "slng"]:
+ writer.begintag("text", tag=tag)
+ writer.newline()
+ writer.write(self.data[tag])
+ writer.newline()
+ writer.endtag("text")
+ writer.newline()
+ else:
+ writer.begintag("hexdata", tag=tag)
+ writer.newline()
+ data = self.data[tag]
+ if min(data) >= 0x20 and max(data) <= 0x7E:
+ writer.comment("ascii: " + data.decode("ascii"))
+ writer.newline()
+ writer.dumphex(data)
+ writer.endtag("hexdata")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "hexdata":
+ self.data[attrs["tag"]] = readHex(content)
+ elif name == "text" and attrs["tag"] in ["dlng", "slng"]:
+ self.data[attrs["tag"]] = strjoin(content).strip()
+ else:
+ raise TTLibError("can't handle '%s' element" % name)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e5fa77ce73fff4906d92610afd9914eb2b61a3c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_t.py
@@ -0,0 +1,14 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
+class table__m_o_r_t(BaseTTXConverter):
+ """The AAT ``mort`` table contains glyph transformations used for script shaping and
+ for various other optional smart features.
+
+ Note: ``mort`` has been deprecated in favor of the newer ``morx`` table.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_x.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_x.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfb3abcc5e35e426bbd670417c429dd4b4ce0962
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_m_o_r_x.py
@@ -0,0 +1,15 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+class table__m_o_r_x(BaseTTXConverter):
+ """The AAT ``morx`` table contains glyph transformations used for script shaping and
+ for various other optional smart features, akin to ``GSUB`` and ``GPOS`` features
+ in OpenType Layout.
+
+ Note: ``morx`` is a replacement for the now deprecated ``mort`` table.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_n_a_m_e.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_n_a_m_e.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5913639bcf1823bbde584d1f2bdbe56a56b88d9
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_n_a_m_e.py
@@ -0,0 +1,1242 @@
+# -*- coding: utf-8 -*-
+from __future__ import annotations
+
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import (
+ bytechr,
+ byteord,
+ bytesjoin,
+ strjoin,
+ tobytes,
+ tostr,
+ safeEval,
+)
+from fontTools.misc.encodingTools import getEncoding
+from fontTools.ttLib import newTable
+from fontTools.ttLib.ttVisitor import TTVisitor
+from fontTools import ttLib
+import fontTools.ttLib.tables.otTables as otTables
+from fontTools.ttLib.tables import C_P_A_L_
+from . import DefaultTable
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+nameRecordFormat = """
+ > # big endian
+ platformID: H
+ platEncID: H
+ langID: H
+ nameID: H
+ length: H
+ offset: H
+"""
+
+nameRecordSize = sstruct.calcsize(nameRecordFormat)
+
+
+class table__n_a_m_e(DefaultTable.DefaultTable):
+ """Naming table
+
+ The ``name`` table is used to store a variety of strings that can be
+ associated with user-facing font information. Records in the ``name``
+ table can be tagged with language tags to support multilingual naming
+ and can support platform-specific character-encoding variants.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/name
+ """
+
+ dependencies = ["ltag"]
+
+ def __init__(self, tag=None):
+ super().__init__(tag)
+ self.names = []
+
+ def decompile(self, data, ttFont):
+ format, n, stringOffset = struct.unpack(b">HHH", data[:6])
+ expectedStringOffset = 6 + n * nameRecordSize
+ if stringOffset != expectedStringOffset:
+ log.error(
+ "'name' table stringOffset incorrect. Expected: %s; Actual: %s",
+ expectedStringOffset,
+ stringOffset,
+ )
+ stringData = data[stringOffset:]
+ data = data[6:]
+ self.names: list[NameRecord] = []
+ for i in range(n):
+ if len(data) < 12:
+ log.error("skipping malformed name record #%d", i)
+ continue
+ name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
+ name.string = stringData[name.offset : name.offset + name.length]
+ if name.offset + name.length > len(stringData):
+ log.error("skipping malformed name record #%d", i)
+ continue
+ assert len(name.string) == name.length
+ # if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
+ # if len(name.string) % 2:
+ # print "2-byte string doesn't have even length!"
+ # print name.__dict__
+ del name.offset, name.length
+ self.names.append(name)
+
+ def compile(self, ttFont):
+ names = self.names
+ names.sort() # sort according to the spec; see NameRecord.__lt__()
+ stringData = b""
+ format = 0
+ n = len(names)
+ stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
+ data = struct.pack(b">HHH", format, n, stringOffset)
+ lastoffset = 0
+ done = {} # remember the data so we can reuse the "pointers"
+ for name in names:
+ string = name.toBytes()
+ if string in done:
+ name.offset, name.length = done[string]
+ else:
+ name.offset, name.length = done[string] = len(stringData), len(string)
+ stringData = bytesjoin([stringData, string])
+ data = data + sstruct.pack(nameRecordFormat, name)
+ return data + stringData
+
+ def toXML(self, writer, ttFont):
+ for name in self.names:
+ name.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "namerecord":
+ return # ignore unknown tags
+ name = NameRecord()
+ self.names.append(name)
+ name.fromXML(name, attrs, content, ttFont)
+
+ def getName(
+ self, nameID: int, platformID: int, platEncID: int, langID: int | None = None
+ ) -> "NameRecord | None":
+ for namerecord in self.names:
+ if (
+ namerecord.nameID == nameID
+ and namerecord.platformID == platformID
+ and namerecord.platEncID == platEncID
+ ):
+ if langID is None or namerecord.langID == langID:
+ return namerecord
+ return None # not found
+
+ def getDebugName(self, nameID: int) -> str | None:
+ englishName: str | None = None
+ someName: str | None = None
+ for name in self.names:
+ if name.nameID != nameID:
+ continue
+ try:
+ unistr = name.toUnicode()
+ except UnicodeDecodeError:
+ continue
+
+ someName = unistr
+ if (name.platformID, name.langID) in ((1, 0), (3, 0x409)):
+ englishName = unistr
+ break
+ if englishName:
+ return englishName
+ elif someName:
+ return someName
+ else:
+ return None
+
+ def getFirstDebugName(self, nameIDs):
+ for nameID in nameIDs:
+ name = self.getDebugName(nameID)
+ if name is not None:
+ return name
+ return None
+
+ def getBestFamilyName(self):
+ # 21 = WWS Family Name
+ # 16 = Typographic Family Name
+ # 1 = Family Name
+ return self.getFirstDebugName((21, 16, 1))
+
+ def getBestSubFamilyName(self):
+ # 22 = WWS SubFamily Name
+ # 17 = Typographic SubFamily Name
+ # 2 = SubFamily Name
+ return self.getFirstDebugName((22, 17, 2))
+
+ def getBestFullName(self):
+ # 4 = Full Name
+ # 6 = PostScript Name
+ for nameIDs in ((21, 22), (16, 17), (1, 2), (4,), (6,)):
+ if len(nameIDs) == 2:
+ name_fam = self.getDebugName(nameIDs[0])
+ name_subfam = self.getDebugName(nameIDs[1])
+ if None in [name_fam, name_subfam]:
+ continue # if any is None, skip
+ name = f"{name_fam} {name_subfam}"
+ if name_subfam.lower() == "regular":
+ name = f"{name_fam}"
+ return name
+ else:
+ name = self.getDebugName(nameIDs[0])
+ if name is not None:
+ return name
+ return None
+
+ def setName(self, string, nameID, platformID, platEncID, langID):
+ """Set the 'string' for the name record identified by 'nameID', 'platformID',
+ 'platEncID' and 'langID'. If a record with that nameID doesn't exist, create it
+ and append to the name table.
+
+ 'string' can be of type `str` (`unicode` in PY2) or `bytes`. In the latter case,
+ it is assumed to be already encoded with the correct plaform-specific encoding
+ identified by the (platformID, platEncID, langID) triplet. A warning is issued
+ to prevent unexpected results.
+ """
+ if not isinstance(string, str):
+ if isinstance(string, bytes):
+ log.warning(
+ "name string is bytes, ensure it's correctly encoded: %r", string
+ )
+ else:
+ raise TypeError(
+ "expected unicode or bytes, found %s: %r"
+ % (type(string).__name__, string)
+ )
+ namerecord = self.getName(nameID, platformID, platEncID, langID)
+ if namerecord:
+ namerecord.string = string
+ else:
+ self.names.append(makeName(string, nameID, platformID, platEncID, langID))
+
+ def removeNames(self, nameID=None, platformID=None, platEncID=None, langID=None):
+ """Remove any name records identified by the given combination of 'nameID',
+ 'platformID', 'platEncID' and 'langID'.
+ """
+ args = {
+ argName: argValue
+ for argName, argValue in (
+ ("nameID", nameID),
+ ("platformID", platformID),
+ ("platEncID", platEncID),
+ ("langID", langID),
+ )
+ if argValue is not None
+ }
+ if not args:
+ # no arguments, nothing to do
+ return
+ self.names = [
+ rec
+ for rec in self.names
+ if any(
+ argValue != getattr(rec, argName) for argName, argValue in args.items()
+ )
+ ]
+
+ @staticmethod
+ def removeUnusedNames(ttFont):
+ """Remove any name records which are not in NameID range 0-255 and not utilized
+ within the font itself."""
+ visitor = NameRecordVisitor()
+ visitor.visit(ttFont)
+ toDelete = set()
+ for record in ttFont["name"].names:
+ # Name IDs 26 to 255, inclusive, are reserved for future standard names.
+ # https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
+ if record.nameID < 256:
+ continue
+ if record.nameID not in visitor.seen:
+ toDelete.add(record.nameID)
+
+ for nameID in toDelete:
+ ttFont["name"].removeNames(nameID)
+ return toDelete
+
+ def _findUnusedNameID(self, minNameID=256):
+ """Finds an unused name id.
+
+ The nameID is assigned in the range between 'minNameID' and 32767 (inclusive),
+ following the last nameID in the name table.
+ """
+ names = self.names
+ nameID = 1 + max([n.nameID for n in names] + [minNameID - 1])
+ if nameID > 32767:
+ raise ValueError("nameID must be less than 32768")
+ return nameID
+
+ def findMultilingualName(
+ self, names, windows=True, mac=True, minNameID=0, ttFont=None
+ ):
+ """Return the name ID of an existing multilingual name that
+ matches the 'names' dictionary, or None if not found.
+
+ 'names' is a dictionary with the name in multiple languages,
+ such as {'en': 'Pale', 'de': 'Blaß', 'de-CH': 'Blass'}.
+ The keys can be arbitrary IETF BCP 47 language codes;
+ the values are Unicode strings.
+
+ If 'windows' is True, the returned name ID is guaranteed
+ exist for all requested languages for platformID=3 and
+ platEncID=1.
+ If 'mac' is True, the returned name ID is guaranteed to exist
+ for all requested languages for platformID=1 and platEncID=0.
+
+ The returned name ID will not be less than the 'minNameID'
+ argument.
+ """
+ # Gather the set of requested
+ # (string, platformID, platEncID, langID)
+ # tuples
+ reqNameSet = set()
+ for lang, name in sorted(names.items()):
+ if windows:
+ windowsName = _makeWindowsName(name, None, lang)
+ if windowsName is not None:
+ reqNameSet.add(
+ (
+ windowsName.string,
+ windowsName.platformID,
+ windowsName.platEncID,
+ windowsName.langID,
+ )
+ )
+ if mac:
+ macName = _makeMacName(name, None, lang, ttFont)
+ if macName is not None:
+ reqNameSet.add(
+ (
+ macName.string,
+ macName.platformID,
+ macName.platEncID,
+ macName.langID,
+ )
+ )
+
+ # Collect matching name IDs
+ matchingNames = dict()
+ for name in self.names:
+ try:
+ key = (name.toUnicode(), name.platformID, name.platEncID, name.langID)
+ except UnicodeDecodeError:
+ continue
+ if key in reqNameSet and name.nameID >= minNameID:
+ nameSet = matchingNames.setdefault(name.nameID, set())
+ nameSet.add(key)
+
+ # Return the first name ID that defines all requested strings
+ for nameID, nameSet in sorted(matchingNames.items()):
+ if nameSet == reqNameSet:
+ return nameID
+
+ return None # not found
+
+ def addMultilingualName(
+ self, names, ttFont=None, nameID=None, windows=True, mac=True, minNameID=0
+ ):
+ """Add a multilingual name, returning its name ID
+
+ 'names' is a dictionary with the name in multiple languages,
+ such as {'en': 'Pale', 'de': 'Blaß', 'de-CH': 'Blass'}.
+ The keys can be arbitrary IETF BCP 47 language codes;
+ the values are Unicode strings.
+
+ 'ttFont' is the TTFont to which the names are added, or None.
+ If present, the font's 'ltag' table can get populated
+ to store exotic language codes, which allows encoding
+ names that otherwise cannot get encoded at all.
+
+ 'nameID' is the name ID to be used, or None to let the library
+ find an existing set of name records that match, or pick an
+ unused name ID.
+
+ If 'windows' is True, a platformID=3 name record will be added.
+ If 'mac' is True, a platformID=1 name record will be added.
+
+ If the 'nameID' argument is None, the created nameID will not
+ be less than the 'minNameID' argument.
+ """
+ if nameID is None:
+ # Reuse nameID if possible
+ nameID = self.findMultilingualName(
+ names, windows=windows, mac=mac, minNameID=minNameID, ttFont=ttFont
+ )
+ if nameID is not None:
+ return nameID
+ nameID = self._findUnusedNameID()
+ # TODO: Should minimize BCP 47 language codes.
+ # https://github.com/fonttools/fonttools/issues/930
+ for lang, name in sorted(names.items()):
+ if windows:
+ windowsName = _makeWindowsName(name, nameID, lang)
+ if windowsName is not None:
+ self.names.append(windowsName)
+ else:
+ # We cannot not make a Windows name: make sure we add a
+ # Mac name as a fallback. This can happen for exotic
+ # BCP47 language tags that have no Windows language code.
+ mac = True
+ if mac:
+ macName = _makeMacName(name, nameID, lang, ttFont)
+ if macName is not None:
+ self.names.append(macName)
+ return nameID
+
+ def addName(self, string, platforms=((1, 0, 0), (3, 1, 0x409)), minNameID=255):
+ """Add a new name record containing 'string' for each (platformID, platEncID,
+ langID) tuple specified in the 'platforms' list.
+
+ The nameID is assigned in the range between 'minNameID'+1 and 32767 (inclusive),
+ following the last nameID in the name table.
+ If no 'platforms' are specified, two English name records are added, one for the
+ Macintosh (platformID=0), and one for the Windows platform (3).
+
+ The 'string' must be a Unicode string, so it can be encoded with different,
+ platform-specific encodings.
+
+ Return the new nameID.
+ """
+ assert (
+ len(platforms) > 0
+ ), "'platforms' must contain at least one (platformID, platEncID, langID) tuple"
+ if not isinstance(string, str):
+ raise TypeError(
+ "expected str, found %s: %r" % (type(string).__name__, string)
+ )
+ nameID = self._findUnusedNameID(minNameID + 1)
+ for platformID, platEncID, langID in platforms:
+ self.names.append(makeName(string, nameID, platformID, platEncID, langID))
+ return nameID
+
+
+def makeName(string, nameID, platformID, platEncID, langID):
+ name = NameRecord()
+ name.string, name.nameID, name.platformID, name.platEncID, name.langID = (
+ string,
+ nameID,
+ platformID,
+ platEncID,
+ langID,
+ )
+ return name
+
+
+def _makeWindowsName(name, nameID, language):
+ """Create a NameRecord for the Microsoft Windows platform
+
+ 'language' is an arbitrary IETF BCP 47 language identifier such
+ as 'en', 'de-CH', 'de-AT-1901', or 'fa-Latn'. If Microsoft Windows
+ does not support the desired language, the result will be None.
+ Future versions of fonttools might return a NameRecord for the
+ OpenType 'name' table format 1, but this is not implemented yet.
+ """
+ langID = _WINDOWS_LANGUAGE_CODES.get(language.lower())
+ if langID is not None:
+ return makeName(name, nameID, 3, 1, langID)
+ else:
+ log.warning(
+ "cannot add Windows name in language %s "
+ "because fonttools does not yet support "
+ "name table format 1" % language
+ )
+ return None
+
+
+def _makeMacName(name, nameID, language, font=None):
+ """Create a NameRecord for Apple platforms
+
+ 'language' is an arbitrary IETF BCP 47 language identifier such
+ as 'en', 'de-CH', 'de-AT-1901', or 'fa-Latn'. When possible, we
+ create a Macintosh NameRecord that is understood by old applications
+ (platform ID 1 and an old-style Macintosh language enum). If this
+ is not possible, we create a Unicode NameRecord (platform ID 0)
+ whose language points to the font’s 'ltag' table. The latter
+ can encode any string in any language, but legacy applications
+ might not recognize the format (in which case they will ignore
+ those names).
+
+ 'font' should be the TTFont for which you want to create a name.
+ If 'font' is None, we only return NameRecords for legacy Macintosh;
+ in that case, the result will be None for names that need to
+ be encoded with an 'ltag' table.
+
+ See the section “The language identifier” in Apple’s specification:
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+ """
+ macLang = _MAC_LANGUAGE_CODES.get(language.lower())
+ macScript = _MAC_LANGUAGE_TO_SCRIPT.get(macLang)
+ if macLang is not None and macScript is not None:
+ encoding = getEncoding(1, macScript, macLang, default="ascii")
+ # Check if we can actually encode this name. If we can't,
+ # for example because we have no support for the legacy
+ # encoding, or because the name string contains Unicode
+ # characters that the legacy encoding cannot represent,
+ # we fall back to encoding the name in Unicode and put
+ # the language tag into the ltag table.
+ try:
+ _ = tobytes(name, encoding, errors="strict")
+ return makeName(name, nameID, 1, macScript, macLang)
+ except UnicodeEncodeError:
+ pass
+ if font is not None:
+ ltag = font.tables.get("ltag")
+ if ltag is None:
+ ltag = font["ltag"] = newTable("ltag")
+ # 0 = Unicode; 4 = “Unicode 2.0 or later semantics (non-BMP characters allowed)”
+ # “The preferred platform-specific code for Unicode would be 3 or 4.”
+ # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+ return makeName(name, nameID, 0, 4, ltag.addTag(language))
+ else:
+ log.warning(
+ "cannot store language %s into 'ltag' table "
+ "without having access to the TTFont object" % language
+ )
+ return None
+
+
+class NameRecord(object):
+ def getEncoding(self, default="ascii"):
+ """Returns the Python encoding name for this name entry based on its platformID,
+ platEncID, and langID. If encoding for these values is not known, by default
+ 'ascii' is returned. That can be overriden by passing a value to the default
+ argument.
+ """
+ return getEncoding(self.platformID, self.platEncID, self.langID, default)
+
+ def encodingIsUnicodeCompatible(self):
+ return self.getEncoding(None) in ["utf_16_be", "ucs2be", "ascii", "latin1"]
+
+ def __str__(self):
+ return self.toStr(errors="backslashreplace")
+
+ def isUnicode(self):
+ return self.platformID == 0 or (
+ self.platformID == 3 and self.platEncID in [0, 1, 10]
+ )
+
+ def toUnicode(self, errors: str = "strict") -> str:
+ """
+ If self.string is a Unicode string, return it; otherwise try decoding the
+ bytes in self.string to a Unicode string using the encoding of this
+ entry as returned by self.getEncoding(); Note that self.getEncoding()
+ returns 'ascii' if the encoding is unknown to the library.
+
+ Certain heuristics are performed to recover data from bytes that are
+ ill-formed in the chosen encoding, or that otherwise look misencoded
+ (mostly around bad UTF-16BE encoded bytes, or bytes that look like UTF-16BE
+ but marked otherwise). If the bytes are ill-formed and the heuristics fail,
+ the error is handled according to the errors parameter to this function, which is
+ passed to the underlying decode() function; by default it throws a
+ UnicodeDecodeError exception.
+
+ Note: The mentioned heuristics mean that roundtripping a font to XML and back
+ to binary might recover some misencoded data whereas just loading the font
+ and saving it back will not change them.
+ """
+
+ def isascii(b: int) -> bool:
+ return (b >= 0x20 and b <= 0x7E) or b in [0x09, 0x0A, 0x0D]
+
+ encoding = self.getEncoding()
+ string = self.string
+
+ if (
+ isinstance(string, bytes)
+ and encoding == "utf_16_be"
+ and len(string) % 2 == 1
+ ):
+ # Recover badly encoded UTF-16 strings that have an odd number of bytes:
+ # - If the last byte is zero, drop it. Otherwise,
+ # - If all the odd bytes are zero and all the even bytes are ASCII,
+ # prepend one zero byte. Otherwise,
+ # - If first byte is zero and all other bytes are ASCII, insert zero
+ # bytes between consecutive ASCII bytes.
+ #
+ # (Yes, I've seen all of these in the wild... sigh)
+ if byteord(string[-1]) == 0:
+ string = string[:-1]
+ elif all(
+ byteord(b) == 0 if i % 2 else isascii(byteord(b))
+ for i, b in enumerate(string)
+ ):
+ string = b"\0" + string
+ elif byteord(string[0]) == 0 and all(
+ isascii(byteord(b)) for b in string[1:]
+ ):
+ string = bytesjoin(b"\0" + bytechr(byteord(b)) for b in string[1:])
+
+ string = tostr(string, encoding=encoding, errors=errors)
+
+ # If decoded strings still looks like UTF-16BE, it suggests a double-encoding.
+ # Fix it up.
+ if all(
+ ord(c) == 0 if i % 2 == 0 else isascii(ord(c)) for i, c in enumerate(string)
+ ):
+ # If string claims to be Mac encoding, but looks like UTF-16BE with ASCII text,
+ # narrow it down.
+ string = "".join(c for c in string[1::2])
+
+ return string
+
+ def toBytes(self, errors="strict"):
+ """If self.string is a bytes object, return it; otherwise try encoding
+ the Unicode string in self.string to bytes using the encoding of this
+ entry as returned by self.getEncoding(); Note that self.getEncoding()
+ returns 'ascii' if the encoding is unknown to the library.
+
+ If the Unicode string cannot be encoded to bytes in the chosen encoding,
+ the error is handled according to the errors parameter to this function,
+ which is passed to the underlying encode() function; by default it throws a
+ UnicodeEncodeError exception.
+ """
+ return tobytes(self.string, encoding=self.getEncoding(), errors=errors)
+
+ toStr = toUnicode
+
+ def toXML(self, writer, ttFont):
+ try:
+ unistr = self.toUnicode()
+ except UnicodeDecodeError:
+ unistr = None
+ attrs = [
+ ("nameID", self.nameID),
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("langID", hex(self.langID)),
+ ]
+
+ if unistr is None or not self.encodingIsUnicodeCompatible():
+ attrs.append(("unicode", unistr is not None))
+
+ writer.begintag("namerecord", attrs)
+ writer.newline()
+ if unistr is not None:
+ writer.write(unistr)
+ else:
+ writer.write8bit(self.string)
+ writer.newline()
+ writer.endtag("namerecord")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.nameID = safeEval(attrs["nameID"])
+ self.platformID = safeEval(attrs["platformID"])
+ self.platEncID = safeEval(attrs["platEncID"])
+ self.langID = safeEval(attrs["langID"])
+ s = strjoin(content).strip()
+ encoding = self.getEncoding()
+ if self.encodingIsUnicodeCompatible() or safeEval(
+ attrs.get("unicode", "False")
+ ):
+ self.string = s.encode(encoding)
+ else:
+ # This is the inverse of write8bit...
+ self.string = s.encode("latin1")
+
+ def __lt__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+
+ try:
+ selfTuple = (
+ self.platformID,
+ self.platEncID,
+ self.langID,
+ self.nameID,
+ )
+ otherTuple = (
+ other.platformID,
+ other.platEncID,
+ other.langID,
+ other.nameID,
+ )
+ except AttributeError:
+ # This can only happen for
+ # 1) an object that is not a NameRecord, or
+ # 2) an unlikely incomplete NameRecord object which has not been
+ # fully populated
+ return NotImplemented
+
+ try:
+ # Include the actual NameRecord string in the comparison tuples
+ selfTuple = selfTuple + (self.toBytes(),)
+ otherTuple = otherTuple + (other.toBytes(),)
+ except UnicodeEncodeError as e:
+ # toBytes caused an encoding error in either of the two, so content
+ # to sorting based on IDs only
+ log.error("NameRecord sorting failed to encode: %s" % e)
+
+ # Implemented so that list.sort() sorts according to the spec by using
+ # the order of the tuple items and their comparison
+ return selfTuple < otherTuple
+
+ def __repr__(self):
+ return "" % (
+ self.nameID,
+ self.platformID,
+ self.langID,
+ )
+
+
+# Windows language ID → IETF BCP-47 language tag
+#
+# While Microsoft indicates a region/country for all its language
+# IDs, we follow Unicode practice by omitting “most likely subtags”
+# as per Unicode CLDR. For example, English is simply “en” and not
+# “en-Latn” because according to Unicode, the default script
+# for English is Latin.
+#
+# http://www.unicode.org/cldr/charts/latest/supplemental/likely_subtags.html
+# http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+_WINDOWS_LANGUAGES = {
+ 0x0436: "af",
+ 0x041C: "sq",
+ 0x0484: "gsw",
+ 0x045E: "am",
+ 0x1401: "ar-DZ",
+ 0x3C01: "ar-BH",
+ 0x0C01: "ar",
+ 0x0801: "ar-IQ",
+ 0x2C01: "ar-JO",
+ 0x3401: "ar-KW",
+ 0x3001: "ar-LB",
+ 0x1001: "ar-LY",
+ 0x1801: "ary",
+ 0x2001: "ar-OM",
+ 0x4001: "ar-QA",
+ 0x0401: "ar-SA",
+ 0x2801: "ar-SY",
+ 0x1C01: "aeb",
+ 0x3801: "ar-AE",
+ 0x2401: "ar-YE",
+ 0x042B: "hy",
+ 0x044D: "as",
+ 0x082C: "az-Cyrl",
+ 0x042C: "az",
+ 0x046D: "ba",
+ 0x042D: "eu",
+ 0x0423: "be",
+ 0x0845: "bn",
+ 0x0445: "bn-IN",
+ 0x201A: "bs-Cyrl",
+ 0x141A: "bs",
+ 0x047E: "br",
+ 0x0402: "bg",
+ 0x0403: "ca",
+ 0x0C04: "zh-HK",
+ 0x1404: "zh-MO",
+ 0x0804: "zh",
+ 0x1004: "zh-SG",
+ 0x0404: "zh-TW",
+ 0x0483: "co",
+ 0x041A: "hr",
+ 0x101A: "hr-BA",
+ 0x0405: "cs",
+ 0x0406: "da",
+ 0x048C: "prs",
+ 0x0465: "dv",
+ 0x0813: "nl-BE",
+ 0x0413: "nl",
+ 0x0C09: "en-AU",
+ 0x2809: "en-BZ",
+ 0x1009: "en-CA",
+ 0x2409: "en-029",
+ 0x4009: "en-IN",
+ 0x1809: "en-IE",
+ 0x2009: "en-JM",
+ 0x4409: "en-MY",
+ 0x1409: "en-NZ",
+ 0x3409: "en-PH",
+ 0x4809: "en-SG",
+ 0x1C09: "en-ZA",
+ 0x2C09: "en-TT",
+ 0x0809: "en-GB",
+ 0x0409: "en",
+ 0x3009: "en-ZW",
+ 0x0425: "et",
+ 0x0438: "fo",
+ 0x0464: "fil",
+ 0x040B: "fi",
+ 0x080C: "fr-BE",
+ 0x0C0C: "fr-CA",
+ 0x040C: "fr",
+ 0x140C: "fr-LU",
+ 0x180C: "fr-MC",
+ 0x100C: "fr-CH",
+ 0x0462: "fy",
+ 0x0456: "gl",
+ 0x0437: "ka",
+ 0x0C07: "de-AT",
+ 0x0407: "de",
+ 0x1407: "de-LI",
+ 0x1007: "de-LU",
+ 0x0807: "de-CH",
+ 0x0408: "el",
+ 0x046F: "kl",
+ 0x0447: "gu",
+ 0x0468: "ha",
+ 0x040D: "he",
+ 0x0439: "hi",
+ 0x040E: "hu",
+ 0x040F: "is",
+ 0x0470: "ig",
+ 0x0421: "id",
+ 0x045D: "iu",
+ 0x085D: "iu-Latn",
+ 0x083C: "ga",
+ 0x0434: "xh",
+ 0x0435: "zu",
+ 0x0410: "it",
+ 0x0810: "it-CH",
+ 0x0411: "ja",
+ 0x044B: "kn",
+ 0x043F: "kk",
+ 0x0453: "km",
+ 0x0486: "quc",
+ 0x0487: "rw",
+ 0x0441: "sw",
+ 0x0457: "kok",
+ 0x0412: "ko",
+ 0x0440: "ky",
+ 0x0454: "lo",
+ 0x0426: "lv",
+ 0x0427: "lt",
+ 0x082E: "dsb",
+ 0x046E: "lb",
+ 0x042F: "mk",
+ 0x083E: "ms-BN",
+ 0x043E: "ms",
+ 0x044C: "ml",
+ 0x043A: "mt",
+ 0x0481: "mi",
+ 0x047A: "arn",
+ 0x044E: "mr",
+ 0x047C: "moh",
+ 0x0450: "mn",
+ 0x0850: "mn-CN",
+ 0x0461: "ne",
+ 0x0414: "nb",
+ 0x0814: "nn",
+ 0x0482: "oc",
+ 0x0448: "or",
+ 0x0463: "ps",
+ 0x0415: "pl",
+ 0x0416: "pt",
+ 0x0816: "pt-PT",
+ 0x0446: "pa",
+ 0x046B: "qu-BO",
+ 0x086B: "qu-EC",
+ 0x0C6B: "qu",
+ 0x0418: "ro",
+ 0x0417: "rm",
+ 0x0419: "ru",
+ 0x243B: "smn",
+ 0x103B: "smj-NO",
+ 0x143B: "smj",
+ 0x0C3B: "se-FI",
+ 0x043B: "se",
+ 0x083B: "se-SE",
+ 0x203B: "sms",
+ 0x183B: "sma-NO",
+ 0x1C3B: "sms",
+ 0x044F: "sa",
+ 0x1C1A: "sr-Cyrl-BA",
+ 0x0C1A: "sr",
+ 0x181A: "sr-Latn-BA",
+ 0x081A: "sr-Latn",
+ 0x046C: "nso",
+ 0x0432: "tn",
+ 0x045B: "si",
+ 0x041B: "sk",
+ 0x0424: "sl",
+ 0x2C0A: "es-AR",
+ 0x400A: "es-BO",
+ 0x340A: "es-CL",
+ 0x240A: "es-CO",
+ 0x140A: "es-CR",
+ 0x1C0A: "es-DO",
+ 0x300A: "es-EC",
+ 0x440A: "es-SV",
+ 0x100A: "es-GT",
+ 0x480A: "es-HN",
+ 0x080A: "es-MX",
+ 0x4C0A: "es-NI",
+ 0x180A: "es-PA",
+ 0x3C0A: "es-PY",
+ 0x280A: "es-PE",
+ 0x500A: "es-PR",
+ # Microsoft has defined two different language codes for
+ # “Spanish with modern sorting” and “Spanish with traditional
+ # sorting”. This makes sense for collation APIs, and it would be
+ # possible to express this in BCP 47 language tags via Unicode
+ # extensions (eg., “es-u-co-trad” is “Spanish with traditional
+ # sorting”). However, for storing names in fonts, this distinction
+ # does not make sense, so we use “es” in both cases.
+ 0x0C0A: "es",
+ 0x040A: "es",
+ 0x540A: "es-US",
+ 0x380A: "es-UY",
+ 0x200A: "es-VE",
+ 0x081D: "sv-FI",
+ 0x041D: "sv",
+ 0x045A: "syr",
+ 0x0428: "tg",
+ 0x085F: "tzm",
+ 0x0449: "ta",
+ 0x0444: "tt",
+ 0x044A: "te",
+ 0x041E: "th",
+ 0x0451: "bo",
+ 0x041F: "tr",
+ 0x0442: "tk",
+ 0x0480: "ug",
+ 0x0422: "uk",
+ 0x042E: "hsb",
+ 0x0420: "ur",
+ 0x0843: "uz-Cyrl",
+ 0x0443: "uz",
+ 0x042A: "vi",
+ 0x0452: "cy",
+ 0x0488: "wo",
+ 0x0485: "sah",
+ 0x0478: "ii",
+ 0x046A: "yo",
+}
+
+
+_MAC_LANGUAGES = {
+ 0: "en",
+ 1: "fr",
+ 2: "de",
+ 3: "it",
+ 4: "nl",
+ 5: "sv",
+ 6: "es",
+ 7: "da",
+ 8: "pt",
+ 9: "no",
+ 10: "he",
+ 11: "ja",
+ 12: "ar",
+ 13: "fi",
+ 14: "el",
+ 15: "is",
+ 16: "mt",
+ 17: "tr",
+ 18: "hr",
+ 19: "zh-Hant",
+ 20: "ur",
+ 21: "hi",
+ 22: "th",
+ 23: "ko",
+ 24: "lt",
+ 25: "pl",
+ 26: "hu",
+ 27: "es",
+ 28: "lv",
+ 29: "se",
+ 30: "fo",
+ 31: "fa",
+ 32: "ru",
+ 33: "zh",
+ 34: "nl-BE",
+ 35: "ga",
+ 36: "sq",
+ 37: "ro",
+ 38: "cz",
+ 39: "sk",
+ 40: "sl",
+ 41: "yi",
+ 42: "sr",
+ 43: "mk",
+ 44: "bg",
+ 45: "uk",
+ 46: "be",
+ 47: "uz",
+ 48: "kk",
+ 49: "az-Cyrl",
+ 50: "az-Arab",
+ 51: "hy",
+ 52: "ka",
+ 53: "mo",
+ 54: "ky",
+ 55: "tg",
+ 56: "tk",
+ 57: "mn-CN",
+ 58: "mn",
+ 59: "ps",
+ 60: "ks",
+ 61: "ku",
+ 62: "sd",
+ 63: "bo",
+ 64: "ne",
+ 65: "sa",
+ 66: "mr",
+ 67: "bn",
+ 68: "as",
+ 69: "gu",
+ 70: "pa",
+ 71: "or",
+ 72: "ml",
+ 73: "kn",
+ 74: "ta",
+ 75: "te",
+ 76: "si",
+ 77: "my",
+ 78: "km",
+ 79: "lo",
+ 80: "vi",
+ 81: "id",
+ 82: "tl",
+ 83: "ms",
+ 84: "ms-Arab",
+ 85: "am",
+ 86: "ti",
+ 87: "om",
+ 88: "so",
+ 89: "sw",
+ 90: "rw",
+ 91: "rn",
+ 92: "ny",
+ 93: "mg",
+ 94: "eo",
+ 128: "cy",
+ 129: "eu",
+ 130: "ca",
+ 131: "la",
+ 132: "qu",
+ 133: "gn",
+ 134: "ay",
+ 135: "tt",
+ 136: "ug",
+ 137: "dz",
+ 138: "jv",
+ 139: "su",
+ 140: "gl",
+ 141: "af",
+ 142: "br",
+ 143: "iu",
+ 144: "gd",
+ 145: "gv",
+ 146: "ga",
+ 147: "to",
+ 148: "el-polyton",
+ 149: "kl",
+ 150: "az",
+ 151: "nn",
+}
+
+
+_WINDOWS_LANGUAGE_CODES = {
+ lang.lower(): code for code, lang in _WINDOWS_LANGUAGES.items()
+}
+_MAC_LANGUAGE_CODES = {lang.lower(): code for code, lang in _MAC_LANGUAGES.items()}
+
+
+# MacOS language ID → MacOS script ID
+#
+# Note that the script ID is not sufficient to determine what encoding
+# to use in TrueType files. For some languages, MacOS used a modification
+# of a mainstream script. For example, an Icelandic name would be stored
+# with smRoman in the TrueType naming table, but the actual encoding
+# is a special Icelandic version of the normal Macintosh Roman encoding.
+# As another example, Inuktitut uses an 8-bit encoding for Canadian Aboriginal
+# Syllables but MacOS had run out of available script codes, so this was
+# done as a (pretty radical) “modification” of Ethiopic.
+#
+# http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt
+_MAC_LANGUAGE_TO_SCRIPT = {
+ 0: 0, # langEnglish → smRoman
+ 1: 0, # langFrench → smRoman
+ 2: 0, # langGerman → smRoman
+ 3: 0, # langItalian → smRoman
+ 4: 0, # langDutch → smRoman
+ 5: 0, # langSwedish → smRoman
+ 6: 0, # langSpanish → smRoman
+ 7: 0, # langDanish → smRoman
+ 8: 0, # langPortuguese → smRoman
+ 9: 0, # langNorwegian → smRoman
+ 10: 5, # langHebrew → smHebrew
+ 11: 1, # langJapanese → smJapanese
+ 12: 4, # langArabic → smArabic
+ 13: 0, # langFinnish → smRoman
+ 14: 6, # langGreek → smGreek
+ 15: 0, # langIcelandic → smRoman (modified)
+ 16: 0, # langMaltese → smRoman
+ 17: 0, # langTurkish → smRoman (modified)
+ 18: 0, # langCroatian → smRoman (modified)
+ 19: 2, # langTradChinese → smTradChinese
+ 20: 4, # langUrdu → smArabic
+ 21: 9, # langHindi → smDevanagari
+ 22: 21, # langThai → smThai
+ 23: 3, # langKorean → smKorean
+ 24: 29, # langLithuanian → smCentralEuroRoman
+ 25: 29, # langPolish → smCentralEuroRoman
+ 26: 29, # langHungarian → smCentralEuroRoman
+ 27: 29, # langEstonian → smCentralEuroRoman
+ 28: 29, # langLatvian → smCentralEuroRoman
+ 29: 0, # langSami → smRoman
+ 30: 0, # langFaroese → smRoman (modified)
+ 31: 4, # langFarsi → smArabic (modified)
+ 32: 7, # langRussian → smCyrillic
+ 33: 25, # langSimpChinese → smSimpChinese
+ 34: 0, # langFlemish → smRoman
+ 35: 0, # langIrishGaelic → smRoman (modified)
+ 36: 0, # langAlbanian → smRoman
+ 37: 0, # langRomanian → smRoman (modified)
+ 38: 29, # langCzech → smCentralEuroRoman
+ 39: 29, # langSlovak → smCentralEuroRoman
+ 40: 0, # langSlovenian → smRoman (modified)
+ 41: 5, # langYiddish → smHebrew
+ 42: 7, # langSerbian → smCyrillic
+ 43: 7, # langMacedonian → smCyrillic
+ 44: 7, # langBulgarian → smCyrillic
+ 45: 7, # langUkrainian → smCyrillic (modified)
+ 46: 7, # langByelorussian → smCyrillic
+ 47: 7, # langUzbek → smCyrillic
+ 48: 7, # langKazakh → smCyrillic
+ 49: 7, # langAzerbaijani → smCyrillic
+ 50: 4, # langAzerbaijanAr → smArabic
+ 51: 24, # langArmenian → smArmenian
+ 52: 23, # langGeorgian → smGeorgian
+ 53: 7, # langMoldavian → smCyrillic
+ 54: 7, # langKirghiz → smCyrillic
+ 55: 7, # langTajiki → smCyrillic
+ 56: 7, # langTurkmen → smCyrillic
+ 57: 27, # langMongolian → smMongolian
+ 58: 7, # langMongolianCyr → smCyrillic
+ 59: 4, # langPashto → smArabic
+ 60: 4, # langKurdish → smArabic
+ 61: 4, # langKashmiri → smArabic
+ 62: 4, # langSindhi → smArabic
+ 63: 26, # langTibetan → smTibetan
+ 64: 9, # langNepali → smDevanagari
+ 65: 9, # langSanskrit → smDevanagari
+ 66: 9, # langMarathi → smDevanagari
+ 67: 13, # langBengali → smBengali
+ 68: 13, # langAssamese → smBengali
+ 69: 11, # langGujarati → smGujarati
+ 70: 10, # langPunjabi → smGurmukhi
+ 71: 12, # langOriya → smOriya
+ 72: 17, # langMalayalam → smMalayalam
+ 73: 16, # langKannada → smKannada
+ 74: 14, # langTamil → smTamil
+ 75: 15, # langTelugu → smTelugu
+ 76: 18, # langSinhalese → smSinhalese
+ 77: 19, # langBurmese → smBurmese
+ 78: 20, # langKhmer → smKhmer
+ 79: 22, # langLao → smLao
+ 80: 30, # langVietnamese → smVietnamese
+ 81: 0, # langIndonesian → smRoman
+ 82: 0, # langTagalog → smRoman
+ 83: 0, # langMalayRoman → smRoman
+ 84: 4, # langMalayArabic → smArabic
+ 85: 28, # langAmharic → smEthiopic
+ 86: 28, # langTigrinya → smEthiopic
+ 87: 28, # langOromo → smEthiopic
+ 88: 0, # langSomali → smRoman
+ 89: 0, # langSwahili → smRoman
+ 90: 0, # langKinyarwanda → smRoman
+ 91: 0, # langRundi → smRoman
+ 92: 0, # langNyanja → smRoman
+ 93: 0, # langMalagasy → smRoman
+ 94: 0, # langEsperanto → smRoman
+ 128: 0, # langWelsh → smRoman (modified)
+ 129: 0, # langBasque → smRoman
+ 130: 0, # langCatalan → smRoman
+ 131: 0, # langLatin → smRoman
+ 132: 0, # langQuechua → smRoman
+ 133: 0, # langGuarani → smRoman
+ 134: 0, # langAymara → smRoman
+ 135: 7, # langTatar → smCyrillic
+ 136: 4, # langUighur → smArabic
+ 137: 26, # langDzongkha → smTibetan
+ 138: 0, # langJavaneseRom → smRoman
+ 139: 0, # langSundaneseRom → smRoman
+ 140: 0, # langGalician → smRoman
+ 141: 0, # langAfrikaans → smRoman
+ 142: 0, # langBreton → smRoman (modified)
+ 143: 28, # langInuktitut → smEthiopic (modified)
+ 144: 0, # langScottishGaelic → smRoman (modified)
+ 145: 0, # langManxGaelic → smRoman (modified)
+ 146: 0, # langIrishGaelicScript → smRoman (modified)
+ 147: 0, # langTongan → smRoman
+ 148: 6, # langGreekAncient → smRoman
+ 149: 0, # langGreenlandic → smRoman
+ 150: 0, # langAzerbaijanRoman → smRoman
+ 151: 0, # langNynorsk → smRoman
+}
+
+
+class NameRecordVisitor(TTVisitor):
+ # Font tables that have NameIDs we need to collect.
+ TABLES = ("GSUB", "GPOS", "fvar", "CPAL", "STAT")
+
+ def __init__(self):
+ self.seen = set()
+
+
+@NameRecordVisitor.register_attrs(
+ (
+ (otTables.FeatureParamsSize, ("SubfamilyNameID",)),
+ (otTables.FeatureParamsStylisticSet, ("UINameID",)),
+ (otTables.STAT, ("ElidedFallbackNameID",)),
+ (otTables.AxisRecord, ("AxisNameID",)),
+ (otTables.AxisValue, ("ValueNameID",)),
+ (otTables.FeatureName, ("FeatureNameID",)),
+ (otTables.Setting, ("SettingNameID",)),
+ )
+)
+def visit(visitor, obj, attr, value):
+ visitor.seen.add(value)
+
+
+@NameRecordVisitor.register(otTables.FeatureParamsCharacterVariants)
+def visit(visitor, obj):
+ for attr in ("FeatUILabelNameID", "FeatUITooltipTextNameID", "SampleTextNameID"):
+ value = getattr(obj, attr)
+ visitor.seen.add(value)
+ # also include the sequence of UI strings for individual variants, if any
+ if obj.FirstParamUILabelNameID == 0 or obj.NumNamedParameters == 0:
+ return
+ visitor.seen.update(
+ range(
+ obj.FirstParamUILabelNameID,
+ obj.FirstParamUILabelNameID + obj.NumNamedParameters,
+ )
+ )
+
+
+@NameRecordVisitor.register(ttLib.getTableClass("fvar"))
+def visit(visitor, obj):
+ for inst in obj.instances:
+ if inst.postscriptNameID != 0xFFFF:
+ visitor.seen.add(inst.postscriptNameID)
+ visitor.seen.add(inst.subfamilyNameID)
+
+ for axis in obj.axes:
+ visitor.seen.add(axis.axisNameID)
+
+
+@NameRecordVisitor.register(ttLib.getTableClass("CPAL"))
+def visit(visitor, obj):
+ if obj.version == 1:
+ visitor.seen.update(obj.paletteLabels)
+ visitor.seen.update(obj.paletteEntryLabels)
+
+
+@NameRecordVisitor.register(ttLib.TTFont)
+def visit(visitor, font, *args, **kwargs):
+ if hasattr(visitor, "font"):
+ return False
+
+ visitor.font = font
+ for tag in visitor.TABLES:
+ if tag in font:
+ visitor.visit(font[tag], *args, **kwargs)
+ del visitor.font
+ return False
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_o_p_b_d.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_o_p_b_d.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4c5f568290e20563992de91c7a0f5e6928652bf
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_o_p_b_d.py
@@ -0,0 +1,14 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+class table__o_p_b_d(BaseTTXConverter):
+ """Optical Bounds table
+
+ The AAT ``opbd`` table contains optical boundary points for glyphs, which
+ applications can use for the visual alignment of lines of text.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_o_s_t.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_o_s_t.py
new file mode 100644
index 0000000000000000000000000000000000000000..1564c89347b4b7934b978c8f4ae383851b6d18d5
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_o_s_t.py
@@ -0,0 +1,319 @@
+from fontTools import ttLib
+from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import bytechr, byteord, tobytes, tostr, safeEval, readHex
+from . import DefaultTable
+import sys
+import struct
+import array
+import logging
+
+log = logging.getLogger(__name__)
+
+postFormat = """
+ >
+ formatType: 16.16F
+ italicAngle: 16.16F # italic angle in degrees
+ underlinePosition: h
+ underlineThickness: h
+ isFixedPitch: L
+ minMemType42: L # minimum memory if TrueType font is downloaded
+ maxMemType42: L # maximum memory if TrueType font is downloaded
+ minMemType1: L # minimum memory if Type1 font is downloaded
+ maxMemType1: L # maximum memory if Type1 font is downloaded
+"""
+
+postFormatSize = sstruct.calcsize(postFormat)
+
+
+class table__p_o_s_t(DefaultTable.DefaultTable):
+ """PostScript table
+
+ The ``post`` table contains information needed to use the font on
+ PostScript printers, including the PostScript names of glyphs and
+ data that was stored in the ``FontInfo`` dictionary for Type 1 fonts.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/post
+ """
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(postFormat, data[:postFormatSize], self)
+ data = data[postFormatSize:]
+ if self.formatType == 1.0:
+ self.decode_format_1_0(data, ttFont)
+ elif self.formatType == 2.0:
+ self.decode_format_2_0(data, ttFont)
+ elif self.formatType == 3.0:
+ self.decode_format_3_0(data, ttFont)
+ elif self.formatType == 4.0:
+ self.decode_format_4_0(data, ttFont)
+ else:
+ # supported format
+ raise ttLib.TTLibError(
+ "'post' table format %f not supported" % self.formatType
+ )
+
+ def compile(self, ttFont):
+ data = sstruct.pack(postFormat, self)
+ if self.formatType == 1.0:
+ pass # we're done
+ elif self.formatType == 2.0:
+ data = data + self.encode_format_2_0(ttFont)
+ elif self.formatType == 3.0:
+ pass # we're done
+ elif self.formatType == 4.0:
+ data = data + self.encode_format_4_0(ttFont)
+ else:
+ # supported format
+ raise ttLib.TTLibError(
+ "'post' table format %f not supported" % self.formatType
+ )
+ return data
+
+ def getGlyphOrder(self):
+ """This function will get called by a ttLib.TTFont instance.
+ Do not call this function yourself, use TTFont().getGlyphOrder()
+ or its relatives instead!
+ """
+ if not hasattr(self, "glyphOrder"):
+ raise ttLib.TTLibError("illegal use of getGlyphOrder()")
+ glyphOrder = self.glyphOrder
+ del self.glyphOrder
+ return glyphOrder
+
+ def decode_format_1_0(self, data, ttFont):
+ self.glyphOrder = standardGlyphOrder[: ttFont["maxp"].numGlyphs]
+
+ def decode_format_2_0(self, data, ttFont):
+ (numGlyphs,) = struct.unpack(">H", data[:2])
+ numGlyphs = int(numGlyphs)
+ if numGlyphs > ttFont["maxp"].numGlyphs:
+ # Assume the numGlyphs field is bogus, so sync with maxp.
+ # I've seen this in one font, and if the assumption is
+ # wrong elsewhere, well, so be it: it's hard enough to
+ # work around _one_ non-conforming post format...
+ numGlyphs = ttFont["maxp"].numGlyphs
+ data = data[2:]
+ indices = array.array("H")
+ indices.frombytes(data[: 2 * numGlyphs])
+ if sys.byteorder != "big":
+ indices.byteswap()
+ data = data[2 * numGlyphs :]
+ maxIndex = max(indices)
+ self.extraNames = extraNames = unpackPStrings(data, maxIndex - 257)
+ self.glyphOrder = glyphOrder = [""] * int(ttFont["maxp"].numGlyphs)
+ for glyphID in range(numGlyphs):
+ index = indices[glyphID]
+ if index > 257:
+ try:
+ name = extraNames[index - 258]
+ except IndexError:
+ name = ""
+ else:
+ # fetch names from standard list
+ name = standardGlyphOrder[index]
+ glyphOrder[glyphID] = name
+ self.build_psNameMapping(ttFont)
+
+ def build_psNameMapping(self, ttFont):
+ mapping = {}
+ allNames = {}
+ glyphOrderNames = set(self.glyphOrder)
+ for i in range(ttFont["maxp"].numGlyphs):
+ glyphName = psName = self.glyphOrder[i]
+ if glyphName == "":
+ glyphName = "glyph%.5d" % i
+
+ if glyphName in allNames:
+ # make up a new glyphName that's unique
+ n = allNames[glyphName]
+ # check if the glyph name exists in the glyph order
+ while f"{glyphName}.{n}" in glyphOrderNames:
+ n += 1
+ allNames[glyphName] = n + 1
+ glyphName = f"{glyphName}.{n}"
+
+ allNames[glyphName] = 1
+ if glyphName != psName:
+ self.glyphOrder[i] = glyphName
+ mapping[glyphName] = psName
+
+ self.mapping = mapping
+
+ def decode_format_3_0(self, data, ttFont):
+ # Setting self.glyphOrder to None will cause the TTFont object
+ # try and construct glyph names from a Unicode cmap table.
+ self.glyphOrder = None
+
+ def decode_format_4_0(self, data, ttFont):
+ from fontTools import agl
+
+ numGlyphs = ttFont["maxp"].numGlyphs
+ indices = array.array("H")
+ indices.frombytes(data)
+ if sys.byteorder != "big":
+ indices.byteswap()
+ # In some older fonts, the size of the post table doesn't match
+ # the number of glyphs. Sometimes it's bigger, sometimes smaller.
+ self.glyphOrder = glyphOrder = [""] * int(numGlyphs)
+ for i in range(min(len(indices), numGlyphs)):
+ if indices[i] == 0xFFFF:
+ self.glyphOrder[i] = ""
+ elif indices[i] in agl.UV2AGL:
+ self.glyphOrder[i] = agl.UV2AGL[indices[i]]
+ else:
+ self.glyphOrder[i] = "uni%04X" % indices[i]
+ self.build_psNameMapping(ttFont)
+
+ def encode_format_2_0(self, ttFont):
+ numGlyphs = ttFont["maxp"].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ assert len(glyphOrder) == numGlyphs
+ indices = array.array("H")
+ extraDict = {}
+ extraNames = self.extraNames = [
+ n for n in self.extraNames if n not in standardGlyphOrder
+ ]
+ for i, name in enumerate(extraNames):
+ extraDict[name] = i
+ for glyphName in glyphOrder:
+ if glyphName in self.mapping:
+ psName = self.mapping[glyphName]
+ else:
+ psName = glyphName
+ if psName in extraDict:
+ index = 258 + extraDict[psName]
+ elif psName in standardGlyphOrder:
+ index = standardGlyphOrder.index(psName)
+ else:
+ index = 258 + len(extraNames)
+ extraDict[psName] = len(extraNames)
+ extraNames.append(psName)
+ indices.append(index)
+ if sys.byteorder != "big":
+ indices.byteswap()
+ return (
+ struct.pack(">H", numGlyphs) + indices.tobytes() + packPStrings(extraNames)
+ )
+
+ def encode_format_4_0(self, ttFont):
+ from fontTools import agl
+
+ numGlyphs = ttFont["maxp"].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ assert len(glyphOrder) == numGlyphs
+ indices = array.array("H")
+ for glyphID in glyphOrder:
+ glyphID = glyphID.split("#")[0]
+ if glyphID in agl.AGL2UV:
+ indices.append(agl.AGL2UV[glyphID])
+ elif len(glyphID) == 7 and glyphID[:3] == "uni":
+ indices.append(int(glyphID[3:], 16))
+ else:
+ indices.append(0xFFFF)
+ if sys.byteorder != "big":
+ indices.byteswap()
+ return indices.tobytes()
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(postFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+ if hasattr(self, "mapping"):
+ writer.begintag("psNames")
+ writer.newline()
+ writer.comment(
+ "This file uses unique glyph names based on the information\n"
+ "found in the 'post' table. Since these names might not be unique,\n"
+ "we have to invent artificial names in case of clashes. In order to\n"
+ "be able to retain the original information, we need a name to\n"
+ "ps name mapping for those cases where they differ. That's what\n"
+ "you see below.\n"
+ )
+ writer.newline()
+ items = sorted(self.mapping.items())
+ for name, psName in items:
+ writer.simpletag("psName", name=name, psName=psName)
+ writer.newline()
+ writer.endtag("psNames")
+ writer.newline()
+ if hasattr(self, "extraNames"):
+ writer.begintag("extraNames")
+ writer.newline()
+ writer.comment(
+ "following are the name that are not taken from the standard Mac glyph order"
+ )
+ writer.newline()
+ for name in self.extraNames:
+ writer.simpletag("psName", name=name)
+ writer.newline()
+ writer.endtag("extraNames")
+ writer.newline()
+ if hasattr(self, "data"):
+ writer.begintag("hexdata")
+ writer.newline()
+ writer.dumphex(self.data)
+ writer.endtag("hexdata")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name not in ("psNames", "extraNames", "hexdata"):
+ setattr(self, name, safeEval(attrs["value"]))
+ elif name == "psNames":
+ self.mapping = {}
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "psName":
+ self.mapping[attrs["name"]] = attrs["psName"]
+ elif name == "extraNames":
+ self.extraNames = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "psName":
+ self.extraNames.append(attrs["name"])
+ else:
+ self.data = readHex(content)
+
+
+def unpackPStrings(data, n):
+ # extract n Pascal strings from data.
+ # if there is not enough data, use ""
+
+ strings = []
+ index = 0
+ dataLen = len(data)
+
+ for _ in range(n):
+ if dataLen <= index:
+ length = 0
+ else:
+ length = byteord(data[index])
+ index += 1
+
+ if dataLen <= index + length - 1:
+ name = ""
+ else:
+ name = tostr(data[index : index + length], encoding="latin1")
+ strings.append(name)
+ index += length
+
+ if index < dataLen:
+ log.warning("%d extra bytes in post.stringData array", dataLen - index)
+
+ elif dataLen < index:
+ log.warning("not enough data in post.stringData array")
+
+ return strings
+
+
+def packPStrings(strings):
+ data = b""
+ for s in strings:
+ data = data + bytechr(len(s)) + tobytes(s, encoding="latin1")
+ return data
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_e_p.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_e_p.py
new file mode 100644
index 0000000000000000000000000000000000000000..cf4a5dd24f27ab08082cf9976bc2f5a9e1a967a5
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_e_p.py
@@ -0,0 +1,16 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("fpgm")
+
+
+class table__p_r_e_p(superclass):
+ """Control Value Program table
+
+ The ``prep`` table contains TrueType instructions that can makee font-wide
+ alterations to the Control Value Table. It may potentially be executed
+ before any glyph is processed.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/prep
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_o_p.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_o_p.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d7de8098de797268cd14607939e78e0c5c7bf5a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_p_r_o_p.py
@@ -0,0 +1,12 @@
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6prop.html
+class table__p_r_o_p(BaseTTXConverter):
+ """The AAT ``prop`` table can store a variety of per-glyph properties, such as
+ Unicode directionality or whether glyphs are non-spacing marks.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6prop.html
+ """
+
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_s_b_i_x.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_s_b_i_x.py
new file mode 100644
index 0000000000000000000000000000000000000000..a282ea9a60d1a1e5f58747db6079d7c493504295
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_s_b_i_x.py
@@ -0,0 +1,129 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from . import DefaultTable
+from .sbixStrike import Strike
+
+
+sbixHeaderFormat = """
+ >
+ version: H # Version number (set to 1)
+ flags: H # The only two bits used in the flags field are bits 0
+ # and 1. For historical reasons, bit 0 must always be 1.
+ # Bit 1 is a sbixDrawOutlines flag and is interpreted as
+ # follows:
+ # 0: Draw only 'sbix' bitmaps
+ # 1: Draw both 'sbix' bitmaps and outlines, in that
+ # order
+ numStrikes: L # Number of bitmap strikes to follow
+"""
+sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat)
+
+
+sbixStrikeOffsetFormat = """
+ >
+ strikeOffset: L # Offset from begining of table to data for the
+ # individual strike
+"""
+sbixStrikeOffsetFormatSize = sstruct.calcsize(sbixStrikeOffsetFormat)
+
+
+class table__s_b_i_x(DefaultTable.DefaultTable):
+ """Standard Bitmap Graphics table
+
+ The ``sbix`` table stores bitmap image data in standard graphics formats
+ like JPEG, PNG, or TIFF. The glyphs for which the ``sbix`` table provides
+ data are indexed by Glyph ID. For each such glyph, the ``sbix`` table can
+ hold different data for different sizes, called "strikes."
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/sbix
+ """
+
+ def __init__(self, tag=None):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ self.version = 1
+ self.flags = 1
+ self.numStrikes = 0
+ self.strikes = {}
+ self.strikeOffsets = []
+
+ def decompile(self, data, ttFont):
+ # read table header
+ sstruct.unpack(sbixHeaderFormat, data[:sbixHeaderFormatSize], self)
+ # collect offsets to individual strikes in self.strikeOffsets
+ for i in range(self.numStrikes):
+ current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize
+ offset_entry = sbixStrikeOffset()
+ sstruct.unpack(
+ sbixStrikeOffsetFormat,
+ data[current_offset : current_offset + sbixStrikeOffsetFormatSize],
+ offset_entry,
+ )
+ self.strikeOffsets.append(offset_entry.strikeOffset)
+
+ # decompile Strikes
+ for i in range(self.numStrikes - 1, -1, -1):
+ current_strike = Strike(rawdata=data[self.strikeOffsets[i] :])
+ data = data[: self.strikeOffsets[i]]
+ current_strike.decompile(ttFont)
+ # print " Strike length: %xh" % len(bitmapSetData)
+ # print "Number of Glyph entries:", len(current_strike.glyphs)
+ if current_strike.ppem in self.strikes:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike")
+ self.strikes[current_strike.ppem] = current_strike
+
+ # after the glyph data records have been extracted, we don't need the offsets anymore
+ del self.strikeOffsets
+ del self.numStrikes
+
+ def compile(self, ttFont):
+ sbixData = b""
+ self.numStrikes = len(self.strikes)
+ sbixHeader = sstruct.pack(sbixHeaderFormat, self)
+
+ # calculate offset to start of first strike
+ setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes
+
+ for si in sorted(self.strikes.keys()):
+ current_strike = self.strikes[si]
+ current_strike.compile(ttFont)
+ # append offset to this strike to table header
+ current_strike.strikeOffset = setOffset
+ sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike)
+ setOffset += len(current_strike.data)
+ sbixData += current_strike.data
+
+ return sbixHeader + sbixData
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.simpletag("version", value=self.version)
+ xmlWriter.newline()
+ xmlWriter.simpletag("flags", value=num2binary(self.flags, 16))
+ xmlWriter.newline()
+ for i in sorted(self.strikes.keys()):
+ self.strikes[i].toXML(xmlWriter, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ setattr(self, name, safeEval(attrs["value"]))
+ elif name == "flags":
+ setattr(self, name, binary2num(attrs["value"]))
+ elif name == "strike":
+ current_strike = Strike()
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ current_strike.fromXML(name, attrs, content, ttFont)
+ self.strikes[current_strike.ppem] = current_strike
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
+
+
+# Helper classes
+
+
+class sbixStrikeOffset(object):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_t_r_a_k.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_t_r_a_k.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0e8e19e08a87514a252b96a6836a442646e484c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_t_r_a_k.py
@@ -0,0 +1,332 @@
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+)
+from fontTools.misc.textTools import bytesjoin, safeEval
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import struct
+from collections.abc import MutableMapping
+
+
+# Apple's documentation of 'trak':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html
+
+TRAK_HEADER_FORMAT = """
+ > # big endian
+ version: 16.16F
+ format: H
+ horizOffset: H
+ vertOffset: H
+ reserved: H
+"""
+
+TRAK_HEADER_FORMAT_SIZE = sstruct.calcsize(TRAK_HEADER_FORMAT)
+
+
+TRACK_DATA_FORMAT = """
+ > # big endian
+ nTracks: H
+ nSizes: H
+ sizeTableOffset: L
+"""
+
+TRACK_DATA_FORMAT_SIZE = sstruct.calcsize(TRACK_DATA_FORMAT)
+
+
+TRACK_TABLE_ENTRY_FORMAT = """
+ > # big endian
+ track: 16.16F
+ nameIndex: H
+ offset: H
+"""
+
+TRACK_TABLE_ENTRY_FORMAT_SIZE = sstruct.calcsize(TRACK_TABLE_ENTRY_FORMAT)
+
+
+# size values are actually '16.16F' fixed-point values, but here I do the
+# fixedToFloat conversion manually instead of relying on sstruct
+SIZE_VALUE_FORMAT = ">l"
+SIZE_VALUE_FORMAT_SIZE = struct.calcsize(SIZE_VALUE_FORMAT)
+
+# per-Size values are in 'FUnits', i.e. 16-bit signed integers
+PER_SIZE_VALUE_FORMAT = ">h"
+PER_SIZE_VALUE_FORMAT_SIZE = struct.calcsize(PER_SIZE_VALUE_FORMAT)
+
+
+class table__t_r_a_k(DefaultTable.DefaultTable):
+ """The AAT ``trak`` table can store per-size adjustments to each glyph's
+ sidebearings to make when tracking is enabled, which applications can
+ use to provide more visually balanced line spacing.
+
+ See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html
+ """
+
+ dependencies = ["name"]
+
+ def compile(self, ttFont):
+ dataList = []
+ offset = TRAK_HEADER_FORMAT_SIZE
+ for direction in ("horiz", "vert"):
+ trackData = getattr(self, direction + "Data", TrackData())
+ offsetName = direction + "Offset"
+ # set offset to 0 if None or empty
+ if not trackData:
+ setattr(self, offsetName, 0)
+ continue
+ # TrackData table format must be longword aligned
+ alignedOffset = (offset + 3) & ~3
+ padding, offset = b"\x00" * (alignedOffset - offset), alignedOffset
+ setattr(self, offsetName, offset)
+
+ data = trackData.compile(offset)
+ offset += len(data)
+ dataList.append(padding + data)
+
+ self.reserved = 0
+ tableData = bytesjoin([sstruct.pack(TRAK_HEADER_FORMAT, self)] + dataList)
+ return tableData
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(TRAK_HEADER_FORMAT, data[:TRAK_HEADER_FORMAT_SIZE], self)
+ for direction in ("horiz", "vert"):
+ trackData = TrackData()
+ offset = getattr(self, direction + "Offset")
+ if offset != 0:
+ trackData.decompile(data, offset)
+ setattr(self, direction + "Data", trackData)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ writer.simpletag("format", value=self.format)
+ writer.newline()
+ for direction in ("horiz", "vert"):
+ dataName = direction + "Data"
+ writer.begintag(dataName)
+ writer.newline()
+ trackData = getattr(self, dataName, TrackData())
+ trackData.toXML(writer, ttFont)
+ writer.endtag(dataName)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "version":
+ self.version = safeEval(attrs["value"])
+ elif name == "format":
+ self.format = safeEval(attrs["value"])
+ elif name in ("horizData", "vertData"):
+ trackData = TrackData()
+ setattr(self, name, trackData)
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content_ = element
+ trackData.fromXML(name, attrs, content_, ttFont)
+
+
+class TrackData(MutableMapping):
+ def __init__(self, initialdata={}):
+ self._map = dict(initialdata)
+
+ def compile(self, offset):
+ nTracks = len(self)
+ sizes = self.sizes()
+ nSizes = len(sizes)
+
+ # offset to the start of the size subtable
+ offset += TRACK_DATA_FORMAT_SIZE + TRACK_TABLE_ENTRY_FORMAT_SIZE * nTracks
+ trackDataHeader = sstruct.pack(
+ TRACK_DATA_FORMAT,
+ {"nTracks": nTracks, "nSizes": nSizes, "sizeTableOffset": offset},
+ )
+
+ entryDataList = []
+ perSizeDataList = []
+ # offset to per-size tracking values
+ offset += SIZE_VALUE_FORMAT_SIZE * nSizes
+ # sort track table entries by track value
+ for track, entry in sorted(self.items()):
+ assert entry.nameIndex is not None
+ entry.track = track
+ entry.offset = offset
+ entryDataList += [sstruct.pack(TRACK_TABLE_ENTRY_FORMAT, entry)]
+ # sort per-size values by size
+ for size, value in sorted(entry.items()):
+ perSizeDataList += [struct.pack(PER_SIZE_VALUE_FORMAT, value)]
+ offset += PER_SIZE_VALUE_FORMAT_SIZE * nSizes
+ # sort size values
+ sizeDataList = [
+ struct.pack(SIZE_VALUE_FORMAT, fl2fi(sv, 16)) for sv in sorted(sizes)
+ ]
+
+ data = bytesjoin(
+ [trackDataHeader] + entryDataList + sizeDataList + perSizeDataList
+ )
+ return data
+
+ def decompile(self, data, offset):
+ # initial offset is from the start of trak table to the current TrackData
+ trackDataHeader = data[offset : offset + TRACK_DATA_FORMAT_SIZE]
+ if len(trackDataHeader) != TRACK_DATA_FORMAT_SIZE:
+ raise TTLibError("not enough data to decompile TrackData header")
+ sstruct.unpack(TRACK_DATA_FORMAT, trackDataHeader, self)
+ offset += TRACK_DATA_FORMAT_SIZE
+
+ nSizes = self.nSizes
+ sizeTableOffset = self.sizeTableOffset
+ sizeTable = []
+ for i in range(nSizes):
+ sizeValueData = data[
+ sizeTableOffset : sizeTableOffset + SIZE_VALUE_FORMAT_SIZE
+ ]
+ if len(sizeValueData) < SIZE_VALUE_FORMAT_SIZE:
+ raise TTLibError("not enough data to decompile TrackData size subtable")
+ (sizeValue,) = struct.unpack(SIZE_VALUE_FORMAT, sizeValueData)
+ sizeTable.append(fi2fl(sizeValue, 16))
+ sizeTableOffset += SIZE_VALUE_FORMAT_SIZE
+
+ for i in range(self.nTracks):
+ entry = TrackTableEntry()
+ entryData = data[offset : offset + TRACK_TABLE_ENTRY_FORMAT_SIZE]
+ if len(entryData) < TRACK_TABLE_ENTRY_FORMAT_SIZE:
+ raise TTLibError("not enough data to decompile TrackTableEntry record")
+ sstruct.unpack(TRACK_TABLE_ENTRY_FORMAT, entryData, entry)
+ perSizeOffset = entry.offset
+ for j in range(nSizes):
+ size = sizeTable[j]
+ perSizeValueData = data[
+ perSizeOffset : perSizeOffset + PER_SIZE_VALUE_FORMAT_SIZE
+ ]
+ if len(perSizeValueData) < PER_SIZE_VALUE_FORMAT_SIZE:
+ raise TTLibError(
+ "not enough data to decompile per-size track values"
+ )
+ (perSizeValue,) = struct.unpack(PER_SIZE_VALUE_FORMAT, perSizeValueData)
+ entry[size] = perSizeValue
+ perSizeOffset += PER_SIZE_VALUE_FORMAT_SIZE
+ self[entry.track] = entry
+ offset += TRACK_TABLE_ENTRY_FORMAT_SIZE
+
+ def toXML(self, writer, ttFont):
+ nTracks = len(self)
+ nSizes = len(self.sizes())
+ writer.comment("nTracks=%d, nSizes=%d" % (nTracks, nSizes))
+ writer.newline()
+ for track, entry in sorted(self.items()):
+ assert entry.nameIndex is not None
+ entry.track = track
+ entry.toXML(writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "trackEntry":
+ return
+ entry = TrackTableEntry()
+ entry.fromXML(name, attrs, content, ttFont)
+ self[entry.track] = entry
+
+ def sizes(self):
+ if not self:
+ return frozenset()
+ tracks = list(self.tracks())
+ sizes = self[tracks.pop(0)].sizes()
+ for track in tracks:
+ entrySizes = self[track].sizes()
+ if sizes != entrySizes:
+ raise TTLibError(
+ "'trak' table entries must specify the same sizes: "
+ "%s != %s" % (sorted(sizes), sorted(entrySizes))
+ )
+ return frozenset(sizes)
+
+ def __getitem__(self, track):
+ return self._map[track]
+
+ def __delitem__(self, track):
+ del self._map[track]
+
+ def __setitem__(self, track, entry):
+ self._map[track] = entry
+
+ def __len__(self):
+ return len(self._map)
+
+ def __iter__(self):
+ return iter(self._map)
+
+ def keys(self):
+ return self._map.keys()
+
+ tracks = keys
+
+ def __repr__(self):
+ return "TrackData({})".format(self._map if self else "")
+
+
+class TrackTableEntry(MutableMapping):
+ def __init__(self, values={}, nameIndex=None):
+ self.nameIndex = nameIndex
+ self._map = dict(values)
+
+ def toXML(self, writer, ttFont):
+ name = ttFont["name"].getDebugName(self.nameIndex)
+ writer.begintag(
+ "trackEntry",
+ (("value", fl2str(self.track, 16)), ("nameIndex", self.nameIndex)),
+ )
+ writer.newline()
+ if name:
+ writer.comment(name)
+ writer.newline()
+ for size, perSizeValue in sorted(self.items()):
+ writer.simpletag("track", size=fl2str(size, 16), value=perSizeValue)
+ writer.newline()
+ writer.endtag("trackEntry")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ self.track = str2fl(attrs["value"], 16)
+ self.nameIndex = safeEval(attrs["nameIndex"])
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, _ = element
+ if name != "track":
+ continue
+ size = str2fl(attrs["size"], 16)
+ self[size] = safeEval(attrs["value"])
+
+ def __getitem__(self, size):
+ return self._map[size]
+
+ def __delitem__(self, size):
+ del self._map[size]
+
+ def __setitem__(self, size, value):
+ self._map[size] = value
+
+ def __len__(self):
+ return len(self._map)
+
+ def __iter__(self):
+ return iter(self._map)
+
+ def keys(self):
+ return self._map.keys()
+
+ sizes = keys
+
+ def __repr__(self):
+ return "TrackTableEntry({}, nameIndex={})".format(self._map, self.nameIndex)
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return self.nameIndex == other.nameIndex and dict(self) == dict(other)
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_h_e_a.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_h_e_a.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c6fa3aefe2e91c042948b4d21a7df3e8087a17c
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_h_e_a.py
@@ -0,0 +1,139 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import (
+ ensureVersionIsLong as fi2ve,
+ versionToFixed as ve2fi,
+)
+from . import DefaultTable
+import math
+
+
+vheaFormat = """
+ > # big endian
+ tableVersion: L
+ ascent: h
+ descent: h
+ lineGap: h
+ advanceHeightMax: H
+ minTopSideBearing: h
+ minBottomSideBearing: h
+ yMaxExtent: h
+ caretSlopeRise: h
+ caretSlopeRun: h
+ caretOffset: h
+ reserved1: h
+ reserved2: h
+ reserved3: h
+ reserved4: h
+ metricDataFormat: h
+ numberOfVMetrics: H
+"""
+
+
+class table__v_h_e_a(DefaultTable.DefaultTable):
+ """Vertical Header table
+
+ The ``vhea`` table contains information needed during vertical
+ text layout.
+
+ .. note::
+ This converter class is kept in sync with the :class:`._h_h_e_a.table__h_h_e_a`
+ table constructor.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/vhea
+ """
+
+ # Note: Keep in sync with table__h_h_e_a
+
+ dependencies = ["vmtx", "glyf", "CFF ", "CFF2"]
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(vheaFormat, data, self)
+
+ def compile(self, ttFont):
+ if ttFont.recalcBBoxes and (
+ ttFont.isLoaded("glyf")
+ or ttFont.isLoaded("CFF ")
+ or ttFont.isLoaded("CFF2")
+ ):
+ self.recalc(ttFont)
+ self.tableVersion = fi2ve(self.tableVersion)
+ return sstruct.pack(vheaFormat, self)
+
+ def recalc(self, ttFont):
+ if "vmtx" not in ttFont:
+ return
+
+ vmtxTable = ttFont["vmtx"]
+ self.advanceHeightMax = max(adv for adv, _ in vmtxTable.metrics.values())
+
+ boundsHeightDict = {}
+ if "glyf" in ttFont:
+ glyfTable = ttFont["glyf"]
+ for name in ttFont.getGlyphOrder():
+ g = glyfTable[name]
+ if g.numberOfContours == 0:
+ continue
+ if g.numberOfContours < 0 and not hasattr(g, "yMax"):
+ # Composite glyph without extents set.
+ # Calculate those.
+ g.recalcBounds(glyfTable)
+ boundsHeightDict[name] = g.yMax - g.yMin
+ elif "CFF " in ttFont or "CFF2" in ttFont:
+ if "CFF " in ttFont:
+ topDict = ttFont["CFF "].cff.topDictIndex[0]
+ else:
+ topDict = ttFont["CFF2"].cff.topDictIndex[0]
+ charStrings = topDict.CharStrings
+ for name in ttFont.getGlyphOrder():
+ cs = charStrings[name]
+ bounds = cs.calcBounds(charStrings)
+ if bounds is not None:
+ boundsHeightDict[name] = int(
+ math.ceil(bounds[3]) - math.floor(bounds[1])
+ )
+
+ if boundsHeightDict:
+ minTopSideBearing = float("inf")
+ minBottomSideBearing = float("inf")
+ yMaxExtent = -float("inf")
+ for name, boundsHeight in boundsHeightDict.items():
+ advanceHeight, tsb = vmtxTable[name]
+ bsb = advanceHeight - tsb - boundsHeight
+ extent = tsb + boundsHeight
+ minTopSideBearing = min(minTopSideBearing, tsb)
+ minBottomSideBearing = min(minBottomSideBearing, bsb)
+ yMaxExtent = max(yMaxExtent, extent)
+ self.minTopSideBearing = minTopSideBearing
+ self.minBottomSideBearing = minBottomSideBearing
+ self.yMaxExtent = yMaxExtent
+
+ else: # No glyph has outlines.
+ self.minTopSideBearing = 0
+ self.minBottomSideBearing = 0
+ self.yMaxExtent = 0
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(vheaFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name == "tableVersion":
+ value = fi2ve(value)
+ value = "0x%08x" % value
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "tableVersion":
+ setattr(self, name, ve2fi(attrs["value"]))
+ return
+ setattr(self, name, safeEval(attrs["value"]))
+
+ # reserved0 is caretOffset for legacy reasons
+ @property
+ def reserved0(self):
+ return self.caretOffset
+
+ @reserved0.setter
+ def reserved0(self, value):
+ self.caretOffset = value
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_m_t_x.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_m_t_x.py
new file mode 100644
index 0000000000000000000000000000000000000000..32662fc6a257e9bb3da60b259756d8f9f7fa7919
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/_v_m_t_x.py
@@ -0,0 +1,19 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("hmtx")
+
+
+class table__v_m_t_x(superclass):
+ """Vertical Metrics table
+
+ The ``vmtx`` table contains per-glyph metrics for the glyphs in a
+ ``glyf``, ``CFF ``, or ``CFF2`` table, as needed for vertical text
+ layout.
+
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/vmtx
+ """
+
+ headerTag = "vhea"
+ advanceName = "height"
+ sideBearingName = "tsb"
+ numberOfMetricsName = "numberOfVMetrics"
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/asciiTable.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/asciiTable.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f81c526b372b268b253da47c337715e316ee4d4
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/asciiTable.py
@@ -0,0 +1,20 @@
+from fontTools.misc.textTools import strjoin, tobytes, tostr
+from . import DefaultTable
+
+
+class asciiTable(DefaultTable.DefaultTable):
+ def toXML(self, writer, ttFont):
+ data = tostr(self.data)
+ # removing null bytes. XXX needed??
+ data = data.split("\0")
+ data = strjoin(data)
+ writer.begintag("source")
+ writer.newline()
+ writer.write_noindent(data)
+ writer.newline()
+ writer.endtag("source")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ lines = strjoin(content).split("\n")
+ self.data = tobytes("\n".join(lines[1:-1]))
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/grUtils.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/grUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..785684b1eb30a76ae598bfe46416d4556fc422a0
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/grUtils.py
@@ -0,0 +1,92 @@
+import struct, warnings
+
+try:
+ import lz4
+except ImportError:
+ lz4 = None
+else:
+ import lz4.block
+
+# old scheme for VERSION < 0.9 otherwise use lz4.block
+
+
+def decompress(data):
+ (compression,) = struct.unpack(">L", data[4:8])
+ scheme = compression >> 27
+ size = compression & 0x07FFFFFF
+ if scheme == 0:
+ pass
+ elif scheme == 1 and lz4:
+ res = lz4.block.decompress(struct.pack("L", (scheme << 27) + (len(data) & 0x07FFFFFF))
+ if scheme == 0:
+ return data
+ elif scheme == 1 and lz4:
+ res = lz4.block.compress(
+ data, mode="high_compression", compression=16, store_size=False
+ )
+ return hdr + res
+ else:
+ warnings.warn("Table failed to compress by unsupported compression scheme")
+ return data
+
+
+def _entries(attrs, sameval):
+ ak = 0
+ vals = []
+ lastv = 0
+ for k, v in attrs:
+ if len(vals) and (k != ak + 1 or (sameval and v != lastv)):
+ yield (ak - len(vals) + 1, len(vals), vals)
+ vals = []
+ ak = k
+ vals.append(v)
+ lastv = v
+ yield (ak - len(vals) + 1, len(vals), vals)
+
+
+def entries(attributes, sameval=False):
+ g = _entries(sorted(attributes.items(), key=lambda x: int(x[0])), sameval)
+ return g
+
+
+def bininfo(num, size=1):
+ if num == 0:
+ return struct.pack(">4H", 0, 0, 0, 0)
+ srange = 1
+ select = 0
+ while srange <= num:
+ srange *= 2
+ select += 1
+ select -= 1
+ srange //= 2
+ srange *= size
+ shift = num * size - srange
+ return struct.pack(">4H", num, srange, select, shift)
+
+
+def num2tag(n):
+ if n < 0x200000:
+ return str(n)
+ else:
+ return (
+ struct.unpack("4s", struct.pack(">L", n))[0].replace(b"\000", b"").decode()
+ )
+
+
+def tag2num(n):
+ try:
+ return int(n)
+ except ValueError:
+ n = (n + " ")[:4]
+ return struct.unpack(">L", n.encode("ascii"))[0]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/otBase.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/otBase.py
new file mode 100644
index 0000000000000000000000000000000000000000..41557cfa4b41f2531fe4b930c29551e960d1d1ce
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/otBase.py
@@ -0,0 +1,1458 @@
+from fontTools.config import OPTIONS
+from fontTools.misc.textTools import Tag, bytesjoin
+from .DefaultTable import DefaultTable
+from enum import IntEnum
+import sys
+import array
+import struct
+import logging
+from functools import lru_cache
+from typing import Iterator, NamedTuple, Optional, Tuple
+
+log = logging.getLogger(__name__)
+
+have_uharfbuzz = False
+try:
+ import uharfbuzz as hb
+
+ have_uharfbuzz = True
+except ImportError:
+ pass
+
+USE_HARFBUZZ_REPACKER = OPTIONS[f"{__name__}:USE_HARFBUZZ_REPACKER"]
+
+
+class OverflowErrorRecord(object):
+ def __init__(self, overflowTuple):
+ self.tableType = overflowTuple[0]
+ self.LookupListIndex = overflowTuple[1]
+ self.SubTableIndex = overflowTuple[2]
+ self.itemName = overflowTuple[3]
+ self.itemIndex = overflowTuple[4]
+
+ def __repr__(self):
+ return str(
+ (
+ self.tableType,
+ "LookupIndex:",
+ self.LookupListIndex,
+ "SubTableIndex:",
+ self.SubTableIndex,
+ "ItemName:",
+ self.itemName,
+ "ItemIndex:",
+ self.itemIndex,
+ )
+ )
+
+
+class OTLOffsetOverflowError(Exception):
+ def __init__(self, overflowErrorRecord):
+ self.value = overflowErrorRecord
+
+ def __str__(self):
+ return repr(self.value)
+
+
+class RepackerState(IntEnum):
+ # Repacking control flow is implemnted using a state machine. The state machine table:
+ #
+ # State | Packing Success | Packing Failed | Exception Raised |
+ # ------------+-----------------+----------------+------------------+
+ # PURE_FT | Return result | PURE_FT | Return failure |
+ # HB_FT | Return result | HB_FT | FT_FALLBACK |
+ # FT_FALLBACK | HB_FT | FT_FALLBACK | Return failure |
+
+ # Pack only with fontTools, don't allow sharing between extensions.
+ PURE_FT = 1
+
+ # Attempt to pack with harfbuzz (allowing sharing between extensions)
+ # use fontTools to attempt overflow resolution.
+ HB_FT = 2
+
+ # Fallback if HB/FT packing gets stuck. Pack only with fontTools, don't allow sharing between
+ # extensions.
+ FT_FALLBACK = 3
+
+
+class BaseTTXConverter(DefaultTable):
+ """Generic base class for TTX table converters. It functions as an
+ adapter between the TTX (ttLib actually) table model and the model
+ we use for OpenType tables, which is necessarily subtly different.
+ """
+
+ def decompile(self, data, font):
+ """Create an object from the binary data. Called automatically on access."""
+ from . import otTables
+
+ reader = OTTableReader(data, tableTag=self.tableTag)
+ tableClass = getattr(otTables, self.tableTag)
+ self.table = tableClass()
+ self.table.decompile(reader, font)
+
+ def compile(self, font):
+ """Compiles the table into binary. Called automatically on save."""
+
+ # General outline:
+ # Create a top-level OTTableWriter for the GPOS/GSUB table.
+ # Call the compile method for the the table
+ # for each 'converter' record in the table converter list
+ # call converter's write method for each item in the value.
+ # - For simple items, the write method adds a string to the
+ # writer's self.items list.
+ # - For Struct/Table/Subtable items, it add first adds new writer to the
+ # to the writer's self.items, then calls the item's compile method.
+ # This creates a tree of writers, rooted at the GUSB/GPOS writer, with
+ # each writer representing a table, and the writer.items list containing
+ # the child data strings and writers.
+ # call the getAllData method
+ # call _doneWriting, which removes duplicates
+ # call _gatherTables. This traverses the tables, adding unique occurences to a flat list of tables
+ # Traverse the flat list of tables, calling getDataLength on each to update their position
+ # Traverse the flat list of tables again, calling getData each get the data in the table, now that
+ # pos's and offset are known.
+
+ # If a lookup subtable overflows an offset, we have to start all over.
+ overflowRecord = None
+ # this is 3-state option: default (None) means automatically use hb.repack or
+ # silently fall back if it fails; True, use it and raise error if not possible
+ # or it errors out; False, don't use it, even if you can.
+ use_hb_repack = font.cfg[USE_HARFBUZZ_REPACKER]
+ if self.tableTag in ("GSUB", "GPOS"):
+ if use_hb_repack is False:
+ log.debug(
+ "hb.repack disabled, compiling '%s' with pure-python serializer",
+ self.tableTag,
+ )
+ elif not have_uharfbuzz:
+ if use_hb_repack is True:
+ raise ImportError("No module named 'uharfbuzz'")
+ else:
+ assert use_hb_repack is None
+ log.debug(
+ "uharfbuzz not found, compiling '%s' with pure-python serializer",
+ self.tableTag,
+ )
+
+ if (
+ use_hb_repack in (None, True)
+ and have_uharfbuzz
+ and self.tableTag in ("GSUB", "GPOS")
+ ):
+ state = RepackerState.HB_FT
+ else:
+ state = RepackerState.PURE_FT
+
+ hb_first_error_logged = False
+ lastOverflowRecord = None
+ while True:
+ try:
+ writer = OTTableWriter(tableTag=self.tableTag)
+ self.table.compile(writer, font)
+ if state == RepackerState.HB_FT:
+ return self.tryPackingHarfbuzz(writer, hb_first_error_logged)
+ elif state == RepackerState.PURE_FT:
+ return self.tryPackingFontTools(writer)
+ elif state == RepackerState.FT_FALLBACK:
+ # Run packing with FontTools only, but don't return the result as it will
+ # not be optimally packed. Once a successful packing has been found, state is
+ # changed back to harfbuzz packing to produce the final, optimal, packing.
+ self.tryPackingFontTools(writer)
+ log.debug(
+ "Re-enabling sharing between extensions and switching back to "
+ "harfbuzz+fontTools packing."
+ )
+ state = RepackerState.HB_FT
+
+ except OTLOffsetOverflowError as e:
+ hb_first_error_logged = True
+ ok = self.tryResolveOverflow(font, e, lastOverflowRecord)
+ lastOverflowRecord = e.value
+
+ if ok:
+ continue
+
+ if state is RepackerState.HB_FT:
+ log.debug(
+ "Harfbuzz packing out of resolutions, disabling sharing between extensions and "
+ "switching to fontTools only packing."
+ )
+ state = RepackerState.FT_FALLBACK
+ else:
+ raise
+
+ def tryPackingHarfbuzz(self, writer, hb_first_error_logged):
+ try:
+ log.debug("serializing '%s' with hb.repack", self.tableTag)
+ return writer.getAllDataUsingHarfbuzz(self.tableTag)
+ except (ValueError, MemoryError, hb.RepackerError) as e:
+ # Only log hb repacker errors the first time they occur in
+ # the offset-overflow resolution loop, they are just noisy.
+ # Maybe we can revisit this if/when uharfbuzz actually gives
+ # us more info as to why hb.repack failed...
+ if not hb_first_error_logged:
+ error_msg = f"{type(e).__name__}"
+ if str(e) != "":
+ error_msg += f": {e}"
+ log.warning(
+ "hb.repack failed to serialize '%s', attempting fonttools resolutions "
+ "; the error message was: %s",
+ self.tableTag,
+ error_msg,
+ )
+ hb_first_error_logged = True
+ return writer.getAllData(remove_duplicate=False)
+
+ def tryPackingFontTools(self, writer):
+ return writer.getAllData()
+
+ def tryResolveOverflow(self, font, e, lastOverflowRecord):
+ ok = 0
+ if lastOverflowRecord == e.value:
+ # Oh well...
+ return ok
+
+ overflowRecord = e.value
+ log.info("Attempting to fix OTLOffsetOverflowError %s", e)
+
+ if overflowRecord.itemName is None:
+ from .otTables import fixLookupOverFlows
+
+ ok = fixLookupOverFlows(font, overflowRecord)
+ else:
+ from .otTables import fixSubTableOverFlows
+
+ ok = fixSubTableOverFlows(font, overflowRecord)
+
+ if ok:
+ return ok
+
+ # Try upgrading lookup to Extension and hope
+ # that cross-lookup sharing not happening would
+ # fix overflow...
+ from .otTables import fixLookupOverFlows
+
+ return fixLookupOverFlows(font, overflowRecord)
+
+ def toXML(self, writer, font):
+ self.table.toXML2(writer, font)
+
+ def fromXML(self, name, attrs, content, font):
+ from . import otTables
+
+ if not hasattr(self, "table"):
+ tableClass = getattr(otTables, self.tableTag)
+ self.table = tableClass()
+ self.table.fromXML(name, attrs, content, font)
+ self.table.populateDefaults()
+
+ def ensureDecompiled(self, recurse=True):
+ self.table.ensureDecompiled(recurse=recurse)
+
+
+# https://github.com/fonttools/fonttools/pull/2285#issuecomment-834652928
+assert len(struct.pack("i", 0)) == 4
+assert array.array("i").itemsize == 4, "Oops, file a bug against fonttools."
+
+
+class OTTableReader(object):
+ """Helper class to retrieve data from an OpenType table."""
+
+ __slots__ = ("data", "offset", "pos", "localState", "tableTag")
+
+ def __init__(self, data, localState=None, offset=0, tableTag=None):
+ self.data = data
+ self.offset = offset
+ self.pos = offset
+ self.localState = localState
+ self.tableTag = tableTag
+
+ def advance(self, count):
+ self.pos += count
+
+ def seek(self, pos):
+ self.pos = pos
+
+ def copy(self):
+ other = self.__class__(self.data, self.localState, self.offset, self.tableTag)
+ other.pos = self.pos
+ return other
+
+ def getSubReader(self, offset):
+ offset = self.offset + offset
+ return self.__class__(self.data, self.localState, offset, self.tableTag)
+
+ def readValue(self, typecode, staticSize):
+ pos = self.pos
+ newpos = pos + staticSize
+ (value,) = struct.unpack(f">{typecode}", self.data[pos:newpos])
+ self.pos = newpos
+ return value
+
+ def readArray(self, typecode, staticSize, count):
+ pos = self.pos
+ newpos = pos + count * staticSize
+ value = array.array(typecode, self.data[pos:newpos])
+ if sys.byteorder != "big":
+ value.byteswap()
+ self.pos = newpos
+ return value.tolist()
+
+ def readInt8(self):
+ return self.readValue("b", staticSize=1)
+
+ def readInt8Array(self, count):
+ return self.readArray("b", staticSize=1, count=count)
+
+ def readShort(self):
+ return self.readValue("h", staticSize=2)
+
+ def readShortArray(self, count):
+ return self.readArray("h", staticSize=2, count=count)
+
+ def readLong(self):
+ return self.readValue("i", staticSize=4)
+
+ def readLongArray(self, count):
+ return self.readArray("i", staticSize=4, count=count)
+
+ def readUInt8(self):
+ return self.readValue("B", staticSize=1)
+
+ def readUInt8Array(self, count):
+ return self.readArray("B", staticSize=1, count=count)
+
+ def readUShort(self):
+ return self.readValue("H", staticSize=2)
+
+ def readUShortArray(self, count):
+ return self.readArray("H", staticSize=2, count=count)
+
+ def readULong(self):
+ return self.readValue("I", staticSize=4)
+
+ def readULongArray(self, count):
+ return self.readArray("I", staticSize=4, count=count)
+
+ def readUInt24(self):
+ pos = self.pos
+ newpos = pos + 3
+ (value,) = struct.unpack(">l", b"\0" + self.data[pos:newpos])
+ self.pos = newpos
+ return value
+
+ def readUInt24Array(self, count):
+ return [self.readUInt24() for _ in range(count)]
+
+ def readTag(self):
+ pos = self.pos
+ newpos = pos + 4
+ value = Tag(self.data[pos:newpos])
+ assert len(value) == 4, value
+ self.pos = newpos
+ return value
+
+ def readData(self, count):
+ pos = self.pos
+ newpos = pos + count
+ value = self.data[pos:newpos]
+ self.pos = newpos
+ return value
+
+ def __setitem__(self, name, value):
+ state = self.localState.copy() if self.localState else dict()
+ state[name] = value
+ self.localState = state
+
+ def __getitem__(self, name):
+ return self.localState and self.localState[name]
+
+ def __contains__(self, name):
+ return self.localState and name in self.localState
+
+
+class OffsetToWriter(object):
+ def __init__(self, subWriter, offsetSize):
+ self.subWriter = subWriter
+ self.offsetSize = offsetSize
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.subWriter == other.subWriter and self.offsetSize == other.offsetSize
+
+ def __hash__(self):
+ # only works after self._doneWriting() has been called
+ return hash((self.subWriter, self.offsetSize))
+
+
+class OTTableWriter(object):
+ """Helper class to gather and assemble data for OpenType tables."""
+
+ def __init__(self, localState=None, tableTag=None):
+ self.items = []
+ self.pos = None
+ self.localState = localState
+ self.tableTag = tableTag
+ self.parent = None
+ self.name = ""
+
+ def __setitem__(self, name, value):
+ state = self.localState.copy() if self.localState else dict()
+ state[name] = value
+ self.localState = state
+
+ def __getitem__(self, name):
+ return self.localState[name]
+
+ def __delitem__(self, name):
+ del self.localState[name]
+
+ # assembler interface
+
+ def getDataLength(self):
+ """Return the length of this table in bytes, without subtables."""
+ l = 0
+ for item in self.items:
+ if hasattr(item, "getCountData"):
+ l += item.size
+ elif hasattr(item, "subWriter"):
+ l += item.offsetSize
+ else:
+ l = l + len(item)
+ return l
+
+ def getData(self):
+ """Assemble the data for this writer/table, without subtables."""
+ items = list(self.items) # make a shallow copy
+ pos = self.pos
+ numItems = len(items)
+ for i in range(numItems):
+ item = items[i]
+
+ if hasattr(item, "subWriter"):
+ if item.offsetSize == 4:
+ items[i] = packULong(item.subWriter.pos - pos)
+ elif item.offsetSize == 2:
+ try:
+ items[i] = packUShort(item.subWriter.pos - pos)
+ except struct.error:
+ # provide data to fix overflow problem.
+ overflowErrorRecord = self.getOverflowErrorRecord(
+ item.subWriter
+ )
+
+ raise OTLOffsetOverflowError(overflowErrorRecord)
+ elif item.offsetSize == 3:
+ items[i] = packUInt24(item.subWriter.pos - pos)
+ else:
+ raise ValueError(item.offsetSize)
+
+ return bytesjoin(items)
+
+ def getDataForHarfbuzz(self):
+ """Assemble the data for this writer/table with all offset field set to 0"""
+ items = list(self.items)
+ packFuncs = {2: packUShort, 3: packUInt24, 4: packULong}
+ for i, item in enumerate(items):
+ if hasattr(item, "subWriter"):
+ # Offset value is not needed in harfbuzz repacker, so setting offset to 0 to avoid overflow here
+ if item.offsetSize in packFuncs:
+ items[i] = packFuncs[item.offsetSize](0)
+ else:
+ raise ValueError(item.offsetSize)
+
+ return bytesjoin(items)
+
+ def __hash__(self):
+ # only works after self._doneWriting() has been called
+ return hash(self.items)
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.items == other.items
+
+ def _doneWriting(self, internedTables, shareExtension=False):
+ # Convert CountData references to data string items
+ # collapse duplicate table references to a unique entry
+ # "tables" are OTTableWriter objects.
+
+ # For Extension Lookup types, we can
+ # eliminate duplicates only within the tree under the Extension Lookup,
+ # as offsets may exceed 64K even between Extension LookupTable subtables.
+ isExtension = hasattr(self, "Extension")
+
+ # Certain versions of Uniscribe reject the font if the GSUB/GPOS top-level
+ # arrays (ScriptList, FeatureList, LookupList) point to the same, possibly
+ # empty, array. So, we don't share those.
+ # See: https://github.com/fonttools/fonttools/issues/518
+ dontShare = hasattr(self, "DontShare")
+
+ if isExtension and not shareExtension:
+ internedTables = {}
+
+ items = self.items
+ for i, item in enumerate(items):
+ if hasattr(item, "getCountData"):
+ items[i] = item.getCountData()
+ elif hasattr(item, "subWriter"):
+ item.subWriter._doneWriting(
+ internedTables, shareExtension=shareExtension
+ )
+ # At this point, all subwriters are hashable based on their items.
+ # (See hash and comparison magic methods above.) So the ``setdefault``
+ # call here will return the first writer object we've seen with
+ # equal content, or store it in the dictionary if it's not been
+ # seen yet. We therefore replace the subwriter object with an equivalent
+ # object, which deduplicates the tree.
+ if not dontShare:
+ items[i].subWriter = internedTables.setdefault(
+ item.subWriter, item.subWriter
+ )
+ self.items = tuple(items)
+
+ def _gatherTables(self, tables, extTables, done):
+ # Convert table references in self.items tree to a flat
+ # list of tables in depth-first traversal order.
+ # "tables" are OTTableWriter objects.
+ # We do the traversal in reverse order at each level, in order to
+ # resolve duplicate references to be the last reference in the list of tables.
+ # For extension lookups, duplicate references can be merged only within the
+ # writer tree under the extension lookup.
+
+ done[id(self)] = True
+
+ numItems = len(self.items)
+ iRange = list(range(numItems))
+ iRange.reverse()
+
+ isExtension = hasattr(self, "Extension")
+
+ selfTables = tables
+
+ if isExtension:
+ assert (
+ extTables is not None
+ ), "Program or XML editing error. Extension subtables cannot contain extensions subtables"
+ tables, extTables, done = extTables, None, {}
+
+ # add Coverage table if it is sorted last.
+ sortCoverageLast = False
+ if hasattr(self, "sortCoverageLast"):
+ # Find coverage table
+ for i in range(numItems):
+ item = self.items[i]
+ if (
+ hasattr(item, "subWriter")
+ and getattr(item.subWriter, "name", None) == "Coverage"
+ ):
+ sortCoverageLast = True
+ break
+ if id(item.subWriter) not in done:
+ item.subWriter._gatherTables(tables, extTables, done)
+ else:
+ # We're a new parent of item
+ pass
+
+ for i in iRange:
+ item = self.items[i]
+ if not hasattr(item, "subWriter"):
+ continue
+
+ if (
+ sortCoverageLast
+ and (i == 1)
+ and getattr(item.subWriter, "name", None) == "Coverage"
+ ):
+ # we've already 'gathered' it above
+ continue
+
+ if id(item.subWriter) not in done:
+ item.subWriter._gatherTables(tables, extTables, done)
+ else:
+ # Item is already written out by other parent
+ pass
+
+ selfTables.append(self)
+
+ def _gatherGraphForHarfbuzz(self, tables, obj_list, done, objidx, virtual_edges):
+ real_links = []
+ virtual_links = []
+ item_idx = objidx
+
+ # Merge virtual_links from parent
+ for idx in virtual_edges:
+ virtual_links.append((0, 0, idx))
+
+ sortCoverageLast = False
+ coverage_idx = 0
+ if hasattr(self, "sortCoverageLast"):
+ # Find coverage table
+ for i, item in enumerate(self.items):
+ if getattr(item, "name", None) == "Coverage":
+ sortCoverageLast = True
+ if id(item) not in done:
+ coverage_idx = item_idx = item._gatherGraphForHarfbuzz(
+ tables, obj_list, done, item_idx, virtual_edges
+ )
+ else:
+ coverage_idx = done[id(item)]
+ virtual_edges.append(coverage_idx)
+ break
+
+ child_idx = 0
+ offset_pos = 0
+ for i, item in enumerate(self.items):
+ if hasattr(item, "subWriter"):
+ pos = offset_pos
+ elif hasattr(item, "getCountData"):
+ offset_pos += item.size
+ continue
+ else:
+ offset_pos = offset_pos + len(item)
+ continue
+
+ if id(item.subWriter) not in done:
+ child_idx = item_idx = item.subWriter._gatherGraphForHarfbuzz(
+ tables, obj_list, done, item_idx, virtual_edges
+ )
+ else:
+ child_idx = done[id(item.subWriter)]
+
+ real_edge = (pos, item.offsetSize, child_idx)
+ real_links.append(real_edge)
+ offset_pos += item.offsetSize
+
+ tables.append(self)
+ obj_list.append((real_links, virtual_links))
+ item_idx += 1
+ done[id(self)] = item_idx
+ if sortCoverageLast:
+ virtual_edges.pop()
+
+ return item_idx
+
+ def getAllDataUsingHarfbuzz(self, tableTag):
+ """The Whole table is represented as a Graph.
+ Assemble graph data and call Harfbuzz repacker to pack the table.
+ Harfbuzz repacker is faster and retain as much sub-table sharing as possible, see also:
+ https://github.com/harfbuzz/harfbuzz/blob/main/docs/repacker.md
+ The input format for hb.repack() method is explained here:
+ https://github.com/harfbuzz/uharfbuzz/blob/main/src/uharfbuzz/_harfbuzz.pyx#L1149
+ """
+ internedTables = {}
+ self._doneWriting(internedTables, shareExtension=True)
+ tables = []
+ obj_list = []
+ done = {}
+ objidx = 0
+ virtual_edges = []
+ self._gatherGraphForHarfbuzz(tables, obj_list, done, objidx, virtual_edges)
+ # Gather all data in two passes: the absolute positions of all
+ # subtable are needed before the actual data can be assembled.
+ pos = 0
+ for table in tables:
+ table.pos = pos
+ pos = pos + table.getDataLength()
+
+ data = []
+ for table in tables:
+ tableData = table.getDataForHarfbuzz()
+ data.append(tableData)
+
+ return hb.serialize_with_tag(str(tableTag), data, obj_list)
+
+ def getAllData(self, remove_duplicate=True):
+ """Assemble all data, including all subtables."""
+ if remove_duplicate:
+ internedTables = {}
+ self._doneWriting(internedTables)
+ tables = []
+ extTables = []
+ done = {}
+ self._gatherTables(tables, extTables, done)
+ tables.reverse()
+ extTables.reverse()
+ # Gather all data in two passes: the absolute positions of all
+ # subtable are needed before the actual data can be assembled.
+ pos = 0
+ for table in tables:
+ table.pos = pos
+ pos = pos + table.getDataLength()
+
+ for table in extTables:
+ table.pos = pos
+ pos = pos + table.getDataLength()
+
+ data = []
+ for table in tables:
+ tableData = table.getData()
+ data.append(tableData)
+
+ for table in extTables:
+ tableData = table.getData()
+ data.append(tableData)
+
+ return bytesjoin(data)
+
+ # interface for gathering data, as used by table.compile()
+
+ def getSubWriter(self):
+ subwriter = self.__class__(self.localState, self.tableTag)
+ subwriter.parent = (
+ self # because some subtables have idential values, we discard
+ )
+ # the duplicates under the getAllData method. Hence some
+ # subtable writers can have more than one parent writer.
+ # But we just care about first one right now.
+ return subwriter
+
+ def writeValue(self, typecode, value):
+ self.items.append(struct.pack(f">{typecode}", value))
+
+ def writeArray(self, typecode, values):
+ a = array.array(typecode, values)
+ if sys.byteorder != "big":
+ a.byteswap()
+ self.items.append(a.tobytes())
+
+ def writeInt8(self, value):
+ assert -128 <= value < 128, value
+ self.items.append(struct.pack(">b", value))
+
+ def writeInt8Array(self, values):
+ self.writeArray("b", values)
+
+ def writeShort(self, value):
+ assert -32768 <= value < 32768, value
+ self.items.append(struct.pack(">h", value))
+
+ def writeShortArray(self, values):
+ self.writeArray("h", values)
+
+ def writeLong(self, value):
+ self.items.append(struct.pack(">i", value))
+
+ def writeLongArray(self, values):
+ self.writeArray("i", values)
+
+ def writeUInt8(self, value):
+ assert 0 <= value < 256, value
+ self.items.append(struct.pack(">B", value))
+
+ def writeUInt8Array(self, values):
+ self.writeArray("B", values)
+
+ def writeUShort(self, value):
+ assert 0 <= value < 0x10000, value
+ self.items.append(struct.pack(">H", value))
+
+ def writeUShortArray(self, values):
+ self.writeArray("H", values)
+
+ def writeULong(self, value):
+ self.items.append(struct.pack(">I", value))
+
+ def writeULongArray(self, values):
+ self.writeArray("I", values)
+
+ def writeUInt24(self, value):
+ assert 0 <= value < 0x1000000, value
+ b = struct.pack(">L", value)
+ self.items.append(b[1:])
+
+ def writeUInt24Array(self, values):
+ for value in values:
+ self.writeUInt24(value)
+
+ def writeTag(self, tag):
+ tag = Tag(tag).tobytes()
+ assert len(tag) == 4, tag
+ self.items.append(tag)
+
+ def writeSubTable(self, subWriter, offsetSize):
+ self.items.append(OffsetToWriter(subWriter, offsetSize))
+
+ def writeCountReference(self, table, name, size=2, value=None):
+ ref = CountReference(table, name, size=size, value=value)
+ self.items.append(ref)
+ return ref
+
+ def writeStruct(self, format, values):
+ data = struct.pack(*(format,) + values)
+ self.items.append(data)
+
+ def writeData(self, data):
+ self.items.append(data)
+
+ def getOverflowErrorRecord(self, item):
+ LookupListIndex = SubTableIndex = itemName = itemIndex = None
+ if self.name == "LookupList":
+ LookupListIndex = item.repeatIndex
+ elif self.name == "Lookup":
+ LookupListIndex = self.repeatIndex
+ SubTableIndex = item.repeatIndex
+ else:
+ itemName = getattr(item, "name", "")
+ if hasattr(item, "repeatIndex"):
+ itemIndex = item.repeatIndex
+ if self.name == "SubTable":
+ LookupListIndex = self.parent.repeatIndex
+ SubTableIndex = self.repeatIndex
+ elif self.name == "ExtSubTable":
+ LookupListIndex = self.parent.parent.repeatIndex
+ SubTableIndex = self.parent.repeatIndex
+ else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
+ itemName = ".".join([self.name, itemName])
+ p1 = self.parent
+ while p1 and p1.name not in ["ExtSubTable", "SubTable"]:
+ itemName = ".".join([p1.name, itemName])
+ p1 = p1.parent
+ if p1:
+ if p1.name == "ExtSubTable":
+ LookupListIndex = p1.parent.parent.repeatIndex
+ SubTableIndex = p1.parent.repeatIndex
+ else:
+ LookupListIndex = p1.parent.repeatIndex
+ SubTableIndex = p1.repeatIndex
+
+ return OverflowErrorRecord(
+ (self.tableTag, LookupListIndex, SubTableIndex, itemName, itemIndex)
+ )
+
+
+class CountReference(object):
+ """A reference to a Count value, not a count of references."""
+
+ def __init__(self, table, name, size=None, value=None):
+ self.table = table
+ self.name = name
+ self.size = size
+ if value is not None:
+ self.setValue(value)
+
+ def setValue(self, value):
+ table = self.table
+ name = self.name
+ if table[name] is None:
+ table[name] = value
+ else:
+ assert table[name] == value, (name, table[name], value)
+
+ def getValue(self):
+ return self.table[self.name]
+
+ def getCountData(self):
+ v = self.table[self.name]
+ if v is None:
+ v = 0
+ return {1: packUInt8, 2: packUShort, 4: packULong}[self.size](v)
+
+
+def packUInt8(value):
+ return struct.pack(">B", value)
+
+
+def packUShort(value):
+ return struct.pack(">H", value)
+
+
+def packULong(value):
+ assert 0 <= value < 0x100000000, value
+ return struct.pack(">I", value)
+
+
+def packUInt24(value):
+ assert 0 <= value < 0x1000000, value
+ return struct.pack(">I", value)[1:]
+
+
+class BaseTable(object):
+ """Generic base class for all OpenType (sub)tables."""
+
+ def __getattr__(self, attr):
+ reader = self.__dict__.get("reader")
+ if reader:
+ del self.reader
+ font = self.font
+ del self.font
+ self.decompile(reader, font)
+ return getattr(self, attr)
+
+ raise AttributeError(attr)
+
+ def ensureDecompiled(self, recurse=False):
+ reader = self.__dict__.get("reader")
+ if reader:
+ del self.reader
+ font = self.font
+ del self.font
+ self.decompile(reader, font)
+ if recurse:
+ for subtable in self.iterSubTables():
+ subtable.value.ensureDecompiled(recurse)
+
+ def __getstate__(self):
+ # before copying/pickling 'lazy' objects, make a shallow copy of OTTableReader
+ # https://github.com/fonttools/fonttools/issues/2965
+ if "reader" in self.__dict__:
+ state = self.__dict__.copy()
+ state["reader"] = self.__dict__["reader"].copy()
+ return state
+ return self.__dict__
+
+ @classmethod
+ def getRecordSize(cls, reader):
+ totalSize = 0
+ for conv in cls.converters:
+ size = conv.getRecordSize(reader)
+ if size is NotImplemented:
+ return NotImplemented
+ countValue = 1
+ if conv.repeat:
+ if conv.repeat in reader:
+ countValue = reader[conv.repeat] + conv.aux
+ else:
+ return NotImplemented
+ totalSize += size * countValue
+ return totalSize
+
+ def getConverters(self):
+ return self.converters
+
+ def getConverterByName(self, name):
+ return self.convertersByName[name]
+
+ def populateDefaults(self, propagator=None):
+ for conv in self.getConverters():
+ if conv.repeat:
+ if not hasattr(self, conv.name):
+ setattr(self, conv.name, [])
+ countValue = len(getattr(self, conv.name)) - conv.aux
+ try:
+ count_conv = self.getConverterByName(conv.repeat)
+ setattr(self, conv.repeat, countValue)
+ except KeyError:
+ # conv.repeat is a propagated count
+ if propagator and conv.repeat in propagator:
+ propagator[conv.repeat].setValue(countValue)
+ else:
+ if conv.aux and not eval(conv.aux, None, self.__dict__):
+ continue
+ if hasattr(self, conv.name):
+ continue # Warn if it should NOT be present?!
+ if hasattr(conv, "writeNullOffset"):
+ setattr(self, conv.name, None) # Warn?
+ # elif not conv.isCount:
+ # # Warn?
+ # pass
+ if hasattr(conv, "DEFAULT"):
+ # OptionalValue converters (e.g. VarIndex)
+ setattr(self, conv.name, conv.DEFAULT)
+
+ def decompile(self, reader, font):
+ self.readFormat(reader)
+ table = {}
+ self.__rawTable = table # for debugging
+ for conv in self.getConverters():
+ if conv.name == "SubTable":
+ conv = conv.getConverter(reader.tableTag, table["LookupType"])
+ if conv.name == "ExtSubTable":
+ conv = conv.getConverter(reader.tableTag, table["ExtensionLookupType"])
+ if conv.name == "FeatureParams":
+ conv = conv.getConverter(reader["FeatureTag"])
+ if conv.name == "SubStruct":
+ conv = conv.getConverter(reader.tableTag, table["MorphType"])
+ try:
+ if conv.repeat:
+ if isinstance(conv.repeat, int):
+ countValue = conv.repeat
+ elif conv.repeat in table:
+ countValue = table[conv.repeat]
+ else:
+ # conv.repeat is a propagated count
+ countValue = reader[conv.repeat]
+ countValue += conv.aux
+ table[conv.name] = conv.readArray(reader, font, table, countValue)
+ else:
+ if conv.aux and not eval(conv.aux, None, table):
+ continue
+ table[conv.name] = conv.read(reader, font, table)
+ if conv.isPropagated:
+ reader[conv.name] = table[conv.name]
+ except Exception as e:
+ name = conv.name
+ e.args = e.args + (name,)
+ raise
+
+ if hasattr(self, "postRead"):
+ self.postRead(table, font)
+ else:
+ self.__dict__.update(table)
+
+ del self.__rawTable # succeeded, get rid of debugging info
+
+ def compile(self, writer, font):
+ self.ensureDecompiled()
+ # TODO Following hack to be removed by rewriting how FormatSwitching tables
+ # are handled.
+ # https://github.com/fonttools/fonttools/pull/2238#issuecomment-805192631
+ if hasattr(self, "preWrite"):
+ deleteFormat = not hasattr(self, "Format")
+ table = self.preWrite(font)
+ deleteFormat = deleteFormat and hasattr(self, "Format")
+ else:
+ deleteFormat = False
+ table = self.__dict__.copy()
+
+ # some count references may have been initialized in a custom preWrite; we set
+ # these in the writer's state beforehand (instead of sequentially) so they will
+ # be propagated to all nested subtables even if the count appears in the current
+ # table only *after* the offset to the subtable that it is counting.
+ for conv in self.getConverters():
+ if conv.isCount and conv.isPropagated:
+ value = table.get(conv.name)
+ if isinstance(value, CountReference):
+ writer[conv.name] = value
+
+ if hasattr(self, "sortCoverageLast"):
+ writer.sortCoverageLast = 1
+
+ if hasattr(self, "DontShare"):
+ writer.DontShare = True
+
+ if hasattr(self.__class__, "LookupType"):
+ writer["LookupType"].setValue(self.__class__.LookupType)
+
+ self.writeFormat(writer)
+ for conv in self.getConverters():
+ value = table.get(
+ conv.name
+ ) # TODO Handle defaults instead of defaulting to None!
+ if conv.repeat:
+ if value is None:
+ value = []
+ countValue = len(value) - conv.aux
+ if isinstance(conv.repeat, int):
+ assert len(value) == conv.repeat, "expected %d values, got %d" % (
+ conv.repeat,
+ len(value),
+ )
+ elif conv.repeat in table:
+ CountReference(table, conv.repeat, value=countValue)
+ else:
+ # conv.repeat is a propagated count
+ writer[conv.repeat].setValue(countValue)
+ try:
+ conv.writeArray(writer, font, table, value)
+ except Exception as e:
+ e.args = e.args + (conv.name + "[]",)
+ raise
+ elif conv.isCount:
+ # Special-case Count values.
+ # Assumption: a Count field will *always* precede
+ # the actual array(s).
+ # We need a default value, as it may be set later by a nested
+ # table. We will later store it here.
+ # We add a reference: by the time the data is assembled
+ # the Count value will be filled in.
+ # We ignore the current count value since it will be recomputed,
+ # unless it's a CountReference that was already initialized in a custom preWrite.
+ if isinstance(value, CountReference):
+ ref = value
+ ref.size = conv.staticSize
+ writer.writeData(ref)
+ table[conv.name] = ref.getValue()
+ else:
+ ref = writer.writeCountReference(table, conv.name, conv.staticSize)
+ table[conv.name] = None
+ if conv.isPropagated:
+ writer[conv.name] = ref
+ elif conv.isLookupType:
+ # We make sure that subtables have the same lookup type,
+ # and that the type is the same as the one set on the
+ # Lookup object, if any is set.
+ if conv.name not in table:
+ table[conv.name] = None
+ ref = writer.writeCountReference(
+ table, conv.name, conv.staticSize, table[conv.name]
+ )
+ writer["LookupType"] = ref
+ else:
+ if conv.aux and not eval(conv.aux, None, table):
+ continue
+ try:
+ conv.write(writer, font, table, value)
+ except Exception as e:
+ name = value.__class__.__name__ if value is not None else conv.name
+ e.args = e.args + (name,)
+ raise
+ if conv.isPropagated:
+ writer[conv.name] = value
+
+ if deleteFormat:
+ del self.Format
+
+ def readFormat(self, reader):
+ pass
+
+ def writeFormat(self, writer):
+ pass
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ tableName = name if name else self.__class__.__name__
+ if attrs is None:
+ attrs = []
+ if hasattr(self, "Format"):
+ attrs = attrs + [("Format", self.Format)]
+ xmlWriter.begintag(tableName, attrs)
+ xmlWriter.newline()
+ self.toXML2(xmlWriter, font)
+ xmlWriter.endtag(tableName)
+ xmlWriter.newline()
+
+ def toXML2(self, xmlWriter, font):
+ # Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
+ # This is because in TTX our parent writes our main tag, and in otBase.py we
+ # do it ourselves. I think I'm getting schizophrenic...
+ for conv in self.getConverters():
+ if conv.repeat:
+ value = getattr(self, conv.name, [])
+ for i, item in enumerate(value):
+ conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)])
+ else:
+ if conv.aux and not eval(conv.aux, None, vars(self)):
+ continue
+ value = getattr(
+ self, conv.name, None
+ ) # TODO Handle defaults instead of defaulting to None!
+ conv.xmlWrite(xmlWriter, font, value, conv.name, [])
+
+ def fromXML(self, name, attrs, content, font):
+ try:
+ conv = self.getConverterByName(name)
+ except KeyError:
+ raise # XXX on KeyError, raise nice error
+ value = conv.xmlRead(attrs, content, font)
+ # Some manually-written tables have a conv.repeat of ""
+ # to represent lists. Hence comparing to None here to
+ # allow those lists to be read correctly from XML.
+ if conv.repeat is not None:
+ seq = getattr(self, conv.name, None)
+ if seq is None:
+ seq = []
+ setattr(self, conv.name, seq)
+ seq.append(value)
+ else:
+ setattr(self, conv.name, value)
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+
+ self.ensureDecompiled()
+ other.ensureDecompiled()
+
+ return self.__dict__ == other.__dict__
+
+ class SubTableEntry(NamedTuple):
+ """See BaseTable.iterSubTables()"""
+
+ name: str
+ value: "BaseTable"
+ index: Optional[int] = None # index into given array, None for single values
+
+ def iterSubTables(self) -> Iterator[SubTableEntry]:
+ """Yield (name, value, index) namedtuples for all subtables of current table.
+
+ A sub-table is an instance of BaseTable (or subclass thereof) that is a child
+ of self, the current parent table.
+ The tuples also contain the attribute name (str) of the of parent table to get
+ a subtable, and optionally, for lists of subtables (i.e. attributes associated
+ with a converter that has a 'repeat'), an index into the list containing the
+ given subtable value.
+ This method can be useful to traverse trees of otTables.
+ """
+ for conv in self.getConverters():
+ name = conv.name
+ value = getattr(self, name, None)
+ if value is None:
+ continue
+ if isinstance(value, BaseTable):
+ yield self.SubTableEntry(name, value)
+ elif isinstance(value, list):
+ yield from (
+ self.SubTableEntry(name, v, index=i)
+ for i, v in enumerate(value)
+ if isinstance(v, BaseTable)
+ )
+
+ # instance (not @class)method for consistency with FormatSwitchingBaseTable
+ def getVariableAttrs(self):
+ return getVariableAttrs(self.__class__)
+
+
+class FormatSwitchingBaseTable(BaseTable):
+ """Minor specialization of BaseTable, for tables that have multiple
+ formats, eg. CoverageFormat1 vs. CoverageFormat2."""
+
+ @classmethod
+ def getRecordSize(cls, reader):
+ return NotImplemented
+
+ def getConverters(self):
+ try:
+ fmt = self.Format
+ except AttributeError:
+ # some FormatSwitchingBaseTables (e.g. Coverage) no longer have 'Format'
+ # attribute after fully decompiled, only gain one in preWrite before being
+ # recompiled. In the decompiled state, these hand-coded classes defined in
+ # otTables.py lose their format-specific nature and gain more high-level
+ # attributes that are not tied to converters.
+ return []
+ return self.converters.get(self.Format, [])
+
+ def getConverterByName(self, name):
+ return self.convertersByName[self.Format][name]
+
+ def readFormat(self, reader):
+ self.Format = reader.readUShort()
+
+ def writeFormat(self, writer):
+ writer.writeUShort(self.Format)
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ BaseTable.toXML(self, xmlWriter, font, attrs, name)
+
+ def getVariableAttrs(self):
+ return getVariableAttrs(self.__class__, self.Format)
+
+
+class UInt8FormatSwitchingBaseTable(FormatSwitchingBaseTable):
+ def readFormat(self, reader):
+ self.Format = reader.readUInt8()
+
+ def writeFormat(self, writer):
+ writer.writeUInt8(self.Format)
+
+
+formatSwitchingBaseTables = {
+ "uint16": FormatSwitchingBaseTable,
+ "uint8": UInt8FormatSwitchingBaseTable,
+}
+
+
+def getFormatSwitchingBaseTableClass(formatType):
+ try:
+ return formatSwitchingBaseTables[formatType]
+ except KeyError:
+ raise TypeError(f"Unsupported format type: {formatType!r}")
+
+
+# memoize since these are parsed from otData.py, thus stay constant
+@lru_cache()
+def getVariableAttrs(cls: BaseTable, fmt: Optional[int] = None) -> Tuple[str]:
+ """Return sequence of variable table field names (can be empty).
+
+ Attributes are deemed "variable" when their otData.py's description contain
+ 'VarIndexBase + {offset}', e.g. COLRv1 PaintVar* tables.
+ """
+ if not issubclass(cls, BaseTable):
+ raise TypeError(cls)
+ if issubclass(cls, FormatSwitchingBaseTable):
+ if fmt is None:
+ raise TypeError(f"'fmt' is required for format-switching {cls.__name__}")
+ converters = cls.convertersByName[fmt]
+ else:
+ converters = cls.convertersByName
+ # assume if no 'VarIndexBase' field is present, table has no variable fields
+ if "VarIndexBase" not in converters:
+ return ()
+ varAttrs = {}
+ for name, conv in converters.items():
+ offset = conv.getVarIndexOffset()
+ if offset is not None:
+ varAttrs[name] = offset
+ return tuple(sorted(varAttrs, key=varAttrs.__getitem__))
+
+
+#
+# Support for ValueRecords
+#
+# This data type is so different from all other OpenType data types that
+# it requires quite a bit of code for itself. It even has special support
+# in OTTableReader and OTTableWriter...
+#
+
+valueRecordFormat = [
+ # Mask Name isDevice signed
+ (0x0001, "XPlacement", 0, 1),
+ (0x0002, "YPlacement", 0, 1),
+ (0x0004, "XAdvance", 0, 1),
+ (0x0008, "YAdvance", 0, 1),
+ (0x0010, "XPlaDevice", 1, 0),
+ (0x0020, "YPlaDevice", 1, 0),
+ (0x0040, "XAdvDevice", 1, 0),
+ (0x0080, "YAdvDevice", 1, 0),
+ # reserved:
+ (0x0100, "Reserved1", 0, 0),
+ (0x0200, "Reserved2", 0, 0),
+ (0x0400, "Reserved3", 0, 0),
+ (0x0800, "Reserved4", 0, 0),
+ (0x1000, "Reserved5", 0, 0),
+ (0x2000, "Reserved6", 0, 0),
+ (0x4000, "Reserved7", 0, 0),
+ (0x8000, "Reserved8", 0, 0),
+]
+
+
+def _buildDict():
+ d = {}
+ for mask, name, isDevice, signed in valueRecordFormat:
+ d[name] = mask, isDevice, signed
+ return d
+
+
+valueRecordFormatDict = _buildDict()
+
+
+class ValueRecordFactory(object):
+ """Given a format code, this object convert ValueRecords."""
+
+ def __init__(self, valueFormat):
+ format = []
+ for mask, name, isDevice, signed in valueRecordFormat:
+ if valueFormat & mask:
+ format.append((name, isDevice, signed))
+ self.format = format
+
+ def __len__(self):
+ return len(self.format)
+
+ def readValueRecord(self, reader, font):
+ format = self.format
+ if not format:
+ return None
+ valueRecord = ValueRecord()
+ for name, isDevice, signed in format:
+ if signed:
+ value = reader.readShort()
+ else:
+ value = reader.readUShort()
+ if isDevice:
+ if value:
+ from . import otTables
+
+ subReader = reader.getSubReader(value)
+ value = getattr(otTables, name)()
+ value.decompile(subReader, font)
+ else:
+ value = None
+ setattr(valueRecord, name, value)
+ return valueRecord
+
+ def writeValueRecord(self, writer, font, valueRecord):
+ for name, isDevice, signed in self.format:
+ value = getattr(valueRecord, name, 0)
+ if isDevice:
+ if value:
+ subWriter = writer.getSubWriter()
+ writer.writeSubTable(subWriter, offsetSize=2)
+ value.compile(subWriter, font)
+ else:
+ writer.writeUShort(0)
+ elif signed:
+ writer.writeShort(value)
+ else:
+ writer.writeUShort(value)
+
+
+class ValueRecord(object):
+ # see ValueRecordFactory
+
+ def __init__(self, valueFormat=None, src=None):
+ if valueFormat is not None:
+ for mask, name, isDevice, signed in valueRecordFormat:
+ if valueFormat & mask:
+ setattr(self, name, None if isDevice else 0)
+ if src is not None:
+ for key, val in src.__dict__.items():
+ if not hasattr(self, key):
+ continue
+ setattr(self, key, val)
+ elif src is not None:
+ self.__dict__ = src.__dict__.copy()
+
+ def getFormat(self):
+ format = 0
+ for name in self.__dict__.keys():
+ format = format | valueRecordFormatDict[name][0]
+ return format
+
+ def getEffectiveFormat(self):
+ format = 0
+ for name, value in self.__dict__.items():
+ if value:
+ format = format | valueRecordFormatDict[name][0]
+ return format
+
+ def toXML(self, xmlWriter, font, valueName, attrs=None):
+ if attrs is None:
+ simpleItems = []
+ else:
+ simpleItems = list(attrs)
+ for mask, name, isDevice, format in valueRecordFormat[:4]: # "simple" values
+ if hasattr(self, name):
+ simpleItems.append((name, getattr(self, name)))
+ deviceItems = []
+ for mask, name, isDevice, format in valueRecordFormat[4:8]: # device records
+ if hasattr(self, name):
+ device = getattr(self, name)
+ if device is not None:
+ deviceItems.append((name, device))
+ if deviceItems:
+ xmlWriter.begintag(valueName, simpleItems)
+ xmlWriter.newline()
+ for name, deviceRecord in deviceItems:
+ if deviceRecord is not None:
+ deviceRecord.toXML(xmlWriter, font, name=name)
+ xmlWriter.endtag(valueName)
+ xmlWriter.newline()
+ else:
+ xmlWriter.simpletag(valueName, simpleItems)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ from . import otTables
+
+ for k, v in attrs.items():
+ setattr(self, k, int(v))
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ value = getattr(otTables, name)()
+ for elem2 in content:
+ if not isinstance(elem2, tuple):
+ continue
+ name2, attrs2, content2 = elem2
+ value.fromXML(name2, attrs2, content2, font)
+ setattr(self, name, value)
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/otConverters.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/otConverters.py
new file mode 100644
index 0000000000000000000000000000000000000000..75bfd7f6ca525b9167c1701f8e194aab29b2de8a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/otConverters.py
@@ -0,0 +1,2068 @@
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+ ensureVersionIsLong as fi2ve,
+ versionToFixed as ve2fi,
+)
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools.misc.roundTools import nearestMultipleShortestRepr, otRound
+from fontTools.misc.textTools import bytesjoin, tobytes, tostr, pad, safeEval
+from fontTools.misc.lazyTools import LazyList
+from fontTools.ttLib import OPTIMIZE_FONT_SPEED, getSearchRange
+from .otBase import (
+ CountReference,
+ FormatSwitchingBaseTable,
+ OTTableReader,
+ OTTableWriter,
+ ValueRecordFactory,
+)
+from .otTables import (
+ lookupTypes,
+ VarCompositeGlyph,
+ AATStateTable,
+ AATState,
+ AATAction,
+ ContextualMorphAction,
+ LigatureMorphAction,
+ InsertionMorphAction,
+ MorxSubtable,
+ ExtendMode as _ExtendMode,
+ CompositeMode as _CompositeMode,
+ NO_VARIATION_INDEX,
+)
+from itertools import zip_longest, accumulate
+from functools import partial
+from types import SimpleNamespace
+import re
+import struct
+from typing import Optional
+import logging
+
+
+log = logging.getLogger(__name__)
+istuple = lambda t: isinstance(t, tuple)
+
+
+def buildConverters(tableSpec, tableNamespace):
+ """Given a table spec from otData.py, build a converter object for each
+ field of the table. This is called for each table in otData.py, and
+ the results are assigned to the corresponding class in otTables.py."""
+ converters = []
+ convertersByName = {}
+ for tp, name, repeat, aux, descr in tableSpec:
+ tableName = name
+ if name.startswith("ValueFormat"):
+ assert tp == "uint16"
+ converterClass = ValueFormat
+ elif name.endswith("Count") or name in ("StructLength", "MorphType"):
+ converterClass = {
+ "uint8": ComputedUInt8,
+ "uint16": ComputedUShort,
+ "uint32": ComputedULong,
+ }[tp]
+ elif name == "SubTable":
+ converterClass = SubTable
+ elif name == "ExtSubTable":
+ converterClass = ExtSubTable
+ elif name == "SubStruct":
+ converterClass = SubStruct
+ elif name == "FeatureParams":
+ converterClass = FeatureParams
+ elif name in ("CIDGlyphMapping", "GlyphCIDMapping"):
+ converterClass = StructWithLength
+ else:
+ if not tp in converterMapping and "(" not in tp:
+ tableName = tp
+ converterClass = Struct
+ else:
+ converterClass = eval(tp, tableNamespace, converterMapping)
+
+ conv = converterClass(name, repeat, aux, description=descr)
+
+ if conv.tableClass:
+ # A "template" such as OffsetTo(AType) knows the table class already
+ tableClass = conv.tableClass
+ elif tp in ("MortChain", "MortSubtable", "MorxChain"):
+ tableClass = tableNamespace.get(tp)
+ else:
+ tableClass = tableNamespace.get(tableName)
+
+ if not conv.tableClass:
+ conv.tableClass = tableClass
+
+ if name in ["SubTable", "ExtSubTable", "SubStruct"]:
+ conv.lookupTypes = tableNamespace["lookupTypes"]
+ # also create reverse mapping
+ for t in conv.lookupTypes.values():
+ for cls in t.values():
+ convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
+ if name == "FeatureParams":
+ conv.featureParamTypes = tableNamespace["featureParamTypes"]
+ conv.defaultFeatureParams = tableNamespace["FeatureParams"]
+ for cls in conv.featureParamTypes.values():
+ convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
+ converters.append(conv)
+ assert name not in convertersByName, name
+ convertersByName[name] = conv
+ return converters, convertersByName
+
+
+class BaseConverter(object):
+ """Base class for converter objects. Apart from the constructor, this
+ is an abstract class."""
+
+ def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
+ self.name = name
+ self.repeat = repeat
+ self.aux = aux
+ if self.aux and not self.repeat:
+ self.aux = compile(self.aux, "", "eval")
+ self.tableClass = tableClass
+ self.isCount = name.endswith("Count") or name in [
+ "DesignAxisRecordSize",
+ "ValueRecordSize",
+ ]
+ self.isLookupType = name.endswith("LookupType") or name == "MorphType"
+ self.isPropagated = name in [
+ "ClassCount",
+ "Class2Count",
+ "FeatureTag",
+ "SettingsCount",
+ "VarRegionCount",
+ "MappingCount",
+ "RegionAxisCount",
+ "DesignAxisCount",
+ "DesignAxisRecordSize",
+ "AxisValueCount",
+ "ValueRecordSize",
+ "AxisCount",
+ "BaseGlyphRecordCount",
+ "LayerRecordCount",
+ "AxisIndicesList",
+ ]
+ self.description = description
+
+ def readArray(self, reader, font, tableDict, count):
+ """Read an array of values from the reader."""
+ lazy = font.lazy and count > 8
+ if lazy:
+ recordSize = self.getRecordSize(reader)
+ if recordSize is NotImplemented:
+ lazy = False
+ if not lazy:
+ l = []
+ for i in range(count):
+ l.append(self.read(reader, font, tableDict))
+ return l
+ else:
+
+ def get_read_item():
+ reader_copy = reader.copy()
+ pos = reader.pos
+
+ def read_item(i):
+ reader_copy.seek(pos + i * recordSize)
+ return self.read(reader_copy, font, {})
+
+ return read_item
+
+ read_item = get_read_item()
+ l = LazyList(read_item for i in range(count))
+ reader.advance(count * recordSize)
+
+ return l
+
+ def getRecordSize(self, reader):
+ if hasattr(self, "staticSize"):
+ return self.staticSize
+ return NotImplemented
+
+ def read(self, reader, font, tableDict):
+ """Read a value from the reader."""
+ raise NotImplementedError(self)
+
+ def writeArray(self, writer, font, tableDict, values):
+ try:
+ for i, value in enumerate(values):
+ self.write(writer, font, tableDict, value, i)
+ except Exception as e:
+ e.args = e.args + (i,)
+ raise
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ """Write a value to the writer."""
+ raise NotImplementedError(self)
+
+ def xmlRead(self, attrs, content, font):
+ """Read a value from XML."""
+ raise NotImplementedError(self)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ """Write a value to XML."""
+ raise NotImplementedError(self)
+
+ varIndexBasePlusOffsetRE = re.compile(r"VarIndexBase\s*\+\s*(\d+)")
+
+ def getVarIndexOffset(self) -> Optional[int]:
+ """If description has `VarIndexBase + {offset}`, return the offset else None."""
+ m = self.varIndexBasePlusOffsetRE.search(self.description)
+ if not m:
+ return None
+ return int(m.group(1))
+
+
+class SimpleValue(BaseConverter):
+ @staticmethod
+ def toString(value):
+ return value
+
+ @staticmethod
+ def fromString(value):
+ return value
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", self.toString(value))])
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ return self.fromString(attrs["value"])
+
+
+class OptionalValue(SimpleValue):
+ DEFAULT = None
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if value != self.DEFAULT:
+ attrs.append(("value", self.toString(value)))
+ xmlWriter.simpletag(name, attrs)
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ if "value" in attrs:
+ return self.fromString(attrs["value"])
+ return self.DEFAULT
+
+
+class IntValue(SimpleValue):
+ @staticmethod
+ def fromString(value):
+ return int(value, 0)
+
+
+class Long(IntValue):
+ staticSize = 4
+
+ def read(self, reader, font, tableDict):
+ return reader.readLong()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readLongArray(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeLong(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeLongArray(values)
+
+
+class ULong(IntValue):
+ staticSize = 4
+
+ def read(self, reader, font, tableDict):
+ return reader.readULong()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readULongArray(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeULong(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeULongArray(values)
+
+
+class Flags32(ULong):
+ @staticmethod
+ def toString(value):
+ return "0x%08X" % value
+
+
+class VarIndex(OptionalValue, ULong):
+ DEFAULT = NO_VARIATION_INDEX
+
+
+class Short(IntValue):
+ staticSize = 2
+
+ def read(self, reader, font, tableDict):
+ return reader.readShort()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readShortArray(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeShort(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeShortArray(values)
+
+
+class UShort(IntValue):
+ staticSize = 2
+
+ def read(self, reader, font, tableDict):
+ return reader.readUShort()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readUShortArray(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUShort(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeUShortArray(values)
+
+
+class Int8(IntValue):
+ staticSize = 1
+
+ def read(self, reader, font, tableDict):
+ return reader.readInt8()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readInt8Array(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeInt8(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeInt8Array(values)
+
+
+class UInt8(IntValue):
+ staticSize = 1
+
+ def read(self, reader, font, tableDict):
+ return reader.readUInt8()
+
+ def readArray(self, reader, font, tableDict, count):
+ return reader.readUInt8Array(count)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUInt8(value)
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeUInt8Array(values)
+
+
+class UInt24(IntValue):
+ staticSize = 3
+
+ def read(self, reader, font, tableDict):
+ return reader.readUInt24()
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUInt24(value)
+
+
+class ComputedInt(IntValue):
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if value is not None:
+ xmlWriter.comment("%s=%s" % (name, value))
+ xmlWriter.newline()
+
+
+class ComputedUInt8(ComputedInt, UInt8):
+ pass
+
+
+class ComputedUShort(ComputedInt, UShort):
+ pass
+
+
+class ComputedULong(ComputedInt, ULong):
+ pass
+
+
+class Tag(SimpleValue):
+ staticSize = 4
+
+ def read(self, reader, font, tableDict):
+ return reader.readTag()
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeTag(value)
+
+
+class GlyphID(SimpleValue):
+ staticSize = 2
+ typecode = "H"
+
+ def readArray(self, reader, font, tableDict, count):
+ return font.getGlyphNameMany(
+ reader.readArray(self.typecode, self.staticSize, count)
+ )
+
+ def read(self, reader, font, tableDict):
+ return font.getGlyphName(reader.readValue(self.typecode, self.staticSize))
+
+ def writeArray(self, writer, font, tableDict, values):
+ writer.writeArray(self.typecode, font.getGlyphIDMany(values))
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeValue(self.typecode, font.getGlyphID(value))
+
+
+class GlyphID32(GlyphID):
+ staticSize = 4
+ typecode = "L"
+
+
+class NameID(UShort):
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ if font and value:
+ nameTable = font.get("name")
+ if nameTable:
+ name = nameTable.getDebugName(value)
+ xmlWriter.write(" ")
+ if name:
+ xmlWriter.comment(name)
+ else:
+ xmlWriter.comment("missing from name table")
+ log.warning("name id %d missing from name table" % value)
+ xmlWriter.newline()
+
+
+class STATFlags(UShort):
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ flags = []
+ if value & 0x01:
+ flags.append("OlderSiblingFontAttribute")
+ if value & 0x02:
+ flags.append("ElidableAxisValueName")
+ if flags:
+ xmlWriter.write(" ")
+ xmlWriter.comment(" ".join(flags))
+ xmlWriter.newline()
+
+
+class FloatValue(SimpleValue):
+ @staticmethod
+ def fromString(value):
+ return float(value)
+
+
+class DeciPoints(FloatValue):
+ staticSize = 2
+
+ def read(self, reader, font, tableDict):
+ return reader.readUShort() / 10
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUShort(round(value * 10))
+
+
+class BaseFixedValue(FloatValue):
+ staticSize = NotImplemented
+ precisionBits = NotImplemented
+ readerMethod = NotImplemented
+ writerMethod = NotImplemented
+
+ def read(self, reader, font, tableDict):
+ return self.fromInt(getattr(reader, self.readerMethod)())
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ getattr(writer, self.writerMethod)(self.toInt(value))
+
+ @classmethod
+ def fromInt(cls, value):
+ return fi2fl(value, cls.precisionBits)
+
+ @classmethod
+ def toInt(cls, value):
+ return fl2fi(value, cls.precisionBits)
+
+ @classmethod
+ def fromString(cls, value):
+ return str2fl(value, cls.precisionBits)
+
+ @classmethod
+ def toString(cls, value):
+ return fl2str(value, cls.precisionBits)
+
+
+class Fixed(BaseFixedValue):
+ staticSize = 4
+ precisionBits = 16
+ readerMethod = "readLong"
+ writerMethod = "writeLong"
+
+
+class F2Dot14(BaseFixedValue):
+ staticSize = 2
+ precisionBits = 14
+ readerMethod = "readShort"
+ writerMethod = "writeShort"
+
+
+class Angle(F2Dot14):
+ # angles are specified in degrees, and encoded as F2Dot14 fractions of half
+ # circle: e.g. 1.0 => 180, -0.5 => -90, -2.0 => -360, etc.
+ bias = 0.0
+ factor = 1.0 / (1 << 14) * 180 # 0.010986328125
+
+ @classmethod
+ def fromInt(cls, value):
+ return (super().fromInt(value) + cls.bias) * 180
+
+ @classmethod
+ def toInt(cls, value):
+ return super().toInt((value / 180) - cls.bias)
+
+ @classmethod
+ def fromString(cls, value):
+ # quantize to nearest multiples of minimum fixed-precision angle
+ return otRound(float(value) / cls.factor) * cls.factor
+
+ @classmethod
+ def toString(cls, value):
+ return nearestMultipleShortestRepr(value, cls.factor)
+
+
+class BiasedAngle(Angle):
+ # A bias of 1.0 is used in the representation of start and end angles
+ # of COLRv1 PaintSweepGradients to allow for encoding +360deg
+ bias = 1.0
+
+
+class Version(SimpleValue):
+ staticSize = 4
+
+ def read(self, reader, font, tableDict):
+ value = reader.readLong()
+ return value
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ value = fi2ve(value)
+ writer.writeLong(value)
+
+ @staticmethod
+ def fromString(value):
+ return ve2fi(value)
+
+ @staticmethod
+ def toString(value):
+ return "0x%08x" % value
+
+ @staticmethod
+ def fromFloat(v):
+ return fl2fi(v, 16)
+
+
+class Char64(SimpleValue):
+ """An ASCII string with up to 64 characters.
+
+ Unused character positions are filled with 0x00 bytes.
+ Used in Apple AAT fonts in the `gcid` table.
+ """
+
+ staticSize = 64
+
+ def read(self, reader, font, tableDict):
+ data = reader.readData(self.staticSize)
+ zeroPos = data.find(b"\0")
+ if zeroPos >= 0:
+ data = data[:zeroPos]
+ s = tostr(data, encoding="ascii", errors="replace")
+ if s != tostr(data, encoding="ascii", errors="ignore"):
+ log.warning('replaced non-ASCII characters in "%s"' % s)
+ return s
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ data = tobytes(value, encoding="ascii", errors="replace")
+ if data != tobytes(value, encoding="ascii", errors="ignore"):
+ log.warning('replacing non-ASCII characters in "%s"' % value)
+ if len(data) > self.staticSize:
+ log.warning(
+ 'truncating overlong "%s" to %d bytes' % (value, self.staticSize)
+ )
+ data = (data + b"\0" * self.staticSize)[: self.staticSize]
+ writer.writeData(data)
+
+
+class Struct(BaseConverter):
+ def getRecordSize(self, reader):
+ return self.tableClass and self.tableClass.getRecordSize(reader)
+
+ def read(self, reader, font, tableDict):
+ table = self.tableClass()
+ table.decompile(reader, font)
+ return table
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ value.compile(writer, font)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if value is None:
+ if attrs:
+ # If there are attributes (probably index), then
+ # don't drop this even if it's NULL. It will mess
+ # up the array indices of the containing element.
+ xmlWriter.simpletag(name, attrs + [("empty", 1)])
+ xmlWriter.newline()
+ else:
+ pass # NULL table, ignore
+ else:
+ value.toXML(xmlWriter, font, attrs, name=name)
+
+ def xmlRead(self, attrs, content, font):
+ if "empty" in attrs and safeEval(attrs["empty"]):
+ return None
+ table = self.tableClass()
+ Format = attrs.get("Format")
+ if Format is not None:
+ table.Format = int(Format)
+
+ noPostRead = not hasattr(table, "postRead")
+ if noPostRead:
+ # TODO Cache table.hasPropagated.
+ cleanPropagation = False
+ for conv in table.getConverters():
+ if conv.isPropagated:
+ cleanPropagation = True
+ if not hasattr(font, "_propagator"):
+ font._propagator = {}
+ propagator = font._propagator
+ assert conv.name not in propagator, (conv.name, propagator)
+ setattr(table, conv.name, None)
+ propagator[conv.name] = CountReference(table.__dict__, conv.name)
+
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ table.fromXML(name, attrs, content, font)
+ else:
+ pass
+
+ table.populateDefaults(propagator=getattr(font, "_propagator", None))
+
+ if noPostRead:
+ if cleanPropagation:
+ for conv in table.getConverters():
+ if conv.isPropagated:
+ propagator = font._propagator
+ del propagator[conv.name]
+ if not propagator:
+ del font._propagator
+
+ return table
+
+ def __repr__(self):
+ return "Struct of " + repr(self.tableClass)
+
+
+class StructWithLength(Struct):
+ def read(self, reader, font, tableDict):
+ pos = reader.pos
+ table = self.tableClass()
+ table.decompile(reader, font)
+ reader.seek(pos + table.StructLength)
+ return table
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ for convIndex, conv in enumerate(value.getConverters()):
+ if conv.name == "StructLength":
+ break
+ lengthIndex = len(writer.items) + convIndex
+ if isinstance(value, FormatSwitchingBaseTable):
+ lengthIndex += 1 # implicit Format field
+ deadbeef = {1: 0xDE, 2: 0xDEAD, 4: 0xDEADBEEF}[conv.staticSize]
+
+ before = writer.getDataLength()
+ value.StructLength = deadbeef
+ value.compile(writer, font)
+ length = writer.getDataLength() - before
+ lengthWriter = writer.getSubWriter()
+ conv.write(lengthWriter, font, tableDict, length)
+ assert writer.items[lengthIndex] == b"\xde\xad\xbe\xef"[: conv.staticSize]
+ writer.items[lengthIndex] = lengthWriter.getAllData()
+
+
+class Table(Struct):
+ staticSize = 2
+
+ def readOffset(self, reader):
+ return reader.readUShort()
+
+ def writeNullOffset(self, writer):
+ writer.writeUShort(0)
+
+ def read(self, reader, font, tableDict):
+ offset = self.readOffset(reader)
+ if offset == 0:
+ return None
+ table = self.tableClass()
+ reader = reader.getSubReader(offset)
+ if font.lazy:
+ table.reader = reader
+ table.font = font
+ else:
+ table.decompile(reader, font)
+ return table
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ if value is None:
+ self.writeNullOffset(writer)
+ else:
+ subWriter = writer.getSubWriter()
+ subWriter.name = self.name
+ if repeatIndex is not None:
+ subWriter.repeatIndex = repeatIndex
+ writer.writeSubTable(subWriter, offsetSize=self.staticSize)
+ value.compile(subWriter, font)
+
+
+class LTable(Table):
+ staticSize = 4
+
+ def readOffset(self, reader):
+ return reader.readULong()
+
+ def writeNullOffset(self, writer):
+ writer.writeULong(0)
+
+
+# Table pointed to by a 24-bit, 3-byte long offset
+class Table24(Table):
+ staticSize = 3
+
+ def readOffset(self, reader):
+ return reader.readUInt24()
+
+ def writeNullOffset(self, writer):
+ writer.writeUInt24(0)
+
+
+# TODO Clean / merge the SubTable and SubStruct
+
+
+class SubStruct(Struct):
+ def getConverter(self, tableType, lookupType):
+ tableClass = self.lookupTypes[tableType][lookupType]
+ return self.__class__(self.name, self.repeat, self.aux, tableClass)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ super(SubStruct, self).xmlWrite(xmlWriter, font, value, None, attrs)
+
+
+class SubTable(Table):
+ def getConverter(self, tableType, lookupType):
+ tableClass = self.lookupTypes[tableType][lookupType]
+ return self.__class__(self.name, self.repeat, self.aux, tableClass)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ super(SubTable, self).xmlWrite(xmlWriter, font, value, None, attrs)
+
+
+class ExtSubTable(LTable, SubTable):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.Extension = True # actually, mere presence of the field flags it as an Ext Subtable writer.
+ Table.write(self, writer, font, tableDict, value, repeatIndex)
+
+
+class FeatureParams(Table):
+ def getConverter(self, featureTag):
+ tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
+ return self.__class__(self.name, self.repeat, self.aux, tableClass)
+
+
+class ValueFormat(IntValue):
+ staticSize = 2
+
+ def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
+ BaseConverter.__init__(
+ self, name, repeat, aux, tableClass, description=description
+ )
+ self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
+
+ def read(self, reader, font, tableDict):
+ format = reader.readUShort()
+ reader[self.which] = ValueRecordFactory(format)
+ return format
+
+ def write(self, writer, font, tableDict, format, repeatIndex=None):
+ writer.writeUShort(format)
+ writer[self.which] = ValueRecordFactory(format)
+
+
+class ValueRecord(ValueFormat):
+ def getRecordSize(self, reader):
+ return 2 * len(reader[self.which])
+
+ def read(self, reader, font, tableDict):
+ return reader[self.which].readValueRecord(reader, font)
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer[self.which].writeValueRecord(writer, font, value)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if value is None:
+ pass # NULL table, ignore
+ else:
+ value.toXML(xmlWriter, font, self.name, attrs)
+
+ def xmlRead(self, attrs, content, font):
+ from .otBase import ValueRecord
+
+ value = ValueRecord()
+ value.fromXML(None, attrs, content, font)
+ return value
+
+
+class AATLookup(BaseConverter):
+ BIN_SEARCH_HEADER_SIZE = 10
+
+ def __init__(self, name, repeat, aux, tableClass, *, description=""):
+ BaseConverter.__init__(
+ self, name, repeat, aux, tableClass, description=description
+ )
+ if issubclass(self.tableClass, SimpleValue):
+ self.converter = self.tableClass(name="Value", repeat=None, aux=None)
+ else:
+ self.converter = Table(
+ name="Value", repeat=None, aux=None, tableClass=self.tableClass
+ )
+
+ def read(self, reader, font, tableDict):
+ format = reader.readUShort()
+ if format == 0:
+ return self.readFormat0(reader, font)
+ elif format == 2:
+ return self.readFormat2(reader, font)
+ elif format == 4:
+ return self.readFormat4(reader, font)
+ elif format == 6:
+ return self.readFormat6(reader, font)
+ elif format == 8:
+ return self.readFormat8(reader, font)
+ else:
+ assert False, "unsupported lookup format: %d" % format
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ values = list(
+ sorted([(font.getGlyphID(glyph), val) for glyph, val in value.items()])
+ )
+ # TODO: Also implement format 4.
+ formats = list(
+ sorted(
+ filter(
+ None,
+ [
+ self.buildFormat0(writer, font, values),
+ self.buildFormat2(writer, font, values),
+ self.buildFormat6(writer, font, values),
+ self.buildFormat8(writer, font, values),
+ ],
+ )
+ )
+ )
+ # We use the format ID as secondary sort key to make the output
+ # deterministic when multiple formats have same encoded size.
+ dataSize, lookupFormat, writeMethod = formats[0]
+ pos = writer.getDataLength()
+ writeMethod()
+ actualSize = writer.getDataLength() - pos
+ assert (
+ actualSize == dataSize
+ ), "AATLookup format %d claimed to write %d bytes, but wrote %d" % (
+ lookupFormat,
+ dataSize,
+ actualSize,
+ )
+
+ @staticmethod
+ def writeBinSearchHeader(writer, numUnits, unitSize):
+ writer.writeUShort(unitSize)
+ writer.writeUShort(numUnits)
+ searchRange, entrySelector, rangeShift = getSearchRange(
+ n=numUnits, itemSize=unitSize
+ )
+ writer.writeUShort(searchRange)
+ writer.writeUShort(entrySelector)
+ writer.writeUShort(rangeShift)
+
+ def buildFormat0(self, writer, font, values):
+ numGlyphs = len(font.getGlyphOrder())
+ if len(values) != numGlyphs:
+ return None
+ valueSize = self.converter.staticSize
+ return (
+ 2 + numGlyphs * valueSize,
+ 0,
+ lambda: self.writeFormat0(writer, font, values),
+ )
+
+ def writeFormat0(self, writer, font, values):
+ writer.writeUShort(0)
+ for glyphID_, value in values:
+ self.converter.write(
+ writer, font, tableDict=None, value=value, repeatIndex=None
+ )
+
+ def buildFormat2(self, writer, font, values):
+ segStart, segValue = values[0]
+ segEnd = segStart
+ segments = []
+ for glyphID, curValue in values[1:]:
+ if glyphID != segEnd + 1 or curValue != segValue:
+ segments.append((segStart, segEnd, segValue))
+ segStart = segEnd = glyphID
+ segValue = curValue
+ else:
+ segEnd = glyphID
+ segments.append((segStart, segEnd, segValue))
+ valueSize = self.converter.staticSize
+ numUnits, unitSize = len(segments) + 1, valueSize + 4
+ return (
+ 2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize,
+ 2,
+ lambda: self.writeFormat2(writer, font, segments),
+ )
+
+ def writeFormat2(self, writer, font, segments):
+ writer.writeUShort(2)
+ valueSize = self.converter.staticSize
+ numUnits, unitSize = len(segments), valueSize + 4
+ self.writeBinSearchHeader(writer, numUnits, unitSize)
+ for firstGlyph, lastGlyph, value in segments:
+ writer.writeUShort(lastGlyph)
+ writer.writeUShort(firstGlyph)
+ self.converter.write(
+ writer, font, tableDict=None, value=value, repeatIndex=None
+ )
+ writer.writeUShort(0xFFFF)
+ writer.writeUShort(0xFFFF)
+ writer.writeData(b"\x00" * valueSize)
+
+ def buildFormat6(self, writer, font, values):
+ valueSize = self.converter.staticSize
+ numUnits, unitSize = len(values), valueSize + 2
+ return (
+ 2 + self.BIN_SEARCH_HEADER_SIZE + (numUnits + 1) * unitSize,
+ 6,
+ lambda: self.writeFormat6(writer, font, values),
+ )
+
+ def writeFormat6(self, writer, font, values):
+ writer.writeUShort(6)
+ valueSize = self.converter.staticSize
+ numUnits, unitSize = len(values), valueSize + 2
+ self.writeBinSearchHeader(writer, numUnits, unitSize)
+ for glyphID, value in values:
+ writer.writeUShort(glyphID)
+ self.converter.write(
+ writer, font, tableDict=None, value=value, repeatIndex=None
+ )
+ writer.writeUShort(0xFFFF)
+ writer.writeData(b"\x00" * valueSize)
+
+ def buildFormat8(self, writer, font, values):
+ minGlyphID, maxGlyphID = values[0][0], values[-1][0]
+ if len(values) != maxGlyphID - minGlyphID + 1:
+ return None
+ valueSize = self.converter.staticSize
+ return (
+ 6 + len(values) * valueSize,
+ 8,
+ lambda: self.writeFormat8(writer, font, values),
+ )
+
+ def writeFormat8(self, writer, font, values):
+ firstGlyphID = values[0][0]
+ writer.writeUShort(8)
+ writer.writeUShort(firstGlyphID)
+ writer.writeUShort(len(values))
+ for _, value in values:
+ self.converter.write(
+ writer, font, tableDict=None, value=value, repeatIndex=None
+ )
+
+ def readFormat0(self, reader, font):
+ numGlyphs = len(font.getGlyphOrder())
+ data = self.converter.readArray(reader, font, tableDict=None, count=numGlyphs)
+ return {font.getGlyphName(k): value for k, value in enumerate(data)}
+
+ def readFormat2(self, reader, font):
+ mapping = {}
+ pos = reader.pos - 2 # start of table is at UShort for format
+ unitSize, numUnits = reader.readUShort(), reader.readUShort()
+ assert unitSize >= 4 + self.converter.staticSize, unitSize
+ for i in range(numUnits):
+ reader.seek(pos + i * unitSize + 12)
+ last = reader.readUShort()
+ first = reader.readUShort()
+ value = self.converter.read(reader, font, tableDict=None)
+ if last != 0xFFFF:
+ for k in range(first, last + 1):
+ mapping[font.getGlyphName(k)] = value
+ return mapping
+
+ def readFormat4(self, reader, font):
+ mapping = {}
+ pos = reader.pos - 2 # start of table is at UShort for format
+ unitSize = reader.readUShort()
+ assert unitSize >= 6, unitSize
+ for i in range(reader.readUShort()):
+ reader.seek(pos + i * unitSize + 12)
+ last = reader.readUShort()
+ first = reader.readUShort()
+ offset = reader.readUShort()
+ if last != 0xFFFF:
+ dataReader = reader.getSubReader(0) # relative to current position
+ dataReader.seek(pos + offset) # relative to start of table
+ data = self.converter.readArray(
+ dataReader, font, tableDict=None, count=last - first + 1
+ )
+ for k, v in enumerate(data):
+ mapping[font.getGlyphName(first + k)] = v
+ return mapping
+
+ def readFormat6(self, reader, font):
+ mapping = {}
+ pos = reader.pos - 2 # start of table is at UShort for format
+ unitSize = reader.readUShort()
+ assert unitSize >= 2 + self.converter.staticSize, unitSize
+ for i in range(reader.readUShort()):
+ reader.seek(pos + i * unitSize + 12)
+ glyphID = reader.readUShort()
+ value = self.converter.read(reader, font, tableDict=None)
+ if glyphID != 0xFFFF:
+ mapping[font.getGlyphName(glyphID)] = value
+ return mapping
+
+ def readFormat8(self, reader, font):
+ first = reader.readUShort()
+ count = reader.readUShort()
+ data = self.converter.readArray(reader, font, tableDict=None, count=count)
+ return {font.getGlyphName(first + k): value for (k, value) in enumerate(data)}
+
+ def xmlRead(self, attrs, content, font):
+ value = {}
+ for element in content:
+ if isinstance(element, tuple):
+ name, a, eltContent = element
+ if name == "Lookup":
+ value[a["glyph"]] = self.converter.xmlRead(a, eltContent, font)
+ return value
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.begintag(name, attrs)
+ xmlWriter.newline()
+ for glyph, value in sorted(value.items()):
+ self.converter.xmlWrite(
+ xmlWriter, font, value=value, name="Lookup", attrs=[("glyph", glyph)]
+ )
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+
+# The AAT 'ankr' table has an unusual structure: An offset to an AATLookup
+# followed by an offset to a glyph data table. Other than usual, the
+# offsets in the AATLookup are not relative to the beginning of
+# the beginning of the 'ankr' table, but relative to the glyph data table.
+# So, to find the anchor data for a glyph, one needs to add the offset
+# to the data table to the offset found in the AATLookup, and then use
+# the sum of these two offsets to find the actual data.
+class AATLookupWithDataOffset(BaseConverter):
+ def read(self, reader, font, tableDict):
+ lookupOffset = reader.readULong()
+ dataOffset = reader.readULong()
+ lookupReader = reader.getSubReader(lookupOffset)
+ lookup = AATLookup("DataOffsets", None, None, UShort)
+ offsets = lookup.read(lookupReader, font, tableDict)
+ result = {}
+ for glyph, offset in offsets.items():
+ dataReader = reader.getSubReader(offset + dataOffset)
+ item = self.tableClass()
+ item.decompile(dataReader, font)
+ result[glyph] = item
+ return result
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ # We do not work with OTTableWriter sub-writers because
+ # the offsets in our AATLookup are relative to our data
+ # table, for which we need to provide an offset value itself.
+ # It might have been possible to somehow make a kludge for
+ # performing this indirect offset computation directly inside
+ # OTTableWriter. But this would have made the internal logic
+ # of OTTableWriter even more complex than it already is,
+ # so we decided to roll our own offset computation for the
+ # contents of the AATLookup and associated data table.
+ offsetByGlyph, offsetByData, dataLen = {}, {}, 0
+ compiledData = []
+ for glyph in sorted(value, key=font.getGlyphID):
+ subWriter = OTTableWriter()
+ value[glyph].compile(subWriter, font)
+ data = subWriter.getAllData()
+ offset = offsetByData.get(data, None)
+ if offset == None:
+ offset = dataLen
+ dataLen = dataLen + len(data)
+ offsetByData[data] = offset
+ compiledData.append(data)
+ offsetByGlyph[glyph] = offset
+ # For calculating the offsets to our AATLookup and data table,
+ # we can use the regular OTTableWriter infrastructure.
+ lookupWriter = writer.getSubWriter()
+ lookup = AATLookup("DataOffsets", None, None, UShort)
+ lookup.write(lookupWriter, font, tableDict, offsetByGlyph, None)
+
+ dataWriter = writer.getSubWriter()
+ writer.writeSubTable(lookupWriter, offsetSize=4)
+ writer.writeSubTable(dataWriter, offsetSize=4)
+ for d in compiledData:
+ dataWriter.writeData(d)
+
+ def xmlRead(self, attrs, content, font):
+ lookup = AATLookup("DataOffsets", None, None, self.tableClass)
+ return lookup.xmlRead(attrs, content, font)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ lookup = AATLookup("DataOffsets", None, None, self.tableClass)
+ lookup.xmlWrite(xmlWriter, font, value, name, attrs)
+
+
+class MorxSubtableConverter(BaseConverter):
+ _PROCESSING_ORDERS = {
+ # bits 30 and 28 of morx.CoverageFlags; see morx spec
+ (False, False): "LayoutOrder",
+ (True, False): "ReversedLayoutOrder",
+ (False, True): "LogicalOrder",
+ (True, True): "ReversedLogicalOrder",
+ }
+
+ _PROCESSING_ORDERS_REVERSED = {val: key for key, val in _PROCESSING_ORDERS.items()}
+
+ def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
+ BaseConverter.__init__(
+ self, name, repeat, aux, tableClass, description=description
+ )
+
+ def _setTextDirectionFromCoverageFlags(self, flags, subtable):
+ if (flags & 0x20) != 0:
+ subtable.TextDirection = "Any"
+ elif (flags & 0x80) != 0:
+ subtable.TextDirection = "Vertical"
+ else:
+ subtable.TextDirection = "Horizontal"
+
+ def read(self, reader, font, tableDict):
+ pos = reader.pos
+ m = MorxSubtable()
+ m.StructLength = reader.readULong()
+ flags = reader.readUInt8()
+ orderKey = ((flags & 0x40) != 0, (flags & 0x10) != 0)
+ m.ProcessingOrder = self._PROCESSING_ORDERS[orderKey]
+ self._setTextDirectionFromCoverageFlags(flags, m)
+ m.Reserved = reader.readUShort()
+ m.Reserved |= (flags & 0xF) << 16
+ m.MorphType = reader.readUInt8()
+ m.SubFeatureFlags = reader.readULong()
+ tableClass = lookupTypes["morx"].get(m.MorphType)
+ if tableClass is None:
+ assert False, "unsupported 'morx' lookup type %s" % m.MorphType
+ # To decode AAT ligatures, we need to know the subtable size.
+ # The easiest way to pass this along is to create a new reader
+ # that works on just the subtable as its data.
+ headerLength = reader.pos - pos
+ data = reader.data[reader.pos : reader.pos + m.StructLength - headerLength]
+ assert len(data) == m.StructLength - headerLength
+ subReader = OTTableReader(data=data, tableTag=reader.tableTag)
+ m.SubStruct = tableClass()
+ m.SubStruct.decompile(subReader, font)
+ reader.seek(pos + m.StructLength)
+ return m
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.begintag(name, attrs)
+ xmlWriter.newline()
+ xmlWriter.comment("StructLength=%d" % value.StructLength)
+ xmlWriter.newline()
+ xmlWriter.simpletag("TextDirection", value=value.TextDirection)
+ xmlWriter.newline()
+ xmlWriter.simpletag("ProcessingOrder", value=value.ProcessingOrder)
+ xmlWriter.newline()
+ if value.Reserved != 0:
+ xmlWriter.simpletag("Reserved", value="0x%04x" % value.Reserved)
+ xmlWriter.newline()
+ xmlWriter.comment("MorphType=%d" % value.MorphType)
+ xmlWriter.newline()
+ xmlWriter.simpletag("SubFeatureFlags", value="0x%08x" % value.SubFeatureFlags)
+ xmlWriter.newline()
+ value.SubStruct.toXML(xmlWriter, font)
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ m = MorxSubtable()
+ covFlags = 0
+ m.Reserved = 0
+ for eltName, eltAttrs, eltContent in filter(istuple, content):
+ if eltName == "CoverageFlags":
+ # Only in XML from old versions of fonttools.
+ covFlags = safeEval(eltAttrs["value"])
+ orderKey = ((covFlags & 0x40) != 0, (covFlags & 0x10) != 0)
+ m.ProcessingOrder = self._PROCESSING_ORDERS[orderKey]
+ self._setTextDirectionFromCoverageFlags(covFlags, m)
+ elif eltName == "ProcessingOrder":
+ m.ProcessingOrder = eltAttrs["value"]
+ assert m.ProcessingOrder in self._PROCESSING_ORDERS_REVERSED, (
+ "unknown ProcessingOrder: %s" % m.ProcessingOrder
+ )
+ elif eltName == "TextDirection":
+ m.TextDirection = eltAttrs["value"]
+ assert m.TextDirection in {"Horizontal", "Vertical", "Any"}, (
+ "unknown TextDirection %s" % m.TextDirection
+ )
+ elif eltName == "Reserved":
+ m.Reserved = safeEval(eltAttrs["value"])
+ elif eltName == "SubFeatureFlags":
+ m.SubFeatureFlags = safeEval(eltAttrs["value"])
+ elif eltName.endswith("Morph"):
+ m.fromXML(eltName, eltAttrs, eltContent, font)
+ else:
+ assert False, eltName
+ m.Reserved = (covFlags & 0xF) << 16 | m.Reserved
+ return m
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ covFlags = (value.Reserved & 0x000F0000) >> 16
+ reverseOrder, logicalOrder = self._PROCESSING_ORDERS_REVERSED[
+ value.ProcessingOrder
+ ]
+ covFlags |= 0x80 if value.TextDirection == "Vertical" else 0
+ covFlags |= 0x40 if reverseOrder else 0
+ covFlags |= 0x20 if value.TextDirection == "Any" else 0
+ covFlags |= 0x10 if logicalOrder else 0
+ value.CoverageFlags = covFlags
+ lengthIndex = len(writer.items)
+ before = writer.getDataLength()
+ value.StructLength = 0xDEADBEEF
+ # The high nibble of value.Reserved is actuallly encoded
+ # into coverageFlags, so we need to clear it here.
+ origReserved = value.Reserved # including high nibble
+ value.Reserved = value.Reserved & 0xFFFF # without high nibble
+ value.compile(writer, font)
+ value.Reserved = origReserved # restore original value
+ assert writer.items[lengthIndex] == b"\xde\xad\xbe\xef"
+ length = writer.getDataLength() - before
+ writer.items[lengthIndex] = struct.pack(">L", length)
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html#ExtendedStateHeader
+# TODO: Untangle the implementation of the various lookup-specific formats.
+class STXHeader(BaseConverter):
+ def __init__(self, name, repeat, aux, tableClass, *, description=""):
+ BaseConverter.__init__(
+ self, name, repeat, aux, tableClass, description=description
+ )
+ assert issubclass(self.tableClass, AATAction)
+ self.classLookup = AATLookup("GlyphClasses", None, None, UShort)
+ if issubclass(self.tableClass, ContextualMorphAction):
+ self.perGlyphLookup = AATLookup("PerGlyphLookup", None, None, GlyphID)
+ else:
+ self.perGlyphLookup = None
+
+ def read(self, reader, font, tableDict):
+ table = AATStateTable()
+ pos = reader.pos
+ classTableReader = reader.getSubReader(0)
+ stateArrayReader = reader.getSubReader(0)
+ entryTableReader = reader.getSubReader(0)
+ actionReader = None
+ ligaturesReader = None
+ table.GlyphClassCount = reader.readULong()
+ classTableReader.seek(pos + reader.readULong())
+ stateArrayReader.seek(pos + reader.readULong())
+ entryTableReader.seek(pos + reader.readULong())
+ if self.perGlyphLookup is not None:
+ perGlyphTableReader = reader.getSubReader(0)
+ perGlyphTableReader.seek(pos + reader.readULong())
+ if issubclass(self.tableClass, LigatureMorphAction):
+ actionReader = reader.getSubReader(0)
+ actionReader.seek(pos + reader.readULong())
+ ligComponentReader = reader.getSubReader(0)
+ ligComponentReader.seek(pos + reader.readULong())
+ ligaturesReader = reader.getSubReader(0)
+ ligaturesReader.seek(pos + reader.readULong())
+ numLigComponents = (ligaturesReader.pos - ligComponentReader.pos) // 2
+ assert numLigComponents >= 0
+ table.LigComponents = ligComponentReader.readUShortArray(numLigComponents)
+ table.Ligatures = self._readLigatures(ligaturesReader, font)
+ elif issubclass(self.tableClass, InsertionMorphAction):
+ actionReader = reader.getSubReader(0)
+ actionReader.seek(pos + reader.readULong())
+ table.GlyphClasses = self.classLookup.read(classTableReader, font, tableDict)
+ numStates = int(
+ (entryTableReader.pos - stateArrayReader.pos) / (table.GlyphClassCount * 2)
+ )
+ for stateIndex in range(numStates):
+ state = AATState()
+ table.States.append(state)
+ for glyphClass in range(table.GlyphClassCount):
+ entryIndex = stateArrayReader.readUShort()
+ state.Transitions[glyphClass] = self._readTransition(
+ entryTableReader, entryIndex, font, actionReader
+ )
+ if self.perGlyphLookup is not None:
+ table.PerGlyphLookups = self._readPerGlyphLookups(
+ table, perGlyphTableReader, font
+ )
+ return table
+
+ def _readTransition(self, reader, entryIndex, font, actionReader):
+ transition = self.tableClass()
+ entryReader = reader.getSubReader(
+ reader.pos + entryIndex * transition.staticSize
+ )
+ transition.decompile(entryReader, font, actionReader)
+ return transition
+
+ def _readLigatures(self, reader, font):
+ limit = len(reader.data)
+ numLigatureGlyphs = (limit - reader.pos) // 2
+ return font.getGlyphNameMany(reader.readUShortArray(numLigatureGlyphs))
+
+ def _countPerGlyphLookups(self, table):
+ # Somewhat annoyingly, the morx table does not encode
+ # the size of the per-glyph table. So we need to find
+ # the maximum value that MorphActions use as index
+ # into this table.
+ numLookups = 0
+ for state in table.States:
+ for t in state.Transitions.values():
+ if isinstance(t, ContextualMorphAction):
+ if t.MarkIndex != 0xFFFF:
+ numLookups = max(numLookups, t.MarkIndex + 1)
+ if t.CurrentIndex != 0xFFFF:
+ numLookups = max(numLookups, t.CurrentIndex + 1)
+ return numLookups
+
+ def _readPerGlyphLookups(self, table, reader, font):
+ pos = reader.pos
+ lookups = []
+ for _ in range(self._countPerGlyphLookups(table)):
+ lookupReader = reader.getSubReader(0)
+ lookupReader.seek(pos + reader.readULong())
+ lookups.append(self.perGlyphLookup.read(lookupReader, font, {}))
+ return lookups
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ glyphClassWriter = OTTableWriter()
+ self.classLookup.write(
+ glyphClassWriter, font, tableDict, value.GlyphClasses, repeatIndex=None
+ )
+ glyphClassData = pad(glyphClassWriter.getAllData(), 2)
+ glyphClassCount = max(value.GlyphClasses.values()) + 1
+ glyphClassTableOffset = 16 # size of STXHeader
+ if self.perGlyphLookup is not None:
+ glyphClassTableOffset += 4
+
+ glyphClassTableOffset += self.tableClass.actionHeaderSize
+ actionData, actionIndex = self.tableClass.compileActions(font, value.States)
+ stateArrayData, entryTableData = self._compileStates(
+ font, value.States, glyphClassCount, actionIndex
+ )
+ stateArrayOffset = glyphClassTableOffset + len(glyphClassData)
+ entryTableOffset = stateArrayOffset + len(stateArrayData)
+ perGlyphOffset = entryTableOffset + len(entryTableData)
+ perGlyphData = pad(self._compilePerGlyphLookups(value, font), 4)
+ if actionData is not None:
+ actionOffset = entryTableOffset + len(entryTableData)
+ else:
+ actionOffset = None
+
+ ligaturesOffset, ligComponentsOffset = None, None
+ ligComponentsData = self._compileLigComponents(value, font)
+ ligaturesData = self._compileLigatures(value, font)
+ if ligComponentsData is not None:
+ assert len(perGlyphData) == 0
+ ligComponentsOffset = actionOffset + len(actionData)
+ ligaturesOffset = ligComponentsOffset + len(ligComponentsData)
+
+ writer.writeULong(glyphClassCount)
+ writer.writeULong(glyphClassTableOffset)
+ writer.writeULong(stateArrayOffset)
+ writer.writeULong(entryTableOffset)
+ if self.perGlyphLookup is not None:
+ writer.writeULong(perGlyphOffset)
+ if actionOffset is not None:
+ writer.writeULong(actionOffset)
+ if ligComponentsOffset is not None:
+ writer.writeULong(ligComponentsOffset)
+ writer.writeULong(ligaturesOffset)
+ writer.writeData(glyphClassData)
+ writer.writeData(stateArrayData)
+ writer.writeData(entryTableData)
+ writer.writeData(perGlyphData)
+ if actionData is not None:
+ writer.writeData(actionData)
+ if ligComponentsData is not None:
+ writer.writeData(ligComponentsData)
+ if ligaturesData is not None:
+ writer.writeData(ligaturesData)
+
+ def _compileStates(self, font, states, glyphClassCount, actionIndex):
+ stateArrayWriter = OTTableWriter()
+ entries, entryIDs = [], {}
+ for state in states:
+ for glyphClass in range(glyphClassCount):
+ transition = state.Transitions[glyphClass]
+ entryWriter = OTTableWriter()
+ transition.compile(entryWriter, font, actionIndex)
+ entryData = entryWriter.getAllData()
+ assert (
+ len(entryData) == transition.staticSize
+ ), "%s has staticSize %d, " "but actually wrote %d bytes" % (
+ repr(transition),
+ transition.staticSize,
+ len(entryData),
+ )
+ entryIndex = entryIDs.get(entryData)
+ if entryIndex is None:
+ entryIndex = len(entries)
+ entryIDs[entryData] = entryIndex
+ entries.append(entryData)
+ stateArrayWriter.writeUShort(entryIndex)
+ stateArrayData = pad(stateArrayWriter.getAllData(), 4)
+ entryTableData = pad(bytesjoin(entries), 4)
+ return stateArrayData, entryTableData
+
+ def _compilePerGlyphLookups(self, table, font):
+ if self.perGlyphLookup is None:
+ return b""
+ numLookups = self._countPerGlyphLookups(table)
+ assert len(table.PerGlyphLookups) == numLookups, (
+ "len(AATStateTable.PerGlyphLookups) is %d, "
+ "but the actions inside the table refer to %d"
+ % (len(table.PerGlyphLookups), numLookups)
+ )
+ writer = OTTableWriter()
+ for lookup in table.PerGlyphLookups:
+ lookupWriter = writer.getSubWriter()
+ self.perGlyphLookup.write(lookupWriter, font, {}, lookup, None)
+ writer.writeSubTable(lookupWriter, offsetSize=4)
+ return writer.getAllData()
+
+ def _compileLigComponents(self, table, font):
+ if not hasattr(table, "LigComponents"):
+ return None
+ writer = OTTableWriter()
+ for component in table.LigComponents:
+ writer.writeUShort(component)
+ return writer.getAllData()
+
+ def _compileLigatures(self, table, font):
+ if not hasattr(table, "Ligatures"):
+ return None
+ writer = OTTableWriter()
+ for glyphName in table.Ligatures:
+ writer.writeUShort(font.getGlyphID(glyphName))
+ return writer.getAllData()
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.begintag(name, attrs)
+ xmlWriter.newline()
+ xmlWriter.comment("GlyphClassCount=%s" % value.GlyphClassCount)
+ xmlWriter.newline()
+ for g, klass in sorted(value.GlyphClasses.items()):
+ xmlWriter.simpletag("GlyphClass", glyph=g, value=klass)
+ xmlWriter.newline()
+ for stateIndex, state in enumerate(value.States):
+ xmlWriter.begintag("State", index=stateIndex)
+ xmlWriter.newline()
+ for glyphClass, trans in sorted(state.Transitions.items()):
+ trans.toXML(
+ xmlWriter,
+ font=font,
+ attrs={"onGlyphClass": glyphClass},
+ name="Transition",
+ )
+ xmlWriter.endtag("State")
+ xmlWriter.newline()
+ for i, lookup in enumerate(value.PerGlyphLookups):
+ xmlWriter.begintag("PerGlyphLookup", index=i)
+ xmlWriter.newline()
+ for glyph, val in sorted(lookup.items()):
+ xmlWriter.simpletag("Lookup", glyph=glyph, value=val)
+ xmlWriter.newline()
+ xmlWriter.endtag("PerGlyphLookup")
+ xmlWriter.newline()
+ if hasattr(value, "LigComponents"):
+ xmlWriter.begintag("LigComponents")
+ xmlWriter.newline()
+ for i, val in enumerate(getattr(value, "LigComponents")):
+ xmlWriter.simpletag("LigComponent", index=i, value=val)
+ xmlWriter.newline()
+ xmlWriter.endtag("LigComponents")
+ xmlWriter.newline()
+ self._xmlWriteLigatures(xmlWriter, font, value, name, attrs)
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def _xmlWriteLigatures(self, xmlWriter, font, value, name, attrs):
+ if not hasattr(value, "Ligatures"):
+ return
+ xmlWriter.begintag("Ligatures")
+ xmlWriter.newline()
+ for i, g in enumerate(getattr(value, "Ligatures")):
+ xmlWriter.simpletag("Ligature", index=i, glyph=g)
+ xmlWriter.newline()
+ xmlWriter.endtag("Ligatures")
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ table = AATStateTable()
+ for eltName, eltAttrs, eltContent in filter(istuple, content):
+ if eltName == "GlyphClass":
+ glyph = eltAttrs["glyph"]
+ value = eltAttrs["value"]
+ table.GlyphClasses[glyph] = safeEval(value)
+ elif eltName == "State":
+ state = self._xmlReadState(eltAttrs, eltContent, font)
+ table.States.append(state)
+ elif eltName == "PerGlyphLookup":
+ lookup = self.perGlyphLookup.xmlRead(eltAttrs, eltContent, font)
+ table.PerGlyphLookups.append(lookup)
+ elif eltName == "LigComponents":
+ table.LigComponents = self._xmlReadLigComponents(
+ eltAttrs, eltContent, font
+ )
+ elif eltName == "Ligatures":
+ table.Ligatures = self._xmlReadLigatures(eltAttrs, eltContent, font)
+ table.GlyphClassCount = max(table.GlyphClasses.values()) + 1
+ return table
+
+ def _xmlReadState(self, attrs, content, font):
+ state = AATState()
+ for eltName, eltAttrs, eltContent in filter(istuple, content):
+ if eltName == "Transition":
+ glyphClass = safeEval(eltAttrs["onGlyphClass"])
+ transition = self.tableClass()
+ transition.fromXML(eltName, eltAttrs, eltContent, font)
+ state.Transitions[glyphClass] = transition
+ return state
+
+ def _xmlReadLigComponents(self, attrs, content, font):
+ ligComponents = []
+ for eltName, eltAttrs, _eltContent in filter(istuple, content):
+ if eltName == "LigComponent":
+ ligComponents.append(safeEval(eltAttrs["value"]))
+ return ligComponents
+
+ def _xmlReadLigatures(self, attrs, content, font):
+ ligs = []
+ for eltName, eltAttrs, _eltContent in filter(istuple, content):
+ if eltName == "Ligature":
+ ligs.append(eltAttrs["glyph"])
+ return ligs
+
+
+class CIDGlyphMap(BaseConverter):
+ def read(self, reader, font, tableDict):
+ numCIDs = reader.readUShort()
+ result = {}
+ for cid, glyphID in enumerate(reader.readUShortArray(numCIDs)):
+ if glyphID != 0xFFFF:
+ result[cid] = font.getGlyphName(glyphID)
+ return result
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ items = {cid: font.getGlyphID(glyph) for cid, glyph in value.items()}
+ count = max(items) + 1 if items else 0
+ writer.writeUShort(count)
+ for cid in range(count):
+ writer.writeUShort(items.get(cid, 0xFFFF))
+
+ def xmlRead(self, attrs, content, font):
+ result = {}
+ for eName, eAttrs, _eContent in filter(istuple, content):
+ if eName == "CID":
+ result[safeEval(eAttrs["cid"])] = eAttrs["glyph"].strip()
+ return result
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.begintag(name, attrs)
+ xmlWriter.newline()
+ for cid, glyph in sorted(value.items()):
+ if glyph is not None and glyph != 0xFFFF:
+ xmlWriter.simpletag("CID", cid=cid, glyph=glyph)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+
+class GlyphCIDMap(BaseConverter):
+ def read(self, reader, font, tableDict):
+ glyphOrder = font.getGlyphOrder()
+ count = reader.readUShort()
+ cids = reader.readUShortArray(count)
+ if count > len(glyphOrder):
+ log.warning(
+ "GlyphCIDMap has %d elements, "
+ "but the font has only %d glyphs; "
+ "ignoring the rest" % (count, len(glyphOrder))
+ )
+ result = {}
+ for glyphID in range(min(len(cids), len(glyphOrder))):
+ cid = cids[glyphID]
+ if cid != 0xFFFF:
+ result[glyphOrder[glyphID]] = cid
+ return result
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ items = {
+ font.getGlyphID(g): cid
+ for g, cid in value.items()
+ if cid is not None and cid != 0xFFFF
+ }
+ count = max(items) + 1 if items else 0
+ writer.writeUShort(count)
+ for glyphID in range(count):
+ writer.writeUShort(items.get(glyphID, 0xFFFF))
+
+ def xmlRead(self, attrs, content, font):
+ result = {}
+ for eName, eAttrs, _eContent in filter(istuple, content):
+ if eName == "CID":
+ result[eAttrs["glyph"]] = safeEval(eAttrs["value"])
+ return result
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.begintag(name, attrs)
+ xmlWriter.newline()
+ for glyph, cid in sorted(value.items()):
+ if cid is not None and cid != 0xFFFF:
+ xmlWriter.simpletag("CID", glyph=glyph, value=cid)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+
+class DeltaValue(BaseConverter):
+ def read(self, reader, font, tableDict):
+ StartSize = tableDict["StartSize"]
+ EndSize = tableDict["EndSize"]
+ DeltaFormat = tableDict["DeltaFormat"]
+ assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
+ nItems = EndSize - StartSize + 1
+ nBits = 1 << DeltaFormat
+ minusOffset = 1 << nBits
+ mask = (1 << nBits) - 1
+ signMask = 1 << (nBits - 1)
+
+ DeltaValue = []
+ tmp, shift = 0, 0
+ for i in range(nItems):
+ if shift == 0:
+ tmp, shift = reader.readUShort(), 16
+ shift = shift - nBits
+ value = (tmp >> shift) & mask
+ if value & signMask:
+ value = value - minusOffset
+ DeltaValue.append(value)
+ return DeltaValue
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ StartSize = tableDict["StartSize"]
+ EndSize = tableDict["EndSize"]
+ DeltaFormat = tableDict["DeltaFormat"]
+ DeltaValue = value
+ assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
+ nItems = EndSize - StartSize + 1
+ nBits = 1 << DeltaFormat
+ assert len(DeltaValue) == nItems
+ mask = (1 << nBits) - 1
+
+ tmp, shift = 0, 16
+ for value in DeltaValue:
+ shift = shift - nBits
+ tmp = tmp | ((value & mask) << shift)
+ if shift == 0:
+ writer.writeUShort(tmp)
+ tmp, shift = 0, 16
+ if shift != 16:
+ writer.writeUShort(tmp)
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ return safeEval(attrs["value"])
+
+
+class VarIdxMapValue(BaseConverter):
+ def read(self, reader, font, tableDict):
+ fmt = tableDict["EntryFormat"]
+ nItems = tableDict["MappingCount"]
+
+ innerBits = 1 + (fmt & 0x000F)
+ innerMask = (1 << innerBits) - 1
+ outerMask = 0xFFFFFFFF - innerMask
+ outerShift = 16 - innerBits
+
+ entrySize = 1 + ((fmt & 0x0030) >> 4)
+ readArray = {
+ 1: reader.readUInt8Array,
+ 2: reader.readUShortArray,
+ 3: reader.readUInt24Array,
+ 4: reader.readULongArray,
+ }[entrySize]
+
+ return [
+ (((raw & outerMask) << outerShift) | (raw & innerMask))
+ for raw in readArray(nItems)
+ ]
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ fmt = tableDict["EntryFormat"]
+ mapping = value
+ writer["MappingCount"].setValue(len(mapping))
+
+ innerBits = 1 + (fmt & 0x000F)
+ innerMask = (1 << innerBits) - 1
+ outerShift = 16 - innerBits
+
+ entrySize = 1 + ((fmt & 0x0030) >> 4)
+ writeArray = {
+ 1: writer.writeUInt8Array,
+ 2: writer.writeUShortArray,
+ 3: writer.writeUInt24Array,
+ 4: writer.writeULongArray,
+ }[entrySize]
+
+ writeArray(
+ [
+ (((idx & 0xFFFF0000) >> outerShift) | (idx & innerMask))
+ for idx in mapping
+ ]
+ )
+
+
+class VarDataValue(BaseConverter):
+ def read(self, reader, font, tableDict):
+ values = []
+
+ regionCount = tableDict["VarRegionCount"]
+ wordCount = tableDict["NumShorts"]
+
+ # https://github.com/fonttools/fonttools/issues/2279
+ longWords = bool(wordCount & 0x8000)
+ wordCount = wordCount & 0x7FFF
+
+ if longWords:
+ readBigArray, readSmallArray = reader.readLongArray, reader.readShortArray
+ else:
+ readBigArray, readSmallArray = reader.readShortArray, reader.readInt8Array
+
+ n1, n2 = min(regionCount, wordCount), max(regionCount, wordCount)
+ values.extend(readBigArray(n1))
+ values.extend(readSmallArray(n2 - n1))
+ if n2 > regionCount: # Padding
+ del values[regionCount:]
+
+ return values
+
+ def write(self, writer, font, tableDict, values, repeatIndex=None):
+ regionCount = tableDict["VarRegionCount"]
+ wordCount = tableDict["NumShorts"]
+
+ # https://github.com/fonttools/fonttools/issues/2279
+ longWords = bool(wordCount & 0x8000)
+ wordCount = wordCount & 0x7FFF
+
+ (writeBigArray, writeSmallArray) = {
+ False: (writer.writeShortArray, writer.writeInt8Array),
+ True: (writer.writeLongArray, writer.writeShortArray),
+ }[longWords]
+
+ n1, n2 = min(regionCount, wordCount), max(regionCount, wordCount)
+ writeBigArray(values[:n1])
+ writeSmallArray(values[n1:regionCount])
+ if n2 > regionCount: # Padding
+ writer.writeSmallArray([0] * (n2 - regionCount))
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ xmlWriter.newline()
+
+ def xmlRead(self, attrs, content, font):
+ return safeEval(attrs["value"])
+
+
+class TupleValues:
+ def read(self, data, font):
+ return TupleVariation.decompileDeltas_(None, data)[0]
+
+ def write(self, writer, font, tableDict, values, repeatIndex=None):
+ optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
+ return bytes(
+ TupleVariation.compileDeltaValues_(values, optimizeSize=not optimizeSpeed)
+ )
+
+ def xmlRead(self, attrs, content, font):
+ return safeEval(attrs["value"])
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ xmlWriter.newline()
+
+
+class CFF2Index(BaseConverter):
+ def __init__(
+ self,
+ name,
+ repeat,
+ aux,
+ tableClass=None,
+ *,
+ itemClass=None,
+ itemConverterClass=None,
+ description="",
+ ):
+ BaseConverter.__init__(
+ self, name, repeat, aux, tableClass, description=description
+ )
+ self._itemClass = itemClass
+ self._converter = (
+ itemConverterClass() if itemConverterClass is not None else None
+ )
+
+ def read(self, reader, font, tableDict):
+ count = reader.readULong()
+ if count == 0:
+ return []
+ offSize = reader.readUInt8()
+
+ def getReadArray(reader, offSize):
+ return {
+ 1: reader.readUInt8Array,
+ 2: reader.readUShortArray,
+ 3: reader.readUInt24Array,
+ 4: reader.readULongArray,
+ }[offSize]
+
+ readArray = getReadArray(reader, offSize)
+
+ lazy = font.lazy is not False and count > 8
+ if not lazy:
+ offsets = readArray(count + 1)
+ items = []
+ lastOffset = offsets.pop(0)
+ reader.readData(lastOffset - 1) # In case first offset is not 1
+
+ for offset in offsets:
+ assert lastOffset <= offset
+ item = reader.readData(offset - lastOffset)
+
+ if self._itemClass is not None:
+ obj = self._itemClass()
+ obj.decompile(item, font, reader.localState)
+ item = obj
+ elif self._converter is not None:
+ item = self._converter.read(item, font)
+
+ items.append(item)
+ lastOffset = offset
+ return items
+ else:
+
+ def get_read_item():
+ reader_copy = reader.copy()
+ offset_pos = reader.pos
+ data_pos = offset_pos + (count + 1) * offSize - 1
+ readArray = getReadArray(reader_copy, offSize)
+
+ def read_item(i):
+ reader_copy.seek(offset_pos + i * offSize)
+ offsets = readArray(2)
+ reader_copy.seek(data_pos + offsets[0])
+ item = reader_copy.readData(offsets[1] - offsets[0])
+
+ if self._itemClass is not None:
+ obj = self._itemClass()
+ obj.decompile(item, font, reader_copy.localState)
+ item = obj
+ elif self._converter is not None:
+ item = self._converter.read(item, font)
+ return item
+
+ return read_item
+
+ read_item = get_read_item()
+ l = LazyList([read_item] * count)
+
+ # TODO: Advance reader
+
+ return l
+
+ def write(self, writer, font, tableDict, values, repeatIndex=None):
+ items = values
+
+ writer.writeULong(len(items))
+ if not len(items):
+ return
+
+ if self._itemClass is not None:
+ items = [item.compile(font) for item in items]
+ elif self._converter is not None:
+ items = [
+ self._converter.write(writer, font, tableDict, item, i)
+ for i, item in enumerate(items)
+ ]
+
+ offsets = [len(item) for item in items]
+ offsets = list(accumulate(offsets, initial=1))
+
+ lastOffset = offsets[-1]
+ offSize = (
+ 1
+ if lastOffset < 0x100
+ else 2 if lastOffset < 0x10000 else 3 if lastOffset < 0x1000000 else 4
+ )
+ writer.writeUInt8(offSize)
+
+ writeArray = {
+ 1: writer.writeUInt8Array,
+ 2: writer.writeUShortArray,
+ 3: writer.writeUInt24Array,
+ 4: writer.writeULongArray,
+ }[offSize]
+
+ writeArray(offsets)
+ for item in items:
+ writer.writeData(item)
+
+ def xmlRead(self, attrs, content, font):
+ if self._itemClass is not None:
+ obj = self._itemClass()
+ obj.fromXML(None, attrs, content, font)
+ return obj
+ elif self._converter is not None:
+ return self._converter.xmlRead(attrs, content, font)
+ else:
+ raise NotImplementedError()
+
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if self._itemClass is not None:
+ for i, item in enumerate(value):
+ item.toXML(xmlWriter, font, [("index", i)], name)
+ elif self._converter is not None:
+ for i, item in enumerate(value):
+ self._converter.xmlWrite(
+ xmlWriter, font, item, name, attrs + [("index", i)]
+ )
+ else:
+ raise NotImplementedError()
+
+
+class LookupFlag(UShort):
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ flags = []
+ if value & 0x01:
+ flags.append("rightToLeft")
+ if value & 0x02:
+ flags.append("ignoreBaseGlyphs")
+ if value & 0x04:
+ flags.append("ignoreLigatures")
+ if value & 0x08:
+ flags.append("ignoreMarks")
+ if value & 0x10:
+ flags.append("useMarkFilteringSet")
+ if value & 0xFF00:
+ flags.append("markAttachmentType[%i]" % (value >> 8))
+ if flags:
+ xmlWriter.comment(" ".join(flags))
+ xmlWriter.newline()
+
+
+class _UInt8Enum(UInt8):
+ enumClass = NotImplemented
+
+ def read(self, reader, font, tableDict):
+ return self.enumClass(super().read(reader, font, tableDict))
+
+ @classmethod
+ def fromString(cls, value):
+ return getattr(cls.enumClass, value.upper())
+
+ @classmethod
+ def toString(cls, value):
+ return cls.enumClass(value).name.lower()
+
+
+class ExtendMode(_UInt8Enum):
+ enumClass = _ExtendMode
+
+
+class CompositeMode(_UInt8Enum):
+ enumClass = _CompositeMode
+
+
+converterMapping = {
+ # type class
+ "int8": Int8,
+ "int16": Short,
+ "int32": Long,
+ "uint8": UInt8,
+ "uint16": UShort,
+ "uint24": UInt24,
+ "uint32": ULong,
+ "char64": Char64,
+ "Flags32": Flags32,
+ "VarIndex": VarIndex,
+ "Version": Version,
+ "Tag": Tag,
+ "GlyphID": GlyphID,
+ "GlyphID32": GlyphID32,
+ "NameID": NameID,
+ "DeciPoints": DeciPoints,
+ "Fixed": Fixed,
+ "F2Dot14": F2Dot14,
+ "Angle": Angle,
+ "BiasedAngle": BiasedAngle,
+ "struct": Struct,
+ "Offset": Table,
+ "LOffset": LTable,
+ "Offset24": Table24,
+ "ValueRecord": ValueRecord,
+ "DeltaValue": DeltaValue,
+ "VarIdxMapValue": VarIdxMapValue,
+ "VarDataValue": VarDataValue,
+ "LookupFlag": LookupFlag,
+ "ExtendMode": ExtendMode,
+ "CompositeMode": CompositeMode,
+ "STATFlags": STATFlags,
+ "TupleList": partial(CFF2Index, itemConverterClass=TupleValues),
+ "VarCompositeGlyphList": partial(CFF2Index, itemClass=VarCompositeGlyph),
+ # AAT
+ "CIDGlyphMap": CIDGlyphMap,
+ "GlyphCIDMap": GlyphCIDMap,
+ "MortChain": StructWithLength,
+ "MortSubtable": StructWithLength,
+ "MorxChain": StructWithLength,
+ "MorxSubtable": MorxSubtableConverter,
+ # "Template" types
+ "AATLookup": lambda C: partial(AATLookup, tableClass=C),
+ "AATLookupWithDataOffset": lambda C: partial(AATLookupWithDataOffset, tableClass=C),
+ "STXHeader": lambda C: partial(STXHeader, tableClass=C),
+ "OffsetTo": lambda C: partial(Table, tableClass=C),
+ "LOffsetTo": lambda C: partial(LTable, tableClass=C),
+ "LOffset24To": lambda C: partial(Table24, tableClass=C),
+}
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/otData.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/otData.py
new file mode 100644
index 0000000000000000000000000000000000000000..f538aeb48ca5515edfb36d012da5f2661b6e832d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/otData.py
@@ -0,0 +1,6400 @@
+otData = [
+ #
+ # common
+ #
+ ("LookupOrder", []),
+ (
+ "ScriptList",
+ [
+ ("uint16", "ScriptCount", None, None, "Number of ScriptRecords"),
+ (
+ "struct",
+ "ScriptRecord",
+ "ScriptCount",
+ 0,
+ "Array of ScriptRecords -listed alphabetically by ScriptTag",
+ ),
+ ],
+ ),
+ (
+ "ScriptRecord",
+ [
+ ("Tag", "ScriptTag", None, None, "4-byte ScriptTag identifier"),
+ (
+ "Offset",
+ "Script",
+ None,
+ None,
+ "Offset to Script table-from beginning of ScriptList",
+ ),
+ ],
+ ),
+ (
+ "Script",
+ [
+ (
+ "Offset",
+ "DefaultLangSys",
+ None,
+ None,
+ "Offset to DefaultLangSys table-from beginning of Script table-may be NULL",
+ ),
+ (
+ "uint16",
+ "LangSysCount",
+ None,
+ None,
+ "Number of LangSysRecords for this script-excluding the DefaultLangSys",
+ ),
+ (
+ "struct",
+ "LangSysRecord",
+ "LangSysCount",
+ 0,
+ "Array of LangSysRecords-listed alphabetically by LangSysTag",
+ ),
+ ],
+ ),
+ (
+ "LangSysRecord",
+ [
+ ("Tag", "LangSysTag", None, None, "4-byte LangSysTag identifier"),
+ (
+ "Offset",
+ "LangSys",
+ None,
+ None,
+ "Offset to LangSys table-from beginning of Script table",
+ ),
+ ],
+ ),
+ (
+ "LangSys",
+ [
+ (
+ "Offset",
+ "LookupOrder",
+ None,
+ None,
+ "= NULL (reserved for an offset to a reordering table)",
+ ),
+ (
+ "uint16",
+ "ReqFeatureIndex",
+ None,
+ None,
+ "Index of a feature required for this language system- if no required features = 0xFFFF",
+ ),
+ (
+ "uint16",
+ "FeatureCount",
+ None,
+ None,
+ "Number of FeatureIndex values for this language system-excludes the required feature",
+ ),
+ (
+ "uint16",
+ "FeatureIndex",
+ "FeatureCount",
+ 0,
+ "Array of indices into the FeatureList-in arbitrary order",
+ ),
+ ],
+ ),
+ (
+ "FeatureList",
+ [
+ (
+ "uint16",
+ "FeatureCount",
+ None,
+ None,
+ "Number of FeatureRecords in this table",
+ ),
+ (
+ "struct",
+ "FeatureRecord",
+ "FeatureCount",
+ 0,
+ "Array of FeatureRecords-zero-based (first feature has FeatureIndex = 0)-listed alphabetically by FeatureTag",
+ ),
+ ],
+ ),
+ (
+ "FeatureRecord",
+ [
+ ("Tag", "FeatureTag", None, None, "4-byte feature identification tag"),
+ (
+ "Offset",
+ "Feature",
+ None,
+ None,
+ "Offset to Feature table-from beginning of FeatureList",
+ ),
+ ],
+ ),
+ (
+ "Feature",
+ [
+ (
+ "Offset",
+ "FeatureParams",
+ None,
+ None,
+ "= NULL (reserved for offset to FeatureParams)",
+ ),
+ (
+ "uint16",
+ "LookupCount",
+ None,
+ None,
+ "Number of LookupList indices for this feature",
+ ),
+ (
+ "uint16",
+ "LookupListIndex",
+ "LookupCount",
+ 0,
+ "Array of LookupList indices for this feature -zero-based (first lookup is LookupListIndex = 0)",
+ ),
+ ],
+ ),
+ ("FeatureParams", []),
+ (
+ "FeatureParamsSize",
+ [
+ (
+ "DeciPoints",
+ "DesignSize",
+ None,
+ None,
+ "The design size in 720/inch units (decipoints).",
+ ),
+ (
+ "uint16",
+ "SubfamilyID",
+ None,
+ None,
+ "Serves as an identifier that associates fonts in a subfamily.",
+ ),
+ ("NameID", "SubfamilyNameID", None, None, "Subfamily NameID."),
+ (
+ "DeciPoints",
+ "RangeStart",
+ None,
+ None,
+ "Small end of recommended usage range (exclusive) in 720/inch units.",
+ ),
+ (
+ "DeciPoints",
+ "RangeEnd",
+ None,
+ None,
+ "Large end of recommended usage range (inclusive) in 720/inch units.",
+ ),
+ ],
+ ),
+ (
+ "FeatureParamsStylisticSet",
+ [
+ ("uint16", "Version", None, None, "Set to 0."),
+ ("NameID", "UINameID", None, None, "UI NameID."),
+ ],
+ ),
+ (
+ "FeatureParamsCharacterVariants",
+ [
+ ("uint16", "Format", None, None, "Set to 0."),
+ ("NameID", "FeatUILabelNameID", None, None, "Feature UI label NameID."),
+ (
+ "NameID",
+ "FeatUITooltipTextNameID",
+ None,
+ None,
+ "Feature UI tooltip text NameID.",
+ ),
+ ("NameID", "SampleTextNameID", None, None, "Sample text NameID."),
+ ("uint16", "NumNamedParameters", None, None, "Number of named parameters."),
+ (
+ "NameID",
+ "FirstParamUILabelNameID",
+ None,
+ None,
+ "First NameID of UI feature parameters.",
+ ),
+ (
+ "uint16",
+ "CharCount",
+ None,
+ None,
+ "Count of characters this feature provides glyph variants for.",
+ ),
+ (
+ "uint24",
+ "Character",
+ "CharCount",
+ 0,
+ "Unicode characters for which this feature provides glyph variants.",
+ ),
+ ],
+ ),
+ (
+ "LookupList",
+ [
+ ("uint16", "LookupCount", None, None, "Number of lookups in this table"),
+ (
+ "Offset",
+ "Lookup",
+ "LookupCount",
+ 0,
+ "Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)",
+ ),
+ ],
+ ),
+ (
+ "Lookup",
+ [
+ (
+ "uint16",
+ "LookupType",
+ None,
+ None,
+ "Different enumerations for GSUB and GPOS",
+ ),
+ ("LookupFlag", "LookupFlag", None, None, "Lookup qualifiers"),
+ (
+ "uint16",
+ "SubTableCount",
+ None,
+ None,
+ "Number of SubTables for this lookup",
+ ),
+ (
+ "Offset",
+ "SubTable",
+ "SubTableCount",
+ 0,
+ "Array of offsets to SubTables-from beginning of Lookup table",
+ ),
+ (
+ "uint16",
+ "MarkFilteringSet",
+ None,
+ "LookupFlag & 0x0010",
+ "If set, indicates that the lookup table structure is followed by a MarkFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.",
+ ),
+ ],
+ ),
+ (
+ "CoverageFormat1",
+ [
+ ("uint16", "CoverageFormat", None, None, "Format identifier-format = 1"),
+ ("uint16", "GlyphCount", None, None, "Number of glyphs in the GlyphArray"),
+ (
+ "GlyphID",
+ "GlyphArray",
+ "GlyphCount",
+ 0,
+ "Array of GlyphIDs-in numerical order",
+ ),
+ ],
+ ),
+ (
+ "CoverageFormat2",
+ [
+ ("uint16", "CoverageFormat", None, None, "Format identifier-format = 2"),
+ ("uint16", "RangeCount", None, None, "Number of RangeRecords"),
+ (
+ "struct",
+ "RangeRecord",
+ "RangeCount",
+ 0,
+ "Array of glyph ranges-ordered by Start GlyphID",
+ ),
+ ],
+ ),
+ (
+ "RangeRecord",
+ [
+ ("GlyphID", "Start", None, None, "First GlyphID in the range"),
+ ("GlyphID", "End", None, None, "Last GlyphID in the range"),
+ (
+ "uint16",
+ "StartCoverageIndex",
+ None,
+ None,
+ "Coverage Index of first GlyphID in range",
+ ),
+ ],
+ ),
+ (
+ "ClassDefFormat1",
+ [
+ ("uint16", "ClassFormat", None, None, "Format identifier-format = 1"),
+ (
+ "GlyphID",
+ "StartGlyph",
+ None,
+ None,
+ "First GlyphID of the ClassValueArray",
+ ),
+ ("uint16", "GlyphCount", None, None, "Size of the ClassValueArray"),
+ (
+ "uint16",
+ "ClassValueArray",
+ "GlyphCount",
+ 0,
+ "Array of Class Values-one per GlyphID",
+ ),
+ ],
+ ),
+ (
+ "ClassDefFormat2",
+ [
+ ("uint16", "ClassFormat", None, None, "Format identifier-format = 2"),
+ ("uint16", "ClassRangeCount", None, None, "Number of ClassRangeRecords"),
+ (
+ "struct",
+ "ClassRangeRecord",
+ "ClassRangeCount",
+ 0,
+ "Array of ClassRangeRecords-ordered by Start GlyphID",
+ ),
+ ],
+ ),
+ (
+ "ClassRangeRecord",
+ [
+ ("GlyphID", "Start", None, None, "First GlyphID in the range"),
+ ("GlyphID", "End", None, None, "Last GlyphID in the range"),
+ ("uint16", "Class", None, None, "Applied to all glyphs in the range"),
+ ],
+ ),
+ (
+ "Device",
+ [
+ ("uint16", "StartSize", None, None, "Smallest size to correct-in ppem"),
+ ("uint16", "EndSize", None, None, "Largest size to correct-in ppem"),
+ (
+ "uint16",
+ "DeltaFormat",
+ None,
+ None,
+ "Format of DeltaValue array data: 1, 2, or 3",
+ ),
+ (
+ "DeltaValue",
+ "DeltaValue",
+ None,
+ "DeltaFormat in (1,2,3)",
+ "Array of compressed data",
+ ),
+ ],
+ ),
+ #
+ # gpos
+ #
+ (
+ "GPOS",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the GPOS table- 0x00010000 or 0x00010001",
+ ),
+ (
+ "Offset",
+ "ScriptList",
+ None,
+ None,
+ "Offset to ScriptList table-from beginning of GPOS table",
+ ),
+ (
+ "Offset",
+ "FeatureList",
+ None,
+ None,
+ "Offset to FeatureList table-from beginning of GPOS table",
+ ),
+ (
+ "Offset",
+ "LookupList",
+ None,
+ None,
+ "Offset to LookupList table-from beginning of GPOS table",
+ ),
+ (
+ "LOffset",
+ "FeatureVariations",
+ None,
+ "Version >= 0x00010001",
+ "Offset to FeatureVariations table-from beginning of GPOS table",
+ ),
+ ],
+ ),
+ (
+ "SinglePosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of SinglePos subtable",
+ ),
+ (
+ "uint16",
+ "ValueFormat",
+ None,
+ None,
+ "Defines the types of data in the ValueRecord",
+ ),
+ (
+ "ValueRecord",
+ "Value",
+ None,
+ None,
+ "Defines positioning value(s)-applied to all glyphs in the Coverage table",
+ ),
+ ],
+ ),
+ (
+ "SinglePosFormat2",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of SinglePos subtable",
+ ),
+ (
+ "uint16",
+ "ValueFormat",
+ None,
+ None,
+ "Defines the types of data in the ValueRecord",
+ ),
+ ("uint16", "ValueCount", None, None, "Number of ValueRecords"),
+ (
+ "ValueRecord",
+ "Value",
+ "ValueCount",
+ 0,
+ "Array of ValueRecords-positioning values applied to glyphs",
+ ),
+ ],
+ ),
+ (
+ "PairPosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of PairPos subtable-only the first glyph in each pair",
+ ),
+ (
+ "uint16",
+ "ValueFormat1",
+ None,
+ None,
+ "Defines the types of data in ValueRecord1-for the first glyph in the pair -may be zero (0)",
+ ),
+ (
+ "uint16",
+ "ValueFormat2",
+ None,
+ None,
+ "Defines the types of data in ValueRecord2-for the second glyph in the pair -may be zero (0)",
+ ),
+ ("uint16", "PairSetCount", None, None, "Number of PairSet tables"),
+ (
+ "Offset",
+ "PairSet",
+ "PairSetCount",
+ 0,
+ "Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "PairSet",
+ [
+ ("uint16", "PairValueCount", None, None, "Number of PairValueRecords"),
+ (
+ "struct",
+ "PairValueRecord",
+ "PairValueCount",
+ 0,
+ "Array of PairValueRecords-ordered by GlyphID of the second glyph",
+ ),
+ ],
+ ),
+ (
+ "PairValueRecord",
+ [
+ (
+ "GlyphID",
+ "SecondGlyph",
+ None,
+ None,
+ "GlyphID of second glyph in the pair-first glyph is listed in the Coverage table",
+ ),
+ (
+ "ValueRecord",
+ "Value1",
+ None,
+ None,
+ "Positioning data for the first glyph in the pair",
+ ),
+ (
+ "ValueRecord",
+ "Value2",
+ None,
+ None,
+ "Positioning data for the second glyph in the pair",
+ ),
+ ],
+ ),
+ (
+ "PairPosFormat2",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of PairPos subtable-for the first glyph of the pair",
+ ),
+ (
+ "uint16",
+ "ValueFormat1",
+ None,
+ None,
+ "ValueRecord definition-for the first glyph of the pair-may be zero (0)",
+ ),
+ (
+ "uint16",
+ "ValueFormat2",
+ None,
+ None,
+ "ValueRecord definition-for the second glyph of the pair-may be zero (0)",
+ ),
+ (
+ "Offset",
+ "ClassDef1",
+ None,
+ None,
+ "Offset to ClassDef table-from beginning of PairPos subtable-for the first glyph of the pair",
+ ),
+ (
+ "Offset",
+ "ClassDef2",
+ None,
+ None,
+ "Offset to ClassDef table-from beginning of PairPos subtable-for the second glyph of the pair",
+ ),
+ (
+ "uint16",
+ "Class1Count",
+ None,
+ None,
+ "Number of classes in ClassDef1 table-includes Class0",
+ ),
+ (
+ "uint16",
+ "Class2Count",
+ None,
+ None,
+ "Number of classes in ClassDef2 table-includes Class0",
+ ),
+ (
+ "struct",
+ "Class1Record",
+ "Class1Count",
+ 0,
+ "Array of Class1 records-ordered by Class1",
+ ),
+ ],
+ ),
+ (
+ "Class1Record",
+ [
+ (
+ "struct",
+ "Class2Record",
+ "Class2Count",
+ 0,
+ "Array of Class2 records-ordered by Class2",
+ ),
+ ],
+ ),
+ (
+ "Class2Record",
+ [
+ (
+ "ValueRecord",
+ "Value1",
+ None,
+ None,
+ "Positioning for first glyph-empty if ValueFormat1 = 0",
+ ),
+ (
+ "ValueRecord",
+ "Value2",
+ None,
+ None,
+ "Positioning for second glyph-empty if ValueFormat2 = 0",
+ ),
+ ],
+ ),
+ (
+ "CursivePosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of CursivePos subtable",
+ ),
+ ("uint16", "EntryExitCount", None, None, "Number of EntryExit records"),
+ (
+ "struct",
+ "EntryExitRecord",
+ "EntryExitCount",
+ 0,
+ "Array of EntryExit records-in Coverage Index order",
+ ),
+ ],
+ ),
+ (
+ "EntryExitRecord",
+ [
+ (
+ "Offset",
+ "EntryAnchor",
+ None,
+ None,
+ "Offset to EntryAnchor table-from beginning of CursivePos subtable-may be NULL",
+ ),
+ (
+ "Offset",
+ "ExitAnchor",
+ None,
+ None,
+ "Offset to ExitAnchor table-from beginning of CursivePos subtable-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "MarkBasePosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "MarkCoverage",
+ None,
+ None,
+ "Offset to MarkCoverage table-from beginning of MarkBasePos subtable",
+ ),
+ (
+ "Offset",
+ "BaseCoverage",
+ None,
+ None,
+ "Offset to BaseCoverage table-from beginning of MarkBasePos subtable",
+ ),
+ ("uint16", "ClassCount", None, None, "Number of classes defined for marks"),
+ (
+ "Offset",
+ "MarkArray",
+ None,
+ None,
+ "Offset to MarkArray table-from beginning of MarkBasePos subtable",
+ ),
+ (
+ "Offset",
+ "BaseArray",
+ None,
+ None,
+ "Offset to BaseArray table-from beginning of MarkBasePos subtable",
+ ),
+ ],
+ ),
+ (
+ "BaseArray",
+ [
+ ("uint16", "BaseCount", None, None, "Number of BaseRecords"),
+ (
+ "struct",
+ "BaseRecord",
+ "BaseCount",
+ 0,
+ "Array of BaseRecords-in order of BaseCoverage Index",
+ ),
+ ],
+ ),
+ (
+ "BaseRecord",
+ [
+ (
+ "Offset",
+ "BaseAnchor",
+ "ClassCount",
+ 0,
+ "Array of offsets (one per class) to Anchor tables-from beginning of BaseArray table-ordered by class-zero-based",
+ ),
+ ],
+ ),
+ (
+ "MarkLigPosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "MarkCoverage",
+ None,
+ None,
+ "Offset to Mark Coverage table-from beginning of MarkLigPos subtable",
+ ),
+ (
+ "Offset",
+ "LigatureCoverage",
+ None,
+ None,
+ "Offset to Ligature Coverage table-from beginning of MarkLigPos subtable",
+ ),
+ ("uint16", "ClassCount", None, None, "Number of defined mark classes"),
+ (
+ "Offset",
+ "MarkArray",
+ None,
+ None,
+ "Offset to MarkArray table-from beginning of MarkLigPos subtable",
+ ),
+ (
+ "Offset",
+ "LigatureArray",
+ None,
+ None,
+ "Offset to LigatureArray table-from beginning of MarkLigPos subtable",
+ ),
+ ],
+ ),
+ (
+ "LigatureArray",
+ [
+ (
+ "uint16",
+ "LigatureCount",
+ None,
+ None,
+ "Number of LigatureAttach table offsets",
+ ),
+ (
+ "Offset",
+ "LigatureAttach",
+ "LigatureCount",
+ 0,
+ "Array of offsets to LigatureAttach tables-from beginning of LigatureArray table-ordered by LigatureCoverage Index",
+ ),
+ ],
+ ),
+ (
+ "LigatureAttach",
+ [
+ (
+ "uint16",
+ "ComponentCount",
+ None,
+ None,
+ "Number of ComponentRecords in this ligature",
+ ),
+ (
+ "struct",
+ "ComponentRecord",
+ "ComponentCount",
+ 0,
+ "Array of Component records-ordered in writing direction",
+ ),
+ ],
+ ),
+ (
+ "ComponentRecord",
+ [
+ (
+ "Offset",
+ "LigatureAnchor",
+ "ClassCount",
+ 0,
+ "Array of offsets (one per class) to Anchor tables-from beginning of LigatureAttach table-ordered by class-NULL if a component does not have an attachment for a class-zero-based array",
+ ),
+ ],
+ ),
+ (
+ "MarkMarkPosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Mark1Coverage",
+ None,
+ None,
+ "Offset to Combining Mark Coverage table-from beginning of MarkMarkPos subtable",
+ ),
+ (
+ "Offset",
+ "Mark2Coverage",
+ None,
+ None,
+ "Offset to Base Mark Coverage table-from beginning of MarkMarkPos subtable",
+ ),
+ (
+ "uint16",
+ "ClassCount",
+ None,
+ None,
+ "Number of Combining Mark classes defined",
+ ),
+ (
+ "Offset",
+ "Mark1Array",
+ None,
+ None,
+ "Offset to MarkArray table for Mark1-from beginning of MarkMarkPos subtable",
+ ),
+ (
+ "Offset",
+ "Mark2Array",
+ None,
+ None,
+ "Offset to Mark2Array table for Mark2-from beginning of MarkMarkPos subtable",
+ ),
+ ],
+ ),
+ (
+ "Mark2Array",
+ [
+ ("uint16", "Mark2Count", None, None, "Number of Mark2 records"),
+ (
+ "struct",
+ "Mark2Record",
+ "Mark2Count",
+ 0,
+ "Array of Mark2 records-in Coverage order",
+ ),
+ ],
+ ),
+ (
+ "Mark2Record",
+ [
+ (
+ "Offset",
+ "Mark2Anchor",
+ "ClassCount",
+ 0,
+ "Array of offsets (one per class) to Anchor tables-from beginning of Mark2Array table-zero-based array",
+ ),
+ ],
+ ),
+ (
+ "PosLookupRecord",
+ [
+ (
+ "uint16",
+ "SequenceIndex",
+ None,
+ None,
+ "Index to input glyph sequence-first glyph = 0",
+ ),
+ (
+ "uint16",
+ "LookupListIndex",
+ None,
+ None,
+ "Lookup to apply to that position-zero-based",
+ ),
+ ],
+ ),
+ (
+ "ContextPosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of ContextPos subtable",
+ ),
+ ("uint16", "PosRuleSetCount", None, None, "Number of PosRuleSet tables"),
+ (
+ "Offset",
+ "PosRuleSet",
+ "PosRuleSetCount",
+ 0,
+ "Array of offsets to PosRuleSet tables-from beginning of ContextPos subtable-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "PosRuleSet",
+ [
+ ("uint16", "PosRuleCount", None, None, "Number of PosRule tables"),
+ (
+ "Offset",
+ "PosRule",
+ "PosRuleCount",
+ 0,
+ "Array of offsets to PosRule tables-from beginning of PosRuleSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "PosRule",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of glyphs in the Input glyph sequence",
+ ),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "GlyphID",
+ "Input",
+ "GlyphCount",
+ -1,
+ "Array of input GlyphIDs-starting with the second glyph",
+ ),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of positioning lookups-in design order",
+ ),
+ ],
+ ),
+ (
+ "ContextPosFormat2",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of ContextPos subtable",
+ ),
+ (
+ "Offset",
+ "ClassDef",
+ None,
+ None,
+ "Offset to ClassDef table-from beginning of ContextPos subtable",
+ ),
+ ("uint16", "PosClassSetCount", None, None, "Number of PosClassSet tables"),
+ (
+ "Offset",
+ "PosClassSet",
+ "PosClassSetCount",
+ 0,
+ "Array of offsets to PosClassSet tables-from beginning of ContextPos subtable-ordered by class-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "PosClassSet",
+ [
+ (
+ "uint16",
+ "PosClassRuleCount",
+ None,
+ None,
+ "Number of PosClassRule tables",
+ ),
+ (
+ "Offset",
+ "PosClassRule",
+ "PosClassRuleCount",
+ 0,
+ "Array of offsets to PosClassRule tables-from beginning of PosClassSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "PosClassRule",
+ [
+ ("uint16", "GlyphCount", None, None, "Number of glyphs to be matched"),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "uint16",
+ "Class",
+ "GlyphCount",
+ -1,
+ "Array of classes-beginning with the second class-to be matched to the input glyph sequence",
+ ),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of positioning lookups-in design order",
+ ),
+ ],
+ ),
+ (
+ "ContextPosFormat3",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 3"),
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of glyphs in the input sequence",
+ ),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "Offset",
+ "Coverage",
+ "GlyphCount",
+ 0,
+ "Array of offsets to Coverage tables-from beginning of ContextPos subtable",
+ ),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of positioning lookups-in design order",
+ ),
+ ],
+ ),
+ (
+ "ChainContextPosFormat1",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of ContextPos subtable",
+ ),
+ (
+ "uint16",
+ "ChainPosRuleSetCount",
+ None,
+ None,
+ "Number of ChainPosRuleSet tables",
+ ),
+ (
+ "Offset",
+ "ChainPosRuleSet",
+ "ChainPosRuleSetCount",
+ 0,
+ "Array of offsets to ChainPosRuleSet tables-from beginning of ContextPos subtable-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "ChainPosRuleSet",
+ [
+ (
+ "uint16",
+ "ChainPosRuleCount",
+ None,
+ None,
+ "Number of ChainPosRule tables",
+ ),
+ (
+ "Offset",
+ "ChainPosRule",
+ "ChainPosRuleCount",
+ 0,
+ "Array of offsets to ChainPosRule tables-from beginning of ChainPosRuleSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "ChainPosRule",
+ [
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)",
+ ),
+ (
+ "GlyphID",
+ "Backtrack",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of backtracking GlyphID's (to be matched before the input sequence)",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the input sequence (includes the first glyph)",
+ ),
+ (
+ "GlyphID",
+ "Input",
+ "InputGlyphCount",
+ -1,
+ "Array of input GlyphIDs (start with second glyph)",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)",
+ ),
+ (
+ "GlyphID",
+ "LookAhead",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of lookahead GlyphID's (to be matched after the input sequence)",
+ ),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of PosLookupRecords (in design order)",
+ ),
+ ],
+ ),
+ (
+ "ChainContextPosFormat2",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of ChainContextPos subtable",
+ ),
+ (
+ "Offset",
+ "BacktrackClassDef",
+ None,
+ None,
+ "Offset to ClassDef table containing backtrack sequence context-from beginning of ChainContextPos subtable",
+ ),
+ (
+ "Offset",
+ "InputClassDef",
+ None,
+ None,
+ "Offset to ClassDef table containing input sequence context-from beginning of ChainContextPos subtable",
+ ),
+ (
+ "Offset",
+ "LookAheadClassDef",
+ None,
+ None,
+ "Offset to ClassDef table containing lookahead sequence context-from beginning of ChainContextPos subtable",
+ ),
+ (
+ "uint16",
+ "ChainPosClassSetCount",
+ None,
+ None,
+ "Number of ChainPosClassSet tables",
+ ),
+ (
+ "Offset",
+ "ChainPosClassSet",
+ "ChainPosClassSetCount",
+ 0,
+ "Array of offsets to ChainPosClassSet tables-from beginning of ChainContextPos subtable-ordered by input class-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "ChainPosClassSet",
+ [
+ (
+ "uint16",
+ "ChainPosClassRuleCount",
+ None,
+ None,
+ "Number of ChainPosClassRule tables",
+ ),
+ (
+ "Offset",
+ "ChainPosClassRule",
+ "ChainPosClassRuleCount",
+ 0,
+ "Array of offsets to ChainPosClassRule tables-from beginning of ChainPosClassSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "ChainPosClassRule",
+ [
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)",
+ ),
+ (
+ "uint16",
+ "Backtrack",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of backtracking classes(to be matched before the input sequence)",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Total number of classes in the input sequence (includes the first class)",
+ ),
+ (
+ "uint16",
+ "Input",
+ "InputGlyphCount",
+ -1,
+ "Array of input classes(start with second class; to be matched with the input glyph sequence)",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Total number of classes in the look ahead sequence (number of classes to be matched after the input sequence)",
+ ),
+ (
+ "uint16",
+ "LookAhead",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of lookahead classes(to be matched after the input sequence)",
+ ),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of PosLookupRecords (in design order)",
+ ),
+ ],
+ ),
+ (
+ "ChainContextPosFormat3",
+ [
+ ("uint16", "PosFormat", None, None, "Format identifier-format = 3"),
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Number of glyphs in the backtracking sequence",
+ ),
+ (
+ "Offset",
+ "BacktrackCoverage",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in backtracking sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Number of glyphs in input sequence",
+ ),
+ (
+ "Offset",
+ "InputCoverage",
+ "InputGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in input sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Number of glyphs in lookahead sequence",
+ ),
+ (
+ "Offset",
+ "LookAheadCoverage",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in lookahead sequence, in glyph sequence order",
+ ),
+ ("uint16", "PosCount", None, None, "Number of PosLookupRecords"),
+ (
+ "struct",
+ "PosLookupRecord",
+ "PosCount",
+ 0,
+ "Array of PosLookupRecords,in design order",
+ ),
+ ],
+ ),
+ (
+ "ExtensionPosFormat1",
+ [
+ ("uint16", "ExtFormat", None, None, "Format identifier. Set to 1."),
+ (
+ "uint16",
+ "ExtensionLookupType",
+ None,
+ None,
+ "Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).",
+ ),
+ ("LOffset", "ExtSubTable", None, None, "Offset to SubTable"),
+ ],
+ ),
+ # ('ValueRecord', [
+ # ('int16', 'XPlacement', None, None, 'Horizontal adjustment for placement-in design units'),
+ # ('int16', 'YPlacement', None, None, 'Vertical adjustment for placement-in design units'),
+ # ('int16', 'XAdvance', None, None, 'Horizontal adjustment for advance-in design units (only used for horizontal writing)'),
+ # ('int16', 'YAdvance', None, None, 'Vertical adjustment for advance-in design units (only used for vertical writing)'),
+ # ('Offset', 'XPlaDevice', None, None, 'Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)'),
+ # ('Offset', 'YPlaDevice', None, None, 'Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)'),
+ # ('Offset', 'XAdvDevice', None, None, 'Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)'),
+ # ('Offset', 'YAdvDevice', None, None, 'Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)'),
+ # ]),
+ (
+ "AnchorFormat1",
+ [
+ ("uint16", "AnchorFormat", None, None, "Format identifier-format = 1"),
+ ("int16", "XCoordinate", None, None, "Horizontal value-in design units"),
+ ("int16", "YCoordinate", None, None, "Vertical value-in design units"),
+ ],
+ ),
+ (
+ "AnchorFormat2",
+ [
+ ("uint16", "AnchorFormat", None, None, "Format identifier-format = 2"),
+ ("int16", "XCoordinate", None, None, "Horizontal value-in design units"),
+ ("int16", "YCoordinate", None, None, "Vertical value-in design units"),
+ ("uint16", "AnchorPoint", None, None, "Index to glyph contour point"),
+ ],
+ ),
+ (
+ "AnchorFormat3",
+ [
+ ("uint16", "AnchorFormat", None, None, "Format identifier-format = 3"),
+ ("int16", "XCoordinate", None, None, "Horizontal value-in design units"),
+ ("int16", "YCoordinate", None, None, "Vertical value-in design units"),
+ (
+ "Offset",
+ "XDeviceTable",
+ None,
+ None,
+ "Offset to Device table for X coordinate- from beginning of Anchor table (may be NULL)",
+ ),
+ (
+ "Offset",
+ "YDeviceTable",
+ None,
+ None,
+ "Offset to Device table for Y coordinate- from beginning of Anchor table (may be NULL)",
+ ),
+ ],
+ ),
+ (
+ "MarkArray",
+ [
+ ("uint16", "MarkCount", None, None, "Number of MarkRecords"),
+ (
+ "struct",
+ "MarkRecord",
+ "MarkCount",
+ 0,
+ "Array of MarkRecords-in Coverage order",
+ ),
+ ],
+ ),
+ (
+ "MarkRecord",
+ [
+ ("uint16", "Class", None, None, "Class defined for this mark"),
+ (
+ "Offset",
+ "MarkAnchor",
+ None,
+ None,
+ "Offset to Anchor table-from beginning of MarkArray table",
+ ),
+ ],
+ ),
+ #
+ # gsub
+ #
+ (
+ "GSUB",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the GSUB table- 0x00010000 or 0x00010001",
+ ),
+ (
+ "Offset",
+ "ScriptList",
+ None,
+ None,
+ "Offset to ScriptList table-from beginning of GSUB table",
+ ),
+ (
+ "Offset",
+ "FeatureList",
+ None,
+ None,
+ "Offset to FeatureList table-from beginning of GSUB table",
+ ),
+ (
+ "Offset",
+ "LookupList",
+ None,
+ None,
+ "Offset to LookupList table-from beginning of GSUB table",
+ ),
+ (
+ "LOffset",
+ "FeatureVariations",
+ None,
+ "Version >= 0x00010001",
+ "Offset to FeatureVariations table-from beginning of GSUB table",
+ ),
+ ],
+ ),
+ (
+ "SingleSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "DeltaGlyphID",
+ None,
+ None,
+ "Add to original GlyphID modulo 65536 to get substitute GlyphID",
+ ),
+ ],
+ ),
+ (
+ "SingleSubstFormat2",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of GlyphIDs in the Substitute array",
+ ),
+ (
+ "GlyphID",
+ "Substitute",
+ "GlyphCount",
+ 0,
+ "Array of substitute GlyphIDs-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "MultipleSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "SequenceCount",
+ None,
+ None,
+ "Number of Sequence table offsets in the Sequence array",
+ ),
+ (
+ "Offset",
+ "Sequence",
+ "SequenceCount",
+ 0,
+ "Array of offsets to Sequence tables-from beginning of Substitution table-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "Sequence",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of GlyphIDs in the Substitute array. This should always be greater than 0.",
+ ),
+ (
+ "GlyphID",
+ "Substitute",
+ "GlyphCount",
+ 0,
+ "String of GlyphIDs to substitute",
+ ),
+ ],
+ ),
+ (
+ "AlternateSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "AlternateSetCount",
+ None,
+ None,
+ "Number of AlternateSet tables",
+ ),
+ (
+ "Offset",
+ "AlternateSet",
+ "AlternateSetCount",
+ 0,
+ "Array of offsets to AlternateSet tables-from beginning of Substitution table-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "AlternateSet",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of GlyphIDs in the Alternate array",
+ ),
+ (
+ "GlyphID",
+ "Alternate",
+ "GlyphCount",
+ 0,
+ "Array of alternate GlyphIDs-in arbitrary order",
+ ),
+ ],
+ ),
+ (
+ "LigatureSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ ("uint16", "LigSetCount", None, None, "Number of LigatureSet tables"),
+ (
+ "Offset",
+ "LigatureSet",
+ "LigSetCount",
+ 0,
+ "Array of offsets to LigatureSet tables-from beginning of Substitution table-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "LigatureSet",
+ [
+ ("uint16", "LigatureCount", None, None, "Number of Ligature tables"),
+ (
+ "Offset",
+ "Ligature",
+ "LigatureCount",
+ 0,
+ "Array of offsets to Ligature tables-from beginning of LigatureSet table-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "Ligature",
+ [
+ ("GlyphID", "LigGlyph", None, None, "GlyphID of ligature to substitute"),
+ ("uint16", "CompCount", None, None, "Number of components in the ligature"),
+ (
+ "GlyphID",
+ "Component",
+ "CompCount",
+ -1,
+ "Array of component GlyphIDs-start with the second component-ordered in writing direction",
+ ),
+ ],
+ ),
+ (
+ "SubstLookupRecord",
+ [
+ (
+ "uint16",
+ "SequenceIndex",
+ None,
+ None,
+ "Index into current glyph sequence-first glyph = 0",
+ ),
+ (
+ "uint16",
+ "LookupListIndex",
+ None,
+ None,
+ "Lookup to apply to that position-zero-based",
+ ),
+ ],
+ ),
+ (
+ "ContextSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "SubRuleSetCount",
+ None,
+ None,
+ "Number of SubRuleSet tables-must equal GlyphCount in Coverage table",
+ ),
+ (
+ "Offset",
+ "SubRuleSet",
+ "SubRuleSetCount",
+ 0,
+ "Array of offsets to SubRuleSet tables-from beginning of Substitution table-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "SubRuleSet",
+ [
+ ("uint16", "SubRuleCount", None, None, "Number of SubRule tables"),
+ (
+ "Offset",
+ "SubRule",
+ "SubRuleCount",
+ 0,
+ "Array of offsets to SubRule tables-from beginning of SubRuleSet table-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "SubRule",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Total number of glyphs in input glyph sequence-includes the first glyph",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "GlyphID",
+ "Input",
+ "GlyphCount",
+ -1,
+ "Array of input GlyphIDs-start with second glyph",
+ ),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of SubstLookupRecords-in design order",
+ ),
+ ],
+ ),
+ (
+ "ContextSubstFormat2",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "Offset",
+ "ClassDef",
+ None,
+ None,
+ "Offset to glyph ClassDef table-from beginning of Substitution table",
+ ),
+ ("uint16", "SubClassSetCount", None, None, "Number of SubClassSet tables"),
+ (
+ "Offset",
+ "SubClassSet",
+ "SubClassSetCount",
+ 0,
+ "Array of offsets to SubClassSet tables-from beginning of Substitution table-ordered by class-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "SubClassSet",
+ [
+ (
+ "uint16",
+ "SubClassRuleCount",
+ None,
+ None,
+ "Number of SubClassRule tables",
+ ),
+ (
+ "Offset",
+ "SubClassRule",
+ "SubClassRuleCount",
+ 0,
+ "Array of offsets to SubClassRule tables-from beginning of SubClassSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "SubClassRule",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Total number of classes specified for the context in the rule-includes the first class",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "uint16",
+ "Class",
+ "GlyphCount",
+ -1,
+ "Array of classes-beginning with the second class-to be matched to the input glyph class sequence",
+ ),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of Substitution lookups-in design order",
+ ),
+ ],
+ ),
+ (
+ "ContextSubstFormat3",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 3"),
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of glyphs in the input glyph sequence",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "Offset",
+ "Coverage",
+ "GlyphCount",
+ 0,
+ "Array of offsets to Coverage table-from beginning of Substitution table-in glyph sequence order",
+ ),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of SubstLookupRecords-in design order",
+ ),
+ ],
+ ),
+ (
+ "ChainContextSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "ChainSubRuleSetCount",
+ None,
+ None,
+ "Number of ChainSubRuleSet tables-must equal GlyphCount in Coverage table",
+ ),
+ (
+ "Offset",
+ "ChainSubRuleSet",
+ "ChainSubRuleSetCount",
+ 0,
+ "Array of offsets to ChainSubRuleSet tables-from beginning of Substitution table-ordered by Coverage Index",
+ ),
+ ],
+ ),
+ (
+ "ChainSubRuleSet",
+ [
+ (
+ "uint16",
+ "ChainSubRuleCount",
+ None,
+ None,
+ "Number of ChainSubRule tables",
+ ),
+ (
+ "Offset",
+ "ChainSubRule",
+ "ChainSubRuleCount",
+ 0,
+ "Array of offsets to ChainSubRule tables-from beginning of ChainSubRuleSet table-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "ChainSubRule",
+ [
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)",
+ ),
+ (
+ "GlyphID",
+ "Backtrack",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of backtracking GlyphID's (to be matched before the input sequence)",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the input sequence (includes the first glyph)",
+ ),
+ (
+ "GlyphID",
+ "Input",
+ "InputGlyphCount",
+ -1,
+ "Array of input GlyphIDs (start with second glyph)",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)",
+ ),
+ (
+ "GlyphID",
+ "LookAhead",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of lookahead GlyphID's (to be matched after the input sequence)",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of SubstLookupRecords (in design order)",
+ ),
+ ],
+ ),
+ (
+ "ChainContextSubstFormat2",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 2"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table-from beginning of Substitution table",
+ ),
+ (
+ "Offset",
+ "BacktrackClassDef",
+ None,
+ None,
+ "Offset to glyph ClassDef table containing backtrack sequence data-from beginning of Substitution table",
+ ),
+ (
+ "Offset",
+ "InputClassDef",
+ None,
+ None,
+ "Offset to glyph ClassDef table containing input sequence data-from beginning of Substitution table",
+ ),
+ (
+ "Offset",
+ "LookAheadClassDef",
+ None,
+ None,
+ "Offset to glyph ClassDef table containing lookahead sequence data-from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "ChainSubClassSetCount",
+ None,
+ None,
+ "Number of ChainSubClassSet tables",
+ ),
+ (
+ "Offset",
+ "ChainSubClassSet",
+ "ChainSubClassSetCount",
+ 0,
+ "Array of offsets to ChainSubClassSet tables-from beginning of Substitution table-ordered by input class-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "ChainSubClassSet",
+ [
+ (
+ "uint16",
+ "ChainSubClassRuleCount",
+ None,
+ None,
+ "Number of ChainSubClassRule tables",
+ ),
+ (
+ "Offset",
+ "ChainSubClassRule",
+ "ChainSubClassRuleCount",
+ 0,
+ "Array of offsets to ChainSubClassRule tables-from beginning of ChainSubClassSet-ordered by preference",
+ ),
+ ],
+ ),
+ (
+ "ChainSubClassRule",
+ [
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Total number of glyphs in the backtrack sequence (number of glyphs to be matched before the first glyph)",
+ ),
+ (
+ "uint16",
+ "Backtrack",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of backtracking classes(to be matched before the input sequence)",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Total number of classes in the input sequence (includes the first class)",
+ ),
+ (
+ "uint16",
+ "Input",
+ "InputGlyphCount",
+ -1,
+ "Array of input classes(start with second class; to be matched with the input glyph sequence)",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Total number of classes in the look ahead sequence (number of classes to be matched after the input sequence)",
+ ),
+ (
+ "uint16",
+ "LookAhead",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of lookahead classes(to be matched after the input sequence)",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of SubstLookupRecords (in design order)",
+ ),
+ ],
+ ),
+ (
+ "ChainContextSubstFormat3",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 3"),
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Number of glyphs in the backtracking sequence",
+ ),
+ (
+ "Offset",
+ "BacktrackCoverage",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in backtracking sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "InputGlyphCount",
+ None,
+ None,
+ "Number of glyphs in input sequence",
+ ),
+ (
+ "Offset",
+ "InputCoverage",
+ "InputGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in input sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Number of glyphs in lookahead sequence",
+ ),
+ (
+ "Offset",
+ "LookAheadCoverage",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in lookahead sequence, in glyph sequence order",
+ ),
+ ("uint16", "SubstCount", None, None, "Number of SubstLookupRecords"),
+ (
+ "struct",
+ "SubstLookupRecord",
+ "SubstCount",
+ 0,
+ "Array of SubstLookupRecords, in design order",
+ ),
+ ],
+ ),
+ (
+ "ExtensionSubstFormat1",
+ [
+ ("uint16", "ExtFormat", None, None, "Format identifier. Set to 1."),
+ (
+ "uint16",
+ "ExtensionLookupType",
+ None,
+ None,
+ "Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).",
+ ),
+ (
+ "LOffset",
+ "ExtSubTable",
+ None,
+ None,
+ "Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)",
+ ),
+ ],
+ ),
+ (
+ "ReverseChainSingleSubstFormat1",
+ [
+ ("uint16", "SubstFormat", None, None, "Format identifier-format = 1"),
+ (
+ "Offset",
+ "Coverage",
+ None,
+ 0,
+ "Offset to Coverage table - from beginning of Substitution table",
+ ),
+ (
+ "uint16",
+ "BacktrackGlyphCount",
+ None,
+ None,
+ "Number of glyphs in the backtracking sequence",
+ ),
+ (
+ "Offset",
+ "BacktrackCoverage",
+ "BacktrackGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in backtracking sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "LookAheadGlyphCount",
+ None,
+ None,
+ "Number of glyphs in lookahead sequence",
+ ),
+ (
+ "Offset",
+ "LookAheadCoverage",
+ "LookAheadGlyphCount",
+ 0,
+ "Array of offsets to coverage tables in lookahead sequence, in glyph sequence order",
+ ),
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of GlyphIDs in the Substitute array",
+ ),
+ (
+ "GlyphID",
+ "Substitute",
+ "GlyphCount",
+ 0,
+ "Array of substitute GlyphIDs-ordered by Coverage index",
+ ),
+ ],
+ ),
+ #
+ # gdef
+ #
+ (
+ "GDEF",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the GDEF table- 0x00010000, 0x00010002, or 0x00010003",
+ ),
+ (
+ "Offset",
+ "GlyphClassDef",
+ None,
+ None,
+ "Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL)",
+ ),
+ (
+ "Offset",
+ "AttachList",
+ None,
+ None,
+ "Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL)",
+ ),
+ (
+ "Offset",
+ "LigCaretList",
+ None,
+ None,
+ "Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL)",
+ ),
+ (
+ "Offset",
+ "MarkAttachClassDef",
+ None,
+ None,
+ "Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL)",
+ ),
+ (
+ "Offset",
+ "MarkGlyphSetsDef",
+ None,
+ "Version >= 0x00010002",
+ "Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)",
+ ),
+ (
+ "LOffset",
+ "VarStore",
+ None,
+ "Version >= 0x00010003",
+ "Offset to variation store (may be NULL)",
+ ),
+ ],
+ ),
+ (
+ "AttachList",
+ [
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table - from beginning of AttachList table",
+ ),
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of glyphs with attachment points",
+ ),
+ (
+ "Offset",
+ "AttachPoint",
+ "GlyphCount",
+ 0,
+ "Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order",
+ ),
+ ],
+ ),
+ (
+ "AttachPoint",
+ [
+ (
+ "uint16",
+ "PointCount",
+ None,
+ None,
+ "Number of attachment points on this glyph",
+ ),
+ (
+ "uint16",
+ "PointIndex",
+ "PointCount",
+ 0,
+ "Array of contour point indices -in increasing numerical order",
+ ),
+ ],
+ ),
+ (
+ "LigCaretList",
+ [
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table - from beginning of LigCaretList table",
+ ),
+ ("uint16", "LigGlyphCount", None, None, "Number of ligature glyphs"),
+ (
+ "Offset",
+ "LigGlyph",
+ "LigGlyphCount",
+ 0,
+ "Array of offsets to LigGlyph tables-from beginning of LigCaretList table-in Coverage Index order",
+ ),
+ ],
+ ),
+ (
+ "LigGlyph",
+ [
+ (
+ "uint16",
+ "CaretCount",
+ None,
+ None,
+ "Number of CaretValues for this ligature (components - 1)",
+ ),
+ (
+ "Offset",
+ "CaretValue",
+ "CaretCount",
+ 0,
+ "Array of offsets to CaretValue tables-from beginning of LigGlyph table-in increasing coordinate order",
+ ),
+ ],
+ ),
+ (
+ "CaretValueFormat1",
+ [
+ ("uint16", "CaretValueFormat", None, None, "Format identifier-format = 1"),
+ ("int16", "Coordinate", None, None, "X or Y value, in design units"),
+ ],
+ ),
+ (
+ "CaretValueFormat2",
+ [
+ ("uint16", "CaretValueFormat", None, None, "Format identifier-format = 2"),
+ ("uint16", "CaretValuePoint", None, None, "Contour point index on glyph"),
+ ],
+ ),
+ (
+ "CaretValueFormat3",
+ [
+ ("uint16", "CaretValueFormat", None, None, "Format identifier-format = 3"),
+ ("int16", "Coordinate", None, None, "X or Y value, in design units"),
+ (
+ "Offset",
+ "DeviceTable",
+ None,
+ None,
+ "Offset to Device table for X or Y value-from beginning of CaretValue table",
+ ),
+ ],
+ ),
+ (
+ "MarkGlyphSetsDef",
+ [
+ ("uint16", "MarkSetTableFormat", None, None, "Format identifier == 1"),
+ ("uint16", "MarkSetCount", None, None, "Number of mark sets defined"),
+ (
+ "LOffset",
+ "Coverage",
+ "MarkSetCount",
+ 0,
+ "Array of offsets to mark set coverage tables.",
+ ),
+ ],
+ ),
+ #
+ # base
+ #
+ (
+ "BASE",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the BASE table-initially 0x00010000",
+ ),
+ (
+ "Offset",
+ "HorizAxis",
+ None,
+ None,
+ "Offset to horizontal Axis table-from beginning of BASE table-may be NULL",
+ ),
+ (
+ "Offset",
+ "VertAxis",
+ None,
+ None,
+ "Offset to vertical Axis table-from beginning of BASE table-may be NULL",
+ ),
+ (
+ "LOffset",
+ "VarStore",
+ None,
+ "Version >= 0x00010001",
+ "Offset to variation store (may be NULL)",
+ ),
+ ],
+ ),
+ (
+ "Axis",
+ [
+ (
+ "Offset",
+ "BaseTagList",
+ None,
+ None,
+ "Offset to BaseTagList table-from beginning of Axis table-may be NULL",
+ ),
+ (
+ "Offset",
+ "BaseScriptList",
+ None,
+ None,
+ "Offset to BaseScriptList table-from beginning of Axis table",
+ ),
+ ],
+ ),
+ (
+ "BaseTagList",
+ [
+ (
+ "uint16",
+ "BaseTagCount",
+ None,
+ None,
+ "Number of baseline identification tags in this text direction-may be zero (0)",
+ ),
+ (
+ "Tag",
+ "BaselineTag",
+ "BaseTagCount",
+ 0,
+ "Array of 4-byte baseline identification tags-must be in alphabetical order",
+ ),
+ ],
+ ),
+ (
+ "BaseScriptList",
+ [
+ (
+ "uint16",
+ "BaseScriptCount",
+ None,
+ None,
+ "Number of BaseScriptRecords defined",
+ ),
+ (
+ "struct",
+ "BaseScriptRecord",
+ "BaseScriptCount",
+ 0,
+ "Array of BaseScriptRecords-in alphabetical order by BaseScriptTag",
+ ),
+ ],
+ ),
+ (
+ "BaseScriptRecord",
+ [
+ ("Tag", "BaseScriptTag", None, None, "4-byte script identification tag"),
+ (
+ "Offset",
+ "BaseScript",
+ None,
+ None,
+ "Offset to BaseScript table-from beginning of BaseScriptList",
+ ),
+ ],
+ ),
+ (
+ "BaseScript",
+ [
+ (
+ "Offset",
+ "BaseValues",
+ None,
+ None,
+ "Offset to BaseValues table-from beginning of BaseScript table-may be NULL",
+ ),
+ (
+ "Offset",
+ "DefaultMinMax",
+ None,
+ None,
+ "Offset to MinMax table- from beginning of BaseScript table-may be NULL",
+ ),
+ (
+ "uint16",
+ "BaseLangSysCount",
+ None,
+ None,
+ "Number of BaseLangSysRecords defined-may be zero (0)",
+ ),
+ (
+ "struct",
+ "BaseLangSysRecord",
+ "BaseLangSysCount",
+ 0,
+ "Array of BaseLangSysRecords-in alphabetical order by BaseLangSysTag",
+ ),
+ ],
+ ),
+ (
+ "BaseLangSysRecord",
+ [
+ (
+ "Tag",
+ "BaseLangSysTag",
+ None,
+ None,
+ "4-byte language system identification tag",
+ ),
+ (
+ "Offset",
+ "MinMax",
+ None,
+ None,
+ "Offset to MinMax table-from beginning of BaseScript table",
+ ),
+ ],
+ ),
+ (
+ "BaseValues",
+ [
+ (
+ "uint16",
+ "DefaultIndex",
+ None,
+ None,
+ "Index number of default baseline for this script-equals index position of baseline tag in BaselineArray of the BaseTagList",
+ ),
+ (
+ "uint16",
+ "BaseCoordCount",
+ None,
+ None,
+ "Number of BaseCoord tables defined-should equal BaseTagCount in the BaseTagList",
+ ),
+ (
+ "Offset",
+ "BaseCoord",
+ "BaseCoordCount",
+ 0,
+ "Array of offsets to BaseCoord-from beginning of BaseValues table-order matches BaselineTag array in the BaseTagList",
+ ),
+ ],
+ ),
+ (
+ "MinMax",
+ [
+ (
+ "Offset",
+ "MinCoord",
+ None,
+ None,
+ "Offset to BaseCoord table-defines minimum extent value-from the beginning of MinMax table-may be NULL",
+ ),
+ (
+ "Offset",
+ "MaxCoord",
+ None,
+ None,
+ "Offset to BaseCoord table-defines maximum extent value-from the beginning of MinMax table-may be NULL",
+ ),
+ (
+ "uint16",
+ "FeatMinMaxCount",
+ None,
+ None,
+ "Number of FeatMinMaxRecords-may be zero (0)",
+ ),
+ (
+ "struct",
+ "FeatMinMaxRecord",
+ "FeatMinMaxCount",
+ 0,
+ "Array of FeatMinMaxRecords-in alphabetical order, by FeatureTableTag",
+ ),
+ ],
+ ),
+ (
+ "FeatMinMaxRecord",
+ [
+ (
+ "Tag",
+ "FeatureTableTag",
+ None,
+ None,
+ "4-byte feature identification tag-must match FeatureTag in FeatureList",
+ ),
+ (
+ "Offset",
+ "MinCoord",
+ None,
+ None,
+ "Offset to BaseCoord table-defines minimum extent value-from beginning of MinMax table-may be NULL",
+ ),
+ (
+ "Offset",
+ "MaxCoord",
+ None,
+ None,
+ "Offset to BaseCoord table-defines maximum extent value-from beginning of MinMax table-may be NULL",
+ ),
+ ],
+ ),
+ (
+ "BaseCoordFormat1",
+ [
+ ("uint16", "BaseCoordFormat", None, None, "Format identifier-format = 1"),
+ ("int16", "Coordinate", None, None, "X or Y value, in design units"),
+ ],
+ ),
+ (
+ "BaseCoordFormat2",
+ [
+ ("uint16", "BaseCoordFormat", None, None, "Format identifier-format = 2"),
+ ("int16", "Coordinate", None, None, "X or Y value, in design units"),
+ ("GlyphID", "ReferenceGlyph", None, None, "GlyphID of control glyph"),
+ (
+ "uint16",
+ "BaseCoordPoint",
+ None,
+ None,
+ "Index of contour point on the ReferenceGlyph",
+ ),
+ ],
+ ),
+ (
+ "BaseCoordFormat3",
+ [
+ ("uint16", "BaseCoordFormat", None, None, "Format identifier-format = 3"),
+ ("int16", "Coordinate", None, None, "X or Y value, in design units"),
+ (
+ "Offset",
+ "DeviceTable",
+ None,
+ None,
+ "Offset to Device table for X or Y value",
+ ),
+ ],
+ ),
+ #
+ # jstf
+ #
+ (
+ "JSTF",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the JSTF table-initially set to 0x00010000",
+ ),
+ (
+ "uint16",
+ "JstfScriptCount",
+ None,
+ None,
+ "Number of JstfScriptRecords in this table",
+ ),
+ (
+ "struct",
+ "JstfScriptRecord",
+ "JstfScriptCount",
+ 0,
+ "Array of JstfScriptRecords-in alphabetical order, by JstfScriptTag",
+ ),
+ ],
+ ),
+ (
+ "JstfScriptRecord",
+ [
+ ("Tag", "JstfScriptTag", None, None, "4-byte JstfScript identification"),
+ (
+ "Offset",
+ "JstfScript",
+ None,
+ None,
+ "Offset to JstfScript table-from beginning of JSTF Header",
+ ),
+ ],
+ ),
+ (
+ "JstfScript",
+ [
+ (
+ "Offset",
+ "ExtenderGlyph",
+ None,
+ None,
+ "Offset to ExtenderGlyph table-from beginning of JstfScript table-may be NULL",
+ ),
+ (
+ "Offset",
+ "DefJstfLangSys",
+ None,
+ None,
+ "Offset to Default JstfLangSys table-from beginning of JstfScript table-may be NULL",
+ ),
+ (
+ "uint16",
+ "JstfLangSysCount",
+ None,
+ None,
+ "Number of JstfLangSysRecords in this table- may be zero (0)",
+ ),
+ (
+ "struct",
+ "JstfLangSysRecord",
+ "JstfLangSysCount",
+ 0,
+ "Array of JstfLangSysRecords-in alphabetical order, by JstfLangSysTag",
+ ),
+ ],
+ ),
+ (
+ "JstfLangSysRecord",
+ [
+ ("Tag", "JstfLangSysTag", None, None, "4-byte JstfLangSys identifier"),
+ (
+ "Offset",
+ "JstfLangSys",
+ None,
+ None,
+ "Offset to JstfLangSys table-from beginning of JstfScript table",
+ ),
+ ],
+ ),
+ (
+ "ExtenderGlyph",
+ [
+ (
+ "uint16",
+ "GlyphCount",
+ None,
+ None,
+ "Number of Extender Glyphs in this script",
+ ),
+ (
+ "GlyphID",
+ "ExtenderGlyph",
+ "GlyphCount",
+ 0,
+ "GlyphIDs-in increasing numerical order",
+ ),
+ ],
+ ),
+ (
+ "JstfLangSys",
+ [
+ (
+ "uint16",
+ "JstfPriorityCount",
+ None,
+ None,
+ "Number of JstfPriority tables",
+ ),
+ (
+ "Offset",
+ "JstfPriority",
+ "JstfPriorityCount",
+ 0,
+ "Array of offsets to JstfPriority tables-from beginning of JstfLangSys table-in priority order",
+ ),
+ ],
+ ),
+ (
+ "JstfPriority",
+ [
+ (
+ "Offset",
+ "ShrinkageEnableGSUB",
+ None,
+ None,
+ "Offset to Shrinkage Enable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ShrinkageDisableGSUB",
+ None,
+ None,
+ "Offset to Shrinkage Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ShrinkageEnableGPOS",
+ None,
+ None,
+ "Offset to Shrinkage Enable JstfGPOSModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ShrinkageDisableGPOS",
+ None,
+ None,
+ "Offset to Shrinkage Disable JstfGPOSModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ShrinkageJstfMax",
+ None,
+ None,
+ "Offset to Shrinkage JstfMax table-from beginning of JstfPriority table -may be NULL",
+ ),
+ (
+ "Offset",
+ "ExtensionEnableGSUB",
+ None,
+ None,
+ "Offset to Extension Enable JstfGSUBModList table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ExtensionDisableGSUB",
+ None,
+ None,
+ "Offset to Extension Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ExtensionEnableGPOS",
+ None,
+ None,
+ "Offset to Extension Enable JstfGSUBModList table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ExtensionDisableGPOS",
+ None,
+ None,
+ "Offset to Extension Disable JstfGSUBModList table-from beginning of JstfPriority table-may be NULL",
+ ),
+ (
+ "Offset",
+ "ExtensionJstfMax",
+ None,
+ None,
+ "Offset to Extension JstfMax table-from beginning of JstfPriority table -may be NULL",
+ ),
+ ],
+ ),
+ (
+ "JstfGSUBModList",
+ [
+ (
+ "uint16",
+ "LookupCount",
+ None,
+ None,
+ "Number of lookups for this modification",
+ ),
+ (
+ "uint16",
+ "GSUBLookupIndex",
+ "LookupCount",
+ 0,
+ "Array of LookupIndex identifiers in GSUB-in increasing numerical order",
+ ),
+ ],
+ ),
+ (
+ "JstfGPOSModList",
+ [
+ (
+ "uint16",
+ "LookupCount",
+ None,
+ None,
+ "Number of lookups for this modification",
+ ),
+ (
+ "uint16",
+ "GPOSLookupIndex",
+ "LookupCount",
+ 0,
+ "Array of LookupIndex identifiers in GPOS-in increasing numerical order",
+ ),
+ ],
+ ),
+ (
+ "JstfMax",
+ [
+ (
+ "uint16",
+ "LookupCount",
+ None,
+ None,
+ "Number of lookup Indices for this modification",
+ ),
+ (
+ "Offset",
+ "Lookup",
+ "LookupCount",
+ 0,
+ "Array of offsets to GPOS-type lookup tables-from beginning of JstfMax table-in design order",
+ ),
+ ],
+ ),
+ #
+ # STAT
+ #
+ (
+ "STAT",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the table-initially set to 0x00010000, currently 0x00010002.",
+ ),
+ (
+ "uint16",
+ "DesignAxisRecordSize",
+ None,
+ None,
+ "Size in bytes of each design axis record",
+ ),
+ ("uint16", "DesignAxisCount", None, None, "Number of design axis records"),
+ (
+ "LOffsetTo(AxisRecordArray)",
+ "DesignAxisRecord",
+ None,
+ None,
+ "Offset in bytes from the beginning of the STAT table to the start of the design axes array",
+ ),
+ ("uint16", "AxisValueCount", None, None, "Number of axis value tables"),
+ (
+ "LOffsetTo(AxisValueArray)",
+ "AxisValueArray",
+ None,
+ None,
+ "Offset in bytes from the beginning of the STAT table to the start of the axes value offset array",
+ ),
+ (
+ "NameID",
+ "ElidedFallbackNameID",
+ None,
+ "Version >= 0x00010001",
+ "NameID to use when all style attributes are elided.",
+ ),
+ ],
+ ),
+ (
+ "AxisRecordArray",
+ [
+ ("AxisRecord", "Axis", "DesignAxisCount", 0, "Axis records"),
+ ],
+ ),
+ (
+ "AxisRecord",
+ [
+ (
+ "Tag",
+ "AxisTag",
+ None,
+ None,
+ "A tag identifying the axis of design variation",
+ ),
+ (
+ "NameID",
+ "AxisNameID",
+ None,
+ None,
+ 'The name ID for entries in the "name" table that provide a display string for this axis',
+ ),
+ (
+ "uint16",
+ "AxisOrdering",
+ None,
+ None,
+ "A value that applications can use to determine primary sorting of face names, or for ordering of descriptors when composing family or face names",
+ ),
+ (
+ "uint8",
+ "MoreBytes",
+ "DesignAxisRecordSize",
+ -8,
+ "Extra bytes. Set to empty array.",
+ ),
+ ],
+ ),
+ (
+ "AxisValueArray",
+ [
+ ("Offset", "AxisValue", "AxisValueCount", 0, "Axis values"),
+ ],
+ ),
+ (
+ "AxisValueFormat1",
+ [
+ ("uint16", "Format", None, None, "Format, = 1"),
+ (
+ "uint16",
+ "AxisIndex",
+ None,
+ None,
+ "Index into the axis record array identifying the axis of design variation to which the axis value record applies.",
+ ),
+ ("STATFlags", "Flags", None, None, "Flags."),
+ ("NameID", "ValueNameID", None, None, ""),
+ ("Fixed", "Value", None, None, ""),
+ ],
+ ),
+ (
+ "AxisValueFormat2",
+ [
+ ("uint16", "Format", None, None, "Format, = 2"),
+ (
+ "uint16",
+ "AxisIndex",
+ None,
+ None,
+ "Index into the axis record array identifying the axis of design variation to which the axis value record applies.",
+ ),
+ ("STATFlags", "Flags", None, None, "Flags."),
+ ("NameID", "ValueNameID", None, None, ""),
+ ("Fixed", "NominalValue", None, None, ""),
+ ("Fixed", "RangeMinValue", None, None, ""),
+ ("Fixed", "RangeMaxValue", None, None, ""),
+ ],
+ ),
+ (
+ "AxisValueFormat3",
+ [
+ ("uint16", "Format", None, None, "Format, = 3"),
+ (
+ "uint16",
+ "AxisIndex",
+ None,
+ None,
+ "Index into the axis record array identifying the axis of design variation to which the axis value record applies.",
+ ),
+ ("STATFlags", "Flags", None, None, "Flags."),
+ ("NameID", "ValueNameID", None, None, ""),
+ ("Fixed", "Value", None, None, ""),
+ ("Fixed", "LinkedValue", None, None, ""),
+ ],
+ ),
+ (
+ "AxisValueFormat4",
+ [
+ ("uint16", "Format", None, None, "Format, = 4"),
+ (
+ "uint16",
+ "AxisCount",
+ None,
+ None,
+ "The total number of axes contributing to this axis-values combination.",
+ ),
+ ("STATFlags", "Flags", None, None, "Flags."),
+ ("NameID", "ValueNameID", None, None, ""),
+ (
+ "struct",
+ "AxisValueRecord",
+ "AxisCount",
+ 0,
+ "Array of AxisValue records that provide the combination of axis values, one for each contributing axis. ",
+ ),
+ ],
+ ),
+ (
+ "AxisValueRecord",
+ [
+ (
+ "uint16",
+ "AxisIndex",
+ None,
+ None,
+ "Index into the axis record array identifying the axis of design variation to which the axis value record applies.",
+ ),
+ ("Fixed", "Value", None, None, "A numeric value for this attribute value."),
+ ],
+ ),
+ #
+ # Variation fonts
+ #
+ # GSUB/GPOS FeatureVariations
+ (
+ "FeatureVariations",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the table-initially set to 0x00010000",
+ ),
+ (
+ "uint32",
+ "FeatureVariationCount",
+ None,
+ None,
+ "Number of records in the FeatureVariationRecord array",
+ ),
+ (
+ "struct",
+ "FeatureVariationRecord",
+ "FeatureVariationCount",
+ 0,
+ "Array of FeatureVariationRecord",
+ ),
+ ],
+ ),
+ (
+ "FeatureVariationRecord",
+ [
+ (
+ "LOffset",
+ "ConditionSet",
+ None,
+ None,
+ "Offset to a ConditionSet table, from beginning of the FeatureVariations table.",
+ ),
+ (
+ "LOffset",
+ "FeatureTableSubstitution",
+ None,
+ None,
+ "Offset to a FeatureTableSubstitution table, from beginning of the FeatureVariations table",
+ ),
+ ],
+ ),
+ (
+ "ConditionList",
+ [
+ (
+ "uint32",
+ "ConditionCount",
+ None,
+ None,
+ "Number of condition tables in the ConditionTable array",
+ ),
+ (
+ "LOffset",
+ "ConditionTable",
+ "ConditionCount",
+ 0,
+ "Array of offset to condition tables, from the beginning of the ConditionList table.",
+ ),
+ ],
+ ),
+ (
+ "ConditionSet",
+ [
+ (
+ "uint16",
+ "ConditionCount",
+ None,
+ None,
+ "Number of condition tables in the ConditionTable array",
+ ),
+ (
+ "LOffset",
+ "ConditionTable",
+ "ConditionCount",
+ 0,
+ "Array of offset to condition tables, from the beginning of the ConditionSet table.",
+ ),
+ ],
+ ),
+ (
+ "ConditionTableFormat1",
+ [
+ ("uint16", "Format", None, None, "Format, = 1"),
+ (
+ "uint16",
+ "AxisIndex",
+ None,
+ None,
+ "Index for the variation axis within the fvar table, base 0.",
+ ),
+ (
+ "F2Dot14",
+ "FilterRangeMinValue",
+ None,
+ None,
+ "Minimum normalized axis value of the font variation instances that satisfy this condition.",
+ ),
+ (
+ "F2Dot14",
+ "FilterRangeMaxValue",
+ None,
+ None,
+ "Maximum value that satisfies this condition.",
+ ),
+ ],
+ ),
+ (
+ "ConditionTableFormat2",
+ [
+ ("uint16", "Format", None, None, "Format, = 2"),
+ (
+ "int16",
+ "DefaultValue",
+ None,
+ None,
+ "Value at default instance.",
+ ),
+ (
+ "uint32",
+ "VarIdx",
+ None,
+ None,
+ "Variation index to vary the value based on current designspace location.",
+ ),
+ ],
+ ),
+ (
+ "ConditionTableFormat3",
+ [
+ ("uint16", "Format", None, None, "Format, = 3"),
+ (
+ "uint8",
+ "ConditionCount",
+ None,
+ None,
+ "Index for the variation axis within the fvar table, base 0.",
+ ),
+ (
+ "Offset24",
+ "ConditionTable",
+ "ConditionCount",
+ 0,
+ "Array of condition tables for this conjunction (AND) expression.",
+ ),
+ ],
+ ),
+ (
+ "ConditionTableFormat4",
+ [
+ ("uint16", "Format", None, None, "Format, = 4"),
+ (
+ "uint8",
+ "ConditionCount",
+ None,
+ None,
+ "Index for the variation axis within the fvar table, base 0.",
+ ),
+ (
+ "Offset24",
+ "ConditionTable",
+ "ConditionCount",
+ 0,
+ "Array of condition tables for this disjunction (OR) expression.",
+ ),
+ ],
+ ),
+ (
+ "ConditionTableFormat5",
+ [
+ ("uint16", "Format", None, None, "Format, = 5"),
+ (
+ "Offset24",
+ "ConditionTable",
+ None,
+ None,
+ "Condition to negate.",
+ ),
+ ],
+ ),
+ (
+ "FeatureTableSubstitution",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the table-initially set to 0x00010000",
+ ),
+ (
+ "uint16",
+ "SubstitutionCount",
+ None,
+ None,
+ "Number of records in the FeatureVariationRecords array",
+ ),
+ (
+ "FeatureTableSubstitutionRecord",
+ "SubstitutionRecord",
+ "SubstitutionCount",
+ 0,
+ "Array of FeatureTableSubstitutionRecord",
+ ),
+ ],
+ ),
+ (
+ "FeatureTableSubstitutionRecord",
+ [
+ ("uint16", "FeatureIndex", None, None, "The feature table index to match."),
+ (
+ "LOffset",
+ "Feature",
+ None,
+ None,
+ "Offset to an alternate feature table, from start of the FeatureTableSubstitution table.",
+ ),
+ ],
+ ),
+ # VariationStore
+ (
+ "VarRegionAxis",
+ [
+ ("F2Dot14", "StartCoord", None, None, ""),
+ ("F2Dot14", "PeakCoord", None, None, ""),
+ ("F2Dot14", "EndCoord", None, None, ""),
+ ],
+ ),
+ (
+ "VarRegion",
+ [
+ ("struct", "VarRegionAxis", "RegionAxisCount", 0, ""),
+ ],
+ ),
+ (
+ "VarRegionList",
+ [
+ ("uint16", "RegionAxisCount", None, None, ""),
+ ("uint16", "RegionCount", None, None, ""),
+ ("VarRegion", "Region", "RegionCount", 0, ""),
+ ],
+ ),
+ (
+ "VarData",
+ [
+ ("uint16", "ItemCount", None, None, ""),
+ ("uint16", "NumShorts", None, None, ""),
+ ("uint16", "VarRegionCount", None, None, ""),
+ ("uint16", "VarRegionIndex", "VarRegionCount", 0, ""),
+ ("VarDataValue", "Item", "ItemCount", 0, ""),
+ ],
+ ),
+ (
+ "VarStore",
+ [
+ ("uint16", "Format", None, None, "Set to 1."),
+ ("LOffset", "VarRegionList", None, None, ""),
+ ("uint16", "VarDataCount", None, None, ""),
+ ("LOffset", "VarData", "VarDataCount", 0, ""),
+ ],
+ ),
+ # Variation helpers
+ (
+ "VarIdxMap",
+ [
+ ("uint16", "EntryFormat", None, None, ""), # Automatically computed
+ ("uint16", "MappingCount", None, None, ""), # Automatically computed
+ ("VarIdxMapValue", "mapping", "", 0, "Array of compressed data"),
+ ],
+ ),
+ (
+ "DeltaSetIndexMapFormat0",
+ [
+ ("uint8", "Format", None, None, "Format of the DeltaSetIndexMap = 0"),
+ ("uint8", "EntryFormat", None, None, ""), # Automatically computed
+ ("uint16", "MappingCount", None, None, ""), # Automatically computed
+ ("VarIdxMapValue", "mapping", "", 0, "Array of compressed data"),
+ ],
+ ),
+ (
+ "DeltaSetIndexMapFormat1",
+ [
+ ("uint8", "Format", None, None, "Format of the DeltaSetIndexMap = 1"),
+ ("uint8", "EntryFormat", None, None, ""), # Automatically computed
+ ("uint32", "MappingCount", None, None, ""), # Automatically computed
+ ("VarIdxMapValue", "mapping", "", 0, "Array of compressed data"),
+ ],
+ ),
+ # MultiVariationStore
+ (
+ "SparseVarRegionAxis",
+ [
+ ("uint16", "AxisIndex", None, None, ""),
+ ("F2Dot14", "StartCoord", None, None, ""),
+ ("F2Dot14", "PeakCoord", None, None, ""),
+ ("F2Dot14", "EndCoord", None, None, ""),
+ ],
+ ),
+ (
+ "SparseVarRegion",
+ [
+ ("uint16", "SparseRegionCount", None, None, ""),
+ ("struct", "SparseVarRegionAxis", "SparseRegionCount", 0, ""),
+ ],
+ ),
+ (
+ "SparseVarRegionList",
+ [
+ ("uint16", "RegionCount", None, None, ""),
+ ("LOffsetTo(SparseVarRegion)", "Region", "RegionCount", 0, ""),
+ ],
+ ),
+ (
+ "MultiVarData",
+ [
+ ("uint8", "Format", None, None, "Set to 1."),
+ ("uint16", "VarRegionCount", None, None, ""),
+ ("uint16", "VarRegionIndex", "VarRegionCount", 0, ""),
+ ("TupleList", "Item", "", 0, ""),
+ ],
+ ),
+ (
+ "MultiVarStore",
+ [
+ ("uint16", "Format", None, None, "Set to 1."),
+ ("LOffset", "SparseVarRegionList", None, None, ""),
+ ("uint16", "MultiVarDataCount", None, None, ""),
+ ("LOffset", "MultiVarData", "MultiVarDataCount", 0, ""),
+ ],
+ ),
+ # VariableComposites
+ (
+ "VARC",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the HVAR table-initially = 0x00010000",
+ ),
+ ("LOffset", "Coverage", None, None, ""),
+ ("LOffset", "MultiVarStore", None, None, "(may be NULL)"),
+ ("LOffset", "ConditionList", None, None, "(may be NULL)"),
+ ("LOffset", "AxisIndicesList", None, None, "(may be NULL)"),
+ ("LOffset", "VarCompositeGlyphs", None, None, ""),
+ ],
+ ),
+ (
+ "AxisIndicesList",
+ [
+ ("TupleList", "Item", "", 0, ""),
+ ],
+ ),
+ (
+ "VarCompositeGlyphs",
+ [
+ ("VarCompositeGlyphList", "VarCompositeGlyph", "", None, ""),
+ ],
+ ),
+ # Glyph advance variations
+ (
+ "HVAR",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the HVAR table-initially = 0x00010000",
+ ),
+ ("LOffset", "VarStore", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "AdvWidthMap", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "LsbMap", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "RsbMap", None, None, ""),
+ ],
+ ),
+ (
+ "VVAR",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the VVAR table-initially = 0x00010000",
+ ),
+ ("LOffset", "VarStore", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "AdvHeightMap", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "TsbMap", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "BsbMap", None, None, ""),
+ ("LOffsetTo(VarIdxMap)", "VOrgMap", None, None, "Vertical origin mapping."),
+ ],
+ ),
+ # Font-wide metrics variations
+ (
+ "MetricsValueRecord",
+ [
+ ("Tag", "ValueTag", None, None, "4-byte font-wide measure identifier"),
+ ("uint32", "VarIdx", None, None, "Combined outer-inner variation index"),
+ (
+ "uint8",
+ "MoreBytes",
+ "ValueRecordSize",
+ -8,
+ "Extra bytes. Set to empty array.",
+ ),
+ ],
+ ),
+ (
+ "MVAR",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the MVAR table-initially = 0x00010000",
+ ),
+ ("uint16", "Reserved", None, None, "Set to 0"),
+ ("uint16", "ValueRecordSize", None, None, ""),
+ ("uint16", "ValueRecordCount", None, None, ""),
+ ("Offset", "VarStore", None, None, ""),
+ ("MetricsValueRecord", "ValueRecord", "ValueRecordCount", 0, ""),
+ ],
+ ),
+ #
+ # math
+ #
+ (
+ "MATH",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the MATH table-initially set to 0x00010000.",
+ ),
+ (
+ "Offset",
+ "MathConstants",
+ None,
+ None,
+ "Offset to MathConstants table - from the beginning of MATH table.",
+ ),
+ (
+ "Offset",
+ "MathGlyphInfo",
+ None,
+ None,
+ "Offset to MathGlyphInfo table - from the beginning of MATH table.",
+ ),
+ (
+ "Offset",
+ "MathVariants",
+ None,
+ None,
+ "Offset to MathVariants table - from the beginning of MATH table.",
+ ),
+ ],
+ ),
+ (
+ "MathValueRecord",
+ [
+ ("int16", "Value", None, None, "The X or Y value in design units."),
+ (
+ "Offset",
+ "DeviceTable",
+ None,
+ None,
+ "Offset to the device table - from the beginning of parent table. May be NULL. Suggested format for device table is 1.",
+ ),
+ ],
+ ),
+ (
+ "MathConstants",
+ [
+ (
+ "int16",
+ "ScriptPercentScaleDown",
+ None,
+ None,
+ "Percentage of scaling down for script level 1. Suggested value: 80%.",
+ ),
+ (
+ "int16",
+ "ScriptScriptPercentScaleDown",
+ None,
+ None,
+ "Percentage of scaling down for script level 2 (ScriptScript). Suggested value: 60%.",
+ ),
+ (
+ "uint16",
+ "DelimitedSubFormulaMinHeight",
+ None,
+ None,
+ "Minimum height required for a delimited expression to be treated as a subformula. Suggested value: normal line height x1.5.",
+ ),
+ (
+ "uint16",
+ "DisplayOperatorMinHeight",
+ None,
+ None,
+ "Minimum height of n-ary operators (such as integral and summation) for formulas in display mode.",
+ ),
+ (
+ "MathValueRecord",
+ "MathLeading",
+ None,
+ None,
+ "White space to be left between math formulas to ensure proper line spacing. For example, for applications that treat line gap as a part of line ascender, formulas with ink going above (os2.sTypoAscender + os2.sTypoLineGap - MathLeading) or with ink going below os2.sTypoDescender will result in increasing line height.",
+ ),
+ ("MathValueRecord", "AxisHeight", None, None, "Axis height of the font."),
+ (
+ "MathValueRecord",
+ "AccentBaseHeight",
+ None,
+ None,
+ "Maximum (ink) height of accent base that does not require raising the accents. Suggested: x-height of the font (os2.sxHeight) plus any possible overshots.",
+ ),
+ (
+ "MathValueRecord",
+ "FlattenedAccentBaseHeight",
+ None,
+ None,
+ "Maximum (ink) height of accent base that does not require flattening the accents. Suggested: cap height of the font (os2.sCapHeight).",
+ ),
+ (
+ "MathValueRecord",
+ "SubscriptShiftDown",
+ None,
+ None,
+ "The standard shift down applied to subscript elements. Positive for moving in the downward direction. Suggested: os2.ySubscriptYOffset.",
+ ),
+ (
+ "MathValueRecord",
+ "SubscriptTopMax",
+ None,
+ None,
+ "Maximum allowed height of the (ink) top of subscripts that does not require moving subscripts further down. Suggested: 4/5 x-height.",
+ ),
+ (
+ "MathValueRecord",
+ "SubscriptBaselineDropMin",
+ None,
+ None,
+ "Minimum allowed drop of the baseline of subscripts relative to the (ink) bottom of the base. Checked for bases that are treated as a box or extended shape. Positive for subscript baseline dropped below the base bottom.",
+ ),
+ (
+ "MathValueRecord",
+ "SuperscriptShiftUp",
+ None,
+ None,
+ "Standard shift up applied to superscript elements. Suggested: os2.ySuperscriptYOffset.",
+ ),
+ (
+ "MathValueRecord",
+ "SuperscriptShiftUpCramped",
+ None,
+ None,
+ "Standard shift of superscripts relative to the base, in cramped style.",
+ ),
+ (
+ "MathValueRecord",
+ "SuperscriptBottomMin",
+ None,
+ None,
+ "Minimum allowed height of the (ink) bottom of superscripts that does not require moving subscripts further up. Suggested: 1/4 x-height.",
+ ),
+ (
+ "MathValueRecord",
+ "SuperscriptBaselineDropMax",
+ None,
+ None,
+ "Maximum allowed drop of the baseline of superscripts relative to the (ink) top of the base. Checked for bases that are treated as a box or extended shape. Positive for superscript baseline below the base top.",
+ ),
+ (
+ "MathValueRecord",
+ "SubSuperscriptGapMin",
+ None,
+ None,
+ "Minimum gap between the superscript and subscript ink. Suggested: 4x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "SuperscriptBottomMaxWithSubscript",
+ None,
+ None,
+ "The maximum level to which the (ink) bottom of superscript can be pushed to increase the gap between superscript and subscript, before subscript starts being moved down. Suggested: 4/5 x-height.",
+ ),
+ (
+ "MathValueRecord",
+ "SpaceAfterScript",
+ None,
+ None,
+ "Extra white space to be added after each subscript and superscript. Suggested: 0.5pt for a 12 pt font.",
+ ),
+ (
+ "MathValueRecord",
+ "UpperLimitGapMin",
+ None,
+ None,
+ "Minimum gap between the (ink) bottom of the upper limit, and the (ink) top of the base operator.",
+ ),
+ (
+ "MathValueRecord",
+ "UpperLimitBaselineRiseMin",
+ None,
+ None,
+ "Minimum distance between baseline of upper limit and (ink) top of the base operator.",
+ ),
+ (
+ "MathValueRecord",
+ "LowerLimitGapMin",
+ None,
+ None,
+ "Minimum gap between (ink) top of the lower limit, and (ink) bottom of the base operator.",
+ ),
+ (
+ "MathValueRecord",
+ "LowerLimitBaselineDropMin",
+ None,
+ None,
+ "Minimum distance between baseline of the lower limit and (ink) bottom of the base operator.",
+ ),
+ (
+ "MathValueRecord",
+ "StackTopShiftUp",
+ None,
+ None,
+ "Standard shift up applied to the top element of a stack.",
+ ),
+ (
+ "MathValueRecord",
+ "StackTopDisplayStyleShiftUp",
+ None,
+ None,
+ "Standard shift up applied to the top element of a stack in display style.",
+ ),
+ (
+ "MathValueRecord",
+ "StackBottomShiftDown",
+ None,
+ None,
+ "Standard shift down applied to the bottom element of a stack. Positive for moving in the downward direction.",
+ ),
+ (
+ "MathValueRecord",
+ "StackBottomDisplayStyleShiftDown",
+ None,
+ None,
+ "Standard shift down applied to the bottom element of a stack in display style. Positive for moving in the downward direction.",
+ ),
+ (
+ "MathValueRecord",
+ "StackGapMin",
+ None,
+ None,
+ "Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element. Suggested: 3x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "StackDisplayStyleGapMin",
+ None,
+ None,
+ "Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element in display style. Suggested: 7x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "StretchStackTopShiftUp",
+ None,
+ None,
+ "Standard shift up applied to the top element of the stretch stack.",
+ ),
+ (
+ "MathValueRecord",
+ "StretchStackBottomShiftDown",
+ None,
+ None,
+ "Standard shift down applied to the bottom element of the stretch stack. Positive for moving in the downward direction.",
+ ),
+ (
+ "MathValueRecord",
+ "StretchStackGapAboveMin",
+ None,
+ None,
+ "Minimum gap between the ink of the stretched element, and the (ink) bottom of the element above. Suggested: UpperLimitGapMin",
+ ),
+ (
+ "MathValueRecord",
+ "StretchStackGapBelowMin",
+ None,
+ None,
+ "Minimum gap between the ink of the stretched element, and the (ink) top of the element below. Suggested: LowerLimitGapMin.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionNumeratorShiftUp",
+ None,
+ None,
+ "Standard shift up applied to the numerator.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionNumeratorDisplayStyleShiftUp",
+ None,
+ None,
+ "Standard shift up applied to the numerator in display style. Suggested: StackTopDisplayStyleShiftUp.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionDenominatorShiftDown",
+ None,
+ None,
+ "Standard shift down applied to the denominator. Positive for moving in the downward direction.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionDenominatorDisplayStyleShiftDown",
+ None,
+ None,
+ "Standard shift down applied to the denominator in display style. Positive for moving in the downward direction. Suggested: StackBottomDisplayStyleShiftDown.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionNumeratorGapMin",
+ None,
+ None,
+ "Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar. Suggested: default rule thickness",
+ ),
+ (
+ "MathValueRecord",
+ "FractionNumDisplayStyleGapMin",
+ None,
+ None,
+ "Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionRuleThickness",
+ None,
+ None,
+ "Thickness of the fraction bar. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "FractionDenominatorGapMin",
+ None,
+ None,
+ "Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar. Suggested: default rule thickness",
+ ),
+ (
+ "MathValueRecord",
+ "FractionDenomDisplayStyleGapMin",
+ None,
+ None,
+ "Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "SkewedFractionHorizontalGap",
+ None,
+ None,
+ "Horizontal distance between the top and bottom elements of a skewed fraction.",
+ ),
+ (
+ "MathValueRecord",
+ "SkewedFractionVerticalGap",
+ None,
+ None,
+ "Vertical distance between the ink of the top and bottom elements of a skewed fraction.",
+ ),
+ (
+ "MathValueRecord",
+ "OverbarVerticalGap",
+ None,
+ None,
+ "Distance between the overbar and the (ink) top of he base. Suggested: 3x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "OverbarRuleThickness",
+ None,
+ None,
+ "Thickness of overbar. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "OverbarExtraAscender",
+ None,
+ None,
+ "Extra white space reserved above the overbar. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "UnderbarVerticalGap",
+ None,
+ None,
+ "Distance between underbar and (ink) bottom of the base. Suggested: 3x default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "UnderbarRuleThickness",
+ None,
+ None,
+ "Thickness of underbar. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "UnderbarExtraDescender",
+ None,
+ None,
+ "Extra white space reserved below the underbar. Always positive. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalVerticalGap",
+ None,
+ None,
+ "Space between the (ink) top of the expression and the bar over it. Suggested: 1 1/4 default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalDisplayStyleVerticalGap",
+ None,
+ None,
+ "Space between the (ink) top of the expression and the bar over it. Suggested: default rule thickness + 1/4 x-height.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalRuleThickness",
+ None,
+ None,
+ "Thickness of the radical rule. This is the thickness of the rule in designed or constructed radical signs. Suggested: default rule thickness.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalExtraAscender",
+ None,
+ None,
+ "Extra white space reserved above the radical. Suggested: RadicalRuleThickness.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalKernBeforeDegree",
+ None,
+ None,
+ "Extra horizontal kern before the degree of a radical, if such is present. Suggested: 5/18 of em.",
+ ),
+ (
+ "MathValueRecord",
+ "RadicalKernAfterDegree",
+ None,
+ None,
+ "Negative kern after the degree of a radical, if such is present. Suggested: 10/18 of em.",
+ ),
+ (
+ "uint16",
+ "RadicalDegreeBottomRaisePercent",
+ None,
+ None,
+ "Height of the bottom of the radical degree, if such is present, in proportion to the ascender of the radical sign. Suggested: 60%.",
+ ),
+ ],
+ ),
+ (
+ "MathGlyphInfo",
+ [
+ (
+ "Offset",
+ "MathItalicsCorrectionInfo",
+ None,
+ None,
+ "Offset to MathItalicsCorrectionInfo table - from the beginning of MathGlyphInfo table.",
+ ),
+ (
+ "Offset",
+ "MathTopAccentAttachment",
+ None,
+ None,
+ "Offset to MathTopAccentAttachment table - from the beginning of MathGlyphInfo table.",
+ ),
+ (
+ "Offset",
+ "ExtendedShapeCoverage",
+ None,
+ None,
+ "Offset to coverage table for Extended Shape glyphs - from the beginning of MathGlyphInfo table. When the left or right glyph of a box is an extended shape variant, the (ink) box (and not the default position defined by values in MathConstants table) should be used for vertical positioning purposes. May be NULL.",
+ ),
+ (
+ "Offset",
+ "MathKernInfo",
+ None,
+ None,
+ "Offset to MathKernInfo table - from the beginning of MathGlyphInfo table.",
+ ),
+ ],
+ ),
+ (
+ "MathItalicsCorrectionInfo",
+ [
+ (
+ "Offset",
+ "Coverage",
+ None,
+ None,
+ "Offset to Coverage table - from the beginning of MathItalicsCorrectionInfo table.",
+ ),
+ (
+ "uint16",
+ "ItalicsCorrectionCount",
+ None,
+ None,
+ "Number of italics correction values. Should coincide with the number of covered glyphs.",
+ ),
+ (
+ "MathValueRecord",
+ "ItalicsCorrection",
+ "ItalicsCorrectionCount",
+ 0,
+ "Array of MathValueRecords defining italics correction values for each covered glyph.",
+ ),
+ ],
+ ),
+ (
+ "MathTopAccentAttachment",
+ [
+ (
+ "Offset",
+ "TopAccentCoverage",
+ None,
+ None,
+ "Offset to Coverage table - from the beginning of MathTopAccentAttachment table.",
+ ),
+ (
+ "uint16",
+ "TopAccentAttachmentCount",
+ None,
+ None,
+ "Number of top accent attachment point values. Should coincide with the number of covered glyphs",
+ ),
+ (
+ "MathValueRecord",
+ "TopAccentAttachment",
+ "TopAccentAttachmentCount",
+ 0,
+ "Array of MathValueRecords defining top accent attachment points for each covered glyph",
+ ),
+ ],
+ ),
+ (
+ "MathKernInfo",
+ [
+ (
+ "Offset",
+ "MathKernCoverage",
+ None,
+ None,
+ "Offset to Coverage table - from the beginning of the MathKernInfo table.",
+ ),
+ ("uint16", "MathKernCount", None, None, "Number of MathKernInfoRecords."),
+ (
+ "MathKernInfoRecord",
+ "MathKernInfoRecords",
+ "MathKernCount",
+ 0,
+ "Array of MathKernInfoRecords, per-glyph information for mathematical positioning of subscripts and superscripts.",
+ ),
+ ],
+ ),
+ (
+ "MathKernInfoRecord",
+ [
+ (
+ "Offset",
+ "TopRightMathKern",
+ None,
+ None,
+ "Offset to MathKern table for top right corner - from the beginning of MathKernInfo table. May be NULL.",
+ ),
+ (
+ "Offset",
+ "TopLeftMathKern",
+ None,
+ None,
+ "Offset to MathKern table for the top left corner - from the beginning of MathKernInfo table. May be NULL.",
+ ),
+ (
+ "Offset",
+ "BottomRightMathKern",
+ None,
+ None,
+ "Offset to MathKern table for bottom right corner - from the beginning of MathKernInfo table. May be NULL.",
+ ),
+ (
+ "Offset",
+ "BottomLeftMathKern",
+ None,
+ None,
+ "Offset to MathKern table for bottom left corner - from the beginning of MathKernInfo table. May be NULL.",
+ ),
+ ],
+ ),
+ (
+ "MathKern",
+ [
+ (
+ "uint16",
+ "HeightCount",
+ None,
+ None,
+ "Number of heights on which the kern value changes.",
+ ),
+ (
+ "MathValueRecord",
+ "CorrectionHeight",
+ "HeightCount",
+ 0,
+ "Array of correction heights at which the kern value changes. Sorted by the height value in design units.",
+ ),
+ (
+ "MathValueRecord",
+ "KernValue",
+ "HeightCount",
+ 1,
+ "Array of kern values corresponding to heights. First value is the kern value for all heights less or equal than the first height in this table.Last value is the value to be applied for all heights greater than the last height in this table. Negative values are interpreted as move glyphs closer to each other.",
+ ),
+ ],
+ ),
+ (
+ "MathVariants",
+ [
+ (
+ "uint16",
+ "MinConnectorOverlap",
+ None,
+ None,
+ "Minimum overlap of connecting glyphs during glyph construction, in design units.",
+ ),
+ (
+ "Offset",
+ "VertGlyphCoverage",
+ None,
+ None,
+ "Offset to Coverage table - from the beginning of MathVariants table.",
+ ),
+ (
+ "Offset",
+ "HorizGlyphCoverage",
+ None,
+ None,
+ "Offset to Coverage table - from the beginning of MathVariants table.",
+ ),
+ (
+ "uint16",
+ "VertGlyphCount",
+ None,
+ None,
+ "Number of glyphs for which information is provided for vertically growing variants.",
+ ),
+ (
+ "uint16",
+ "HorizGlyphCount",
+ None,
+ None,
+ "Number of glyphs for which information is provided for horizontally growing variants.",
+ ),
+ (
+ "Offset",
+ "VertGlyphConstruction",
+ "VertGlyphCount",
+ 0,
+ "Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in vertical direction.",
+ ),
+ (
+ "Offset",
+ "HorizGlyphConstruction",
+ "HorizGlyphCount",
+ 0,
+ "Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in horizontal direction.",
+ ),
+ ],
+ ),
+ (
+ "MathGlyphConstruction",
+ [
+ (
+ "Offset",
+ "GlyphAssembly",
+ None,
+ None,
+ "Offset to GlyphAssembly table for this shape - from the beginning of MathGlyphConstruction table. May be NULL",
+ ),
+ (
+ "uint16",
+ "VariantCount",
+ None,
+ None,
+ "Count of glyph growing variants for this glyph.",
+ ),
+ (
+ "MathGlyphVariantRecord",
+ "MathGlyphVariantRecord",
+ "VariantCount",
+ 0,
+ "MathGlyphVariantRecords for alternative variants of the glyphs.",
+ ),
+ ],
+ ),
+ (
+ "MathGlyphVariantRecord",
+ [
+ ("GlyphID", "VariantGlyph", None, None, "Glyph ID for the variant."),
+ (
+ "uint16",
+ "AdvanceMeasurement",
+ None,
+ None,
+ "Advance width/height, in design units, of the variant, in the direction of requested glyph extension.",
+ ),
+ ],
+ ),
+ (
+ "GlyphAssembly",
+ [
+ (
+ "MathValueRecord",
+ "ItalicsCorrection",
+ None,
+ None,
+ "Italics correction of this GlyphAssembly. Should not depend on the assembly size.",
+ ),
+ ("uint16", "PartCount", None, None, "Number of parts in this assembly."),
+ (
+ "GlyphPartRecord",
+ "PartRecords",
+ "PartCount",
+ 0,
+ "Array of part records, from left to right and bottom to top.",
+ ),
+ ],
+ ),
+ (
+ "GlyphPartRecord",
+ [
+ ("GlyphID", "glyph", None, None, "Glyph ID for the part."),
+ (
+ "uint16",
+ "StartConnectorLength",
+ None,
+ None,
+ "Advance width/ height of the straight bar connector material, in design units, is at the beginning of the glyph, in the direction of the extension.",
+ ),
+ (
+ "uint16",
+ "EndConnectorLength",
+ None,
+ None,
+ "Advance width/ height of the straight bar connector material, in design units, is at the end of the glyph, in the direction of the extension.",
+ ),
+ (
+ "uint16",
+ "FullAdvance",
+ None,
+ None,
+ "Full advance width/height for this part, in the direction of the extension. In design units.",
+ ),
+ (
+ "uint16",
+ "PartFlags",
+ None,
+ None,
+ "Part qualifiers. PartFlags enumeration currently uses only one bit: 0x0001 fExtender: If set, the part can be skipped or repeated. 0xFFFE Reserved",
+ ),
+ ],
+ ),
+ ##
+ ## Apple Advanced Typography (AAT) tables
+ ##
+ (
+ "AATLookupSegment",
+ [
+ ("uint16", "lastGlyph", None, None, "Last glyph index in this segment."),
+ ("uint16", "firstGlyph", None, None, "First glyph index in this segment."),
+ (
+ "uint16",
+ "value",
+ None,
+ None,
+ "A 16-bit offset from the start of the table to the data.",
+ ),
+ ],
+ ),
+ #
+ # ankr
+ #
+ (
+ "ankr",
+ [
+ ("struct", "AnchorPoints", None, None, "Anchor points table."),
+ ],
+ ),
+ (
+ "AnchorPointsFormat0",
+ [
+ ("uint16", "Format", None, None, "Format of the anchor points table, = 0."),
+ ("uint16", "Flags", None, None, "Flags. Currenty unused, set to zero."),
+ (
+ "AATLookupWithDataOffset(AnchorGlyphData)",
+ "Anchors",
+ None,
+ None,
+ "Table of with anchor overrides for each glyph.",
+ ),
+ ],
+ ),
+ (
+ "AnchorGlyphData",
+ [
+ (
+ "uint32",
+ "AnchorPointCount",
+ None,
+ None,
+ "Number of anchor points for this glyph.",
+ ),
+ (
+ "struct",
+ "AnchorPoint",
+ "AnchorPointCount",
+ 0,
+ "Individual anchor points.",
+ ),
+ ],
+ ),
+ (
+ "AnchorPoint",
+ [
+ ("int16", "XCoordinate", None, None, "X coordinate of this anchor point."),
+ ("int16", "YCoordinate", None, None, "Y coordinate of this anchor point."),
+ ],
+ ),
+ #
+ # bsln
+ #
+ (
+ "bsln",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version number of the AAT baseline table (0x00010000 for the initial version).",
+ ),
+ ("struct", "Baseline", None, None, "Baseline table."),
+ ],
+ ),
+ (
+ "BaselineFormat0",
+ [
+ ("uint16", "Format", None, None, "Format of the baseline table, = 0."),
+ (
+ "uint16",
+ "DefaultBaseline",
+ None,
+ None,
+ "Default baseline value for all glyphs. This value can be from 0 through 31.",
+ ),
+ (
+ "uint16",
+ "Delta",
+ 32,
+ 0,
+ "These are the FUnit distance deltas from the font’s natural baseline to the other baselines used in the font. A total of 32 deltas must be assigned.",
+ ),
+ ],
+ ),
+ (
+ "BaselineFormat1",
+ [
+ ("uint16", "Format", None, None, "Format of the baseline table, = 1."),
+ (
+ "uint16",
+ "DefaultBaseline",
+ None,
+ None,
+ "Default baseline value for all glyphs. This value can be from 0 through 31.",
+ ),
+ (
+ "uint16",
+ "Delta",
+ 32,
+ 0,
+ "These are the FUnit distance deltas from the font’s natural baseline to the other baselines used in the font. A total of 32 deltas must be assigned.",
+ ),
+ (
+ "AATLookup(uint16)",
+ "BaselineValues",
+ None,
+ None,
+ "Lookup table that maps glyphs to their baseline values.",
+ ),
+ ],
+ ),
+ (
+ "BaselineFormat2",
+ [
+ ("uint16", "Format", None, None, "Format of the baseline table, = 1."),
+ (
+ "uint16",
+ "DefaultBaseline",
+ None,
+ None,
+ "Default baseline value for all glyphs. This value can be from 0 through 31.",
+ ),
+ (
+ "GlyphID",
+ "StandardGlyph",
+ None,
+ None,
+ "Glyph index of the glyph in this font to be used to set the baseline values. This glyph must contain a set of control points (whose numbers are contained in the following field) that determines baseline distances.",
+ ),
+ (
+ "uint16",
+ "ControlPoint",
+ 32,
+ 0,
+ "Array of 32 control point numbers, associated with the standard glyph. A value of 0xFFFF means there is no corresponding control point in the standard glyph.",
+ ),
+ ],
+ ),
+ (
+ "BaselineFormat3",
+ [
+ ("uint16", "Format", None, None, "Format of the baseline table, = 1."),
+ (
+ "uint16",
+ "DefaultBaseline",
+ None,
+ None,
+ "Default baseline value for all glyphs. This value can be from 0 through 31.",
+ ),
+ (
+ "GlyphID",
+ "StandardGlyph",
+ None,
+ None,
+ "Glyph index of the glyph in this font to be used to set the baseline values. This glyph must contain a set of control points (whose numbers are contained in the following field) that determines baseline distances.",
+ ),
+ (
+ "uint16",
+ "ControlPoint",
+ 32,
+ 0,
+ "Array of 32 control point numbers, associated with the standard glyph. A value of 0xFFFF means there is no corresponding control point in the standard glyph.",
+ ),
+ (
+ "AATLookup(uint16)",
+ "BaselineValues",
+ None,
+ None,
+ "Lookup table that maps glyphs to their baseline values.",
+ ),
+ ],
+ ),
+ #
+ # cidg
+ #
+ (
+ "cidg",
+ [
+ ("struct", "CIDGlyphMapping", None, None, "CID-to-glyph mapping table."),
+ ],
+ ),
+ (
+ "CIDGlyphMappingFormat0",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the CID-to-glyph mapping table, = 0.",
+ ),
+ ("uint16", "DataFormat", None, None, "Currenty unused, set to zero."),
+ ("uint32", "StructLength", None, None, "Size of the table in bytes."),
+ ("uint16", "Registry", None, None, "The registry ID."),
+ (
+ "char64",
+ "RegistryName",
+ None,
+ None,
+ "The registry name in ASCII; unused bytes should be set to 0.",
+ ),
+ ("uint16", "Order", None, None, "The order ID."),
+ (
+ "char64",
+ "OrderName",
+ None,
+ None,
+ "The order name in ASCII; unused bytes should be set to 0.",
+ ),
+ ("uint16", "SupplementVersion", None, None, "The supplement version."),
+ (
+ "CIDGlyphMap",
+ "Mapping",
+ None,
+ None,
+ "A mapping from CIDs to the glyphs in the font, starting with CID 0. If a CID from the identified collection has no glyph in the font, 0xFFFF is used",
+ ),
+ ],
+ ),
+ #
+ # feat
+ #
+ (
+ "feat",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the feat table-initially set to 0x00010000.",
+ ),
+ ("FeatureNames", "FeatureNames", None, None, "The feature names."),
+ ],
+ ),
+ (
+ "FeatureNames",
+ [
+ (
+ "uint16",
+ "FeatureNameCount",
+ None,
+ None,
+ "Number of entries in the feature name array.",
+ ),
+ ("uint16", "Reserved1", None, None, "Reserved (set to zero)."),
+ ("uint32", "Reserved2", None, None, "Reserved (set to zero)."),
+ (
+ "FeatureName",
+ "FeatureName",
+ "FeatureNameCount",
+ 0,
+ "The feature name array.",
+ ),
+ ],
+ ),
+ (
+ "FeatureName",
+ [
+ ("uint16", "FeatureType", None, None, "Feature type."),
+ (
+ "uint16",
+ "SettingsCount",
+ None,
+ None,
+ "The number of records in the setting name array.",
+ ),
+ (
+ "LOffset",
+ "Settings",
+ None,
+ None,
+ "Offset to setting table for this feature.",
+ ),
+ (
+ "uint16",
+ "FeatureFlags",
+ None,
+ None,
+ "Single-bit flags associated with the feature type.",
+ ),
+ (
+ "NameID",
+ "FeatureNameID",
+ None,
+ None,
+ "The name table index for the feature name.",
+ ),
+ ],
+ ),
+ (
+ "Settings",
+ [
+ ("Setting", "Setting", "SettingsCount", 0, "The setting array."),
+ ],
+ ),
+ (
+ "Setting",
+ [
+ ("uint16", "SettingValue", None, None, "The setting."),
+ (
+ "NameID",
+ "SettingNameID",
+ None,
+ None,
+ "The name table index for the setting name.",
+ ),
+ ],
+ ),
+ #
+ # gcid
+ #
+ (
+ "gcid",
+ [
+ ("struct", "GlyphCIDMapping", None, None, "Glyph to CID mapping table."),
+ ],
+ ),
+ (
+ "GlyphCIDMappingFormat0",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the glyph-to-CID mapping table, = 0.",
+ ),
+ ("uint16", "DataFormat", None, None, "Currenty unused, set to zero."),
+ ("uint32", "StructLength", None, None, "Size of the table in bytes."),
+ ("uint16", "Registry", None, None, "The registry ID."),
+ (
+ "char64",
+ "RegistryName",
+ None,
+ None,
+ "The registry name in ASCII; unused bytes should be set to 0.",
+ ),
+ ("uint16", "Order", None, None, "The order ID."),
+ (
+ "char64",
+ "OrderName",
+ None,
+ None,
+ "The order name in ASCII; unused bytes should be set to 0.",
+ ),
+ ("uint16", "SupplementVersion", None, None, "The supplement version."),
+ (
+ "GlyphCIDMap",
+ "Mapping",
+ None,
+ None,
+ "The CIDs for the glyphs in the font, starting with glyph 0. If a glyph does not correspond to a CID in the identified collection, 0xFFFF is used",
+ ),
+ ],
+ ),
+ #
+ # lcar
+ #
+ (
+ "lcar",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version number of the ligature caret table (0x00010000 for the initial version).",
+ ),
+ ("struct", "LigatureCarets", None, None, "Ligature carets table."),
+ ],
+ ),
+ (
+ "LigatureCaretsFormat0",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the ligature caret table. Format 0 indicates division points are distances in font units, Format 1 indicates division points are indexes of control points.",
+ ),
+ (
+ "AATLookup(LigCaretDistances)",
+ "Carets",
+ None,
+ None,
+ "Lookup table associating ligature glyphs with their caret positions, in font unit distances.",
+ ),
+ ],
+ ),
+ (
+ "LigatureCaretsFormat1",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the ligature caret table. Format 0 indicates division points are distances in font units, Format 1 indicates division points are indexes of control points.",
+ ),
+ (
+ "AATLookup(LigCaretPoints)",
+ "Carets",
+ None,
+ None,
+ "Lookup table associating ligature glyphs with their caret positions, as control points.",
+ ),
+ ],
+ ),
+ (
+ "LigCaretDistances",
+ [
+ ("uint16", "DivsionPointCount", None, None, "Number of division points."),
+ (
+ "int16",
+ "DivisionPoint",
+ "DivsionPointCount",
+ 0,
+ "Distance in font units through which a subdivision is made orthogonally to the baseline.",
+ ),
+ ],
+ ),
+ (
+ "LigCaretPoints",
+ [
+ ("uint16", "DivsionPointCount", None, None, "Number of division points."),
+ (
+ "int16",
+ "DivisionPoint",
+ "DivsionPointCount",
+ 0,
+ "The number of the control point through which a subdivision is made orthogonally to the baseline.",
+ ),
+ ],
+ ),
+ #
+ # mort
+ #
+ (
+ "mort",
+ [
+ ("Version", "Version", None, None, "Version of the mort table."),
+ (
+ "uint32",
+ "MorphChainCount",
+ None,
+ None,
+ "Number of metamorphosis chains.",
+ ),
+ (
+ "MortChain",
+ "MorphChain",
+ "MorphChainCount",
+ 0,
+ "Array of metamorphosis chains.",
+ ),
+ ],
+ ),
+ (
+ "MortChain",
+ [
+ (
+ "Flags32",
+ "DefaultFlags",
+ None,
+ None,
+ "The default specification for subtables.",
+ ),
+ (
+ "uint32",
+ "StructLength",
+ None,
+ None,
+ "Total byte count, including this header; must be a multiple of 4.",
+ ),
+ (
+ "uint16",
+ "MorphFeatureCount",
+ None,
+ None,
+ "Number of metamorphosis feature entries.",
+ ),
+ (
+ "uint16",
+ "MorphSubtableCount",
+ None,
+ None,
+ "The number of subtables in the chain.",
+ ),
+ (
+ "struct",
+ "MorphFeature",
+ "MorphFeatureCount",
+ 0,
+ "Array of metamorphosis features.",
+ ),
+ (
+ "MortSubtable",
+ "MorphSubtable",
+ "MorphSubtableCount",
+ 0,
+ "Array of metamorphosis subtables.",
+ ),
+ ],
+ ),
+ (
+ "MortSubtable",
+ [
+ (
+ "uint16",
+ "StructLength",
+ None,
+ None,
+ "Total subtable length, including this header.",
+ ),
+ (
+ "uint8",
+ "CoverageFlags",
+ None,
+ None,
+ "Most significant byte of coverage flags.",
+ ),
+ ("uint8", "MorphType", None, None, "Subtable type."),
+ (
+ "Flags32",
+ "SubFeatureFlags",
+ None,
+ None,
+ "The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero).",
+ ),
+ ("SubStruct", "SubStruct", None, None, "SubTable."),
+ ],
+ ),
+ #
+ # morx
+ #
+ (
+ "morx",
+ [
+ ("uint16", "Version", None, None, "Version of the morx table."),
+ ("uint16", "Reserved", None, None, "Reserved (set to zero)."),
+ (
+ "uint32",
+ "MorphChainCount",
+ None,
+ None,
+ "Number of extended metamorphosis chains.",
+ ),
+ (
+ "MorxChain",
+ "MorphChain",
+ "MorphChainCount",
+ 0,
+ "Array of extended metamorphosis chains.",
+ ),
+ ],
+ ),
+ (
+ "MorxChain",
+ [
+ (
+ "Flags32",
+ "DefaultFlags",
+ None,
+ None,
+ "The default specification for subtables.",
+ ),
+ (
+ "uint32",
+ "StructLength",
+ None,
+ None,
+ "Total byte count, including this header; must be a multiple of 4.",
+ ),
+ (
+ "uint32",
+ "MorphFeatureCount",
+ None,
+ None,
+ "Number of feature subtable entries.",
+ ),
+ (
+ "uint32",
+ "MorphSubtableCount",
+ None,
+ None,
+ "The number of subtables in the chain.",
+ ),
+ (
+ "MorphFeature",
+ "MorphFeature",
+ "MorphFeatureCount",
+ 0,
+ "Array of metamorphosis features.",
+ ),
+ (
+ "MorxSubtable",
+ "MorphSubtable",
+ "MorphSubtableCount",
+ 0,
+ "Array of extended metamorphosis subtables.",
+ ),
+ ],
+ ),
+ (
+ "MorphFeature",
+ [
+ ("uint16", "FeatureType", None, None, "The type of feature."),
+ (
+ "uint16",
+ "FeatureSetting",
+ None,
+ None,
+ "The feature's setting (aka selector).",
+ ),
+ (
+ "Flags32",
+ "EnableFlags",
+ None,
+ None,
+ "Flags for the settings that this feature and setting enables.",
+ ),
+ (
+ "Flags32",
+ "DisableFlags",
+ None,
+ None,
+ "Complement of flags for the settings that this feature and setting disable.",
+ ),
+ ],
+ ),
+ # Apple TrueType Reference Manual, chapter “The ‘morx’ table”,
+ # section “Metamorphosis Subtables”.
+ # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+ (
+ "MorxSubtable",
+ [
+ (
+ "uint32",
+ "StructLength",
+ None,
+ None,
+ "Total subtable length, including this header.",
+ ),
+ (
+ "uint8",
+ "CoverageFlags",
+ None,
+ None,
+ "Most significant byte of coverage flags.",
+ ),
+ ("uint16", "Reserved", None, None, "Unused."),
+ ("uint8", "MorphType", None, None, "Subtable type."),
+ (
+ "Flags32",
+ "SubFeatureFlags",
+ None,
+ None,
+ "The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero).",
+ ),
+ ("SubStruct", "SubStruct", None, None, "SubTable."),
+ ],
+ ),
+ (
+ "StateHeader",
+ [
+ (
+ "uint32",
+ "ClassCount",
+ None,
+ None,
+ "Number of classes, which is the number of 16-bit entry indices in a single line in the state array.",
+ ),
+ (
+ "uint32",
+ "MorphClass",
+ None,
+ None,
+ "Offset from the start of this state table header to the start of the class table.",
+ ),
+ (
+ "uint32",
+ "StateArrayOffset",
+ None,
+ None,
+ "Offset from the start of this state table header to the start of the state array.",
+ ),
+ (
+ "uint32",
+ "EntryTableOffset",
+ None,
+ None,
+ "Offset from the start of this state table header to the start of the entry table.",
+ ),
+ ],
+ ),
+ (
+ "RearrangementMorph",
+ [
+ (
+ "STXHeader(RearrangementMorphAction)",
+ "StateTable",
+ None,
+ None,
+ "Finite-state transducer table for indic rearrangement.",
+ ),
+ ],
+ ),
+ (
+ "ContextualMorph",
+ [
+ (
+ "STXHeader(ContextualMorphAction)",
+ "StateTable",
+ None,
+ None,
+ "Finite-state transducer for contextual glyph substitution.",
+ ),
+ ],
+ ),
+ (
+ "LigatureMorph",
+ [
+ (
+ "STXHeader(LigatureMorphAction)",
+ "StateTable",
+ None,
+ None,
+ "Finite-state transducer for ligature substitution.",
+ ),
+ ],
+ ),
+ (
+ "NoncontextualMorph",
+ [
+ (
+ "AATLookup(GlyphID)",
+ "Substitution",
+ None,
+ None,
+ "The noncontextual glyph substitution table.",
+ ),
+ ],
+ ),
+ (
+ "InsertionMorph",
+ [
+ (
+ "STXHeader(InsertionMorphAction)",
+ "StateTable",
+ None,
+ None,
+ "Finite-state transducer for glyph insertion.",
+ ),
+ ],
+ ),
+ (
+ "MorphClass",
+ [
+ (
+ "uint16",
+ "FirstGlyph",
+ None,
+ None,
+ "Glyph index of the first glyph in the class table.",
+ ),
+ # ('uint16', 'GlyphCount', None, None, 'Number of glyphs in class table.'),
+ # ('uint8', 'GlyphClass', 'GlyphCount', 0, 'The class codes (indexed by glyph index minus firstGlyph). Class codes range from 0 to the value of stateSize minus 1.'),
+ ],
+ ),
+ # If the 'morx' table version is 3 or greater, then the last subtable in the chain is followed by a subtableGlyphCoverageArray, as described below.
+ # ('Offset', 'MarkGlyphSetsDef', None, 'round(Version*0x10000) >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
+ #
+ # prop
+ #
+ (
+ "prop",
+ [
+ (
+ "Fixed",
+ "Version",
+ None,
+ None,
+ "Version number of the AAT glyphs property table. Version 1.0 is the initial table version. Version 2.0, which is recognized by macOS 8.5 and later, adds support for the “attaches on right” bit. Version 3.0, which gets recognized by macOS X and iOS, adds support for the additional directional properties defined in Unicode 3.0.",
+ ),
+ ("struct", "GlyphProperties", None, None, "Glyph properties."),
+ ],
+ ),
+ (
+ "GlyphPropertiesFormat0",
+ [
+ ("uint16", "Format", None, None, "Format, = 0."),
+ (
+ "uint16",
+ "DefaultProperties",
+ None,
+ None,
+ "Default properties applied to a glyph. Since there is no lookup table in prop format 0, the default properties get applied to every glyph in the font.",
+ ),
+ ],
+ ),
+ (
+ "GlyphPropertiesFormat1",
+ [
+ ("uint16", "Format", None, None, "Format, = 1."),
+ (
+ "uint16",
+ "DefaultProperties",
+ None,
+ None,
+ "Default properties applied to a glyph if that glyph is not present in the Properties lookup table.",
+ ),
+ (
+ "AATLookup(uint16)",
+ "Properties",
+ None,
+ None,
+ "Lookup data associating glyphs with their properties.",
+ ),
+ ],
+ ),
+ #
+ # opbd
+ #
+ (
+ "opbd",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version number of the optical bounds table (0x00010000 for the initial version).",
+ ),
+ ("struct", "OpticalBounds", None, None, "Optical bounds table."),
+ ],
+ ),
+ (
+ "OpticalBoundsFormat0",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the optical bounds table, = 0.",
+ ),
+ (
+ "AATLookup(OpticalBoundsDeltas)",
+ "OpticalBoundsDeltas",
+ None,
+ None,
+ "Lookup table associating glyphs with their optical bounds, given as deltas in font units.",
+ ),
+ ],
+ ),
+ (
+ "OpticalBoundsFormat1",
+ [
+ (
+ "uint16",
+ "Format",
+ None,
+ None,
+ "Format of the optical bounds table, = 1.",
+ ),
+ (
+ "AATLookup(OpticalBoundsPoints)",
+ "OpticalBoundsPoints",
+ None,
+ None,
+ "Lookup table associating glyphs with their optical bounds, given as references to control points.",
+ ),
+ ],
+ ),
+ (
+ "OpticalBoundsDeltas",
+ [
+ (
+ "int16",
+ "Left",
+ None,
+ None,
+ "Delta value for the left-side optical edge.",
+ ),
+ ("int16", "Top", None, None, "Delta value for the top-side optical edge."),
+ (
+ "int16",
+ "Right",
+ None,
+ None,
+ "Delta value for the right-side optical edge.",
+ ),
+ (
+ "int16",
+ "Bottom",
+ None,
+ None,
+ "Delta value for the bottom-side optical edge.",
+ ),
+ ],
+ ),
+ (
+ "OpticalBoundsPoints",
+ [
+ (
+ "int16",
+ "Left",
+ None,
+ None,
+ "Control point index for the left-side optical edge, or -1 if this glyph has none.",
+ ),
+ (
+ "int16",
+ "Top",
+ None,
+ None,
+ "Control point index for the top-side optical edge, or -1 if this glyph has none.",
+ ),
+ (
+ "int16",
+ "Right",
+ None,
+ None,
+ "Control point index for the right-side optical edge, or -1 if this glyph has none.",
+ ),
+ (
+ "int16",
+ "Bottom",
+ None,
+ None,
+ "Control point index for the bottom-side optical edge, or -1 if this glyph has none.",
+ ),
+ ],
+ ),
+ #
+ # TSIC
+ #
+ (
+ "TSIC",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of table initially set to 0x00010000.",
+ ),
+ ("uint16", "Flags", None, None, "TSIC flags - set to 0"),
+ ("uint16", "AxisCount", None, None, "Axis count from fvar"),
+ ("uint16", "RecordCount", None, None, "TSIC record count"),
+ ("uint16", "Reserved", None, None, "Set to 0"),
+ ("Tag", "AxisArray", "AxisCount", 0, "Array of axis tags in fvar order"),
+ (
+ "LocationRecord",
+ "RecordLocations",
+ "RecordCount",
+ 0,
+ "Location in variation space of TSIC record",
+ ),
+ ("TSICRecord", "Record", "RecordCount", 0, "Array of TSIC records"),
+ ],
+ ),
+ (
+ "LocationRecord",
+ [
+ ("F2Dot14", "Axis", "AxisCount", 0, "Axis record"),
+ ],
+ ),
+ (
+ "TSICRecord",
+ [
+ ("uint16", "Flags", None, None, "Record flags - set to 0"),
+ ("uint16", "NumCVTEntries", None, None, "Number of CVT number value pairs"),
+ ("uint16", "NameLength", None, None, "Length of optional user record name"),
+ ("uint16", "NameArray", "NameLength", 0, "Unicode 16 name"),
+ ("uint16", "CVTArray", "NumCVTEntries", 0, "CVT number array"),
+ ("int16", "CVTValueArray", "NumCVTEntries", 0, "CVT value"),
+ ],
+ ),
+ #
+ # COLR
+ #
+ (
+ "COLR",
+ [
+ ("uint16", "Version", None, None, "Table version number (starts at 0)."),
+ (
+ "uint16",
+ "BaseGlyphRecordCount",
+ None,
+ None,
+ "Number of Base Glyph Records.",
+ ),
+ (
+ "LOffset",
+ "BaseGlyphRecordArray",
+ None,
+ None,
+ "Offset (from beginning of COLR table) to Base Glyph records.",
+ ),
+ (
+ "LOffset",
+ "LayerRecordArray",
+ None,
+ None,
+ "Offset (from beginning of COLR table) to Layer Records.",
+ ),
+ ("uint16", "LayerRecordCount", None, None, "Number of Layer Records."),
+ (
+ "LOffset",
+ "BaseGlyphList",
+ None,
+ "Version >= 1",
+ "Offset (from beginning of COLR table) to array of Version-1 Base Glyph records.",
+ ),
+ (
+ "LOffset",
+ "LayerList",
+ None,
+ "Version >= 1",
+ "Offset (from beginning of COLR table) to LayerList.",
+ ),
+ (
+ "LOffset",
+ "ClipList",
+ None,
+ "Version >= 1",
+ "Offset to ClipList table (may be NULL)",
+ ),
+ (
+ "LOffsetTo(DeltaSetIndexMap)",
+ "VarIndexMap",
+ None,
+ "Version >= 1",
+ "Offset to DeltaSetIndexMap table (may be NULL)",
+ ),
+ (
+ "LOffset",
+ "VarStore",
+ None,
+ "Version >= 1",
+ "Offset to variation store (may be NULL)",
+ ),
+ ],
+ ),
+ (
+ "BaseGlyphRecordArray",
+ [
+ (
+ "BaseGlyphRecord",
+ "BaseGlyphRecord",
+ "BaseGlyphRecordCount",
+ 0,
+ "Base Glyph records.",
+ ),
+ ],
+ ),
+ (
+ "BaseGlyphRecord",
+ [
+ (
+ "GlyphID",
+ "BaseGlyph",
+ None,
+ None,
+ "Glyph ID of reference glyph. This glyph is for reference only and is not rendered for color.",
+ ),
+ (
+ "uint16",
+ "FirstLayerIndex",
+ None,
+ None,
+ "Index (from beginning of the Layer Records) to the layer record. There will be numLayers consecutive entries for this base glyph.",
+ ),
+ (
+ "uint16",
+ "NumLayers",
+ None,
+ None,
+ "Number of color layers associated with this glyph.",
+ ),
+ ],
+ ),
+ (
+ "LayerRecordArray",
+ [
+ ("LayerRecord", "LayerRecord", "LayerRecordCount", 0, "Layer records."),
+ ],
+ ),
+ (
+ "LayerRecord",
+ [
+ (
+ "GlyphID",
+ "LayerGlyph",
+ None,
+ None,
+ "Glyph ID of layer glyph (must be in z-order from bottom to top).",
+ ),
+ (
+ "uint16",
+ "PaletteIndex",
+ None,
+ None,
+ "Index value to use with a selected color palette.",
+ ),
+ ],
+ ),
+ (
+ "BaseGlyphList",
+ [
+ (
+ "uint32",
+ "BaseGlyphCount",
+ None,
+ None,
+ "Number of Version-1 Base Glyph records",
+ ),
+ (
+ "struct",
+ "BaseGlyphPaintRecord",
+ "BaseGlyphCount",
+ 0,
+ "Array of Version-1 Base Glyph records",
+ ),
+ ],
+ ),
+ (
+ "BaseGlyphPaintRecord",
+ [
+ ("GlyphID", "BaseGlyph", None, None, "Glyph ID of reference glyph."),
+ (
+ "LOffset",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of BaseGlyphPaintRecord) to Paint, typically a PaintColrLayers.",
+ ),
+ ],
+ ),
+ (
+ "LayerList",
+ [
+ ("uint32", "LayerCount", None, None, "Number of Version-1 Layers"),
+ (
+ "LOffset",
+ "Paint",
+ "LayerCount",
+ 0,
+ "Array of offsets to Paint tables, from the start of the LayerList table.",
+ ),
+ ],
+ ),
+ (
+ "ClipListFormat1",
+ [
+ (
+ "uint8",
+ "Format",
+ None,
+ None,
+ "Format for ClipList with 16bit glyph IDs: 1",
+ ),
+ ("uint32", "ClipCount", None, None, "Number of Clip records."),
+ (
+ "struct",
+ "ClipRecord",
+ "ClipCount",
+ 0,
+ "Array of Clip records sorted by glyph ID.",
+ ),
+ ],
+ ),
+ (
+ "ClipRecord",
+ [
+ ("uint16", "StartGlyphID", None, None, "First glyph ID in the range."),
+ ("uint16", "EndGlyphID", None, None, "Last glyph ID in the range."),
+ ("Offset24", "ClipBox", None, None, "Offset to a ClipBox table."),
+ ],
+ ),
+ (
+ "ClipBoxFormat1",
+ [
+ (
+ "uint8",
+ "Format",
+ None,
+ None,
+ "Format for ClipBox without variation: set to 1.",
+ ),
+ ("int16", "xMin", None, None, "Minimum x of clip box."),
+ ("int16", "yMin", None, None, "Minimum y of clip box."),
+ ("int16", "xMax", None, None, "Maximum x of clip box."),
+ ("int16", "yMax", None, None, "Maximum y of clip box."),
+ ],
+ ),
+ (
+ "ClipBoxFormat2",
+ [
+ ("uint8", "Format", None, None, "Format for variable ClipBox: set to 2."),
+ ("int16", "xMin", None, None, "Minimum x of clip box. VarIndexBase + 0."),
+ ("int16", "yMin", None, None, "Minimum y of clip box. VarIndexBase + 1."),
+ ("int16", "xMax", None, None, "Maximum x of clip box. VarIndexBase + 2."),
+ ("int16", "yMax", None, None, "Maximum y of clip box. VarIndexBase + 3."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # COLRv1 Affine2x3 uses the same column-major order to serialize a 2D
+ # Affine Transformation as the one used by fontTools.misc.transform.
+ # However, for historical reasons, the labels 'xy' and 'yx' are swapped.
+ # Their fundamental meaning is the same though.
+ # COLRv1 Affine2x3 follows the names found in FreeType and Cairo.
+ # In all case, the second element in the 6-tuple correspond to the
+ # y-part of the x basis vector, and the third to the x-part of the y
+ # basis vector.
+ # See https://github.com/googlefonts/colr-gradients-spec/pull/85
+ (
+ "Affine2x3",
+ [
+ ("Fixed", "xx", None, None, "x-part of x basis vector"),
+ ("Fixed", "yx", None, None, "y-part of x basis vector"),
+ ("Fixed", "xy", None, None, "x-part of y basis vector"),
+ ("Fixed", "yy", None, None, "y-part of y basis vector"),
+ ("Fixed", "dx", None, None, "Translation in x direction"),
+ ("Fixed", "dy", None, None, "Translation in y direction"),
+ ],
+ ),
+ (
+ "VarAffine2x3",
+ [
+ ("Fixed", "xx", None, None, "x-part of x basis vector. VarIndexBase + 0."),
+ ("Fixed", "yx", None, None, "y-part of x basis vector. VarIndexBase + 1."),
+ ("Fixed", "xy", None, None, "x-part of y basis vector. VarIndexBase + 2."),
+ ("Fixed", "yy", None, None, "y-part of y basis vector. VarIndexBase + 3."),
+ (
+ "Fixed",
+ "dx",
+ None,
+ None,
+ "Translation in x direction. VarIndexBase + 4.",
+ ),
+ (
+ "Fixed",
+ "dy",
+ None,
+ None,
+ "Translation in y direction. VarIndexBase + 5.",
+ ),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ (
+ "ColorStop",
+ [
+ ("F2Dot14", "StopOffset", None, None, ""),
+ ("uint16", "PaletteIndex", None, None, "Index for a CPAL palette entry."),
+ ("F2Dot14", "Alpha", None, None, "Values outsided [0.,1.] reserved"),
+ ],
+ ),
+ (
+ "VarColorStop",
+ [
+ ("F2Dot14", "StopOffset", None, None, "VarIndexBase + 0."),
+ ("uint16", "PaletteIndex", None, None, "Index for a CPAL palette entry."),
+ (
+ "F2Dot14",
+ "Alpha",
+ None,
+ None,
+ "Values outsided [0.,1.] reserved. VarIndexBase + 1.",
+ ),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ (
+ "ColorLine",
+ [
+ (
+ "ExtendMode",
+ "Extend",
+ None,
+ None,
+ "Enum {PAD = 0, REPEAT = 1, REFLECT = 2}",
+ ),
+ ("uint16", "StopCount", None, None, "Number of Color stops."),
+ ("ColorStop", "ColorStop", "StopCount", 0, "Array of Color stops."),
+ ],
+ ),
+ (
+ "VarColorLine",
+ [
+ (
+ "ExtendMode",
+ "Extend",
+ None,
+ None,
+ "Enum {PAD = 0, REPEAT = 1, REFLECT = 2}",
+ ),
+ ("uint16", "StopCount", None, None, "Number of Color stops."),
+ ("VarColorStop", "ColorStop", "StopCount", 0, "Array of Color stops."),
+ ],
+ ),
+ # PaintColrLayers
+ (
+ "PaintFormat1",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 1"),
+ (
+ "uint8",
+ "NumLayers",
+ None,
+ None,
+ "Number of offsets to Paint to read from LayerList.",
+ ),
+ ("uint32", "FirstLayerIndex", None, None, "Index into LayerList."),
+ ],
+ ),
+ # PaintSolid
+ (
+ "PaintFormat2",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 2"),
+ ("uint16", "PaletteIndex", None, None, "Index for a CPAL palette entry."),
+ ("F2Dot14", "Alpha", None, None, "Values outsided [0.,1.] reserved"),
+ ],
+ ),
+ # PaintVarSolid
+ (
+ "PaintFormat3",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 3"),
+ ("uint16", "PaletteIndex", None, None, "Index for a CPAL palette entry."),
+ (
+ "F2Dot14",
+ "Alpha",
+ None,
+ None,
+ "Values outsided [0.,1.] reserved. VarIndexBase + 0.",
+ ),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintLinearGradient
+ (
+ "PaintFormat4",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 4"),
+ (
+ "Offset24",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintLinearGradient table) to ColorLine subtable.",
+ ),
+ ("int16", "x0", None, None, ""),
+ ("int16", "y0", None, None, ""),
+ ("int16", "x1", None, None, ""),
+ ("int16", "y1", None, None, ""),
+ ("int16", "x2", None, None, ""),
+ ("int16", "y2", None, None, ""),
+ ],
+ ),
+ # PaintVarLinearGradient
+ (
+ "PaintFormat5",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 5"),
+ (
+ "LOffset24To(VarColorLine)",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintVarLinearGradient table) to VarColorLine subtable.",
+ ),
+ ("int16", "x0", None, None, "VarIndexBase + 0."),
+ ("int16", "y0", None, None, "VarIndexBase + 1."),
+ ("int16", "x1", None, None, "VarIndexBase + 2."),
+ ("int16", "y1", None, None, "VarIndexBase + 3."),
+ ("int16", "x2", None, None, "VarIndexBase + 4."),
+ ("int16", "y2", None, None, "VarIndexBase + 5."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintRadialGradient
+ (
+ "PaintFormat6",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 6"),
+ (
+ "Offset24",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintRadialGradient table) to ColorLine subtable.",
+ ),
+ ("int16", "x0", None, None, ""),
+ ("int16", "y0", None, None, ""),
+ ("uint16", "r0", None, None, ""),
+ ("int16", "x1", None, None, ""),
+ ("int16", "y1", None, None, ""),
+ ("uint16", "r1", None, None, ""),
+ ],
+ ),
+ # PaintVarRadialGradient
+ (
+ "PaintFormat7",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 7"),
+ (
+ "LOffset24To(VarColorLine)",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintVarRadialGradient table) to VarColorLine subtable.",
+ ),
+ ("int16", "x0", None, None, "VarIndexBase + 0."),
+ ("int16", "y0", None, None, "VarIndexBase + 1."),
+ ("uint16", "r0", None, None, "VarIndexBase + 2."),
+ ("int16", "x1", None, None, "VarIndexBase + 3."),
+ ("int16", "y1", None, None, "VarIndexBase + 4."),
+ ("uint16", "r1", None, None, "VarIndexBase + 5."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintSweepGradient
+ (
+ "PaintFormat8",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 8"),
+ (
+ "Offset24",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintSweepGradient table) to ColorLine subtable.",
+ ),
+ ("int16", "centerX", None, None, "Center x coordinate."),
+ ("int16", "centerY", None, None, "Center y coordinate."),
+ (
+ "BiasedAngle",
+ "startAngle",
+ None,
+ None,
+ "Start of the angular range of the gradient.",
+ ),
+ (
+ "BiasedAngle",
+ "endAngle",
+ None,
+ None,
+ "End of the angular range of the gradient.",
+ ),
+ ],
+ ),
+ # PaintVarSweepGradient
+ (
+ "PaintFormat9",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 9"),
+ (
+ "LOffset24To(VarColorLine)",
+ "ColorLine",
+ None,
+ None,
+ "Offset (from beginning of PaintVarSweepGradient table) to VarColorLine subtable.",
+ ),
+ ("int16", "centerX", None, None, "Center x coordinate. VarIndexBase + 0."),
+ ("int16", "centerY", None, None, "Center y coordinate. VarIndexBase + 1."),
+ (
+ "BiasedAngle",
+ "startAngle",
+ None,
+ None,
+ "Start of the angular range of the gradient. VarIndexBase + 2.",
+ ),
+ (
+ "BiasedAngle",
+ "endAngle",
+ None,
+ None,
+ "End of the angular range of the gradient. VarIndexBase + 3.",
+ ),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintGlyph
+ (
+ "PaintFormat10",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 10"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintGlyph table) to Paint subtable.",
+ ),
+ ("GlyphID", "Glyph", None, None, "Glyph ID for the source outline."),
+ ],
+ ),
+ # PaintColrGlyph
+ (
+ "PaintFormat11",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 11"),
+ (
+ "GlyphID",
+ "Glyph",
+ None,
+ None,
+ "Virtual glyph ID for a BaseGlyphList base glyph.",
+ ),
+ ],
+ ),
+ # PaintTransform
+ (
+ "PaintFormat12",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 12"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintTransform table) to Paint subtable.",
+ ),
+ (
+ "LOffset24To(Affine2x3)",
+ "Transform",
+ None,
+ None,
+ "2x3 matrix for 2D affine transformations.",
+ ),
+ ],
+ ),
+ # PaintVarTransform
+ (
+ "PaintFormat13",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 13"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarTransform table) to Paint subtable.",
+ ),
+ (
+ "LOffset24To(VarAffine2x3)",
+ "Transform",
+ None,
+ None,
+ "2x3 matrix for 2D affine transformations.",
+ ),
+ ],
+ ),
+ # PaintTranslate
+ (
+ "PaintFormat14",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 14"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintTranslate table) to Paint subtable.",
+ ),
+ ("int16", "dx", None, None, "Translation in x direction."),
+ ("int16", "dy", None, None, "Translation in y direction."),
+ ],
+ ),
+ # PaintVarTranslate
+ (
+ "PaintFormat15",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 15"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarTranslate table) to Paint subtable.",
+ ),
+ (
+ "int16",
+ "dx",
+ None,
+ None,
+ "Translation in x direction. VarIndexBase + 0.",
+ ),
+ (
+ "int16",
+ "dy",
+ None,
+ None,
+ "Translation in y direction. VarIndexBase + 1.",
+ ),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintScale
+ (
+ "PaintFormat16",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 16"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintScale table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scaleX", None, None, ""),
+ ("F2Dot14", "scaleY", None, None, ""),
+ ],
+ ),
+ # PaintVarScale
+ (
+ "PaintFormat17",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 17"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarScale table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scaleX", None, None, "VarIndexBase + 0."),
+ ("F2Dot14", "scaleY", None, None, "VarIndexBase + 1."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintScaleAroundCenter
+ (
+ "PaintFormat18",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 18"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scaleX", None, None, ""),
+ ("F2Dot14", "scaleY", None, None, ""),
+ ("int16", "centerX", None, None, ""),
+ ("int16", "centerY", None, None, ""),
+ ],
+ ),
+ # PaintVarScaleAroundCenter
+ (
+ "PaintFormat19",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 19"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarScaleAroundCenter table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scaleX", None, None, "VarIndexBase + 0."),
+ ("F2Dot14", "scaleY", None, None, "VarIndexBase + 1."),
+ ("int16", "centerX", None, None, "VarIndexBase + 2."),
+ ("int16", "centerY", None, None, "VarIndexBase + 3."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintScaleUniform
+ (
+ "PaintFormat20",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 20"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintScaleUniform table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scale", None, None, ""),
+ ],
+ ),
+ # PaintVarScaleUniform
+ (
+ "PaintFormat21",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 21"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarScaleUniform table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scale", None, None, "VarIndexBase + 0."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintScaleUniformAroundCenter
+ (
+ "PaintFormat22",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 22"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scale", None, None, ""),
+ ("int16", "centerX", None, None, ""),
+ ("int16", "centerY", None, None, ""),
+ ],
+ ),
+ # PaintVarScaleUniformAroundCenter
+ (
+ "PaintFormat23",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 23"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarScaleUniformAroundCenter table) to Paint subtable.",
+ ),
+ ("F2Dot14", "scale", None, None, "VarIndexBase + 0"),
+ ("int16", "centerX", None, None, "VarIndexBase + 1"),
+ ("int16", "centerY", None, None, "VarIndexBase + 2"),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintRotate
+ (
+ "PaintFormat24",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 24"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintRotate table) to Paint subtable.",
+ ),
+ ("Angle", "angle", None, None, ""),
+ ],
+ ),
+ # PaintVarRotate
+ (
+ "PaintFormat25",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 25"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarRotate table) to Paint subtable.",
+ ),
+ ("Angle", "angle", None, None, "VarIndexBase + 0."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintRotateAroundCenter
+ (
+ "PaintFormat26",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 26"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable.",
+ ),
+ ("Angle", "angle", None, None, ""),
+ ("int16", "centerX", None, None, ""),
+ ("int16", "centerY", None, None, ""),
+ ],
+ ),
+ # PaintVarRotateAroundCenter
+ (
+ "PaintFormat27",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 27"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarRotateAroundCenter table) to Paint subtable.",
+ ),
+ ("Angle", "angle", None, None, "VarIndexBase + 0."),
+ ("int16", "centerX", None, None, "VarIndexBase + 1."),
+ ("int16", "centerY", None, None, "VarIndexBase + 2."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintSkew
+ (
+ "PaintFormat28",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 28"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintSkew table) to Paint subtable.",
+ ),
+ ("Angle", "xSkewAngle", None, None, ""),
+ ("Angle", "ySkewAngle", None, None, ""),
+ ],
+ ),
+ # PaintVarSkew
+ (
+ "PaintFormat29",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 29"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarSkew table) to Paint subtable.",
+ ),
+ ("Angle", "xSkewAngle", None, None, "VarIndexBase + 0."),
+ ("Angle", "ySkewAngle", None, None, "VarIndexBase + 1."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintSkewAroundCenter
+ (
+ "PaintFormat30",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 30"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable.",
+ ),
+ ("Angle", "xSkewAngle", None, None, ""),
+ ("Angle", "ySkewAngle", None, None, ""),
+ ("int16", "centerX", None, None, ""),
+ ("int16", "centerY", None, None, ""),
+ ],
+ ),
+ # PaintVarSkewAroundCenter
+ (
+ "PaintFormat31",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 31"),
+ (
+ "Offset24",
+ "Paint",
+ None,
+ None,
+ "Offset (from beginning of PaintVarSkewAroundCenter table) to Paint subtable.",
+ ),
+ ("Angle", "xSkewAngle", None, None, "VarIndexBase + 0."),
+ ("Angle", "ySkewAngle", None, None, "VarIndexBase + 1."),
+ ("int16", "centerX", None, None, "VarIndexBase + 2."),
+ ("int16", "centerY", None, None, "VarIndexBase + 3."),
+ (
+ "VarIndex",
+ "VarIndexBase",
+ None,
+ None,
+ "Base index into DeltaSetIndexMap.",
+ ),
+ ],
+ ),
+ # PaintComposite
+ (
+ "PaintFormat32",
+ [
+ ("uint8", "PaintFormat", None, None, "Format identifier-format = 32"),
+ (
+ "LOffset24To(Paint)",
+ "SourcePaint",
+ None,
+ None,
+ "Offset (from beginning of PaintComposite table) to source Paint subtable.",
+ ),
+ (
+ "CompositeMode",
+ "CompositeMode",
+ None,
+ None,
+ "A CompositeMode enumeration value.",
+ ),
+ (
+ "LOffset24To(Paint)",
+ "BackdropPaint",
+ None,
+ None,
+ "Offset (from beginning of PaintComposite table) to backdrop Paint subtable.",
+ ),
+ ],
+ ),
+ #
+ # avar
+ #
+ (
+ "AxisValueMap",
+ [
+ (
+ "F2Dot14",
+ "FromCoordinate",
+ None,
+ None,
+ "A normalized coordinate value obtained using default normalization",
+ ),
+ (
+ "F2Dot14",
+ "ToCoordinate",
+ None,
+ None,
+ "The modified, normalized coordinate value",
+ ),
+ ],
+ ),
+ (
+ "AxisSegmentMap",
+ [
+ (
+ "uint16",
+ "PositionMapCount",
+ None,
+ None,
+ "The number of correspondence pairs for this axis",
+ ),
+ (
+ "AxisValueMap",
+ "AxisValueMap",
+ "PositionMapCount",
+ 0,
+ "The array of axis value map records for this axis",
+ ),
+ ],
+ ),
+ (
+ "avar",
+ [
+ (
+ "Version",
+ "Version",
+ None,
+ None,
+ "Version of the avar table- 0x00010000 or 0x00020000",
+ ),
+ ("uint16", "Reserved", None, None, "Permanently reserved; set to zero"),
+ (
+ "uint16",
+ "AxisCount",
+ None,
+ None,
+ 'The number of variation axes for this font. This must be the same number as axisCount in the "fvar" table',
+ ),
+ (
+ "AxisSegmentMap",
+ "AxisSegmentMap",
+ "AxisCount",
+ 0,
+ 'The segment maps array — one segment map for each axis, in the order of axes specified in the "fvar" table',
+ ),
+ (
+ "LOffsetTo(DeltaSetIndexMap)",
+ "VarIdxMap",
+ None,
+ "Version >= 0x00020000",
+ "",
+ ),
+ ("LOffset", "VarStore", None, "Version >= 0x00020000", ""),
+ ],
+ ),
+]
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/otTables.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/otTables.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab5dace7265826cbf4f8aa12a940b26da50b9623
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/otTables.py
@@ -0,0 +1,2703 @@
+# coding: utf-8
+"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various
+OpenType subtables.
+
+Most are constructed upon import from data in otData.py, all are populated with
+converter objects from otConverters.py.
+"""
+import copy
+from enum import IntEnum
+from functools import reduce
+from math import radians
+import itertools
+from collections import defaultdict, namedtuple
+from fontTools.ttLib import OPTIMIZE_FONT_SPEED
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools.ttLib.tables.otTraverse import dfs_base_table
+from fontTools.misc.arrayTools import quantizeRect
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.transform import Transform, Identity, DecomposedTransform
+from fontTools.misc.textTools import bytesjoin, pad, safeEval
+from fontTools.misc.vector import Vector
+from fontTools.pens.boundsPen import ControlBoundsPen
+from fontTools.pens.transformPen import TransformPen
+from .otBase import (
+ BaseTable,
+ FormatSwitchingBaseTable,
+ ValueRecord,
+ CountReference,
+ getFormatSwitchingBaseTableClass,
+)
+from fontTools.misc.fixedTools import (
+ fixedToFloat as fi2fl,
+ floatToFixed as fl2fi,
+ floatToFixedToStr as fl2str,
+ strToFixedToFloat as str2fl,
+)
+from fontTools.feaLib.lookupDebugInfo import LookupDebugInfo, LOOKUP_DEBUG_INFO_KEY
+import logging
+import struct
+import array
+import sys
+from enum import IntFlag
+from typing import TYPE_CHECKING, Iterator, List, Optional, Set
+
+if TYPE_CHECKING:
+ from fontTools.ttLib.ttGlyphSet import _TTGlyphSet
+
+
+log = logging.getLogger(__name__)
+
+
+class VarComponentFlags(IntFlag):
+ RESET_UNSPECIFIED_AXES = 1 << 0
+
+ HAVE_AXES = 1 << 1
+
+ AXIS_VALUES_HAVE_VARIATION = 1 << 2
+ TRANSFORM_HAS_VARIATION = 1 << 3
+
+ HAVE_TRANSLATE_X = 1 << 4
+ HAVE_TRANSLATE_Y = 1 << 5
+ HAVE_ROTATION = 1 << 6
+
+ HAVE_CONDITION = 1 << 7
+
+ HAVE_SCALE_X = 1 << 8
+ HAVE_SCALE_Y = 1 << 9
+ HAVE_TCENTER_X = 1 << 10
+ HAVE_TCENTER_Y = 1 << 11
+
+ GID_IS_24BIT = 1 << 12
+
+ HAVE_SKEW_X = 1 << 13
+ HAVE_SKEW_Y = 1 << 14
+
+ RESERVED_MASK = (1 << 32) - (1 << 15)
+
+
+VarTransformMappingValues = namedtuple(
+ "VarTransformMappingValues",
+ ["flag", "fractionalBits", "scale", "defaultValue"],
+)
+
+VAR_TRANSFORM_MAPPING = {
+ "translateX": VarTransformMappingValues(
+ VarComponentFlags.HAVE_TRANSLATE_X, 0, 1, 0
+ ),
+ "translateY": VarTransformMappingValues(
+ VarComponentFlags.HAVE_TRANSLATE_Y, 0, 1, 0
+ ),
+ "rotation": VarTransformMappingValues(VarComponentFlags.HAVE_ROTATION, 12, 180, 0),
+ "scaleX": VarTransformMappingValues(VarComponentFlags.HAVE_SCALE_X, 10, 1, 1),
+ "scaleY": VarTransformMappingValues(VarComponentFlags.HAVE_SCALE_Y, 10, 1, 1),
+ "skewX": VarTransformMappingValues(VarComponentFlags.HAVE_SKEW_X, 12, -180, 0),
+ "skewY": VarTransformMappingValues(VarComponentFlags.HAVE_SKEW_Y, 12, 180, 0),
+ "tCenterX": VarTransformMappingValues(VarComponentFlags.HAVE_TCENTER_X, 0, 1, 0),
+ "tCenterY": VarTransformMappingValues(VarComponentFlags.HAVE_TCENTER_Y, 0, 1, 0),
+}
+
+# Probably should be somewhere in fontTools.misc
+_packer = {
+ 1: lambda v: struct.pack(">B", v),
+ 2: lambda v: struct.pack(">H", v),
+ 3: lambda v: struct.pack(">L", v)[1:],
+ 4: lambda v: struct.pack(">L", v),
+}
+_unpacker = {
+ 1: lambda v: struct.unpack(">B", v)[0],
+ 2: lambda v: struct.unpack(">H", v)[0],
+ 3: lambda v: struct.unpack(">L", b"\0" + v)[0],
+ 4: lambda v: struct.unpack(">L", v)[0],
+}
+
+
+def _read_uint32var(data, i):
+ """Read a variable-length number from data starting at index i.
+
+ Return the number and the next index.
+ """
+
+ b0 = data[i]
+ if b0 < 0x80:
+ return b0, i + 1
+ elif b0 < 0xC0:
+ return (b0 - 0x80) << 8 | data[i + 1], i + 2
+ elif b0 < 0xE0:
+ return (b0 - 0xC0) << 16 | data[i + 1] << 8 | data[i + 2], i + 3
+ elif b0 < 0xF0:
+ return (b0 - 0xE0) << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[
+ i + 3
+ ], i + 4
+ else:
+ return (b0 - 0xF0) << 32 | data[i + 1] << 24 | data[i + 2] << 16 | data[
+ i + 3
+ ] << 8 | data[i + 4], i + 5
+
+
+def _write_uint32var(v):
+ """Write a variable-length number.
+
+ Return the data.
+ """
+ if v < 0x80:
+ return struct.pack(">B", v)
+ elif v < 0x4000:
+ return struct.pack(">H", (v | 0x8000))
+ elif v < 0x200000:
+ return struct.pack(">L", (v | 0xC00000))[1:]
+ elif v < 0x10000000:
+ return struct.pack(">L", (v | 0xE0000000))
+ else:
+ return struct.pack(">B", 0xF0) + struct.pack(">L", v)
+
+
+class VarComponent:
+ def __init__(self):
+ self.populateDefaults()
+
+ def populateDefaults(self, propagator=None):
+ self.flags = 0
+ self.glyphName = None
+ self.conditionIndex = None
+ self.axisIndicesIndex = None
+ self.axisValues = ()
+ self.axisValuesVarIndex = NO_VARIATION_INDEX
+ self.transformVarIndex = NO_VARIATION_INDEX
+ self.transform = DecomposedTransform()
+
+ def decompile(self, data, font, localState):
+ i = 0
+ self.flags, i = _read_uint32var(data, i)
+ flags = self.flags
+
+ gidSize = 3 if flags & VarComponentFlags.GID_IS_24BIT else 2
+ glyphID = _unpacker[gidSize](data[i : i + gidSize])
+ i += gidSize
+ self.glyphName = font.glyphOrder[glyphID]
+
+ if flags & VarComponentFlags.HAVE_CONDITION:
+ self.conditionIndex, i = _read_uint32var(data, i)
+
+ if flags & VarComponentFlags.HAVE_AXES:
+ self.axisIndicesIndex, i = _read_uint32var(data, i)
+ else:
+ self.axisIndicesIndex = None
+
+ if self.axisIndicesIndex is None:
+ numAxes = 0
+ else:
+ axisIndices = localState["AxisIndicesList"].Item[self.axisIndicesIndex]
+ numAxes = len(axisIndices)
+
+ if flags & VarComponentFlags.HAVE_AXES:
+ axisValues, i = TupleVariation.decompileDeltas_(numAxes, data, i)
+ self.axisValues = tuple(fi2fl(v, 14) for v in axisValues)
+ else:
+ self.axisValues = ()
+ assert len(self.axisValues) == numAxes
+
+ if flags & VarComponentFlags.AXIS_VALUES_HAVE_VARIATION:
+ self.axisValuesVarIndex, i = _read_uint32var(data, i)
+ else:
+ self.axisValuesVarIndex = NO_VARIATION_INDEX
+ if flags & VarComponentFlags.TRANSFORM_HAS_VARIATION:
+ self.transformVarIndex, i = _read_uint32var(data, i)
+ else:
+ self.transformVarIndex = NO_VARIATION_INDEX
+
+ self.transform = DecomposedTransform()
+
+ def read_transform_component(values):
+ nonlocal i
+ if flags & values.flag:
+ v = (
+ fi2fl(
+ struct.unpack(">h", data[i : i + 2])[0], values.fractionalBits
+ )
+ * values.scale
+ )
+ i += 2
+ return v
+ else:
+ return values.defaultValue
+
+ for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
+ value = read_transform_component(mapping_values)
+ setattr(self.transform, attr_name, value)
+
+ if not (flags & VarComponentFlags.HAVE_SCALE_Y):
+ self.transform.scaleY = self.transform.scaleX
+
+ n = flags & VarComponentFlags.RESERVED_MASK
+ while n:
+ _, i = _read_uint32var(data, i)
+ n &= n - 1
+
+ return data[i:]
+
+ def compile(self, font):
+ optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
+
+ data = []
+
+ flags = self.flags
+
+ glyphID = font.getGlyphID(self.glyphName)
+ if glyphID > 65535:
+ flags |= VarComponentFlags.GID_IS_24BIT
+ data.append(_packer[3](glyphID))
+ else:
+ flags &= ~VarComponentFlags.GID_IS_24BIT
+ data.append(_packer[2](glyphID))
+
+ if self.conditionIndex is not None:
+ flags |= VarComponentFlags.HAVE_CONDITION
+ data.append(_write_uint32var(self.conditionIndex))
+
+ numAxes = len(self.axisValues)
+
+ if numAxes:
+ flags |= VarComponentFlags.HAVE_AXES
+ data.append(_write_uint32var(self.axisIndicesIndex))
+ data.append(
+ TupleVariation.compileDeltaValues_(
+ [fl2fi(v, 14) for v in self.axisValues],
+ optimizeSize=not optimizeSpeed,
+ )
+ )
+ else:
+ flags &= ~VarComponentFlags.HAVE_AXES
+
+ if self.axisValuesVarIndex != NO_VARIATION_INDEX:
+ flags |= VarComponentFlags.AXIS_VALUES_HAVE_VARIATION
+ data.append(_write_uint32var(self.axisValuesVarIndex))
+ else:
+ flags &= ~VarComponentFlags.AXIS_VALUES_HAVE_VARIATION
+ if self.transformVarIndex != NO_VARIATION_INDEX:
+ flags |= VarComponentFlags.TRANSFORM_HAS_VARIATION
+ data.append(_write_uint32var(self.transformVarIndex))
+ else:
+ flags &= ~VarComponentFlags.TRANSFORM_HAS_VARIATION
+
+ def write_transform_component(value, values):
+ if flags & values.flag:
+ return struct.pack(
+ ">h", fl2fi(value / values.scale, values.fractionalBits)
+ )
+ else:
+ return b""
+
+ for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
+ value = getattr(self.transform, attr_name)
+ data.append(write_transform_component(value, mapping_values))
+
+ return _write_uint32var(flags) + bytesjoin(data)
+
+ def toXML(self, writer, ttFont, attrs):
+ writer.begintag("VarComponent", attrs)
+ writer.newline()
+
+ def write(name, value, attrs=()):
+ if value is not None:
+ writer.simpletag(name, (("value", value),) + attrs)
+ writer.newline()
+
+ write("glyphName", self.glyphName)
+
+ if self.conditionIndex is not None:
+ write("conditionIndex", self.conditionIndex)
+ if self.axisIndicesIndex is not None:
+ write("axisIndicesIndex", self.axisIndicesIndex)
+ if (
+ self.axisIndicesIndex is not None
+ or self.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES
+ ):
+ if self.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES:
+ attrs = (("resetUnspecifiedAxes", 1),)
+ else:
+ attrs = ()
+ write("axisValues", [float(fl2str(v, 14)) for v in self.axisValues], attrs)
+
+ if self.axisValuesVarIndex != NO_VARIATION_INDEX:
+ write("axisValuesVarIndex", self.axisValuesVarIndex)
+ if self.transformVarIndex != NO_VARIATION_INDEX:
+ write("transformVarIndex", self.transformVarIndex)
+
+ # Only write transform components that are specified in the
+ # flags, even if they are the default value.
+ for attr_name, mapping in VAR_TRANSFORM_MAPPING.items():
+ if not (self.flags & mapping.flag):
+ continue
+ v = getattr(self.transform, attr_name)
+ write(attr_name, fl2str(v, mapping.fractionalBits))
+
+ writer.endtag("VarComponent")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ content = [c for c in content if isinstance(c, tuple)]
+
+ self.populateDefaults()
+
+ for name, attrs, content in content:
+ assert not content
+ v = attrs["value"]
+
+ if name == "glyphName":
+ self.glyphName = v
+ elif name == "conditionIndex":
+ self.conditionIndex = safeEval(v)
+ elif name == "axisIndicesIndex":
+ self.axisIndicesIndex = safeEval(v)
+ elif name == "axisValues":
+ self.axisValues = tuple(str2fl(v, 14) for v in safeEval(v))
+ if safeEval(attrs.get("resetUnspecifiedAxes", "0")):
+ self.flags |= VarComponentFlags.RESET_UNSPECIFIED_AXES
+ elif name == "axisValuesVarIndex":
+ self.axisValuesVarIndex = safeEval(v)
+ elif name == "transformVarIndex":
+ self.transformVarIndex = safeEval(v)
+ elif name in VAR_TRANSFORM_MAPPING:
+ setattr(
+ self.transform,
+ name,
+ safeEval(v),
+ )
+ self.flags |= VAR_TRANSFORM_MAPPING[name].flag
+ else:
+ assert False, name
+
+ def applyTransformDeltas(self, deltas):
+ i = 0
+
+ def read_transform_component_delta(values):
+ nonlocal i
+ if self.flags & values.flag:
+ v = fi2fl(deltas[i], values.fractionalBits) * values.scale
+ i += 1
+ return v
+ else:
+ return 0
+
+ for attr_name, mapping_values in VAR_TRANSFORM_MAPPING.items():
+ value = read_transform_component_delta(mapping_values)
+ setattr(
+ self.transform, attr_name, getattr(self.transform, attr_name) + value
+ )
+
+ if not (self.flags & VarComponentFlags.HAVE_SCALE_Y):
+ self.transform.scaleY = self.transform.scaleX
+
+ assert i == len(deltas), (i, len(deltas))
+
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+
+class VarCompositeGlyph:
+ def __init__(self, components=None):
+ self.components = components if components is not None else []
+
+ def decompile(self, data, font, localState):
+ self.components = []
+ while data:
+ component = VarComponent()
+ data = component.decompile(data, font, localState)
+ self.components.append(component)
+
+ def compile(self, font):
+ data = []
+ for component in self.components:
+ data.append(component.compile(font))
+ return bytesjoin(data)
+
+ def toXML(self, xmlWriter, font, attrs, name):
+ xmlWriter.begintag("VarCompositeGlyph", attrs)
+ xmlWriter.newline()
+ for i, component in enumerate(self.components):
+ component.toXML(xmlWriter, font, [("index", i)])
+ xmlWriter.endtag("VarCompositeGlyph")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ content = [c for c in content if isinstance(c, tuple)]
+ for name, attrs, content in content:
+ assert name == "VarComponent"
+ component = VarComponent()
+ component.fromXML(name, attrs, content, font)
+ self.components.append(component)
+
+
+class AATStateTable(object):
+ def __init__(self):
+ self.GlyphClasses = {} # GlyphID --> GlyphClass
+ self.States = [] # List of AATState, indexed by state number
+ self.PerGlyphLookups = [] # [{GlyphID:GlyphID}, ...]
+
+
+class AATState(object):
+ def __init__(self):
+ self.Transitions = {} # GlyphClass --> AATAction
+
+
+class AATAction(object):
+ _FLAGS = None
+
+ @staticmethod
+ def compileActions(font, states):
+ return (None, None)
+
+ def _writeFlagsToXML(self, xmlWriter):
+ flags = [f for f in self._FLAGS if self.__dict__[f]]
+ if flags:
+ xmlWriter.simpletag("Flags", value=",".join(flags))
+ xmlWriter.newline()
+ if self.ReservedFlags != 0:
+ xmlWriter.simpletag("ReservedFlags", value="0x%04X" % self.ReservedFlags)
+ xmlWriter.newline()
+
+ def _setFlag(self, flag):
+ assert flag in self._FLAGS, "unsupported flag %s" % flag
+ self.__dict__[flag] = True
+
+
+class RearrangementMorphAction(AATAction):
+ staticSize = 4
+ actionHeaderSize = 0
+ _FLAGS = ["MarkFirst", "DontAdvance", "MarkLast"]
+
+ _VERBS = {
+ 0: "no change",
+ 1: "Ax ⇒ xA",
+ 2: "xD ⇒ Dx",
+ 3: "AxD ⇒ DxA",
+ 4: "ABx ⇒ xAB",
+ 5: "ABx ⇒ xBA",
+ 6: "xCD ⇒ CDx",
+ 7: "xCD ⇒ DCx",
+ 8: "AxCD ⇒ CDxA",
+ 9: "AxCD ⇒ DCxA",
+ 10: "ABxD ⇒ DxAB",
+ 11: "ABxD ⇒ DxBA",
+ 12: "ABxCD ⇒ CDxAB",
+ 13: "ABxCD ⇒ CDxBA",
+ 14: "ABxCD ⇒ DCxAB",
+ 15: "ABxCD ⇒ DCxBA",
+ }
+
+ def __init__(self):
+ self.NewState = 0
+ self.Verb = 0
+ self.MarkFirst = False
+ self.DontAdvance = False
+ self.MarkLast = False
+ self.ReservedFlags = 0
+
+ def compile(self, writer, font, actionIndex):
+ assert actionIndex is None
+ writer.writeUShort(self.NewState)
+ assert self.Verb >= 0 and self.Verb <= 15, self.Verb
+ flags = self.Verb | self.ReservedFlags
+ if self.MarkFirst:
+ flags |= 0x8000
+ if self.DontAdvance:
+ flags |= 0x4000
+ if self.MarkLast:
+ flags |= 0x2000
+ writer.writeUShort(flags)
+
+ def decompile(self, reader, font, actionReader):
+ assert actionReader is None
+ self.NewState = reader.readUShort()
+ flags = reader.readUShort()
+ self.Verb = flags & 0xF
+ self.MarkFirst = bool(flags & 0x8000)
+ self.DontAdvance = bool(flags & 0x4000)
+ self.MarkLast = bool(flags & 0x2000)
+ self.ReservedFlags = flags & 0x1FF0
+
+ def toXML(self, xmlWriter, font, attrs, name):
+ xmlWriter.begintag(name, **attrs)
+ xmlWriter.newline()
+ xmlWriter.simpletag("NewState", value=self.NewState)
+ xmlWriter.newline()
+ self._writeFlagsToXML(xmlWriter)
+ xmlWriter.simpletag("Verb", value=self.Verb)
+ verbComment = self._VERBS.get(self.Verb)
+ if verbComment is not None:
+ xmlWriter.comment(verbComment)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ self.NewState = self.Verb = self.ReservedFlags = 0
+ self.MarkFirst = self.DontAdvance = self.MarkLast = False
+ content = [t for t in content if isinstance(t, tuple)]
+ for eltName, eltAttrs, eltContent in content:
+ if eltName == "NewState":
+ self.NewState = safeEval(eltAttrs["value"])
+ elif eltName == "Verb":
+ self.Verb = safeEval(eltAttrs["value"])
+ elif eltName == "ReservedFlags":
+ self.ReservedFlags = safeEval(eltAttrs["value"])
+ elif eltName == "Flags":
+ for flag in eltAttrs["value"].split(","):
+ self._setFlag(flag.strip())
+
+
+class ContextualMorphAction(AATAction):
+ staticSize = 8
+ actionHeaderSize = 0
+ _FLAGS = ["SetMark", "DontAdvance"]
+
+ def __init__(self):
+ self.NewState = 0
+ self.SetMark, self.DontAdvance = False, False
+ self.ReservedFlags = 0
+ self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
+
+ def compile(self, writer, font, actionIndex):
+ assert actionIndex is None
+ writer.writeUShort(self.NewState)
+ flags = self.ReservedFlags
+ if self.SetMark:
+ flags |= 0x8000
+ if self.DontAdvance:
+ flags |= 0x4000
+ writer.writeUShort(flags)
+ writer.writeUShort(self.MarkIndex)
+ writer.writeUShort(self.CurrentIndex)
+
+ def decompile(self, reader, font, actionReader):
+ assert actionReader is None
+ self.NewState = reader.readUShort()
+ flags = reader.readUShort()
+ self.SetMark = bool(flags & 0x8000)
+ self.DontAdvance = bool(flags & 0x4000)
+ self.ReservedFlags = flags & 0x3FFF
+ self.MarkIndex = reader.readUShort()
+ self.CurrentIndex = reader.readUShort()
+
+ def toXML(self, xmlWriter, font, attrs, name):
+ xmlWriter.begintag(name, **attrs)
+ xmlWriter.newline()
+ xmlWriter.simpletag("NewState", value=self.NewState)
+ xmlWriter.newline()
+ self._writeFlagsToXML(xmlWriter)
+ xmlWriter.simpletag("MarkIndex", value=self.MarkIndex)
+ xmlWriter.newline()
+ xmlWriter.simpletag("CurrentIndex", value=self.CurrentIndex)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ self.NewState = self.ReservedFlags = 0
+ self.SetMark = self.DontAdvance = False
+ self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
+ content = [t for t in content if isinstance(t, tuple)]
+ for eltName, eltAttrs, eltContent in content:
+ if eltName == "NewState":
+ self.NewState = safeEval(eltAttrs["value"])
+ elif eltName == "Flags":
+ for flag in eltAttrs["value"].split(","):
+ self._setFlag(flag.strip())
+ elif eltName == "ReservedFlags":
+ self.ReservedFlags = safeEval(eltAttrs["value"])
+ elif eltName == "MarkIndex":
+ self.MarkIndex = safeEval(eltAttrs["value"])
+ elif eltName == "CurrentIndex":
+ self.CurrentIndex = safeEval(eltAttrs["value"])
+
+
+class LigAction(object):
+ def __init__(self):
+ self.Store = False
+ # GlyphIndexDelta is a (possibly negative) delta that gets
+ # added to the glyph ID at the top of the AAT runtime
+ # execution stack. It is *not* a byte offset into the
+ # morx table. The result of the addition, which is performed
+ # at run time by the shaping engine, is an index into
+ # the ligature components table. See 'morx' specification.
+ # In the AAT specification, this field is called Offset;
+ # but its meaning is quite different from other offsets
+ # in either AAT or OpenType, so we use a different name.
+ self.GlyphIndexDelta = 0
+
+
+class LigatureMorphAction(AATAction):
+ staticSize = 6
+
+ # 4 bytes for each of {action,ligComponents,ligatures}Offset
+ actionHeaderSize = 12
+
+ _FLAGS = ["SetComponent", "DontAdvance"]
+
+ def __init__(self):
+ self.NewState = 0
+ self.SetComponent, self.DontAdvance = False, False
+ self.ReservedFlags = 0
+ self.Actions = []
+
+ def compile(self, writer, font, actionIndex):
+ assert actionIndex is not None
+ writer.writeUShort(self.NewState)
+ flags = self.ReservedFlags
+ if self.SetComponent:
+ flags |= 0x8000
+ if self.DontAdvance:
+ flags |= 0x4000
+ if len(self.Actions) > 0:
+ flags |= 0x2000
+ writer.writeUShort(flags)
+ if len(self.Actions) > 0:
+ actions = self.compileLigActions()
+ writer.writeUShort(actionIndex[actions])
+ else:
+ writer.writeUShort(0)
+
+ def decompile(self, reader, font, actionReader):
+ assert actionReader is not None
+ self.NewState = reader.readUShort()
+ flags = reader.readUShort()
+ self.SetComponent = bool(flags & 0x8000)
+ self.DontAdvance = bool(flags & 0x4000)
+ performAction = bool(flags & 0x2000)
+ # As of 2017-09-12, the 'morx' specification says that
+ # the reserved bitmask in ligature subtables is 0x3FFF.
+ # However, the specification also defines a flag 0x2000,
+ # so the reserved value should actually be 0x1FFF.
+ # TODO: Report this specification bug to Apple.
+ self.ReservedFlags = flags & 0x1FFF
+ actionIndex = reader.readUShort()
+ if performAction:
+ self.Actions = self._decompileLigActions(actionReader, actionIndex)
+ else:
+ self.Actions = []
+
+ @staticmethod
+ def compileActions(font, states):
+ result, actions, actionIndex = b"", set(), {}
+ for state in states:
+ for _glyphClass, trans in state.Transitions.items():
+ actions.add(trans.compileLigActions())
+ # Sort the compiled actions in decreasing order of
+ # length, so that the longer sequence come before the
+ # shorter ones. For each compiled action ABCD, its
+ # suffixes BCD, CD, and D do not be encoded separately
+ # (in case they occur); instead, we can just store an
+ # index that points into the middle of the longer
+ # sequence. Every compiled AAT ligature sequence is
+ # terminated with an end-of-sequence flag, which can
+ # only be set on the last element of the sequence.
+ # Therefore, it is sufficient to consider just the
+ # suffixes.
+ for a in sorted(actions, key=lambda x: (-len(x), x)):
+ if a not in actionIndex:
+ for i in range(0, len(a), 4):
+ suffix = a[i:]
+ suffixIndex = (len(result) + i) // 4
+ actionIndex.setdefault(suffix, suffixIndex)
+ result += a
+ result = pad(result, 4)
+ return (result, actionIndex)
+
+ def compileLigActions(self):
+ result = []
+ for i, action in enumerate(self.Actions):
+ last = i == len(self.Actions) - 1
+ value = action.GlyphIndexDelta & 0x3FFFFFFF
+ value |= 0x80000000 if last else 0
+ value |= 0x40000000 if action.Store else 0
+ result.append(struct.pack(">L", value))
+ return bytesjoin(result)
+
+ def _decompileLigActions(self, actionReader, actionIndex):
+ actions = []
+ last = False
+ reader = actionReader.getSubReader(actionReader.pos + actionIndex * 4)
+ while not last:
+ value = reader.readULong()
+ last = bool(value & 0x80000000)
+ action = LigAction()
+ actions.append(action)
+ action.Store = bool(value & 0x40000000)
+ delta = value & 0x3FFFFFFF
+ if delta >= 0x20000000: # sign-extend 30-bit value
+ delta = -0x40000000 + delta
+ action.GlyphIndexDelta = delta
+ return actions
+
+ def fromXML(self, name, attrs, content, font):
+ self.NewState = self.ReservedFlags = 0
+ self.SetComponent = self.DontAdvance = False
+ self.ReservedFlags = 0
+ self.Actions = []
+ content = [t for t in content if isinstance(t, tuple)]
+ for eltName, eltAttrs, eltContent in content:
+ if eltName == "NewState":
+ self.NewState = safeEval(eltAttrs["value"])
+ elif eltName == "Flags":
+ for flag in eltAttrs["value"].split(","):
+ self._setFlag(flag.strip())
+ elif eltName == "ReservedFlags":
+ self.ReservedFlags = safeEval(eltAttrs["value"])
+ elif eltName == "Action":
+ action = LigAction()
+ flags = eltAttrs.get("Flags", "").split(",")
+ flags = [f.strip() for f in flags]
+ action.Store = "Store" in flags
+ action.GlyphIndexDelta = safeEval(eltAttrs["GlyphIndexDelta"])
+ self.Actions.append(action)
+
+ def toXML(self, xmlWriter, font, attrs, name):
+ xmlWriter.begintag(name, **attrs)
+ xmlWriter.newline()
+ xmlWriter.simpletag("NewState", value=self.NewState)
+ xmlWriter.newline()
+ self._writeFlagsToXML(xmlWriter)
+ for action in self.Actions:
+ attribs = [("GlyphIndexDelta", action.GlyphIndexDelta)]
+ if action.Store:
+ attribs.append(("Flags", "Store"))
+ xmlWriter.simpletag("Action", attribs)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+
+class InsertionMorphAction(AATAction):
+ staticSize = 8
+ actionHeaderSize = 4 # 4 bytes for actionOffset
+ _FLAGS = [
+ "SetMark",
+ "DontAdvance",
+ "CurrentIsKashidaLike",
+ "MarkedIsKashidaLike",
+ "CurrentInsertBefore",
+ "MarkedInsertBefore",
+ ]
+
+ def __init__(self):
+ self.NewState = 0
+ for flag in self._FLAGS:
+ setattr(self, flag, False)
+ self.ReservedFlags = 0
+ self.CurrentInsertionAction, self.MarkedInsertionAction = [], []
+
+ def compile(self, writer, font, actionIndex):
+ assert actionIndex is not None
+ writer.writeUShort(self.NewState)
+ flags = self.ReservedFlags
+ if self.SetMark:
+ flags |= 0x8000
+ if self.DontAdvance:
+ flags |= 0x4000
+ if self.CurrentIsKashidaLike:
+ flags |= 0x2000
+ if self.MarkedIsKashidaLike:
+ flags |= 0x1000
+ if self.CurrentInsertBefore:
+ flags |= 0x0800
+ if self.MarkedInsertBefore:
+ flags |= 0x0400
+ flags |= len(self.CurrentInsertionAction) << 5
+ flags |= len(self.MarkedInsertionAction)
+ writer.writeUShort(flags)
+ if len(self.CurrentInsertionAction) > 0:
+ currentIndex = actionIndex[tuple(self.CurrentInsertionAction)]
+ else:
+ currentIndex = 0xFFFF
+ writer.writeUShort(currentIndex)
+ if len(self.MarkedInsertionAction) > 0:
+ markedIndex = actionIndex[tuple(self.MarkedInsertionAction)]
+ else:
+ markedIndex = 0xFFFF
+ writer.writeUShort(markedIndex)
+
+ def decompile(self, reader, font, actionReader):
+ assert actionReader is not None
+ self.NewState = reader.readUShort()
+ flags = reader.readUShort()
+ self.SetMark = bool(flags & 0x8000)
+ self.DontAdvance = bool(flags & 0x4000)
+ self.CurrentIsKashidaLike = bool(flags & 0x2000)
+ self.MarkedIsKashidaLike = bool(flags & 0x1000)
+ self.CurrentInsertBefore = bool(flags & 0x0800)
+ self.MarkedInsertBefore = bool(flags & 0x0400)
+ self.CurrentInsertionAction = self._decompileInsertionAction(
+ actionReader, font, index=reader.readUShort(), count=((flags & 0x03E0) >> 5)
+ )
+ self.MarkedInsertionAction = self._decompileInsertionAction(
+ actionReader, font, index=reader.readUShort(), count=(flags & 0x001F)
+ )
+
+ def _decompileInsertionAction(self, actionReader, font, index, count):
+ if index == 0xFFFF or count == 0:
+ return []
+ reader = actionReader.getSubReader(actionReader.pos + index * 2)
+ return font.getGlyphNameMany(reader.readUShortArray(count))
+
+ def toXML(self, xmlWriter, font, attrs, name):
+ xmlWriter.begintag(name, **attrs)
+ xmlWriter.newline()
+ xmlWriter.simpletag("NewState", value=self.NewState)
+ xmlWriter.newline()
+ self._writeFlagsToXML(xmlWriter)
+ for g in self.CurrentInsertionAction:
+ xmlWriter.simpletag("CurrentInsertionAction", glyph=g)
+ xmlWriter.newline()
+ for g in self.MarkedInsertionAction:
+ xmlWriter.simpletag("MarkedInsertionAction", glyph=g)
+ xmlWriter.newline()
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ self.__init__()
+ content = [t for t in content if isinstance(t, tuple)]
+ for eltName, eltAttrs, eltContent in content:
+ if eltName == "NewState":
+ self.NewState = safeEval(eltAttrs["value"])
+ elif eltName == "Flags":
+ for flag in eltAttrs["value"].split(","):
+ self._setFlag(flag.strip())
+ elif eltName == "CurrentInsertionAction":
+ self.CurrentInsertionAction.append(eltAttrs["glyph"])
+ elif eltName == "MarkedInsertionAction":
+ self.MarkedInsertionAction.append(eltAttrs["glyph"])
+ else:
+ assert False, eltName
+
+ @staticmethod
+ def compileActions(font, states):
+ actions, actionIndex, result = set(), {}, b""
+ for state in states:
+ for _glyphClass, trans in state.Transitions.items():
+ if trans.CurrentInsertionAction is not None:
+ actions.add(tuple(trans.CurrentInsertionAction))
+ if trans.MarkedInsertionAction is not None:
+ actions.add(tuple(trans.MarkedInsertionAction))
+ # Sort the compiled actions in decreasing order of
+ # length, so that the longer sequence come before the
+ # shorter ones.
+ for action in sorted(actions, key=lambda x: (-len(x), x)):
+ # We insert all sub-sequences of the action glyph sequence
+ # into actionIndex. For example, if one action triggers on
+ # glyph sequence [A, B, C, D, E] and another action triggers
+ # on [C, D], we return result=[A, B, C, D, E] (as list of
+ # encoded glyph IDs), and actionIndex={('A','B','C','D','E'): 0,
+ # ('C','D'): 2}.
+ if action in actionIndex:
+ continue
+ for start in range(0, len(action)):
+ startIndex = (len(result) // 2) + start
+ for limit in range(start, len(action)):
+ glyphs = action[start : limit + 1]
+ actionIndex.setdefault(glyphs, startIndex)
+ for glyph in action:
+ glyphID = font.getGlyphID(glyph)
+ result += struct.pack(">H", glyphID)
+ return result, actionIndex
+
+
+class FeatureParams(BaseTable):
+ def compile(self, writer, font):
+ assert (
+ featureParamTypes.get(writer["FeatureTag"]) == self.__class__
+ ), "Wrong FeatureParams type for feature '%s': %s" % (
+ writer["FeatureTag"],
+ self.__class__.__name__,
+ )
+ BaseTable.compile(self, writer, font)
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+
+
+class FeatureParamsSize(FeatureParams):
+ pass
+
+
+class FeatureParamsStylisticSet(FeatureParams):
+ pass
+
+
+class FeatureParamsCharacterVariants(FeatureParams):
+ pass
+
+
+class Coverage(FormatSwitchingBaseTable):
+ # manual implementation to get rid of glyphID dependencies
+
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "glyphs"):
+ self.glyphs = []
+
+ def postRead(self, rawTable, font):
+ if self.Format == 1:
+ self.glyphs = rawTable["GlyphArray"]
+ elif self.Format == 2:
+ glyphs = self.glyphs = []
+ ranges = rawTable["RangeRecord"]
+ # Some SIL fonts have coverage entries that don't have sorted
+ # StartCoverageIndex. If it is so, fixup and warn. We undo
+ # this when writing font out.
+ sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
+ if ranges != sorted_ranges:
+ log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
+ ranges = sorted_ranges
+ del sorted_ranges
+ for r in ranges:
+ start = r.Start
+ end = r.End
+ startID = font.getGlyphID(start)
+ endID = font.getGlyphID(end) + 1
+ glyphs.extend(font.getGlyphNameMany(range(startID, endID)))
+ else:
+ self.glyphs = []
+ log.warning("Unknown Coverage format: %s", self.Format)
+ del self.Format # Don't need this anymore
+
+ def preWrite(self, font):
+ glyphs = getattr(self, "glyphs", None)
+ if glyphs is None:
+ glyphs = self.glyphs = []
+ format = 1
+ rawTable = {"GlyphArray": glyphs}
+ if glyphs:
+ # find out whether Format 2 is more compact or not
+ glyphIDs = font.getGlyphIDMany(glyphs)
+ brokenOrder = sorted(glyphIDs) != glyphIDs
+
+ last = glyphIDs[0]
+ ranges = [[last]]
+ for glyphID in glyphIDs[1:]:
+ if glyphID != last + 1:
+ ranges[-1].append(last)
+ ranges.append([glyphID])
+ last = glyphID
+ ranges[-1].append(last)
+
+ if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
+ # Format 2 is more compact
+ index = 0
+ for i, (start, end) in enumerate(ranges):
+ r = RangeRecord()
+ r.StartID = start
+ r.Start = font.getGlyphName(start)
+ r.End = font.getGlyphName(end)
+ r.StartCoverageIndex = index
+ ranges[i] = r
+ index = index + end - start + 1
+ if brokenOrder:
+ log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
+ ranges.sort(key=lambda a: a.StartID)
+ for r in ranges:
+ del r.StartID
+ format = 2
+ rawTable = {"RangeRecord": ranges}
+ # else:
+ # fallthrough; Format 1 is more compact
+ self.Format = format
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ for glyphName in getattr(self, "glyphs", []):
+ xmlWriter.simpletag("Glyph", value=glyphName)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ glyphs = getattr(self, "glyphs", None)
+ if glyphs is None:
+ glyphs = []
+ self.glyphs = glyphs
+ glyphs.append(attrs["value"])
+
+
+# The special 0xFFFFFFFF delta-set index is used to indicate that there
+# is no variation data in the ItemVariationStore for a given variable field
+NO_VARIATION_INDEX = 0xFFFFFFFF
+
+
+class DeltaSetIndexMap(getFormatSwitchingBaseTableClass("uint8")):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "mapping"):
+ self.mapping = []
+
+ def postRead(self, rawTable, font):
+ assert (rawTable["EntryFormat"] & 0xFFC0) == 0
+ self.mapping = rawTable["mapping"]
+
+ @staticmethod
+ def getEntryFormat(mapping):
+ ored = 0
+ for idx in mapping:
+ ored |= idx
+
+ inner = ored & 0xFFFF
+ innerBits = 0
+ while inner:
+ innerBits += 1
+ inner >>= 1
+ innerBits = max(innerBits, 1)
+ assert innerBits <= 16
+
+ ored = (ored >> (16 - innerBits)) | (ored & ((1 << innerBits) - 1))
+ if ored <= 0x000000FF:
+ entrySize = 1
+ elif ored <= 0x0000FFFF:
+ entrySize = 2
+ elif ored <= 0x00FFFFFF:
+ entrySize = 3
+ else:
+ entrySize = 4
+
+ return ((entrySize - 1) << 4) | (innerBits - 1)
+
+ def preWrite(self, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = self.mapping = []
+ self.Format = 1 if len(mapping) > 0xFFFF else 0
+ rawTable = self.__dict__.copy()
+ rawTable["MappingCount"] = len(mapping)
+ rawTable["EntryFormat"] = self.getEntryFormat(mapping)
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ # Make xml dump less verbose, by omitting no-op entries like:
+ #
+ xmlWriter.comment("Omitted values default to 0xFFFF/0xFFFF (no variations)")
+ xmlWriter.newline()
+ for i, value in enumerate(getattr(self, "mapping", [])):
+ attrs = [("index", i)]
+ if value != NO_VARIATION_INDEX:
+ attrs.extend(
+ [
+ ("outer", value >> 16),
+ ("inner", value & 0xFFFF),
+ ]
+ )
+ xmlWriter.simpletag("Map", attrs)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ self.mapping = mapping = []
+ index = safeEval(attrs["index"])
+ outer = safeEval(attrs.get("outer", "0xFFFF"))
+ inner = safeEval(attrs.get("inner", "0xFFFF"))
+ assert inner <= 0xFFFF
+ mapping.insert(index, (outer << 16) | inner)
+
+ def __getitem__(self, i):
+ return self.mapping[i] if i < len(self.mapping) else NO_VARIATION_INDEX
+
+
+class VarIdxMap(BaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "mapping"):
+ self.mapping = {}
+
+ def postRead(self, rawTable, font):
+ assert (rawTable["EntryFormat"] & 0xFFC0) == 0
+ glyphOrder = font.getGlyphOrder()
+ mapList = rawTable["mapping"]
+ mapList.extend([mapList[-1]] * (len(glyphOrder) - len(mapList)))
+ self.mapping = dict(zip(glyphOrder, mapList))
+
+ def preWrite(self, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = self.mapping = {}
+
+ glyphOrder = font.getGlyphOrder()
+ mapping = [mapping[g] for g in glyphOrder]
+ while len(mapping) > 1 and mapping[-2] == mapping[-1]:
+ del mapping[-1]
+
+ rawTable = {"mapping": mapping}
+ rawTable["MappingCount"] = len(mapping)
+ rawTable["EntryFormat"] = DeltaSetIndexMap.getEntryFormat(mapping)
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ for glyph, value in sorted(getattr(self, "mapping", {}).items()):
+ attrs = (
+ ("glyph", glyph),
+ ("outer", value >> 16),
+ ("inner", value & 0xFFFF),
+ )
+ xmlWriter.simpletag("Map", attrs)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = {}
+ self.mapping = mapping
+ try:
+ glyph = attrs["glyph"]
+ except: # https://github.com/fonttools/fonttools/commit/21cbab8ce9ded3356fef3745122da64dcaf314e9#commitcomment-27649836
+ glyph = font.getGlyphOrder()[attrs["index"]]
+ outer = safeEval(attrs["outer"])
+ inner = safeEval(attrs["inner"])
+ assert inner <= 0xFFFF
+ mapping[glyph] = (outer << 16) | inner
+
+ def __getitem__(self, glyphName):
+ return self.mapping.get(glyphName, NO_VARIATION_INDEX)
+
+
+class VarRegionList(BaseTable):
+ def preWrite(self, font):
+ # The OT spec says VarStore.VarRegionList.RegionAxisCount should always
+ # be equal to the fvar.axisCount, and OTS < v8.0.0 enforces this rule
+ # even when the VarRegionList is empty. We can't treat RegionAxisCount
+ # like a normal propagated count (== len(Region[i].VarRegionAxis)),
+ # otherwise it would default to 0 if VarRegionList is empty.
+ # Thus, we force it to always be equal to fvar.axisCount.
+ # https://github.com/khaledhosny/ots/pull/192
+ fvarTable = font.get("fvar")
+ if fvarTable:
+ self.RegionAxisCount = len(fvarTable.axes)
+ return {
+ **self.__dict__,
+ "RegionAxisCount": CountReference(self.__dict__, "RegionAxisCount"),
+ }
+
+
+class SingleSubst(FormatSwitchingBaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "mapping"):
+ self.mapping = {}
+
+ def postRead(self, rawTable, font):
+ mapping = {}
+ input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
+ if self.Format == 1:
+ delta = rawTable["DeltaGlyphID"]
+ inputGIDS = font.getGlyphIDMany(input)
+ outGIDS = [(glyphID + delta) % 65536 for glyphID in inputGIDS]
+ outNames = font.getGlyphNameMany(outGIDS)
+ for inp, out in zip(input, outNames):
+ mapping[inp] = out
+ elif self.Format == 2:
+ assert (
+ len(input) == rawTable["GlyphCount"]
+ ), "invalid SingleSubstFormat2 table"
+ subst = rawTable["Substitute"]
+ for inp, sub in zip(input, subst):
+ mapping[inp] = sub
+ else:
+ assert 0, "unknown format: %s" % self.Format
+ self.mapping = mapping
+ del self.Format # Don't need this anymore
+
+ def preWrite(self, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = self.mapping = {}
+ items = list(mapping.items())
+ getGlyphID = font.getGlyphID
+ gidItems = [(getGlyphID(a), getGlyphID(b)) for a, b in items]
+ sortableItems = sorted(zip(gidItems, items))
+
+ # figure out format
+ format = 2
+ delta = None
+ for inID, outID in gidItems:
+ if delta is None:
+ delta = (outID - inID) % 65536
+
+ if (inID + delta) % 65536 != outID:
+ break
+ else:
+ if delta is None:
+ # the mapping is empty, better use format 2
+ format = 2
+ else:
+ format = 1
+
+ rawTable = {}
+ self.Format = format
+ cov = Coverage()
+ input = [item[1][0] for item in sortableItems]
+ subst = [item[1][1] for item in sortableItems]
+ cov.glyphs = input
+ rawTable["Coverage"] = cov
+ if format == 1:
+ assert delta is not None
+ rawTable["DeltaGlyphID"] = delta
+ else:
+ rawTable["Substitute"] = subst
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ items = sorted(self.mapping.items())
+ for inGlyph, outGlyph in items:
+ xmlWriter.simpletag("Substitution", [("in", inGlyph), ("out", outGlyph)])
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = {}
+ self.mapping = mapping
+ mapping[attrs["in"]] = attrs["out"]
+
+
+class MultipleSubst(FormatSwitchingBaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "mapping"):
+ self.mapping = {}
+
+ def postRead(self, rawTable, font):
+ mapping = {}
+ if self.Format == 1:
+ glyphs = _getGlyphsFromCoverageTable(rawTable["Coverage"])
+ subst = [s.Substitute for s in rawTable["Sequence"]]
+ mapping = dict(zip(glyphs, subst))
+ else:
+ assert 0, "unknown format: %s" % self.Format
+ self.mapping = mapping
+ del self.Format # Don't need this anymore
+
+ def preWrite(self, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = self.mapping = {}
+ cov = Coverage()
+ cov.glyphs = sorted(list(mapping.keys()), key=font.getGlyphID)
+ self.Format = 1
+ rawTable = {
+ "Coverage": cov,
+ "Sequence": [self.makeSequence_(mapping[glyph]) for glyph in cov.glyphs],
+ }
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ items = sorted(self.mapping.items())
+ for inGlyph, outGlyphs in items:
+ out = ",".join(outGlyphs)
+ xmlWriter.simpletag("Substitution", [("in", inGlyph), ("out", out)])
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ mapping = getattr(self, "mapping", None)
+ if mapping is None:
+ mapping = {}
+ self.mapping = mapping
+
+ # TTX v3.0 and earlier.
+ if name == "Coverage":
+ self.old_coverage_ = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ element_name, element_attrs, _ = element
+ if element_name == "Glyph":
+ self.old_coverage_.append(element_attrs["value"])
+ return
+ if name == "Sequence":
+ index = int(attrs.get("index", len(mapping)))
+ glyph = self.old_coverage_[index]
+ glyph_mapping = mapping[glyph] = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ element_name, element_attrs, _ = element
+ if element_name == "Substitute":
+ glyph_mapping.append(element_attrs["value"])
+ return
+
+ # TTX v3.1 and later.
+ outGlyphs = attrs["out"].split(",") if attrs["out"] else []
+ mapping[attrs["in"]] = [g.strip() for g in outGlyphs]
+
+ @staticmethod
+ def makeSequence_(g):
+ seq = Sequence()
+ seq.Substitute = g
+ return seq
+
+
+class ClassDef(FormatSwitchingBaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "classDefs"):
+ self.classDefs = {}
+
+ def postRead(self, rawTable, font):
+ classDefs = {}
+
+ if self.Format == 1:
+ start = rawTable["StartGlyph"]
+ classList = rawTable["ClassValueArray"]
+ startID = font.getGlyphID(start)
+ endID = startID + len(classList)
+ glyphNames = font.getGlyphNameMany(range(startID, endID))
+ for glyphName, cls in zip(glyphNames, classList):
+ if cls:
+ classDefs[glyphName] = cls
+
+ elif self.Format == 2:
+ records = rawTable["ClassRangeRecord"]
+ for rec in records:
+ cls = rec.Class
+ if not cls:
+ continue
+ start = rec.Start
+ end = rec.End
+ startID = font.getGlyphID(start)
+ endID = font.getGlyphID(end) + 1
+ glyphNames = font.getGlyphNameMany(range(startID, endID))
+ for glyphName in glyphNames:
+ classDefs[glyphName] = cls
+ else:
+ log.warning("Unknown ClassDef format: %s", self.Format)
+ self.classDefs = classDefs
+ del self.Format # Don't need this anymore
+
+ def _getClassRanges(self, font):
+ classDefs = getattr(self, "classDefs", None)
+ if classDefs is None:
+ self.classDefs = {}
+ return
+ getGlyphID = font.getGlyphID
+ items = []
+ for glyphName, cls in classDefs.items():
+ if not cls:
+ continue
+ items.append((getGlyphID(glyphName), glyphName, cls))
+ if items:
+ items.sort()
+ last, lastName, lastCls = items[0]
+ ranges = [[lastCls, last, lastName]]
+ for glyphID, glyphName, cls in items[1:]:
+ if glyphID != last + 1 or cls != lastCls:
+ ranges[-1].extend([last, lastName])
+ ranges.append([cls, glyphID, glyphName])
+ last = glyphID
+ lastName = glyphName
+ lastCls = cls
+ ranges[-1].extend([last, lastName])
+ return ranges
+
+ def preWrite(self, font):
+ format = 2
+ rawTable = {"ClassRangeRecord": []}
+ ranges = self._getClassRanges(font)
+ if ranges:
+ startGlyph = ranges[0][1]
+ endGlyph = ranges[-1][3]
+ glyphCount = endGlyph - startGlyph + 1
+ if len(ranges) * 3 < glyphCount + 1:
+ # Format 2 is more compact
+ for i, (cls, start, startName, end, endName) in enumerate(ranges):
+ rec = ClassRangeRecord()
+ rec.Start = startName
+ rec.End = endName
+ rec.Class = cls
+ ranges[i] = rec
+ format = 2
+ rawTable = {"ClassRangeRecord": ranges}
+ else:
+ # Format 1 is more compact
+ startGlyphName = ranges[0][2]
+ classes = [0] * glyphCount
+ for cls, start, startName, end, endName in ranges:
+ for g in range(start - startGlyph, end - startGlyph + 1):
+ classes[g] = cls
+ format = 1
+ rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
+ self.Format = format
+ return rawTable
+
+ def toXML2(self, xmlWriter, font):
+ items = sorted(self.classDefs.items())
+ for glyphName, cls in items:
+ xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ classDefs = getattr(self, "classDefs", None)
+ if classDefs is None:
+ classDefs = {}
+ self.classDefs = classDefs
+ classDefs[attrs["glyph"]] = int(attrs["class"])
+
+
+class AlternateSubst(FormatSwitchingBaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "alternates"):
+ self.alternates = {}
+
+ def postRead(self, rawTable, font):
+ alternates = {}
+ if self.Format == 1:
+ input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
+ alts = rawTable["AlternateSet"]
+ assert len(input) == len(alts)
+ for inp, alt in zip(input, alts):
+ alternates[inp] = alt.Alternate
+ else:
+ assert 0, "unknown format: %s" % self.Format
+ self.alternates = alternates
+ del self.Format # Don't need this anymore
+
+ def preWrite(self, font):
+ self.Format = 1
+ alternates = getattr(self, "alternates", None)
+ if alternates is None:
+ alternates = self.alternates = {}
+ items = list(alternates.items())
+ for i, (glyphName, set) in enumerate(items):
+ items[i] = font.getGlyphID(glyphName), glyphName, set
+ items.sort()
+ cov = Coverage()
+ cov.glyphs = [item[1] for item in items]
+ alternates = []
+ setList = [item[-1] for item in items]
+ for set in setList:
+ alts = AlternateSet()
+ alts.Alternate = set
+ alternates.append(alts)
+ # a special case to deal with the fact that several hundred Adobe Japan1-5
+ # CJK fonts will overflow an offset if the coverage table isn't pushed to the end.
+ # Also useful in that when splitting a sub-table because of an offset overflow
+ # I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
+ # Allows packing more rules in subtable.
+ self.sortCoverageLast = 1
+ return {"Coverage": cov, "AlternateSet": alternates}
+
+ def toXML2(self, xmlWriter, font):
+ items = sorted(self.alternates.items())
+ for glyphName, alternates in items:
+ xmlWriter.begintag("AlternateSet", glyph=glyphName)
+ xmlWriter.newline()
+ for alt in alternates:
+ xmlWriter.simpletag("Alternate", glyph=alt)
+ xmlWriter.newline()
+ xmlWriter.endtag("AlternateSet")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ alternates = getattr(self, "alternates", None)
+ if alternates is None:
+ alternates = {}
+ self.alternates = alternates
+ glyphName = attrs["glyph"]
+ set = []
+ alternates[glyphName] = set
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ set.append(attrs["glyph"])
+
+
+class LigatureSubst(FormatSwitchingBaseTable):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "ligatures"):
+ self.ligatures = {}
+
+ def postRead(self, rawTable, font):
+ ligatures = {}
+ if self.Format == 1:
+ input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
+ ligSets = rawTable["LigatureSet"]
+ assert len(input) == len(ligSets)
+ for i, inp in enumerate(input):
+ ligatures[inp] = ligSets[i].Ligature
+ else:
+ assert 0, "unknown format: %s" % self.Format
+ self.ligatures = ligatures
+ del self.Format # Don't need this anymore
+
+ @staticmethod
+ def _getLigatureSortKey(components):
+ # Computes a key for ordering ligatures in a GSUB Type-4 lookup.
+
+ # When building the OpenType lookup, we need to make sure that
+ # the longest sequence of components is listed first, so we
+ # use the negative length as the key for sorting.
+ # Note, we no longer need to worry about deterministic order because the
+ # ligature mapping `dict` remembers the insertion order, and this in
+ # turn depends on the order in which the ligatures are written in the FEA.
+ # Since python sort algorithm is stable, the ligatures of equal length
+ # will keep the relative order in which they appear in the feature file.
+ # For example, given the following ligatures (all starting with 'f' and
+ # thus belonging to the same LigatureSet):
+ #
+ # feature liga {
+ # sub f i by f_i;
+ # sub f f f by f_f_f;
+ # sub f f by f_f;
+ # sub f f i by f_f_i;
+ # } liga;
+ #
+ # this should sort to: f_f_f, f_f_i, f_i, f_f
+ # This is also what fea-rs does, see:
+ # https://github.com/adobe-type-tools/afdko/issues/1727
+ # https://github.com/fonttools/fonttools/issues/3428
+ # https://github.com/googlefonts/fontc/pull/680
+ return -len(components)
+
+ def preWrite(self, font):
+ self.Format = 1
+ ligatures = getattr(self, "ligatures", None)
+ if ligatures is None:
+ ligatures = self.ligatures = {}
+
+ if ligatures and isinstance(next(iter(ligatures)), tuple):
+ # New high-level API in v3.1 and later. Note that we just support compiling this
+ # for now. We don't load to this API, and don't do XML with it.
+
+ # ligatures is map from components-sequence to lig-glyph
+ newLigatures = dict()
+ for comps in sorted(ligatures.keys(), key=self._getLigatureSortKey):
+ ligature = Ligature()
+ ligature.Component = comps[1:]
+ ligature.CompCount = len(comps)
+ ligature.LigGlyph = ligatures[comps]
+ newLigatures.setdefault(comps[0], []).append(ligature)
+ ligatures = newLigatures
+
+ items = list(ligatures.items())
+ for i, (glyphName, set) in enumerate(items):
+ items[i] = font.getGlyphID(glyphName), glyphName, set
+ items.sort()
+ cov = Coverage()
+ cov.glyphs = [item[1] for item in items]
+
+ ligSets = []
+ setList = [item[-1] for item in items]
+ for set in setList:
+ ligSet = LigatureSet()
+ ligs = ligSet.Ligature = []
+ for lig in set:
+ ligs.append(lig)
+ ligSets.append(ligSet)
+ # Useful in that when splitting a sub-table because of an offset overflow
+ # I don't need to calculate the change in subtabl offset due to the coverage table size.
+ # Allows packing more rules in subtable.
+ self.sortCoverageLast = 1
+ return {"Coverage": cov, "LigatureSet": ligSets}
+
+ def toXML2(self, xmlWriter, font):
+ items = sorted(self.ligatures.items())
+ for glyphName, ligSets in items:
+ xmlWriter.begintag("LigatureSet", glyph=glyphName)
+ xmlWriter.newline()
+ for lig in ligSets:
+ xmlWriter.simpletag(
+ "Ligature", glyph=lig.LigGlyph, components=",".join(lig.Component)
+ )
+ xmlWriter.newline()
+ xmlWriter.endtag("LigatureSet")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ ligatures = getattr(self, "ligatures", None)
+ if ligatures is None:
+ ligatures = {}
+ self.ligatures = ligatures
+ glyphName = attrs["glyph"]
+ ligs = []
+ ligatures[glyphName] = ligs
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ lig = Ligature()
+ lig.LigGlyph = attrs["glyph"]
+ components = attrs["components"]
+ lig.Component = components.split(",") if components else []
+ lig.CompCount = len(lig.Component)
+ ligs.append(lig)
+
+
+class COLR(BaseTable):
+ def decompile(self, reader, font):
+ # COLRv0 is exceptional in that LayerRecordCount appears *after* the
+ # LayerRecordArray it counts, but the parser logic expects Count fields
+ # to always precede the arrays. Here we work around this by parsing the
+ # LayerRecordCount before the rest of the table, and storing it in
+ # the reader's local state.
+ subReader = reader.getSubReader(offset=0)
+ for conv in self.getConverters():
+ if conv.name != "LayerRecordCount":
+ subReader.advance(conv.staticSize)
+ continue
+ reader[conv.name] = conv.read(subReader, font, tableDict={})
+ break
+ else:
+ raise AssertionError("LayerRecordCount converter not found")
+ return BaseTable.decompile(self, reader, font)
+
+ def preWrite(self, font):
+ # The writer similarly assumes Count values precede the things counted,
+ # thus here we pre-initialize a CountReference; the actual count value
+ # will be set to the lenght of the array by the time this is assembled.
+ self.LayerRecordCount = None
+ return {
+ **self.__dict__,
+ "LayerRecordCount": CountReference(self.__dict__, "LayerRecordCount"),
+ }
+
+ def computeClipBoxes(self, glyphSet: "_TTGlyphSet", quantization: int = 1):
+ if self.Version == 0:
+ return
+
+ clips = {}
+ for rec in self.BaseGlyphList.BaseGlyphPaintRecord:
+ try:
+ clipBox = rec.Paint.computeClipBox(self, glyphSet, quantization)
+ except Exception as e:
+ from fontTools.ttLib import TTLibError
+
+ raise TTLibError(
+ f"Failed to compute COLR ClipBox for {rec.BaseGlyph!r}"
+ ) from e
+
+ if clipBox is not None:
+ clips[rec.BaseGlyph] = clipBox
+
+ hasClipList = hasattr(self, "ClipList") and self.ClipList is not None
+ if not clips:
+ if hasClipList:
+ self.ClipList = None
+ else:
+ if not hasClipList:
+ self.ClipList = ClipList()
+ self.ClipList.Format = 1
+ self.ClipList.clips = clips
+
+
+class LookupList(BaseTable):
+ @property
+ def table(self):
+ for l in self.Lookup:
+ for st in l.SubTable:
+ if type(st).__name__.endswith("Subst"):
+ return "GSUB"
+ if type(st).__name__.endswith("Pos"):
+ return "GPOS"
+ raise ValueError
+
+ def toXML2(self, xmlWriter, font):
+ if (
+ not font
+ or "Debg" not in font
+ or LOOKUP_DEBUG_INFO_KEY not in font["Debg"].data
+ ):
+ return super().toXML2(xmlWriter, font)
+ debugData = font["Debg"].data[LOOKUP_DEBUG_INFO_KEY][self.table]
+ for conv in self.getConverters():
+ if conv.repeat:
+ value = getattr(self, conv.name, [])
+ for lookupIndex, item in enumerate(value):
+ if str(lookupIndex) in debugData:
+ info = LookupDebugInfo(*debugData[str(lookupIndex)])
+ tag = info.location
+ if info.name:
+ tag = f"{info.name}: {tag}"
+ if info.feature:
+ script, language, feature = info.feature
+ tag = f"{tag} in {feature} ({script}/{language})"
+ xmlWriter.comment(tag)
+ xmlWriter.newline()
+
+ conv.xmlWrite(
+ xmlWriter, font, item, conv.name, [("index", lookupIndex)]
+ )
+ else:
+ if conv.aux and not eval(conv.aux, None, vars(self)):
+ continue
+ value = getattr(
+ self, conv.name, None
+ ) # TODO Handle defaults instead of defaulting to None!
+ conv.xmlWrite(xmlWriter, font, value, conv.name, [])
+
+
+class BaseGlyphRecordArray(BaseTable):
+ def preWrite(self, font):
+ self.BaseGlyphRecord = sorted(
+ self.BaseGlyphRecord, key=lambda rec: font.getGlyphID(rec.BaseGlyph)
+ )
+ return self.__dict__.copy()
+
+
+class BaseGlyphList(BaseTable):
+ def preWrite(self, font):
+ self.BaseGlyphPaintRecord = sorted(
+ self.BaseGlyphPaintRecord, key=lambda rec: font.getGlyphID(rec.BaseGlyph)
+ )
+ return self.__dict__.copy()
+
+
+class ClipBoxFormat(IntEnum):
+ Static = 1
+ Variable = 2
+
+ def is_variable(self):
+ return self is self.Variable
+
+ def as_variable(self):
+ return self.Variable
+
+
+class ClipBox(getFormatSwitchingBaseTableClass("uint8")):
+ formatEnum = ClipBoxFormat
+
+ def as_tuple(self):
+ return tuple(getattr(self, conv.name) for conv in self.getConverters())
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}{self.as_tuple()}"
+
+
+class ClipList(getFormatSwitchingBaseTableClass("uint8")):
+ def populateDefaults(self, propagator=None):
+ if not hasattr(self, "clips"):
+ self.clips = {}
+
+ def postRead(self, rawTable, font):
+ clips = {}
+ glyphOrder = font.getGlyphOrder()
+ for i, rec in enumerate(rawTable["ClipRecord"]):
+ if rec.StartGlyphID > rec.EndGlyphID:
+ log.warning(
+ "invalid ClipRecord[%i].StartGlyphID (%i) > "
+ "EndGlyphID (%i); skipped",
+ i,
+ rec.StartGlyphID,
+ rec.EndGlyphID,
+ )
+ continue
+ redefinedGlyphs = []
+ missingGlyphs = []
+ for glyphID in range(rec.StartGlyphID, rec.EndGlyphID + 1):
+ try:
+ glyph = glyphOrder[glyphID]
+ except IndexError:
+ missingGlyphs.append(glyphID)
+ continue
+ if glyph not in clips:
+ clips[glyph] = copy.copy(rec.ClipBox)
+ else:
+ redefinedGlyphs.append(glyphID)
+ if redefinedGlyphs:
+ log.warning(
+ "ClipRecord[%i] overlaps previous records; "
+ "ignoring redefined clip boxes for the "
+ "following glyph ID range: [%i-%i]",
+ i,
+ min(redefinedGlyphs),
+ max(redefinedGlyphs),
+ )
+ if missingGlyphs:
+ log.warning(
+ "ClipRecord[%i] range references missing " "glyph IDs: [%i-%i]",
+ i,
+ min(missingGlyphs),
+ max(missingGlyphs),
+ )
+ self.clips = clips
+
+ def groups(self):
+ glyphsByClip = defaultdict(list)
+ uniqueClips = {}
+ for glyphName, clipBox in self.clips.items():
+ key = clipBox.as_tuple()
+ glyphsByClip[key].append(glyphName)
+ if key not in uniqueClips:
+ uniqueClips[key] = clipBox
+ return {
+ frozenset(glyphs): uniqueClips[key] for key, glyphs in glyphsByClip.items()
+ }
+
+ def preWrite(self, font):
+ if not hasattr(self, "clips"):
+ self.clips = {}
+ clipBoxRanges = {}
+ glyphMap = font.getReverseGlyphMap()
+ for glyphs, clipBox in self.groups().items():
+ glyphIDs = sorted(
+ glyphMap[glyphName] for glyphName in glyphs if glyphName in glyphMap
+ )
+ if not glyphIDs:
+ continue
+ last = glyphIDs[0]
+ ranges = [[last]]
+ for glyphID in glyphIDs[1:]:
+ if glyphID != last + 1:
+ ranges[-1].append(last)
+ ranges.append([glyphID])
+ last = glyphID
+ ranges[-1].append(last)
+ for start, end in ranges:
+ assert (start, end) not in clipBoxRanges
+ clipBoxRanges[(start, end)] = clipBox
+
+ clipRecords = []
+ for (start, end), clipBox in sorted(clipBoxRanges.items()):
+ record = ClipRecord()
+ record.StartGlyphID = start
+ record.EndGlyphID = end
+ record.ClipBox = clipBox
+ clipRecords.append(record)
+ rawTable = {
+ "ClipCount": len(clipRecords),
+ "ClipRecord": clipRecords,
+ }
+ return rawTable
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ tableName = name if name else self.__class__.__name__
+ if attrs is None:
+ attrs = []
+ if hasattr(self, "Format"):
+ attrs.append(("Format", self.Format))
+ xmlWriter.begintag(tableName, attrs)
+ xmlWriter.newline()
+ # sort clips alphabetically to ensure deterministic XML dump
+ for glyphs, clipBox in sorted(
+ self.groups().items(), key=lambda item: min(item[0])
+ ):
+ xmlWriter.begintag("Clip")
+ xmlWriter.newline()
+ for glyphName in sorted(glyphs):
+ xmlWriter.simpletag("Glyph", value=glyphName)
+ xmlWriter.newline()
+ xmlWriter.begintag("ClipBox", [("Format", clipBox.Format)])
+ xmlWriter.newline()
+ clipBox.toXML2(xmlWriter, font)
+ xmlWriter.endtag("ClipBox")
+ xmlWriter.newline()
+ xmlWriter.endtag("Clip")
+ xmlWriter.newline()
+ xmlWriter.endtag(tableName)
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, font):
+ clips = getattr(self, "clips", None)
+ if clips is None:
+ self.clips = clips = {}
+ assert name == "Clip"
+ glyphs = []
+ clipBox = None
+ for elem in content:
+ if not isinstance(elem, tuple):
+ continue
+ name, attrs, content = elem
+ if name == "Glyph":
+ glyphs.append(attrs["value"])
+ elif name == "ClipBox":
+ clipBox = ClipBox()
+ clipBox.Format = safeEval(attrs["Format"])
+ for elem in content:
+ if not isinstance(elem, tuple):
+ continue
+ name, attrs, content = elem
+ clipBox.fromXML(name, attrs, content, font)
+ if clipBox:
+ for glyphName in glyphs:
+ clips[glyphName] = clipBox
+
+
+class ExtendMode(IntEnum):
+ PAD = 0
+ REPEAT = 1
+ REFLECT = 2
+
+
+# Porter-Duff modes for COLRv1 PaintComposite:
+# https://github.com/googlefonts/colr-gradients-spec/tree/off_sub_1#compositemode-enumeration
+class CompositeMode(IntEnum):
+ CLEAR = 0
+ SRC = 1
+ DEST = 2
+ SRC_OVER = 3
+ DEST_OVER = 4
+ SRC_IN = 5
+ DEST_IN = 6
+ SRC_OUT = 7
+ DEST_OUT = 8
+ SRC_ATOP = 9
+ DEST_ATOP = 10
+ XOR = 11
+ PLUS = 12
+ SCREEN = 13
+ OVERLAY = 14
+ DARKEN = 15
+ LIGHTEN = 16
+ COLOR_DODGE = 17
+ COLOR_BURN = 18
+ HARD_LIGHT = 19
+ SOFT_LIGHT = 20
+ DIFFERENCE = 21
+ EXCLUSION = 22
+ MULTIPLY = 23
+ HSL_HUE = 24
+ HSL_SATURATION = 25
+ HSL_COLOR = 26
+ HSL_LUMINOSITY = 27
+
+
+class PaintFormat(IntEnum):
+ PaintColrLayers = 1
+ PaintSolid = 2
+ PaintVarSolid = 3
+ PaintLinearGradient = 4
+ PaintVarLinearGradient = 5
+ PaintRadialGradient = 6
+ PaintVarRadialGradient = 7
+ PaintSweepGradient = 8
+ PaintVarSweepGradient = 9
+ PaintGlyph = 10
+ PaintColrGlyph = 11
+ PaintTransform = 12
+ PaintVarTransform = 13
+ PaintTranslate = 14
+ PaintVarTranslate = 15
+ PaintScale = 16
+ PaintVarScale = 17
+ PaintScaleAroundCenter = 18
+ PaintVarScaleAroundCenter = 19
+ PaintScaleUniform = 20
+ PaintVarScaleUniform = 21
+ PaintScaleUniformAroundCenter = 22
+ PaintVarScaleUniformAroundCenter = 23
+ PaintRotate = 24
+ PaintVarRotate = 25
+ PaintRotateAroundCenter = 26
+ PaintVarRotateAroundCenter = 27
+ PaintSkew = 28
+ PaintVarSkew = 29
+ PaintSkewAroundCenter = 30
+ PaintVarSkewAroundCenter = 31
+ PaintComposite = 32
+
+ def is_variable(self):
+ return self.name.startswith("PaintVar")
+
+ def as_variable(self):
+ if self.is_variable():
+ return self
+ try:
+ return PaintFormat.__members__[f"PaintVar{self.name[5:]}"]
+ except KeyError:
+ return None
+
+
+class Paint(getFormatSwitchingBaseTableClass("uint8")):
+ formatEnum = PaintFormat
+
+ def getFormatName(self):
+ try:
+ return self.formatEnum(self.Format).name
+ except ValueError:
+ raise NotImplementedError(f"Unknown Paint format: {self.Format}")
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ tableName = name if name else self.__class__.__name__
+ if attrs is None:
+ attrs = []
+ attrs.append(("Format", self.Format))
+ xmlWriter.begintag(tableName, attrs)
+ xmlWriter.comment(self.getFormatName())
+ xmlWriter.newline()
+ self.toXML2(xmlWriter, font)
+ xmlWriter.endtag(tableName)
+ xmlWriter.newline()
+
+ def iterPaintSubTables(self, colr: COLR) -> Iterator[BaseTable.SubTableEntry]:
+ if self.Format == PaintFormat.PaintColrLayers:
+ # https://github.com/fonttools/fonttools/issues/2438: don't die when no LayerList exists
+ layers = []
+ if colr.LayerList is not None:
+ layers = colr.LayerList.Paint
+ yield from (
+ BaseTable.SubTableEntry(name="Layers", value=v, index=i)
+ for i, v in enumerate(
+ layers[self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers]
+ )
+ )
+ return
+
+ if self.Format == PaintFormat.PaintColrGlyph:
+ for record in colr.BaseGlyphList.BaseGlyphPaintRecord:
+ if record.BaseGlyph == self.Glyph:
+ yield BaseTable.SubTableEntry(name="BaseGlyph", value=record.Paint)
+ return
+ else:
+ raise KeyError(f"{self.Glyph!r} not in colr.BaseGlyphList")
+
+ for conv in self.getConverters():
+ if conv.tableClass is not None and issubclass(conv.tableClass, type(self)):
+ value = getattr(self, conv.name)
+ yield BaseTable.SubTableEntry(name=conv.name, value=value)
+
+ def getChildren(self, colr) -> List["Paint"]:
+ # this is kept for backward compatibility (e.g. it's used by the subsetter)
+ return [p.value for p in self.iterPaintSubTables(colr)]
+
+ def traverse(self, colr: COLR, callback):
+ """Depth-first traversal of graph rooted at self, callback on each node."""
+ if not callable(callback):
+ raise TypeError("callback must be callable")
+
+ for path in dfs_base_table(
+ self, iter_subtables_fn=lambda paint: paint.iterPaintSubTables(colr)
+ ):
+ paint = path[-1].value
+ callback(paint)
+
+ def getTransform(self) -> Transform:
+ if self.Format == PaintFormat.PaintTransform:
+ t = self.Transform
+ return Transform(t.xx, t.yx, t.xy, t.yy, t.dx, t.dy)
+ elif self.Format == PaintFormat.PaintTranslate:
+ return Identity.translate(self.dx, self.dy)
+ elif self.Format == PaintFormat.PaintScale:
+ return Identity.scale(self.scaleX, self.scaleY)
+ elif self.Format == PaintFormat.PaintScaleAroundCenter:
+ return (
+ Identity.translate(self.centerX, self.centerY)
+ .scale(self.scaleX, self.scaleY)
+ .translate(-self.centerX, -self.centerY)
+ )
+ elif self.Format == PaintFormat.PaintScaleUniform:
+ return Identity.scale(self.scale)
+ elif self.Format == PaintFormat.PaintScaleUniformAroundCenter:
+ return (
+ Identity.translate(self.centerX, self.centerY)
+ .scale(self.scale)
+ .translate(-self.centerX, -self.centerY)
+ )
+ elif self.Format == PaintFormat.PaintRotate:
+ return Identity.rotate(radians(self.angle))
+ elif self.Format == PaintFormat.PaintRotateAroundCenter:
+ return (
+ Identity.translate(self.centerX, self.centerY)
+ .rotate(radians(self.angle))
+ .translate(-self.centerX, -self.centerY)
+ )
+ elif self.Format == PaintFormat.PaintSkew:
+ return Identity.skew(radians(-self.xSkewAngle), radians(self.ySkewAngle))
+ elif self.Format == PaintFormat.PaintSkewAroundCenter:
+ return (
+ Identity.translate(self.centerX, self.centerY)
+ .skew(radians(-self.xSkewAngle), radians(self.ySkewAngle))
+ .translate(-self.centerX, -self.centerY)
+ )
+ if PaintFormat(self.Format).is_variable():
+ raise NotImplementedError(f"Variable Paints not supported: {self.Format}")
+
+ return Identity
+
+ def computeClipBox(
+ self, colr: COLR, glyphSet: "_TTGlyphSet", quantization: int = 1
+ ) -> Optional[ClipBox]:
+ pen = ControlBoundsPen(glyphSet)
+ for path in dfs_base_table(
+ self, iter_subtables_fn=lambda paint: paint.iterPaintSubTables(colr)
+ ):
+ paint = path[-1].value
+ if paint.Format == PaintFormat.PaintGlyph:
+ transformation = reduce(
+ Transform.transform,
+ (st.value.getTransform() for st in path),
+ Identity,
+ )
+ glyphSet[paint.Glyph].draw(TransformPen(pen, transformation))
+
+ if pen.bounds is None:
+ return None
+
+ cb = ClipBox()
+ cb.Format = int(ClipBoxFormat.Static)
+ cb.xMin, cb.yMin, cb.xMax, cb.yMax = quantizeRect(pen.bounds, quantization)
+ return cb
+
+
+# For each subtable format there is a class. However, we don't really distinguish
+# between "field name" and "format name": often these are the same. Yet there's
+# a whole bunch of fields with different names. The following dict is a mapping
+# from "format name" to "field name". _buildClasses() uses this to create a
+# subclass for each alternate field name.
+#
+_equivalents = {
+ "MarkArray": ("Mark1Array",),
+ "LangSys": ("DefaultLangSys",),
+ "Coverage": (
+ "MarkCoverage",
+ "BaseCoverage",
+ "LigatureCoverage",
+ "Mark1Coverage",
+ "Mark2Coverage",
+ "BacktrackCoverage",
+ "InputCoverage",
+ "LookAheadCoverage",
+ "VertGlyphCoverage",
+ "HorizGlyphCoverage",
+ "TopAccentCoverage",
+ "ExtendedShapeCoverage",
+ "MathKernCoverage",
+ ),
+ "ClassDef": (
+ "ClassDef1",
+ "ClassDef2",
+ "BacktrackClassDef",
+ "InputClassDef",
+ "LookAheadClassDef",
+ "GlyphClassDef",
+ "MarkAttachClassDef",
+ ),
+ "Anchor": (
+ "EntryAnchor",
+ "ExitAnchor",
+ "BaseAnchor",
+ "LigatureAnchor",
+ "Mark2Anchor",
+ "MarkAnchor",
+ ),
+ "Device": (
+ "XPlaDevice",
+ "YPlaDevice",
+ "XAdvDevice",
+ "YAdvDevice",
+ "XDeviceTable",
+ "YDeviceTable",
+ "DeviceTable",
+ ),
+ "Axis": (
+ "HorizAxis",
+ "VertAxis",
+ ),
+ "MinMax": ("DefaultMinMax",),
+ "BaseCoord": (
+ "MinCoord",
+ "MaxCoord",
+ ),
+ "JstfLangSys": ("DefJstfLangSys",),
+ "JstfGSUBModList": (
+ "ShrinkageEnableGSUB",
+ "ShrinkageDisableGSUB",
+ "ExtensionEnableGSUB",
+ "ExtensionDisableGSUB",
+ ),
+ "JstfGPOSModList": (
+ "ShrinkageEnableGPOS",
+ "ShrinkageDisableGPOS",
+ "ExtensionEnableGPOS",
+ "ExtensionDisableGPOS",
+ ),
+ "JstfMax": (
+ "ShrinkageJstfMax",
+ "ExtensionJstfMax",
+ ),
+ "MathKern": (
+ "TopRightMathKern",
+ "TopLeftMathKern",
+ "BottomRightMathKern",
+ "BottomLeftMathKern",
+ ),
+ "MathGlyphConstruction": ("VertGlyphConstruction", "HorizGlyphConstruction"),
+}
+
+#
+# OverFlow logic, to automatically create ExtensionLookups
+# XXX This should probably move to otBase.py
+#
+
+
+def fixLookupOverFlows(ttf, overflowRecord):
+ """Either the offset from the LookupList to a lookup overflowed, or
+ an offset from a lookup to a subtable overflowed.
+
+ The table layout is::
+
+ GPSO/GUSB
+ Script List
+ Feature List
+ LookUpList
+ Lookup[0] and contents
+ SubTable offset list
+ SubTable[0] and contents
+ ...
+ SubTable[n] and contents
+ ...
+ Lookup[n] and contents
+ SubTable offset list
+ SubTable[0] and contents
+ ...
+ SubTable[n] and contents
+
+ If the offset to a lookup overflowed (SubTableIndex is None)
+ we must promote the *previous* lookup to an Extension type.
+
+ If the offset from a lookup to subtable overflowed, then we must promote it
+ to an Extension Lookup type.
+ """
+ ok = 0
+ lookupIndex = overflowRecord.LookupListIndex
+ if overflowRecord.SubTableIndex is None:
+ lookupIndex = lookupIndex - 1
+ if lookupIndex < 0:
+ return ok
+ if overflowRecord.tableType == "GSUB":
+ extType = 7
+ elif overflowRecord.tableType == "GPOS":
+ extType = 9
+
+ lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
+ lookup = lookups[lookupIndex]
+ # If the previous lookup is an extType, look further back. Very unlikely, but possible.
+ while lookup.SubTable[0].__class__.LookupType == extType:
+ lookupIndex = lookupIndex - 1
+ if lookupIndex < 0:
+ return ok
+ lookup = lookups[lookupIndex]
+
+ for lookupIndex in range(lookupIndex, len(lookups)):
+ lookup = lookups[lookupIndex]
+ if lookup.LookupType != extType:
+ lookup.LookupType = extType
+ for si, subTable in enumerate(lookup.SubTable):
+ extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
+ extSubTable = extSubTableClass()
+ extSubTable.Format = 1
+ extSubTable.ExtSubTable = subTable
+ lookup.SubTable[si] = extSubTable
+ ok = 1
+ return ok
+
+
+def splitMultipleSubst(oldSubTable, newSubTable, overflowRecord):
+ ok = 1
+ oldMapping = sorted(oldSubTable.mapping.items())
+ oldLen = len(oldMapping)
+
+ if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
+ # Coverage table is written last. Overflow is to or within the
+ # the coverage table. We will just cut the subtable in half.
+ newLen = oldLen // 2
+
+ elif overflowRecord.itemName == "Sequence":
+ # We just need to back up by two items from the overflowed
+ # Sequence index to make sure the offset to the Coverage table
+ # doesn't overflow.
+ newLen = overflowRecord.itemIndex - 1
+
+ newSubTable.mapping = {}
+ for i in range(newLen, oldLen):
+ item = oldMapping[i]
+ key = item[0]
+ newSubTable.mapping[key] = item[1]
+ del oldSubTable.mapping[key]
+
+ return ok
+
+
+def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
+ ok = 1
+ if hasattr(oldSubTable, "sortCoverageLast"):
+ newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
+
+ oldAlts = sorted(oldSubTable.alternates.items())
+ oldLen = len(oldAlts)
+
+ if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
+ # Coverage table is written last. overflow is to or within the
+ # the coverage table. We will just cut the subtable in half.
+ newLen = oldLen // 2
+
+ elif overflowRecord.itemName == "AlternateSet":
+ # We just need to back up by two items
+ # from the overflowed AlternateSet index to make sure the offset
+ # to the Coverage table doesn't overflow.
+ newLen = overflowRecord.itemIndex - 1
+
+ newSubTable.alternates = {}
+ for i in range(newLen, oldLen):
+ item = oldAlts[i]
+ key = item[0]
+ newSubTable.alternates[key] = item[1]
+ del oldSubTable.alternates[key]
+
+ return ok
+
+
+def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
+ ok = 1
+ oldLigs = sorted(oldSubTable.ligatures.items())
+ oldLen = len(oldLigs)
+
+ if overflowRecord.itemName in ["Coverage", "RangeRecord"]:
+ # Coverage table is written last. overflow is to or within the
+ # the coverage table. We will just cut the subtable in half.
+ newLen = oldLen // 2
+
+ elif overflowRecord.itemName == "LigatureSet":
+ # We just need to back up by two items
+ # from the overflowed AlternateSet index to make sure the offset
+ # to the Coverage table doesn't overflow.
+ newLen = overflowRecord.itemIndex - 1
+
+ newSubTable.ligatures = {}
+ for i in range(newLen, oldLen):
+ item = oldLigs[i]
+ key = item[0]
+ newSubTable.ligatures[key] = item[1]
+ del oldSubTable.ligatures[key]
+
+ return ok
+
+
+def splitPairPos(oldSubTable, newSubTable, overflowRecord):
+ st = oldSubTable
+ ok = False
+ newSubTable.Format = oldSubTable.Format
+ if oldSubTable.Format == 1 and len(oldSubTable.PairSet) > 1:
+ for name in "ValueFormat1", "ValueFormat2":
+ setattr(newSubTable, name, getattr(oldSubTable, name))
+
+ # Move top half of coverage to new subtable
+
+ newSubTable.Coverage = oldSubTable.Coverage.__class__()
+
+ coverage = oldSubTable.Coverage.glyphs
+ records = oldSubTable.PairSet
+
+ oldCount = len(oldSubTable.PairSet) // 2
+
+ oldSubTable.Coverage.glyphs = coverage[:oldCount]
+ oldSubTable.PairSet = records[:oldCount]
+
+ newSubTable.Coverage.glyphs = coverage[oldCount:]
+ newSubTable.PairSet = records[oldCount:]
+
+ oldSubTable.PairSetCount = len(oldSubTable.PairSet)
+ newSubTable.PairSetCount = len(newSubTable.PairSet)
+
+ ok = True
+
+ elif oldSubTable.Format == 2 and len(oldSubTable.Class1Record) > 1:
+ if not hasattr(oldSubTable, "Class2Count"):
+ oldSubTable.Class2Count = len(oldSubTable.Class1Record[0].Class2Record)
+ for name in "Class2Count", "ClassDef2", "ValueFormat1", "ValueFormat2":
+ setattr(newSubTable, name, getattr(oldSubTable, name))
+
+ # The two subtables will still have the same ClassDef2 and the table
+ # sharing will still cause the sharing to overflow. As such, disable
+ # sharing on the one that is serialized second (that's oldSubTable).
+ oldSubTable.DontShare = True
+
+ # Move top half of class numbers to new subtable
+
+ newSubTable.Coverage = oldSubTable.Coverage.__class__()
+ newSubTable.ClassDef1 = oldSubTable.ClassDef1.__class__()
+
+ coverage = oldSubTable.Coverage.glyphs
+ classDefs = oldSubTable.ClassDef1.classDefs
+ records = oldSubTable.Class1Record
+
+ oldCount = len(oldSubTable.Class1Record) // 2
+ newGlyphs = set(k for k, v in classDefs.items() if v >= oldCount)
+
+ oldSubTable.Coverage.glyphs = [g for g in coverage if g not in newGlyphs]
+ oldSubTable.ClassDef1.classDefs = {
+ k: v for k, v in classDefs.items() if v < oldCount
+ }
+ oldSubTable.Class1Record = records[:oldCount]
+
+ newSubTable.Coverage.glyphs = [g for g in coverage if g in newGlyphs]
+ newSubTable.ClassDef1.classDefs = {
+ k: (v - oldCount) for k, v in classDefs.items() if v > oldCount
+ }
+ newSubTable.Class1Record = records[oldCount:]
+
+ oldSubTable.Class1Count = len(oldSubTable.Class1Record)
+ newSubTable.Class1Count = len(newSubTable.Class1Record)
+
+ ok = True
+
+ return ok
+
+
+def splitMarkBasePos(oldSubTable, newSubTable, overflowRecord):
+ # split half of the mark classes to the new subtable
+ classCount = oldSubTable.ClassCount
+ if classCount < 2:
+ # oh well, not much left to split...
+ return False
+
+ oldClassCount = classCount // 2
+ newClassCount = classCount - oldClassCount
+
+ oldMarkCoverage, oldMarkRecords = [], []
+ newMarkCoverage, newMarkRecords = [], []
+ for glyphName, markRecord in zip(
+ oldSubTable.MarkCoverage.glyphs, oldSubTable.MarkArray.MarkRecord
+ ):
+ if markRecord.Class < oldClassCount:
+ oldMarkCoverage.append(glyphName)
+ oldMarkRecords.append(markRecord)
+ else:
+ markRecord.Class -= oldClassCount
+ newMarkCoverage.append(glyphName)
+ newMarkRecords.append(markRecord)
+
+ oldBaseRecords, newBaseRecords = [], []
+ for rec in oldSubTable.BaseArray.BaseRecord:
+ oldBaseRecord, newBaseRecord = rec.__class__(), rec.__class__()
+ oldBaseRecord.BaseAnchor = rec.BaseAnchor[:oldClassCount]
+ newBaseRecord.BaseAnchor = rec.BaseAnchor[oldClassCount:]
+ oldBaseRecords.append(oldBaseRecord)
+ newBaseRecords.append(newBaseRecord)
+
+ newSubTable.Format = oldSubTable.Format
+
+ oldSubTable.MarkCoverage.glyphs = oldMarkCoverage
+ newSubTable.MarkCoverage = oldSubTable.MarkCoverage.__class__()
+ newSubTable.MarkCoverage.glyphs = newMarkCoverage
+
+ # share the same BaseCoverage in both halves
+ newSubTable.BaseCoverage = oldSubTable.BaseCoverage
+
+ oldSubTable.ClassCount = oldClassCount
+ newSubTable.ClassCount = newClassCount
+
+ oldSubTable.MarkArray.MarkRecord = oldMarkRecords
+ newSubTable.MarkArray = oldSubTable.MarkArray.__class__()
+ newSubTable.MarkArray.MarkRecord = newMarkRecords
+
+ oldSubTable.MarkArray.MarkCount = len(oldMarkRecords)
+ newSubTable.MarkArray.MarkCount = len(newMarkRecords)
+
+ oldSubTable.BaseArray.BaseRecord = oldBaseRecords
+ newSubTable.BaseArray = oldSubTable.BaseArray.__class__()
+ newSubTable.BaseArray.BaseRecord = newBaseRecords
+
+ oldSubTable.BaseArray.BaseCount = len(oldBaseRecords)
+ newSubTable.BaseArray.BaseCount = len(newBaseRecords)
+
+ return True
+
+
+splitTable = {
+ "GSUB": {
+ # 1: splitSingleSubst,
+ 2: splitMultipleSubst,
+ 3: splitAlternateSubst,
+ 4: splitLigatureSubst,
+ # 5: splitContextSubst,
+ # 6: splitChainContextSubst,
+ # 7: splitExtensionSubst,
+ # 8: splitReverseChainSingleSubst,
+ },
+ "GPOS": {
+ # 1: splitSinglePos,
+ 2: splitPairPos,
+ # 3: splitCursivePos,
+ 4: splitMarkBasePos,
+ # 5: splitMarkLigPos,
+ # 6: splitMarkMarkPos,
+ # 7: splitContextPos,
+ # 8: splitChainContextPos,
+ # 9: splitExtensionPos,
+ },
+}
+
+
+def fixSubTableOverFlows(ttf, overflowRecord):
+ """
+ An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
+ """
+ table = ttf[overflowRecord.tableType].table
+ lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex]
+ subIndex = overflowRecord.SubTableIndex
+ subtable = lookup.SubTable[subIndex]
+
+ # First, try not sharing anything for this subtable...
+ if not hasattr(subtable, "DontShare"):
+ subtable.DontShare = True
+ return True
+
+ if hasattr(subtable, "ExtSubTable"):
+ # We split the subtable of the Extension table, and add a new Extension table
+ # to contain the new subtable.
+
+ subTableType = subtable.ExtSubTable.__class__.LookupType
+ extSubTable = subtable
+ subtable = extSubTable.ExtSubTable
+ newExtSubTableClass = lookupTypes[overflowRecord.tableType][
+ extSubTable.__class__.LookupType
+ ]
+ newExtSubTable = newExtSubTableClass()
+ newExtSubTable.Format = extSubTable.Format
+ toInsert = newExtSubTable
+
+ newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
+ newSubTable = newSubTableClass()
+ newExtSubTable.ExtSubTable = newSubTable
+ else:
+ subTableType = subtable.__class__.LookupType
+ newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
+ newSubTable = newSubTableClass()
+ toInsert = newSubTable
+
+ if hasattr(lookup, "SubTableCount"): # may not be defined yet.
+ lookup.SubTableCount = lookup.SubTableCount + 1
+
+ try:
+ splitFunc = splitTable[overflowRecord.tableType][subTableType]
+ except KeyError:
+ log.error(
+ "Don't know how to split %s lookup type %s",
+ overflowRecord.tableType,
+ subTableType,
+ )
+ return False
+
+ ok = splitFunc(subtable, newSubTable, overflowRecord)
+ if ok:
+ lookup.SubTable.insert(subIndex + 1, toInsert)
+ return ok
+
+
+# End of OverFlow logic
+
+
+def _buildClasses():
+ import re
+ from .otData import otData
+
+ formatPat = re.compile(r"([A-Za-z0-9]+)Format(\d+)$")
+ namespace = globals()
+
+ # populate module with classes
+ for name, table in otData:
+ baseClass = BaseTable
+ m = formatPat.match(name)
+ if m:
+ # XxxFormatN subtable, we only add the "base" table
+ name = m.group(1)
+ # the first row of a format-switching otData table describes the Format;
+ # the first column defines the type of the Format field.
+ # Currently this can be either 'uint16' or 'uint8'.
+ formatType = table[0][0]
+ baseClass = getFormatSwitchingBaseTableClass(formatType)
+ if name not in namespace:
+ # the class doesn't exist yet, so the base implementation is used.
+ cls = type(name, (baseClass,), {})
+ if name in ("GSUB", "GPOS"):
+ cls.DontShare = True
+ namespace[name] = cls
+
+ # link Var{Table} <-> {Table} (e.g. ColorStop <-> VarColorStop, etc.)
+ for name, _ in otData:
+ if name.startswith("Var") and len(name) > 3 and name[3:] in namespace:
+ varType = namespace[name]
+ noVarType = namespace[name[3:]]
+ varType.NoVarType = noVarType
+ noVarType.VarType = varType
+
+ for base, alts in _equivalents.items():
+ base = namespace[base]
+ for alt in alts:
+ namespace[alt] = base
+
+ global lookupTypes
+ lookupTypes = {
+ "GSUB": {
+ 1: SingleSubst,
+ 2: MultipleSubst,
+ 3: AlternateSubst,
+ 4: LigatureSubst,
+ 5: ContextSubst,
+ 6: ChainContextSubst,
+ 7: ExtensionSubst,
+ 8: ReverseChainSingleSubst,
+ },
+ "GPOS": {
+ 1: SinglePos,
+ 2: PairPos,
+ 3: CursivePos,
+ 4: MarkBasePos,
+ 5: MarkLigPos,
+ 6: MarkMarkPos,
+ 7: ContextPos,
+ 8: ChainContextPos,
+ 9: ExtensionPos,
+ },
+ "mort": {
+ 4: NoncontextualMorph,
+ },
+ "morx": {
+ 0: RearrangementMorph,
+ 1: ContextualMorph,
+ 2: LigatureMorph,
+ # 3: Reserved,
+ 4: NoncontextualMorph,
+ 5: InsertionMorph,
+ },
+ }
+ lookupTypes["JSTF"] = lookupTypes["GPOS"] # JSTF contains GPOS
+ for lookupEnum in lookupTypes.values():
+ for enum, cls in lookupEnum.items():
+ cls.LookupType = enum
+
+ global featureParamTypes
+ featureParamTypes = {
+ "size": FeatureParamsSize,
+ }
+ for i in range(1, 20 + 1):
+ featureParamTypes["ss%02d" % i] = FeatureParamsStylisticSet
+ for i in range(1, 99 + 1):
+ featureParamTypes["cv%02d" % i] = FeatureParamsCharacterVariants
+
+ # add converters to classes
+ from .otConverters import buildConverters
+
+ for name, table in otData:
+ m = formatPat.match(name)
+ if m:
+ # XxxFormatN subtable, add converter to "base" table
+ name, format = m.groups()
+ format = int(format)
+ cls = namespace[name]
+ if not hasattr(cls, "converters"):
+ cls.converters = {}
+ cls.convertersByName = {}
+ converters, convertersByName = buildConverters(table[1:], namespace)
+ cls.converters[format] = converters
+ cls.convertersByName[format] = convertersByName
+ # XXX Add staticSize?
+ else:
+ cls = namespace[name]
+ cls.converters, cls.convertersByName = buildConverters(table, namespace)
+ # XXX Add staticSize?
+
+
+_buildClasses()
+
+
+def _getGlyphsFromCoverageTable(coverage):
+ if coverage is None:
+ # empty coverage table
+ return []
+ else:
+ return coverage.glyphs
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/otTraverse.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/otTraverse.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b624533f150cf16079b3acb8d5ae439236ef10a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/otTraverse.py
@@ -0,0 +1,163 @@
+"""Methods for traversing trees of otData-driven OpenType tables."""
+
+from collections import deque
+from typing import Callable, Deque, Iterable, List, Optional, Tuple
+from .otBase import BaseTable
+
+
+__all__ = [
+ "bfs_base_table",
+ "dfs_base_table",
+ "SubTablePath",
+]
+
+
+class SubTablePath(Tuple[BaseTable.SubTableEntry, ...]):
+ def __str__(self) -> str:
+ path_parts = []
+ for entry in self:
+ path_part = entry.name
+ if entry.index is not None:
+ path_part += f"[{entry.index}]"
+ path_parts.append(path_part)
+ return ".".join(path_parts)
+
+
+# Given f(current frontier, new entries) add new entries to frontier
+AddToFrontierFn = Callable[[Deque[SubTablePath], List[SubTablePath]], None]
+
+
+def dfs_base_table(
+ root: BaseTable,
+ root_accessor: Optional[str] = None,
+ skip_root: bool = False,
+ predicate: Optional[Callable[[SubTablePath], bool]] = None,
+ iter_subtables_fn: Optional[
+ Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
+ ] = None,
+) -> Iterable[SubTablePath]:
+ """Depth-first search tree of BaseTables.
+
+ Args:
+ root (BaseTable): the root of the tree.
+ root_accessor (Optional[str]): attribute name for the root table, if any (mostly
+ useful for debugging).
+ skip_root (Optional[bool]): if True, the root itself is not visited, only its
+ children.
+ predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
+ paths. If True, the path is yielded and its subtables are added to the
+ queue. If False, the path is skipped and its subtables are not traversed.
+ iter_subtables_fn (Optional[Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]]):
+ function to iterate over subtables of a table. If None, the default
+ BaseTable.iterSubTables() is used.
+
+ Yields:
+ SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
+ for each of the nodes in the tree. The last entry in a path is the current
+ subtable, whereas preceding ones refer to its parent tables all the way up to
+ the root.
+ """
+ yield from _traverse_ot_data(
+ root,
+ root_accessor,
+ skip_root,
+ predicate,
+ lambda frontier, new: frontier.extendleft(reversed(new)),
+ iter_subtables_fn,
+ )
+
+
+def bfs_base_table(
+ root: BaseTable,
+ root_accessor: Optional[str] = None,
+ skip_root: bool = False,
+ predicate: Optional[Callable[[SubTablePath], bool]] = None,
+ iter_subtables_fn: Optional[
+ Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
+ ] = None,
+) -> Iterable[SubTablePath]:
+ """Breadth-first search tree of BaseTables.
+
+ Args:
+ root
+ the root of the tree.
+ root_accessor (Optional[str]): attribute name for the root table, if any (mostly
+ useful for debugging).
+ skip_root (Optional[bool]): if True, the root itself is not visited, only its
+ children.
+ predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
+ paths. If True, the path is yielded and its subtables are added to the
+ queue. If False, the path is skipped and its subtables are not traversed.
+ iter_subtables_fn (Optional[Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]]):
+ function to iterate over subtables of a table. If None, the default
+ BaseTable.iterSubTables() is used.
+
+ Yields:
+ SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
+ for each of the nodes in the tree. The last entry in a path is the current
+ subtable, whereas preceding ones refer to its parent tables all the way up to
+ the root.
+ """
+ yield from _traverse_ot_data(
+ root,
+ root_accessor,
+ skip_root,
+ predicate,
+ lambda frontier, new: frontier.extend(new),
+ iter_subtables_fn,
+ )
+
+
+def _traverse_ot_data(
+ root: BaseTable,
+ root_accessor: Optional[str],
+ skip_root: bool,
+ predicate: Optional[Callable[[SubTablePath], bool]],
+ add_to_frontier_fn: AddToFrontierFn,
+ iter_subtables_fn: Optional[
+ Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
+ ] = None,
+) -> Iterable[SubTablePath]:
+ # no visited because general otData cannot cycle (forward-offset only)
+ if root_accessor is None:
+ root_accessor = type(root).__name__
+
+ if predicate is None:
+
+ def predicate(path):
+ return True
+
+ if iter_subtables_fn is None:
+
+ def iter_subtables_fn(table):
+ return table.iterSubTables()
+
+ frontier: Deque[SubTablePath] = deque()
+
+ root_entry = BaseTable.SubTableEntry(root_accessor, root)
+ if not skip_root:
+ frontier.append((root_entry,))
+ else:
+ add_to_frontier_fn(
+ frontier,
+ [
+ (root_entry, subtable_entry)
+ for subtable_entry in iter_subtables_fn(root)
+ ],
+ )
+
+ while frontier:
+ # path is (value, attr_name) tuples. attr_name is attr of parent to get value
+ path = frontier.popleft()
+ current = path[-1].value
+
+ if not predicate(path):
+ continue
+
+ yield SubTablePath(path)
+
+ new_entries = [
+ path + (subtable_entry,) for subtable_entry in iter_subtables_fn(current)
+ ]
+
+ add_to_frontier_fn(frontier, new_entries)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixGlyph.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixGlyph.py
new file mode 100644
index 0000000000000000000000000000000000000000..b744a2a3bc88907ef027ad7670b1b04f164dd44a
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixGlyph.py
@@ -0,0 +1,149 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex, safeEval
+import struct
+
+
+sbixGlyphHeaderFormat = """
+ >
+ originOffsetX: h # The x-value of the point in the glyph relative to its
+ # lower-left corner which corresponds to the origin of
+ # the glyph on the screen, that is the point on the
+ # baseline at the left edge of the glyph.
+ originOffsetY: h # The y-value of the point in the glyph relative to its
+ # lower-left corner which corresponds to the origin of
+ # the glyph on the screen, that is the point on the
+ # baseline at the left edge of the glyph.
+ graphicType: 4s # e.g. "png "
+"""
+
+sbixGlyphHeaderFormatSize = sstruct.calcsize(sbixGlyphHeaderFormat)
+
+
+class Glyph(object):
+ def __init__(
+ self,
+ glyphName=None,
+ referenceGlyphName=None,
+ originOffsetX=0,
+ originOffsetY=0,
+ graphicType=None,
+ imageData=None,
+ rawdata=None,
+ gid=0,
+ ):
+ self.gid = gid
+ self.glyphName = glyphName
+ self.referenceGlyphName = referenceGlyphName
+ self.originOffsetX = originOffsetX
+ self.originOffsetY = originOffsetY
+ self.rawdata = rawdata
+ self.graphicType = graphicType
+ self.imageData = imageData
+
+ # fix self.graphicType if it is null terminated or too short
+ if self.graphicType is not None:
+ if self.graphicType[-1] == "\0":
+ self.graphicType = self.graphicType[:-1]
+ if len(self.graphicType) > 4:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError(
+ "Glyph.graphicType must not be longer than 4 characters."
+ )
+ elif len(self.graphicType) < 4:
+ # pad with spaces
+ self.graphicType += " "[: (4 - len(self.graphicType))]
+
+ def is_reference_type(self):
+ """Returns True if this glyph is a reference to another glyph's image data."""
+ return self.graphicType == "dupe" or self.graphicType == "flip"
+
+ def decompile(self, ttFont):
+ self.glyphName = ttFont.getGlyphName(self.gid)
+ if self.rawdata is None:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("No table data to decompile")
+ if len(self.rawdata) > 0:
+ if len(self.rawdata) < sbixGlyphHeaderFormatSize:
+ from fontTools import ttLib
+
+ # print "Glyph %i header too short: Expected %x, got %x." % (self.gid, sbixGlyphHeaderFormatSize, len(self.rawdata))
+ raise ttLib.TTLibError("Glyph header too short.")
+
+ sstruct.unpack(
+ sbixGlyphHeaderFormat, self.rawdata[:sbixGlyphHeaderFormatSize], self
+ )
+
+ if self.is_reference_type():
+ # this glyph is a reference to another glyph's image data
+ (gid,) = struct.unpack(">H", self.rawdata[sbixGlyphHeaderFormatSize:])
+ self.referenceGlyphName = ttFont.getGlyphName(gid)
+ else:
+ self.imageData = self.rawdata[sbixGlyphHeaderFormatSize:]
+ self.referenceGlyphName = None
+ # clean up
+ del self.rawdata
+ del self.gid
+
+ def compile(self, ttFont):
+ if self.glyphName is None:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("Can't compile Glyph without glyph name")
+ # TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
+ # (needed if you just want to compile the sbix table on its own)
+ self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
+ if self.graphicType is None:
+ rawdata = b""
+ else:
+ rawdata = sstruct.pack(sbixGlyphHeaderFormat, self)
+ if self.is_reference_type():
+ rawdata += struct.pack(">H", ttFont.getGlyphID(self.referenceGlyphName))
+ else:
+ assert self.imageData is not None
+ rawdata += self.imageData
+ self.rawdata = rawdata
+
+ def toXML(self, xmlWriter, ttFont):
+ if self.graphicType is None:
+ # TODO: ignore empty glyphs?
+ # a glyph data entry is required for each glyph,
+ # but empty ones can be calculated at compile time
+ xmlWriter.simpletag("glyph", name=self.glyphName)
+ xmlWriter.newline()
+ return
+ xmlWriter.begintag(
+ "glyph",
+ graphicType=self.graphicType,
+ name=self.glyphName,
+ originOffsetX=self.originOffsetX,
+ originOffsetY=self.originOffsetY,
+ )
+ xmlWriter.newline()
+ if self.is_reference_type():
+ # this glyph is a reference to another glyph id.
+ xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
+ else:
+ xmlWriter.begintag("hexdata")
+ xmlWriter.newline()
+ xmlWriter.dumphex(self.imageData)
+ xmlWriter.endtag("hexdata")
+ xmlWriter.newline()
+ xmlWriter.endtag("glyph")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "ref":
+ # this glyph i.e. a reference to another glyph's image data.
+ # in this case imageData contains the glyph id of the reference glyph
+ # get glyph id from glyphname
+ glyphname = safeEval("'''" + attrs["glyphname"] + "'''")
+ self.imageData = struct.pack(">H", ttFont.getGlyphID(glyphname))
+ self.referenceGlyphName = glyphname
+ elif name == "hexdata":
+ self.imageData = readHex(content)
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixStrike.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixStrike.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dfba2e76e899a55b9fa88e2aaba0f5b8c5b142d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/sbixStrike.py
@@ -0,0 +1,177 @@
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from .sbixGlyph import Glyph
+import struct
+
+sbixStrikeHeaderFormat = """
+ >
+ ppem: H # The PPEM for which this strike was designed (e.g., 9,
+ # 12, 24)
+ resolution: H # The screen resolution (in dpi) for which this strike
+ # was designed (e.g., 72)
+"""
+
+sbixGlyphDataOffsetFormat = """
+ >
+ glyphDataOffset: L # Offset from the beginning of the strike data record
+ # to data for the individual glyph
+"""
+
+sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
+sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)
+
+
+class Strike(object):
+ def __init__(self, rawdata=None, ppem=0, resolution=72):
+ self.data = rawdata
+ self.ppem = ppem
+ self.resolution = resolution
+ self.glyphs = {}
+
+ def decompile(self, ttFont):
+ if self.data is None:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError
+ if len(self.data) < sbixStrikeHeaderFormatSize:
+ from fontTools import ttLib
+
+ raise (
+ ttLib.TTLibError,
+ "Strike header too short: Expected %x, got %x.",
+ ) % (sbixStrikeHeaderFormatSize, len(self.data))
+
+ # read Strike header from raw data
+ sstruct.unpack(
+ sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self
+ )
+
+ # calculate number of glyphs
+ (firstGlyphDataOffset,) = struct.unpack(
+ ">L",
+ self.data[
+ sbixStrikeHeaderFormatSize : sbixStrikeHeaderFormatSize
+ + sbixGlyphDataOffsetFormatSize
+ ],
+ )
+ self.numGlyphs = (
+ firstGlyphDataOffset - sbixStrikeHeaderFormatSize
+ ) // sbixGlyphDataOffsetFormatSize - 1
+ # ^ -1 because there's one more offset than glyphs
+
+ # build offset list for single glyph data offsets
+ self.glyphDataOffsets = []
+ for i in range(
+ self.numGlyphs + 1
+ ): # + 1 because there's one more offset than glyphs
+ start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
+ (current_offset,) = struct.unpack(
+ ">L", self.data[start : start + sbixGlyphDataOffsetFormatSize]
+ )
+ self.glyphDataOffsets.append(current_offset)
+
+ # iterate through offset list and slice raw data into glyph data records
+ for i in range(self.numGlyphs):
+ current_glyph = Glyph(
+ rawdata=self.data[
+ self.glyphDataOffsets[i] : self.glyphDataOffsets[i + 1]
+ ],
+ gid=i,
+ )
+ current_glyph.decompile(ttFont)
+ self.glyphs[current_glyph.glyphName] = current_glyph
+ del self.glyphDataOffsets
+ del self.numGlyphs
+ del self.data
+
+ def compile(self, ttFont):
+ self.glyphDataOffsets = b""
+ self.bitmapData = b""
+
+ glyphOrder = ttFont.getGlyphOrder()
+
+ # first glyph starts right after the header
+ currentGlyphDataOffset = (
+ sbixStrikeHeaderFormatSize
+ + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
+ )
+ for glyphName in glyphOrder:
+ if glyphName in self.glyphs:
+ # we have glyph data for this glyph
+ current_glyph = self.glyphs[glyphName]
+ else:
+ # must add empty glyph data record for this glyph
+ current_glyph = Glyph(glyphName=glyphName)
+ current_glyph.compile(ttFont)
+ current_glyph.glyphDataOffset = currentGlyphDataOffset
+ self.bitmapData += current_glyph.rawdata
+ currentGlyphDataOffset += len(current_glyph.rawdata)
+ self.glyphDataOffsets += sstruct.pack(
+ sbixGlyphDataOffsetFormat, current_glyph
+ )
+
+ # add last "offset", really the end address of the last glyph data record
+ dummy = Glyph()
+ dummy.glyphDataOffset = currentGlyphDataOffset
+ self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)
+
+ # pack header
+ self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
+ # add offsets and image data after header
+ self.data += self.glyphDataOffsets + self.bitmapData
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.begintag("strike")
+ xmlWriter.newline()
+ xmlWriter.simpletag("ppem", value=self.ppem)
+ xmlWriter.newline()
+ xmlWriter.simpletag("resolution", value=self.resolution)
+ xmlWriter.newline()
+ glyphOrder = ttFont.getGlyphOrder()
+ for glyphName in glyphOrder:
+ if glyphName in self.glyphs:
+ self.glyphs[glyphName].toXML(xmlWriter, ttFont)
+ # TODO: what if there are more glyph data records than (glyf table) glyphs?
+ xmlWriter.endtag("strike")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name in ["ppem", "resolution"]:
+ setattr(self, name, safeEval(attrs["value"]))
+ elif name == "glyph":
+ if "graphicType" in attrs:
+ myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
+ else:
+ myFormat = None
+ if "glyphname" in attrs:
+ myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
+ elif "name" in attrs:
+ myGlyphName = safeEval("'''" + attrs["name"] + "'''")
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("Glyph must have a glyph name.")
+ if "originOffsetX" in attrs:
+ myOffsetX = safeEval(attrs["originOffsetX"])
+ else:
+ myOffsetX = 0
+ if "originOffsetY" in attrs:
+ myOffsetY = safeEval(attrs["originOffsetY"])
+ else:
+ myOffsetY = 0
+ current_glyph = Glyph(
+ glyphName=myGlyphName,
+ graphicType=myFormat,
+ originOffsetX=myOffsetX,
+ originOffsetY=myOffsetY,
+ )
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ current_glyph.fromXML(name, attrs, content, ttFont)
+ current_glyph.compile(ttFont)
+ self.glyphs[current_glyph.glyphName] = current_glyph
+ else:
+ from fontTools import ttLib
+
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/table_API_readme.txt b/lib/python3.12/site-packages/fontTools/ttLib/tables/table_API_readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7719201a0ea2d4b8f28a3012ad63c1de8bbf10ef
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/table_API_readme.txt
@@ -0,0 +1,91 @@
+This folder is a subpackage of ttLib. Each module here is a
+specialized TT/OT table converter: they can convert raw data
+to Python objects and vice versa. Usually you don't need to
+use the modules directly: they are imported and used
+automatically when needed by ttLib.
+
+If you are writing you own table converter the following is
+important.
+
+The modules here have pretty strange names: this is due to the
+fact that we need to map TT table tags (which are case sensitive)
+to filenames (which on Mac and Win aren't case sensitive) as well
+as to Python identifiers. The latter means it can only contain
+[A-Za-z0-9_] and cannot start with a number.
+
+ttLib provides functions to expand a tag into the format used here:
+
+>>> from fontTools import ttLib
+>>> ttLib.tagToIdentifier("FOO ")
+'F_O_O_'
+>>> ttLib.tagToIdentifier("cvt ")
+'_c_v_t'
+>>> ttLib.tagToIdentifier("OS/2")
+'O_S_2f_2'
+>>> ttLib.tagToIdentifier("glyf")
+'_g_l_y_f'
+>>>
+
+And vice versa:
+
+>>> ttLib.identifierToTag("F_O_O_")
+'FOO '
+>>> ttLib.identifierToTag("_c_v_t")
+'cvt '
+>>> ttLib.identifierToTag("O_S_2f_2")
+'OS/2'
+>>> ttLib.identifierToTag("_g_l_y_f")
+'glyf'
+>>>
+
+Eg. the 'glyf' table converter lives in a Python file called:
+
+ _g_l_y_f.py
+
+The converter itself is a class, named "table_" + expandedtag. Eg:
+
+ class table__g_l_y_f:
+ etc.
+
+Note that if you _do_ need to use such modules or classes manually,
+there are two convenient API functions that let you find them by tag:
+
+>>> ttLib.getTableModule('glyf')
+
+>>> ttLib.getTableClass('glyf')
+
+>>>
+
+You must subclass from DefaultTable.DefaultTable. It provides some default
+behavior, as well as a constructor method (__init__) that you don't need to
+override.
+
+Your converter should minimally provide two methods:
+
+class table_F_O_O_(DefaultTable.DefaultTable): # converter for table 'FOO '
+
+ def decompile(self, data, ttFont):
+ # 'data' is the raw table data. Unpack it into a
+ # Python data structure.
+ # 'ttFont' is a ttLib.TTfile instance, enabling you to
+ # refer to other tables. Do ***not*** keep a reference to
+ # it: it will cause a circular reference (ttFont saves
+ # a reference to us), and that means we'll be leaking
+ # memory. If you need to use it in other methods, just
+ # pass it around as a method argument.
+
+ def compile(self, ttFont):
+ # Return the raw data, as converted from the Python
+ # data structure.
+ # Again, 'ttFont' is there so you can access other tables.
+ # Same warning applies.
+
+If you want to support TTX import/export as well, you need to provide two
+additional methods:
+
+ def toXML(self, writer, ttFont):
+ # XXX
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ # XXX
+
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/tables/ttProgram.py b/lib/python3.12/site-packages/fontTools/ttLib/tables/ttProgram.py
new file mode 100644
index 0000000000000000000000000000000000000000..32a4ec8b20ff8b4c20be33efebea7273af98931b
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/tables/ttProgram.py
@@ -0,0 +1,594 @@
+"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
+
+from __future__ import annotations
+
+from fontTools.misc.textTools import num2binary, binary2num, readHex, strjoin
+import array
+from io import StringIO
+from typing import List
+import re
+import logging
+
+
+log = logging.getLogger(__name__)
+
+# fmt: off
+
+# first, the list of instructions that eat bytes or words from the instruction stream
+
+streamInstructions = [
+#
+# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
+#
+ (0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
+ (0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
+ (0xb0, 'PUSHB', 3, 'PushBytes', 0, -1), # b0, b1,..bn b0, b1, ...,bn
+ (0xb8, 'PUSHW', 3, 'PushWords', 0, -1), # w0,w1,..wn w0 ,w1, ...wn
+]
+
+
+# next, the list of "normal" instructions
+
+instructions = [
+#
+# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
+#
+ (0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
+ (0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
+ (0x60, 'ADD', 0, 'Add', 2, 1), # n2, n1 (n1 + n2)
+ (0x27, 'ALIGNPTS', 0, 'AlignPts', 2, 0), # p2, p1 -
+ (0x3c, 'ALIGNRP', 0, 'AlignRelativePt', -1, 0), # p1, p2, ... , ploopvalue -
+ (0x5a, 'AND', 0, 'LogicalAnd', 2, 1), # e2, e1 b
+ (0x2b, 'CALL', 0, 'CallFunction', 1, 0), # f -
+ (0x67, 'CEILING', 0, 'Ceiling', 1, 1), # n ceil(n)
+ (0x25, 'CINDEX', 0, 'CopyXToTopStack', 1, 1), # k ek
+ (0x22, 'CLEAR', 0, 'ClearStack', -1, 0), # all items on the stack -
+ (0x4f, 'DEBUG', 0, 'DebugCall', 1, 0), # n -
+ (0x73, 'DELTAC1', 0, 'DeltaExceptionC1', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x74, 'DELTAC2', 0, 'DeltaExceptionC2', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x75, 'DELTAC3', 0, 'DeltaExceptionC3', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x5d, 'DELTAP1', 0, 'DeltaExceptionP1', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x71, 'DELTAP2', 0, 'DeltaExceptionP2', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x72, 'DELTAP3', 0, 'DeltaExceptionP3', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x24, 'DEPTH', 0, 'GetDepthStack', 0, 1), # - n
+ (0x62, 'DIV', 0, 'Divide', 2, 1), # n2, n1 (n1 * 64)/ n2
+ (0x20, 'DUP', 0, 'DuplicateTopStack', 1, 2), # e e, e
+ (0x59, 'EIF', 0, 'EndIf', 0, 0), # - -
+ (0x1b, 'ELSE', 0, 'Else', 0, 0), # - -
+ (0x2d, 'ENDF', 0, 'EndFunctionDefinition', 0, 0), # - -
+ (0x54, 'EQ', 0, 'Equal', 2, 1), # e2, e1 b
+ (0x57, 'EVEN', 0, 'Even', 1, 1), # e b
+ (0x2c, 'FDEF', 0, 'FunctionDefinition', 1, 0), # f -
+ (0x4e, 'FLIPOFF', 0, 'SetAutoFlipOff', 0, 0), # - -
+ (0x4d, 'FLIPON', 0, 'SetAutoFlipOn', 0, 0), # - -
+ (0x80, 'FLIPPT', 0, 'FlipPoint', -1, 0), # p1, p2, ..., ploopvalue -
+ (0x82, 'FLIPRGOFF', 0, 'FlipRangeOff', 2, 0), # h, l -
+ (0x81, 'FLIPRGON', 0, 'FlipRangeOn', 2, 0), # h, l -
+ (0x66, 'FLOOR', 0, 'Floor', 1, 1), # n floor(n)
+ (0x46, 'GC', 1, 'GetCoordOnPVector', 1, 1), # p c
+ (0x88, 'GETINFO', 0, 'GetInfo', 1, 1), # selector result
+ (0x91, 'GETVARIATION', 0, 'GetVariation', 0, -1), # - a1,..,an
+ (0x0d, 'GFV', 0, 'GetFVector', 0, 2), # - px, py
+ (0x0c, 'GPV', 0, 'GetPVector', 0, 2), # - px, py
+ (0x52, 'GT', 0, 'GreaterThan', 2, 1), # e2, e1 b
+ (0x53, 'GTEQ', 0, 'GreaterThanOrEqual', 2, 1), # e2, e1 b
+ (0x89, 'IDEF', 0, 'InstructionDefinition', 1, 0), # f -
+ (0x58, 'IF', 0, 'If', 1, 0), # e -
+ (0x8e, 'INSTCTRL', 0, 'SetInstrExecControl', 2, 0), # s, v -
+ (0x39, 'IP', 0, 'InterpolatePts', -1, 0), # p1, p2, ... , ploopvalue -
+ (0x0f, 'ISECT', 0, 'MovePtToIntersect', 5, 0), # a1, a0, b1, b0, p -
+ (0x30, 'IUP', 1, 'InterpolateUntPts', 0, 0), # - -
+ (0x1c, 'JMPR', 0, 'Jump', 1, 0), # offset -
+ (0x79, 'JROF', 0, 'JumpRelativeOnFalse', 2, 0), # e, offset -
+ (0x78, 'JROT', 0, 'JumpRelativeOnTrue', 2, 0), # e, offset -
+ (0x2a, 'LOOPCALL', 0, 'LoopAndCallFunction', 2, 0), # f, count -
+ (0x50, 'LT', 0, 'LessThan', 2, 1), # e2, e1 b
+ (0x51, 'LTEQ', 0, 'LessThenOrEqual', 2, 1), # e2, e1 b
+ (0x8b, 'MAX', 0, 'Maximum', 2, 1), # e2, e1 max(e1, e2)
+ (0x49, 'MD', 1, 'MeasureDistance', 2, 1), # p2,p1 d
+ (0x2e, 'MDAP', 1, 'MoveDirectAbsPt', 1, 0), # p -
+ (0xc0, 'MDRP', 5, 'MoveDirectRelPt', 1, 0), # p -
+ (0x3e, 'MIAP', 1, 'MoveIndirectAbsPt', 2, 0), # n, p -
+ (0x8c, 'MIN', 0, 'Minimum', 2, 1), # e2, e1 min(e1, e2)
+ (0x26, 'MINDEX', 0, 'MoveXToTopStack', 1, 1), # k ek
+ (0xe0, 'MIRP', 5, 'MoveIndirectRelPt', 2, 0), # n, p -
+ (0x4b, 'MPPEM', 0, 'MeasurePixelPerEm', 0, 1), # - ppem
+ (0x4c, 'MPS', 0, 'MeasurePointSize', 0, 1), # - pointSize
+ (0x3a, 'MSIRP', 1, 'MoveStackIndirRelPt', 2, 0), # d, p -
+ (0x63, 'MUL', 0, 'Multiply', 2, 1), # n2, n1 (n1 * n2)/64
+ (0x65, 'NEG', 0, 'Negate', 1, 1), # n -n
+ (0x55, 'NEQ', 0, 'NotEqual', 2, 1), # e2, e1 b
+ (0x5c, 'NOT', 0, 'LogicalNot', 1, 1), # e ( not e )
+ (0x6c, 'NROUND', 2, 'NoRound', 1, 1), # n1 n2
+ (0x56, 'ODD', 0, 'Odd', 1, 1), # e b
+ (0x5b, 'OR', 0, 'LogicalOr', 2, 1), # e2, e1 b
+ (0x21, 'POP', 0, 'PopTopStack', 1, 0), # e -
+ (0x45, 'RCVT', 0, 'ReadCVT', 1, 1), # location value
+ (0x7d, 'RDTG', 0, 'RoundDownToGrid', 0, 0), # - -
+ (0x7a, 'ROFF', 0, 'RoundOff', 0, 0), # - -
+ (0x8a, 'ROLL', 0, 'RollTopThreeStack', 3, 3), # a,b,c b,a,c
+ (0x68, 'ROUND', 2, 'Round', 1, 1), # n1 n2
+ (0x43, 'RS', 0, 'ReadStore', 1, 1), # n v
+ (0x3d, 'RTDG', 0, 'RoundToDoubleGrid', 0, 0), # - -
+ (0x18, 'RTG', 0, 'RoundToGrid', 0, 0), # - -
+ (0x19, 'RTHG', 0, 'RoundToHalfGrid', 0, 0), # - -
+ (0x7c, 'RUTG', 0, 'RoundUpToGrid', 0, 0), # - -
+ (0x77, 'S45ROUND', 0, 'SuperRound45Degrees', 1, 0), # n -
+ (0x7e, 'SANGW', 0, 'SetAngleWeight', 1, 0), # weight -
+ (0x85, 'SCANCTRL', 0, 'ScanConversionControl', 1, 0), # n -
+ (0x8d, 'SCANTYPE', 0, 'ScanType', 1, 0), # n -
+ (0x48, 'SCFS', 0, 'SetCoordFromStackFP', 2, 0), # c, p -
+ (0x1d, 'SCVTCI', 0, 'SetCVTCutIn', 1, 0), # n -
+ (0x5e, 'SDB', 0, 'SetDeltaBaseInGState', 1, 0), # n -
+ (0x86, 'SDPVTL', 1, 'SetDualPVectorToLine', 2, 0), # p2, p1 -
+ (0x5f, 'SDS', 0, 'SetDeltaShiftInGState', 1, 0), # n -
+ (0x0b, 'SFVFS', 0, 'SetFVectorFromStack', 2, 0), # y, x -
+ (0x04, 'SFVTCA', 1, 'SetFVectorToAxis', 0, 0), # - -
+ (0x08, 'SFVTL', 1, 'SetFVectorToLine', 2, 0), # p2, p1 -
+ (0x0e, 'SFVTPV', 0, 'SetFVectorToPVector', 0, 0), # - -
+ (0x34, 'SHC', 1, 'ShiftContourByLastPt', 1, 0), # c -
+ (0x32, 'SHP', 1, 'ShiftPointByLastPoint', -1, 0), # p1, p2, ..., ploopvalue -
+ (0x38, 'SHPIX', 0, 'ShiftZoneByPixel', -1, 0), # d, p1, p2, ..., ploopvalue -
+ (0x36, 'SHZ', 1, 'ShiftZoneByLastPoint', 1, 0), # e -
+ (0x17, 'SLOOP', 0, 'SetLoopVariable', 1, 0), # n -
+ (0x1a, 'SMD', 0, 'SetMinimumDistance', 1, 0), # distance -
+ (0x0a, 'SPVFS', 0, 'SetPVectorFromStack', 2, 0), # y, x -
+ (0x02, 'SPVTCA', 1, 'SetPVectorToAxis', 0, 0), # - -
+ (0x06, 'SPVTL', 1, 'SetPVectorToLine', 2, 0), # p2, p1 -
+ (0x76, 'SROUND', 0, 'SuperRound', 1, 0), # n -
+ (0x10, 'SRP0', 0, 'SetRefPoint0', 1, 0), # p -
+ (0x11, 'SRP1', 0, 'SetRefPoint1', 1, 0), # p -
+ (0x12, 'SRP2', 0, 'SetRefPoint2', 1, 0), # p -
+ (0x1f, 'SSW', 0, 'SetSingleWidth', 1, 0), # n -
+ (0x1e, 'SSWCI', 0, 'SetSingleWidthCutIn', 1, 0), # n -
+ (0x61, 'SUB', 0, 'Subtract', 2, 1), # n2, n1 (n1 - n2)
+ (0x00, 'SVTCA', 1, 'SetFPVectorToAxis', 0, 0), # - -
+ (0x23, 'SWAP', 0, 'SwapTopStack', 2, 2), # e2, e1 e1, e2
+ (0x13, 'SZP0', 0, 'SetZonePointer0', 1, 0), # n -
+ (0x14, 'SZP1', 0, 'SetZonePointer1', 1, 0), # n -
+ (0x15, 'SZP2', 0, 'SetZonePointer2', 1, 0), # n -
+ (0x16, 'SZPS', 0, 'SetZonePointerS', 1, 0), # n -
+ (0x29, 'UTP', 0, 'UnTouchPt', 1, 0), # p -
+ (0x70, 'WCVTF', 0, 'WriteCVTInFUnits', 2, 0), # n, l -
+ (0x44, 'WCVTP', 0, 'WriteCVTInPixels', 2, 0), # v, l -
+ (0x42, 'WS', 0, 'WriteStore', 2, 0), # v, l -
+]
+
+# fmt: on
+
+
+def bitRepr(value, bits):
+ s = ""
+ for i in range(bits):
+ s = "01"[value & 0x1] + s
+ value = value >> 1
+ return s
+
+
+_mnemonicPat = re.compile(r"[A-Z][A-Z0-9]*$")
+
+
+def _makeDict(instructionList):
+ opcodeDict = {}
+ mnemonicDict = {}
+ for op, mnemonic, argBits, name, pops, pushes in instructionList:
+ assert _mnemonicPat.match(mnemonic)
+ mnemonicDict[mnemonic] = op, argBits, name
+ if argBits:
+ argoffset = op
+ for i in range(1 << argBits):
+ opcodeDict[op + i] = mnemonic, argBits, argoffset, name
+ else:
+ opcodeDict[op] = mnemonic, 0, 0, name
+ return opcodeDict, mnemonicDict
+
+
+streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
+opcodeDict, mnemonicDict = _makeDict(instructions)
+
+
+class tt_instructions_error(Exception):
+ def __init__(self, error):
+ self.error = error
+
+ def __str__(self):
+ return "TT instructions error: %s" % repr(self.error)
+
+
+_comment = r"/\*.*?\*/"
+_instruction = r"([A-Z][A-Z0-9]*)\s*\[(.*?)\]"
+_number = r"-?[0-9]+"
+_token = "(%s)|(%s)|(%s)" % (_instruction, _number, _comment)
+
+_tokenRE = re.compile(_token)
+_whiteRE = re.compile(r"\s*")
+
+_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]+).*?\*/")
+
+_indentRE = re.compile(r"^FDEF|IF|ELSE\[ \]\t.+")
+_unindentRE = re.compile(r"^ELSE|ENDF|EIF\[ \]\t.+")
+
+
+def _skipWhite(data, pos):
+ m = _whiteRE.match(data, pos)
+ newPos = m.regs[0][1]
+ assert newPos >= pos
+ return newPos
+
+
+class Program(object):
+ def __init__(self) -> None:
+ pass
+
+ def fromBytecode(self, bytecode: bytes) -> None:
+ self.bytecode = array.array("B", bytecode)
+ if hasattr(self, "assembly"):
+ del self.assembly
+
+ def fromAssembly(self, assembly: List[str] | str) -> None:
+ if isinstance(assembly, list):
+ self.assembly = assembly
+ elif isinstance(assembly, str):
+ self.assembly = assembly.splitlines()
+ else:
+ raise TypeError(f"expected str or List[str], got {type(assembly).__name__}")
+ if hasattr(self, "bytecode"):
+ del self.bytecode
+
+ def getBytecode(self) -> bytes:
+ if not hasattr(self, "bytecode"):
+ self._assemble()
+ return self.bytecode.tobytes()
+
+ def getAssembly(self, preserve=True) -> List[str]:
+ if not hasattr(self, "assembly"):
+ self._disassemble(preserve=preserve)
+ return self.assembly
+
+ def toXML(self, writer, ttFont) -> None:
+ if (
+ not hasattr(ttFont, "disassembleInstructions")
+ or ttFont.disassembleInstructions
+ ):
+ try:
+ assembly = self.getAssembly()
+ except:
+ import traceback
+
+ tmp = StringIO()
+ traceback.print_exc(file=tmp)
+ msg = "An exception occurred during the decompilation of glyph program:\n\n"
+ msg += tmp.getvalue()
+ log.error(msg)
+ writer.begintag("bytecode")
+ writer.newline()
+ writer.comment(msg.strip())
+ writer.newline()
+ writer.dumphex(self.getBytecode())
+ writer.endtag("bytecode")
+ writer.newline()
+ else:
+ if not assembly:
+ return
+ writer.begintag("assembly")
+ writer.newline()
+ i = 0
+ indent = 0
+ nInstr = len(assembly)
+ while i < nInstr:
+ instr = assembly[i]
+ if _unindentRE.match(instr):
+ indent -= 1
+ writer.write(writer.indentwhite * indent)
+ writer.write(instr)
+ writer.newline()
+ m = _pushCountPat.match(instr)
+ i = i + 1
+ if m:
+ nValues = int(m.group(1))
+ line: List[str] = []
+ j = 0
+ for j in range(nValues):
+ if j and not (j % 25):
+ writer.write(writer.indentwhite * indent)
+ writer.write(" ".join(line))
+ writer.newline()
+ line = []
+ line.append(assembly[i + j])
+ writer.write(writer.indentwhite * indent)
+ writer.write(" ".join(line))
+ writer.newline()
+ i = i + j + 1
+ if _indentRE.match(instr):
+ indent += 1
+ writer.endtag("assembly")
+ writer.newline()
+ else:
+ bytecode = self.getBytecode()
+ if not bytecode:
+ return
+ writer.begintag("bytecode")
+ writer.newline()
+ writer.dumphex(bytecode)
+ writer.endtag("bytecode")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont) -> None:
+ if name == "assembly":
+ self.fromAssembly(strjoin(content))
+ self._assemble()
+ del self.assembly
+ else:
+ assert name == "bytecode"
+ self.fromBytecode(readHex(content))
+
+ def _assemble(self) -> None:
+ assembly = " ".join(getattr(self, "assembly", []))
+ bytecode: List[int] = []
+ push = bytecode.append
+ lenAssembly = len(assembly)
+ pos = _skipWhite(assembly, 0)
+ while pos < lenAssembly:
+ m = _tokenRE.match(assembly, pos)
+ if m is None:
+ raise tt_instructions_error(
+ "Syntax error in TT program (%s)" % assembly[pos - 5 : pos + 15]
+ )
+ dummy, mnemonic, arg, number, comment = m.groups()
+ pos = m.regs[0][1]
+ if comment:
+ pos = _skipWhite(assembly, pos)
+ continue
+
+ arg = arg.strip()
+ if mnemonic.startswith("INSTR"):
+ # Unknown instruction
+ op = int(mnemonic[5:])
+ push(op)
+ elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
+ op, argBits, name = mnemonicDict[mnemonic]
+ if len(arg) != argBits:
+ raise tt_instructions_error(
+ "Incorrect number of argument bits (%s[%s])" % (mnemonic, arg)
+ )
+ if arg:
+ arg = binary2num(arg)
+ push(op + arg)
+ else:
+ push(op)
+ else:
+ args = []
+ pos = _skipWhite(assembly, pos)
+ while pos < lenAssembly:
+ m = _tokenRE.match(assembly, pos)
+ if m is None:
+ raise tt_instructions_error(
+ "Syntax error in TT program (%s)" % assembly[pos : pos + 15]
+ )
+ dummy, _mnemonic, arg, number, comment = m.groups()
+ if number is None and comment is None:
+ break
+ pos = m.regs[0][1]
+ pos = _skipWhite(assembly, pos)
+ if comment is not None:
+ continue
+ args.append(int(number))
+ nArgs = len(args)
+ if mnemonic == "PUSH":
+ # Automatically choose the most compact representation
+ nWords = 0
+ while nArgs:
+ while (
+ nWords < nArgs
+ and nWords < 255
+ and not (0 <= args[nWords] <= 255)
+ ):
+ nWords += 1
+ nBytes = 0
+ while (
+ nWords + nBytes < nArgs
+ and nBytes < 255
+ and 0 <= args[nWords + nBytes] <= 255
+ ):
+ nBytes += 1
+ if (
+ nBytes < 2
+ and nWords + nBytes < 255
+ and nWords + nBytes != nArgs
+ ):
+ # Will write bytes as words
+ nWords += nBytes
+ continue
+
+ # Write words
+ if nWords:
+ if nWords <= 8:
+ op, argBits, name = streamMnemonicDict["PUSHW"]
+ op = op + nWords - 1
+ push(op)
+ else:
+ op, argBits, name = streamMnemonicDict["NPUSHW"]
+ push(op)
+ push(nWords)
+ for value in args[:nWords]:
+ assert -32768 <= value < 32768, (
+ "PUSH value out of range %d" % value
+ )
+ push((value >> 8) & 0xFF)
+ push(value & 0xFF)
+
+ # Write bytes
+ if nBytes:
+ pass
+ if nBytes <= 8:
+ op, argBits, name = streamMnemonicDict["PUSHB"]
+ op = op + nBytes - 1
+ push(op)
+ else:
+ op, argBits, name = streamMnemonicDict["NPUSHB"]
+ push(op)
+ push(nBytes)
+ for value in args[nWords : nWords + nBytes]:
+ push(value)
+
+ nTotal = nWords + nBytes
+ args = args[nTotal:]
+ nArgs -= nTotal
+ nWords = 0
+ else:
+ # Write exactly what we've been asked to
+ words = mnemonic[-1] == "W"
+ op, argBits, name = streamMnemonicDict[mnemonic]
+ if mnemonic[0] != "N":
+ assert nArgs <= 8, nArgs
+ op = op + nArgs - 1
+ push(op)
+ else:
+ assert nArgs < 256
+ push(op)
+ push(nArgs)
+ if words:
+ for value in args:
+ assert -32768 <= value < 32768, (
+ "PUSHW value out of range %d" % value
+ )
+ push((value >> 8) & 0xFF)
+ push(value & 0xFF)
+ else:
+ for value in args:
+ assert 0 <= value < 256, (
+ "PUSHB value out of range %d" % value
+ )
+ push(value)
+
+ pos = _skipWhite(assembly, pos)
+
+ if bytecode:
+ assert max(bytecode) < 256 and min(bytecode) >= 0
+ self.bytecode = array.array("B", bytecode)
+
+ def _disassemble(self, preserve=False) -> None:
+ assembly = []
+ i = 0
+ bytecode = getattr(self, "bytecode", [])
+ numBytecode = len(bytecode)
+ while i < numBytecode:
+ op = bytecode[i]
+ try:
+ mnemonic, argBits, argoffset, name = opcodeDict[op]
+ except KeyError:
+ if op in streamOpcodeDict:
+ values = []
+
+ # Merge consecutive PUSH operations
+ while bytecode[i] in streamOpcodeDict:
+ op = bytecode[i]
+ mnemonic, argBits, argoffset, name = streamOpcodeDict[op]
+ words = mnemonic[-1] == "W"
+ if argBits:
+ nValues = op - argoffset + 1
+ else:
+ i = i + 1
+ nValues = bytecode[i]
+ i = i + 1
+ assert nValues > 0
+ if not words:
+ for j in range(nValues):
+ value = bytecode[i]
+ values.append(repr(value))
+ i = i + 1
+ else:
+ for j in range(nValues):
+ # cast to signed int16
+ value = (bytecode[i] << 8) | bytecode[i + 1]
+ if value >= 0x8000:
+ value = value - 0x10000
+ values.append(repr(value))
+ i = i + 2
+ if preserve:
+ break
+
+ if not preserve:
+ mnemonic = "PUSH"
+ nValues = len(values)
+ if nValues == 1:
+ assembly.append("%s[ ] /* 1 value pushed */" % mnemonic)
+ else:
+ assembly.append(
+ "%s[ ] /* %s values pushed */" % (mnemonic, nValues)
+ )
+ assembly.extend(values)
+ else:
+ assembly.append("INSTR%d[ ]" % op)
+ i = i + 1
+ else:
+ if argBits:
+ assembly.append(
+ mnemonic
+ + "[%s] /* %s */" % (num2binary(op - argoffset, argBits), name)
+ )
+ else:
+ assembly.append(mnemonic + "[ ] /* %s */" % name)
+ i = i + 1
+ self.assembly = assembly
+
+ def __bool__(self) -> bool:
+ """
+ >>> p = Program()
+ >>> bool(p)
+ False
+ >>> bc = array.array("B", [0])
+ >>> p.fromBytecode(bc)
+ >>> bool(p)
+ True
+ >>> p.bytecode.pop()
+ 0
+ >>> bool(p)
+ False
+
+ >>> p = Program()
+ >>> asm = ['SVTCA[0]']
+ >>> p.fromAssembly(asm)
+ >>> bool(p)
+ True
+ >>> p.assembly.pop()
+ 'SVTCA[0]'
+ >>> bool(p)
+ False
+ """
+ return (hasattr(self, "assembly") and len(self.assembly) > 0) or (
+ hasattr(self, "bytecode") and len(self.bytecode) > 0
+ )
+
+ __nonzero__ = __bool__
+
+ def __eq__(self, other) -> bool:
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
+
+ def __ne__(self, other) -> bool:
+ result = self.__eq__(other)
+ return result if result is NotImplemented else not result
+
+
+def _test():
+ """
+ >>> _test()
+ True
+ """
+
+ bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033%dL" % len(self.fonts), *offsets))
+
+ if final:
+ final.write(file.getvalue())
+ file.close()
+
+ def saveXML(self, fileOrPath, newlinestr="\n", writeVersion=True, **kwargs):
+ from fontTools.misc import xmlWriter
+
+ writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
+
+ if writeVersion:
+ from fontTools import version
+
+ version = ".".join(version.split(".")[:2])
+ writer.begintag("ttCollection", ttLibVersion=version)
+ else:
+ writer.begintag("ttCollection")
+ writer.newline()
+ writer.newline()
+
+ for font in self.fonts:
+ font._saveXML(writer, writeVersion=False, **kwargs)
+ writer.newline()
+
+ writer.endtag("ttCollection")
+ writer.newline()
+
+ writer.close()
+
+ def __getitem__(self, item):
+ return self.fonts[item]
+
+ def __setitem__(self, item, value):
+ self.fonts[item] = value
+
+ def __delitem__(self, item):
+ return self.fonts[item]
+
+ def __len__(self):
+ return len(self.fonts)
+
+ def __iter__(self):
+ return iter(self.fonts)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/ttFont.py b/lib/python3.12/site-packages/fontTools/ttLib/ttFont.py
new file mode 100644
index 0000000000000000000000000000000000000000..4407e567a367c45e117fac6d0a545c9777335528
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/ttFont.py
@@ -0,0 +1,1701 @@
+from __future__ import annotations
+
+import logging
+import os
+import traceback
+from io import BytesIO, StringIO, UnsupportedOperation
+from typing import TYPE_CHECKING, TypedDict, TypeVar, overload
+
+from fontTools.config import Config
+from fontTools.misc import xmlWriter
+from fontTools.misc.configTools import AbstractConfig
+from fontTools.misc.loggingTools import deprecateArgument
+from fontTools.misc.textTools import Tag, byteord, tostr
+from fontTools.ttLib import TTLibError
+from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
+from fontTools.ttLib.ttGlyphSet import (
+ _TTGlyph, # noqa: F401
+ _TTGlyphSet,
+ _TTGlyphSetCFF,
+ _TTGlyphSetGlyf,
+ _TTGlyphSetVARC,
+)
+
+if TYPE_CHECKING:
+ from collections.abc import Mapping, MutableMapping
+ from types import ModuleType, TracebackType
+ from typing import Any, BinaryIO, Literal, Sequence, TextIO
+
+ from typing_extensions import Self, Unpack
+
+ from fontTools.ttLib.tables import (
+ B_A_S_E_,
+ C_B_D_T_,
+ C_B_L_C_,
+ C_F_F_,
+ C_F_F__2,
+ C_O_L_R_,
+ C_P_A_L_,
+ D_S_I_G_,
+ E_B_D_T_,
+ E_B_L_C_,
+ F_F_T_M_,
+ G_D_E_F_,
+ G_M_A_P_,
+ G_P_K_G_,
+ G_P_O_S_,
+ G_S_U_B_,
+ G_V_A_R_,
+ H_V_A_R_,
+ J_S_T_F_,
+ L_T_S_H_,
+ M_A_T_H_,
+ M_E_T_A_,
+ M_V_A_R_,
+ S_I_N_G_,
+ S_T_A_T_,
+ S_V_G_,
+ T_S_I__0,
+ T_S_I__1,
+ T_S_I__2,
+ T_S_I__3,
+ T_S_I__5,
+ T_S_I_B_,
+ T_S_I_C_,
+ T_S_I_D_,
+ T_S_I_J_,
+ T_S_I_P_,
+ T_S_I_S_,
+ T_S_I_V_,
+ T_T_F_A_,
+ V_A_R_C_,
+ V_D_M_X_,
+ V_O_R_G_,
+ V_V_A_R_,
+ D__e_b_g,
+ F__e_a_t,
+ G__l_a_t,
+ G__l_o_c,
+ O_S_2f_2,
+ S__i_l_f,
+ S__i_l_l,
+ _a_n_k_r,
+ _a_v_a_r,
+ _b_s_l_n,
+ _c_i_d_g,
+ _c_m_a_p,
+ _c_v_a_r,
+ _c_v_t,
+ _f_e_a_t,
+ _f_p_g_m,
+ _f_v_a_r,
+ _g_a_s_p,
+ _g_c_i_d,
+ _g_l_y_f,
+ _g_v_a_r,
+ _h_d_m_x,
+ _h_e_a_d,
+ _h_h_e_a,
+ _h_m_t_x,
+ _k_e_r_n,
+ _l_c_a_r,
+ _l_o_c_a,
+ _l_t_a_g,
+ _m_a_x_p,
+ _m_e_t_a,
+ _m_o_r_t,
+ _m_o_r_x,
+ _n_a_m_e,
+ _o_p_b_d,
+ _p_o_s_t,
+ _p_r_e_p,
+ _p_r_o_p,
+ _s_b_i_x,
+ _t_r_a_k,
+ _v_h_e_a,
+ _v_m_t_x,
+ )
+ from fontTools.ttLib.tables.DefaultTable import DefaultTable
+
+ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
+
+log = logging.getLogger(__name__)
+
+
+_NumberT = TypeVar("_NumberT", bound=float)
+
+
+class TTFont(object):
+ """Represents a TrueType font.
+
+ The object manages file input and output, and offers a convenient way of
+ accessing tables. Tables will be only decompiled when necessary, ie. when
+ they're actually accessed. This means that simple operations can be extremely fast.
+
+ Example usage:
+
+ .. code-block:: pycon
+
+ >>>
+ >> from fontTools import ttLib
+ >> tt = ttLib.TTFont("afont.ttf") # Load an existing font file
+ >> tt['maxp'].numGlyphs
+ 242
+ >> tt['OS/2'].achVendID
+ 'B&H\000'
+ >> tt['head'].unitsPerEm
+ 2048
+
+ For details of the objects returned when accessing each table, see the
+ :doc:`tables ` documentation.
+ To add a table to the font, use the :py:func:`newTable` function:
+
+ .. code-block:: pycon
+
+ >>>
+ >> os2 = newTable("OS/2")
+ >> os2.version = 4
+ >> # set other attributes
+ >> font["OS/2"] = os2
+
+ TrueType fonts can also be serialized to and from XML format (see also the
+ :doc:`ttx ` binary):
+
+ .. code-block:: pycon
+
+ >>
+ >> tt.saveXML("afont.ttx")
+ Dumping 'LTSH' table...
+ Dumping 'OS/2' table...
+ [...]
+
+ >> tt2 = ttLib.TTFont() # Create a new font object
+ >> tt2.importXML("afont.ttx")
+ >> tt2['maxp'].numGlyphs
+ 242
+
+ The TTFont object may be used as a context manager; this will cause the file
+ reader to be closed after the context ``with`` block is exited::
+
+ with TTFont(filename) as f:
+ # Do stuff
+
+ Args:
+ file: When reading a font from disk, either a pathname pointing to a file,
+ or a readable file object.
+ res_name_or_index: If running on a Macintosh, either a sfnt resource name or
+ an sfnt resource index number. If the index number is zero, TTLib will
+ autodetect whether the file is a flat file or a suitcase. (If it is a suitcase,
+ only the first 'sfnt' resource will be read.)
+ sfntVersion (str): When constructing a font object from scratch, sets the four-byte
+ sfnt magic number to be used. Defaults to ``\0\1\0\0`` (TrueType). To create
+ an OpenType file, use ``OTTO``.
+ flavor (str): Set this to ``woff`` when creating a WOFF file or ``woff2`` for a WOFF2
+ file.
+ checkChecksums (int): How checksum data should be treated. Default is 0
+ (no checking). Set to 1 to check and warn on wrong checksums; set to 2 to
+ raise an exception if any wrong checksums are found.
+ recalcBBoxes (bool): If true (the default), recalculates ``glyf``, ``CFF ``,
+ ``head`` bounding box values and ``hhea``/``vhea`` min/max values on save.
+ Also compiles the glyphs on importing, which saves memory consumption and
+ time.
+ ignoreDecompileErrors (bool): If true, exceptions raised during table decompilation
+ will be ignored, and the binary data will be returned for those tables instead.
+ recalcTimestamp (bool): If true (the default), sets the ``modified`` timestamp in
+ the ``head`` table on save.
+ fontNumber (int): The index of the font in a TrueType Collection file.
+ lazy (bool): If lazy is set to True, many data structures are loaded lazily, upon
+ access only. If it is set to False, many data structures are loaded immediately.
+ The default is ``lazy=None`` which is somewhere in between.
+ """
+
+ tables: dict[Tag, DefaultTable | GlyphOrder]
+ reader: SFNTReader | None
+ sfntVersion: str
+ flavor: str | None
+ flavorData: Any | None
+ lazy: bool | None
+ recalcBBoxes: bool
+ recalcTimestamp: bool
+ ignoreDecompileErrors: bool
+ cfg: AbstractConfig
+ glyphOrder: list[str]
+ _reverseGlyphOrderDict: dict[str, int]
+ _tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None
+ disassembleInstructions: bool
+ bitmapGlyphDataFormat: str
+ # Deprecated attributes
+ verbose: bool | None
+ quiet: bool | None
+
+ def __init__(
+ self,
+ file: str | os.PathLike[str] | BinaryIO | None = None,
+ res_name_or_index: str | int | None = None,
+ sfntVersion: str = "\000\001\000\000",
+ flavor: str | None = None,
+ checkChecksums: int = 0,
+ verbose: bool | None = None, # Deprecated
+ recalcBBoxes: bool = True,
+ allowVID: Any = NotImplemented, # Deprecated/Unused
+ ignoreDecompileErrors: bool = False,
+ recalcTimestamp: bool = True,
+ fontNumber: int = -1,
+ lazy: bool | None = None,
+ quiet: bool | None = None, # Deprecated
+ _tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None = None,
+ cfg: Mapping[str, Any] | AbstractConfig = {},
+ ) -> None:
+ # Set deprecated attributes
+ for name in ("verbose", "quiet"):
+ val = locals().get(name)
+ if val is not None:
+ deprecateArgument(name, "configure logging instead")
+ setattr(self, name, val)
+
+ self.lazy = lazy
+ self.recalcBBoxes = recalcBBoxes
+ self.recalcTimestamp = recalcTimestamp
+ self.tables = {}
+ self.reader = None
+ self.cfg = cfg.copy() if isinstance(cfg, AbstractConfig) else Config(cfg)
+ self.ignoreDecompileErrors = ignoreDecompileErrors
+
+ if not file:
+ self.sfntVersion = sfntVersion
+ self.flavor = flavor
+ self.flavorData = None
+ return
+ seekable = True
+ if not hasattr(file, "read"):
+ if not isinstance(file, (str, os.PathLike)):
+ raise TypeError(
+ "fileOrPath must be a file path (str or PathLike) if it isn't an object with a `read` method."
+ )
+ closeStream = True
+ # assume file is a string
+ if res_name_or_index is not None:
+ # see if it contains 'sfnt' resources in the resource or data fork
+ from . import macUtils
+
+ if res_name_or_index == 0:
+ if macUtils.getSFNTResIndices(file):
+ # get the first available sfnt font.
+ file = macUtils.SFNTResourceReader(file, 1)
+ else:
+ file = open(file, "rb")
+ else:
+ file = macUtils.SFNTResourceReader(file, res_name_or_index)
+ else:
+ file = open(file, "rb")
+ else:
+ # assume "file" is a readable file object
+ assert not isinstance(file, (str, os.PathLike))
+ closeStream = False
+ # SFNTReader wants the input file to be seekable.
+ # SpooledTemporaryFile has no seekable() on < 3.11, but still can seek:
+ # https://github.com/fonttools/fonttools/issues/3052
+ if hasattr(file, "seekable"):
+ seekable = file.seekable()
+ elif hasattr(file, "seek"):
+ try:
+ file.seek(0)
+ except UnsupportedOperation:
+ seekable = False
+
+ if not self.lazy:
+ # read input file in memory and wrap a stream around it to allow overwriting
+ if seekable:
+ file.seek(0)
+ tmp = BytesIO(file.read())
+ if hasattr(file, "name"):
+ # save reference to input file name
+ tmp.name = file.name
+ if closeStream:
+ file.close()
+ file = tmp
+ elif not seekable:
+ raise TTLibError("Input file must be seekable when lazy=True")
+ self._tableCache = _tableCache
+ self.reader = SFNTReader(file, checkChecksums, fontNumber=fontNumber)
+ self.sfntVersion = self.reader.sfntVersion
+ self.flavor = self.reader.flavor
+ self.flavorData = self.reader.flavorData
+
+ def __enter__(self) -> Self:
+ return self
+
+ def __exit__(
+ self,
+ exc_type: type[BaseException] | None,
+ exc_value: BaseException | None,
+ traceback: TracebackType | None,
+ ) -> None:
+ self.close()
+
+ def close(self) -> None:
+ """If we still have a reader object, close it."""
+ if self.reader is not None:
+ self.reader.close()
+ self.reader = None
+
+ def save(
+ self, file: str | os.PathLike[str] | BinaryIO, reorderTables: bool | None = True
+ ) -> None:
+ """Save the font to disk.
+
+ Args:
+ file: Similarly to the constructor, can be either a pathname or a writable
+ binary file object.
+ reorderTables (Option[bool]): If true (the default), reorder the tables,
+ sorting them by tag (recommended by the OpenType specification). If
+ false, retain the original font order. If None, reorder by table
+ dependency (fastest).
+ """
+ if not hasattr(file, "write"):
+ if self.lazy and self.reader.file.name == file:
+ raise TTLibError("Can't overwrite TTFont when 'lazy' attribute is True")
+ createStream = True
+ else:
+ # assume "file" is a writable file object
+ createStream = False
+
+ tmp = BytesIO()
+
+ writer_reordersTables = self._save(tmp)
+
+ if not (
+ reorderTables is None
+ or writer_reordersTables
+ or (reorderTables is False and self.reader is None)
+ ):
+ if reorderTables is False:
+ # sort tables using the original font's order
+ if self.reader is None:
+ raise TTLibError(
+ "The original table order is unavailable because there isn't a font to read it from."
+ )
+ tableOrder = list(self.reader.keys())
+ else:
+ # use the recommended order from the OpenType specification
+ tableOrder = None
+ tmp.flush()
+ tmp2 = BytesIO()
+ reorderFontTables(tmp, tmp2, tableOrder)
+ tmp.close()
+ tmp = tmp2
+
+ if createStream:
+ # "file" is a path
+ assert isinstance(file, (str, os.PathLike))
+ with open(file, "wb") as file:
+ file.write(tmp.getvalue())
+ else:
+ assert not isinstance(file, (str, os.PathLike))
+ file.write(tmp.getvalue())
+
+ tmp.close()
+
+ def _save(
+ self,
+ file: BinaryIO,
+ tableCache: MutableMapping[tuple[Tag, bytes], Any] | None = None,
+ ) -> bool:
+ """Internal function, to be shared by save() and TTCollection.save()"""
+
+ if self.recalcTimestamp and "head" in self:
+ # make sure 'head' is loaded so the recalculation is actually done
+ self["head"]
+
+ tags = self.keys()
+ tags.pop(0) # skip GlyphOrder tag
+ numTables = len(tags)
+ # write to a temporary stream to allow saving to unseekable streams
+ writer = SFNTWriter(
+ file, numTables, self.sfntVersion, self.flavor, self.flavorData
+ )
+
+ done = []
+ for tag in tags:
+ self._writeTable(tag, writer, done, tableCache)
+
+ writer.close()
+
+ return writer.reordersTables()
+
+ class XMLSavingOptions(TypedDict):
+ writeVersion: bool
+ quiet: bool | None
+ tables: Sequence[str | bytes] | None
+ skipTables: Sequence[str] | None
+ splitTables: bool
+ splitGlyphs: bool
+ disassembleInstructions: bool
+ bitmapGlyphDataFormat: str
+
+ def saveXML(
+ self,
+ fileOrPath: str | os.PathLike[str] | BinaryIO | TextIO,
+ newlinestr: str = "\n",
+ **kwargs: Unpack[XMLSavingOptions],
+ ) -> None:
+ """Export the font as TTX (an XML-based text file), or as a series of text
+ files when splitTables is true. In the latter case, the 'fileOrPath'
+ argument should be a path to a directory.
+ The 'tables' argument must either be false (dump all tables) or a
+ list of tables to dump. The 'skipTables' argument may be a list of tables
+ to skip, but only when the 'tables' argument is false.
+ """
+
+ writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
+ self._saveXML(writer, **kwargs)
+ writer.close()
+
+ def _saveXML(
+ self,
+ writer: xmlWriter.XMLWriter,
+ writeVersion: bool = True,
+ quiet: bool | None = None, # Deprecated
+ tables: Sequence[str | bytes] | None = None,
+ skipTables: Sequence[str] | None = None,
+ splitTables: bool = False,
+ splitGlyphs: bool = False,
+ disassembleInstructions: bool = True,
+ bitmapGlyphDataFormat: str = "raw",
+ ) -> None:
+ if quiet is not None:
+ deprecateArgument("quiet", "configure logging instead")
+
+ self.disassembleInstructions = disassembleInstructions
+ self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
+ if not tables:
+ tables = self.keys()
+ if skipTables:
+ tables = [tag for tag in tables if tag not in skipTables]
+
+ if writeVersion:
+ from fontTools import version
+
+ version = ".".join(version.split(".")[:2])
+ writer.begintag(
+ "ttFont",
+ sfntVersion=repr(tostr(self.sfntVersion))[1:-1],
+ ttLibVersion=version,
+ )
+ else:
+ writer.begintag("ttFont", sfntVersion=repr(tostr(self.sfntVersion))[1:-1])
+ writer.newline()
+
+ # always splitTables if splitGlyphs is enabled
+ splitTables = splitTables or splitGlyphs
+
+ if not splitTables:
+ writer.newline()
+ else:
+ if writer.filename is None:
+ raise TTLibError(
+ "splitTables requires the file name to be a file system path, not a stream."
+ )
+ path, ext = os.path.splitext(writer.filename)
+
+ for tag in tables:
+ if splitTables:
+ tablePath = path + "." + tagToIdentifier(tag) + ext
+ tableWriter = xmlWriter.XMLWriter(
+ tablePath, newlinestr=writer.newlinestr
+ )
+ tableWriter.begintag("ttFont", ttLibVersion=version)
+ tableWriter.newline()
+ tableWriter.newline()
+ writer.simpletag(tagToXML(tag), src=os.path.basename(tablePath))
+ writer.newline()
+ else:
+ tableWriter = writer
+ self._tableToXML(tableWriter, tag, splitGlyphs=splitGlyphs)
+ if splitTables:
+ tableWriter.endtag("ttFont")
+ tableWriter.newline()
+ tableWriter.close()
+ writer.endtag("ttFont")
+ writer.newline()
+
+ def _tableToXML(
+ self,
+ writer: xmlWriter.XMLWriter,
+ tag: str | bytes,
+ quiet: bool | None = None,
+ splitGlyphs: bool = False,
+ ) -> None:
+ if quiet is not None:
+ deprecateArgument("quiet", "configure logging instead")
+ if tag in self:
+ table = self[tag]
+ report = "Dumping '%s' table..." % tag
+ else:
+ report = "No '%s' table found." % tag
+ log.info(report)
+ if tag not in self:
+ return
+ xmlTag = tagToXML(tag)
+ attrs: dict[str, Any] = {}
+ if hasattr(table, "ERROR"):
+ attrs["ERROR"] = "decompilation error"
+ from .tables.DefaultTable import DefaultTable
+
+ if table.__class__ == DefaultTable:
+ attrs["raw"] = True
+ writer.begintag(xmlTag, **attrs)
+ writer.newline()
+ if tag == "glyf":
+ table.toXML(writer, self, splitGlyphs=splitGlyphs)
+ else:
+ table.toXML(writer, self)
+ writer.endtag(xmlTag)
+ writer.newline()
+ writer.newline()
+
+ def importXML(
+ self, fileOrPath: str | os.PathLike[str] | BinaryIO, quiet: bool | None = None
+ ) -> None:
+ """Import a TTX file (an XML-based text format), so as to recreate
+ a font object.
+ """
+ if quiet is not None:
+ deprecateArgument("quiet", "configure logging instead")
+
+ if "maxp" in self and "post" in self:
+ # Make sure the glyph order is loaded, as it otherwise gets
+ # lost if the XML doesn't contain the glyph order, yet does
+ # contain the table which was originally used to extract the
+ # glyph names from (ie. 'post', 'cmap' or 'CFF ').
+ self.getGlyphOrder()
+
+ from fontTools.misc import xmlReader
+
+ reader = xmlReader.XMLReader(fileOrPath, self)
+ reader.read()
+
+ def isLoaded(self, tag: str | bytes) -> bool:
+ """Return true if the table identified by ``tag`` has been
+ decompiled and loaded into memory."""
+ return tag in self.tables
+
+ def has_key(self, tag: str | bytes) -> bool:
+ """Test if the table identified by ``tag`` is present in the font.
+
+ As well as this method, ``tag in font`` can also be used to determine the
+ presence of the table."""
+ if self.isLoaded(tag):
+ return True
+ elif self.reader and tag in self.reader:
+ return True
+ elif tag == "GlyphOrder":
+ return True
+ else:
+ return False
+
+ __contains__ = has_key
+
+ def keys(self) -> list[str]:
+ """Returns the list of tables in the font, along with the ``GlyphOrder`` pseudo-table."""
+ keys = list(self.tables.keys())
+ if self.reader:
+ for key in list(self.reader.keys()):
+ if key not in keys:
+ keys.append(key)
+
+ if "GlyphOrder" in keys:
+ keys.remove("GlyphOrder")
+ keys = sortedTagList(keys)
+ return ["GlyphOrder"] + keys
+
+ def ensureDecompiled(self, recurse: bool | None = None) -> None:
+ """Decompile all the tables, even if a TTFont was opened in 'lazy' mode."""
+ for tag in self.keys():
+ table = self[tag]
+ if recurse is None:
+ recurse = self.lazy is not False
+ if recurse and hasattr(table, "ensureDecompiled"):
+ table.ensureDecompiled(recurse=recurse)
+ self.lazy = False
+
+ def __len__(self) -> int:
+ return len(list(self.keys()))
+
+ @overload
+ def __getitem__(self, tag: Literal["BASE"]) -> B_A_S_E_.table_B_A_S_E_: ...
+ @overload
+ def __getitem__(self, tag: Literal["CBDT"]) -> C_B_D_T_.table_C_B_D_T_: ...
+ @overload
+ def __getitem__(self, tag: Literal["CBLC"]) -> C_B_L_C_.table_C_B_L_C_: ...
+ @overload
+ def __getitem__(self, tag: Literal["CFF "]) -> C_F_F_.table_C_F_F_: ...
+ @overload
+ def __getitem__(self, tag: Literal["CFF2"]) -> C_F_F__2.table_C_F_F__2: ...
+ @overload
+ def __getitem__(self, tag: Literal["COLR"]) -> C_O_L_R_.table_C_O_L_R_: ...
+ @overload
+ def __getitem__(self, tag: Literal["CPAL"]) -> C_P_A_L_.table_C_P_A_L_: ...
+ @overload
+ def __getitem__(self, tag: Literal["DSIG"]) -> D_S_I_G_.table_D_S_I_G_: ...
+ @overload
+ def __getitem__(self, tag: Literal["EBDT"]) -> E_B_D_T_.table_E_B_D_T_: ...
+ @overload
+ def __getitem__(self, tag: Literal["EBLC"]) -> E_B_L_C_.table_E_B_L_C_: ...
+ @overload
+ def __getitem__(self, tag: Literal["FFTM"]) -> F_F_T_M_.table_F_F_T_M_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GDEF"]) -> G_D_E_F_.table_G_D_E_F_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GMAP"]) -> G_M_A_P_.table_G_M_A_P_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GPKG"]) -> G_P_K_G_.table_G_P_K_G_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GPOS"]) -> G_P_O_S_.table_G_P_O_S_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GSUB"]) -> G_S_U_B_.table_G_S_U_B_: ...
+ @overload
+ def __getitem__(self, tag: Literal["GVAR"]) -> G_V_A_R_.table_G_V_A_R_: ...
+ @overload
+ def __getitem__(self, tag: Literal["HVAR"]) -> H_V_A_R_.table_H_V_A_R_: ...
+ @overload
+ def __getitem__(self, tag: Literal["JSTF"]) -> J_S_T_F_.table_J_S_T_F_: ...
+ @overload
+ def __getitem__(self, tag: Literal["LTSH"]) -> L_T_S_H_.table_L_T_S_H_: ...
+ @overload
+ def __getitem__(self, tag: Literal["MATH"]) -> M_A_T_H_.table_M_A_T_H_: ...
+ @overload
+ def __getitem__(self, tag: Literal["META"]) -> M_E_T_A_.table_M_E_T_A_: ...
+ @overload
+ def __getitem__(self, tag: Literal["MVAR"]) -> M_V_A_R_.table_M_V_A_R_: ...
+ @overload
+ def __getitem__(self, tag: Literal["SING"]) -> S_I_N_G_.table_S_I_N_G_: ...
+ @overload
+ def __getitem__(self, tag: Literal["STAT"]) -> S_T_A_T_.table_S_T_A_T_: ...
+ @overload
+ def __getitem__(self, tag: Literal["SVG "]) -> S_V_G_.table_S_V_G_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSI0"]) -> T_S_I__0.table_T_S_I__0: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSI1"]) -> T_S_I__1.table_T_S_I__1: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSI2"]) -> T_S_I__2.table_T_S_I__2: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSI3"]) -> T_S_I__3.table_T_S_I__3: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSI5"]) -> T_S_I__5.table_T_S_I__5: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIB"]) -> T_S_I_B_.table_T_S_I_B_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIC"]) -> T_S_I_C_.table_T_S_I_C_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSID"]) -> T_S_I_D_.table_T_S_I_D_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIJ"]) -> T_S_I_J_.table_T_S_I_J_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIP"]) -> T_S_I_P_.table_T_S_I_P_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIS"]) -> T_S_I_S_.table_T_S_I_S_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TSIV"]) -> T_S_I_V_.table_T_S_I_V_: ...
+ @overload
+ def __getitem__(self, tag: Literal["TTFA"]) -> T_T_F_A_.table_T_T_F_A_: ...
+ @overload
+ def __getitem__(self, tag: Literal["VARC"]) -> V_A_R_C_.table_V_A_R_C_: ...
+ @overload
+ def __getitem__(self, tag: Literal["VDMX"]) -> V_D_M_X_.table_V_D_M_X_: ...
+ @overload
+ def __getitem__(self, tag: Literal["VORG"]) -> V_O_R_G_.table_V_O_R_G_: ...
+ @overload
+ def __getitem__(self, tag: Literal["VVAR"]) -> V_V_A_R_.table_V_V_A_R_: ...
+ @overload
+ def __getitem__(self, tag: Literal["Debg"]) -> D__e_b_g.table_D__e_b_g: ...
+ @overload
+ def __getitem__(self, tag: Literal["Feat"]) -> F__e_a_t.table_F__e_a_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["Glat"]) -> G__l_a_t.table_G__l_a_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["Gloc"]) -> G__l_o_c.table_G__l_o_c: ...
+ @overload
+ def __getitem__(self, tag: Literal["OS/2"]) -> O_S_2f_2.table_O_S_2f_2: ...
+ @overload
+ def __getitem__(self, tag: Literal["Silf"]) -> S__i_l_f.table_S__i_l_f: ...
+ @overload
+ def __getitem__(self, tag: Literal["Sill"]) -> S__i_l_l.table_S__i_l_l: ...
+ @overload
+ def __getitem__(self, tag: Literal["ankr"]) -> _a_n_k_r.table__a_n_k_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["avar"]) -> _a_v_a_r.table__a_v_a_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["bsln"]) -> _b_s_l_n.table__b_s_l_n: ...
+ @overload
+ def __getitem__(self, tag: Literal["cidg"]) -> _c_i_d_g.table__c_i_d_g: ...
+ @overload
+ def __getitem__(self, tag: Literal["cmap"]) -> _c_m_a_p.table__c_m_a_p: ...
+ @overload
+ def __getitem__(self, tag: Literal["cvar"]) -> _c_v_a_r.table__c_v_a_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["cvt "]) -> _c_v_t.table__c_v_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["feat"]) -> _f_e_a_t.table__f_e_a_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["fpgm"]) -> _f_p_g_m.table__f_p_g_m: ...
+ @overload
+ def __getitem__(self, tag: Literal["fvar"]) -> _f_v_a_r.table__f_v_a_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["gasp"]) -> _g_a_s_p.table__g_a_s_p: ...
+ @overload
+ def __getitem__(self, tag: Literal["gcid"]) -> _g_c_i_d.table__g_c_i_d: ...
+ @overload
+ def __getitem__(self, tag: Literal["glyf"]) -> _g_l_y_f.table__g_l_y_f: ...
+ @overload
+ def __getitem__(self, tag: Literal["gvar"]) -> _g_v_a_r.table__g_v_a_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["hdmx"]) -> _h_d_m_x.table__h_d_m_x: ...
+ @overload
+ def __getitem__(self, tag: Literal["head"]) -> _h_e_a_d.table__h_e_a_d: ...
+ @overload
+ def __getitem__(self, tag: Literal["hhea"]) -> _h_h_e_a.table__h_h_e_a: ...
+ @overload
+ def __getitem__(self, tag: Literal["hmtx"]) -> _h_m_t_x.table__h_m_t_x: ...
+ @overload
+ def __getitem__(self, tag: Literal["kern"]) -> _k_e_r_n.table__k_e_r_n: ...
+ @overload
+ def __getitem__(self, tag: Literal["lcar"]) -> _l_c_a_r.table__l_c_a_r: ...
+ @overload
+ def __getitem__(self, tag: Literal["loca"]) -> _l_o_c_a.table__l_o_c_a: ...
+ @overload
+ def __getitem__(self, tag: Literal["ltag"]) -> _l_t_a_g.table__l_t_a_g: ...
+ @overload
+ def __getitem__(self, tag: Literal["maxp"]) -> _m_a_x_p.table__m_a_x_p: ...
+ @overload
+ def __getitem__(self, tag: Literal["meta"]) -> _m_e_t_a.table__m_e_t_a: ...
+ @overload
+ def __getitem__(self, tag: Literal["mort"]) -> _m_o_r_t.table__m_o_r_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["morx"]) -> _m_o_r_x.table__m_o_r_x: ...
+ @overload
+ def __getitem__(self, tag: Literal["name"]) -> _n_a_m_e.table__n_a_m_e: ...
+ @overload
+ def __getitem__(self, tag: Literal["opbd"]) -> _o_p_b_d.table__o_p_b_d: ...
+ @overload
+ def __getitem__(self, tag: Literal["post"]) -> _p_o_s_t.table__p_o_s_t: ...
+ @overload
+ def __getitem__(self, tag: Literal["prep"]) -> _p_r_e_p.table__p_r_e_p: ...
+ @overload
+ def __getitem__(self, tag: Literal["prop"]) -> _p_r_o_p.table__p_r_o_p: ...
+ @overload
+ def __getitem__(self, tag: Literal["sbix"]) -> _s_b_i_x.table__s_b_i_x: ...
+ @overload
+ def __getitem__(self, tag: Literal["trak"]) -> _t_r_a_k.table__t_r_a_k: ...
+ @overload
+ def __getitem__(self, tag: Literal["vhea"]) -> _v_h_e_a.table__v_h_e_a: ...
+ @overload
+ def __getitem__(self, tag: Literal["vmtx"]) -> _v_m_t_x.table__v_m_t_x: ...
+ @overload
+ def __getitem__(self, tag: Literal["GlyphOrder"]) -> GlyphOrder: ...
+ @overload
+ def __getitem__(self, tag: str | bytes) -> DefaultTable | GlyphOrder: ...
+
+ def __getitem__(self, tag: str | bytes) -> DefaultTable | GlyphOrder:
+ tag = Tag(tag)
+ table = self.tables.get(tag)
+ if table is None:
+ if tag == "GlyphOrder":
+ table = GlyphOrder(tag)
+ self.tables[tag] = table
+ elif self.reader is not None:
+ table = self._readTable(tag)
+ else:
+ raise KeyError("'%s' table not found" % tag)
+ return table
+
+ def _readTable(self, tag: Tag) -> DefaultTable:
+ log.debug("Reading '%s' table from disk", tag)
+ assert self.reader is not None
+ data = self.reader[tag]
+ if self._tableCache is not None:
+ table = self._tableCache.get((tag, data))
+ if table is not None:
+ return table
+ tableClass = getTableClass(tag)
+ table = tableClass(tag)
+ self.tables[tag] = table
+ log.debug("Decompiling '%s' table", tag)
+ try:
+ table.decompile(data, self)
+ except Exception:
+ if not self.ignoreDecompileErrors:
+ raise
+ # fall back to DefaultTable, retaining the binary table data
+ log.exception(
+ "An exception occurred during the decompilation of the '%s' table", tag
+ )
+ from .tables.DefaultTable import DefaultTable
+
+ file = StringIO()
+ traceback.print_exc(file=file)
+ table = DefaultTable(tag)
+ table.ERROR = file.getvalue()
+ self.tables[tag] = table
+ table.decompile(data, self)
+ if self._tableCache is not None:
+ self._tableCache[(tag, data)] = table
+ return table
+
+ def __setitem__(self, tag: str | bytes, table: DefaultTable) -> None:
+ self.tables[Tag(tag)] = table
+
+ def __delitem__(self, tag: str | bytes) -> None:
+ if tag not in self:
+ raise KeyError("'%s' table not found" % tag)
+ if tag in self.tables:
+ del self.tables[tag]
+ if self.reader and tag in self.reader:
+ del self.reader[tag]
+
+ @overload
+ def get(self, tag: Literal["BASE"]) -> B_A_S_E_.table_B_A_S_E_ | None: ...
+ @overload
+ def get(self, tag: Literal["CBDT"]) -> C_B_D_T_.table_C_B_D_T_ | None: ...
+ @overload
+ def get(self, tag: Literal["CBLC"]) -> C_B_L_C_.table_C_B_L_C_ | None: ...
+ @overload
+ def get(self, tag: Literal["CFF "]) -> C_F_F_.table_C_F_F_ | None: ...
+ @overload
+ def get(self, tag: Literal["CFF2"]) -> C_F_F__2.table_C_F_F__2 | None: ...
+ @overload
+ def get(self, tag: Literal["COLR"]) -> C_O_L_R_.table_C_O_L_R_ | None: ...
+ @overload
+ def get(self, tag: Literal["CPAL"]) -> C_P_A_L_.table_C_P_A_L_ | None: ...
+ @overload
+ def get(self, tag: Literal["DSIG"]) -> D_S_I_G_.table_D_S_I_G_ | None: ...
+ @overload
+ def get(self, tag: Literal["EBDT"]) -> E_B_D_T_.table_E_B_D_T_ | None: ...
+ @overload
+ def get(self, tag: Literal["EBLC"]) -> E_B_L_C_.table_E_B_L_C_ | None: ...
+ @overload
+ def get(self, tag: Literal["FFTM"]) -> F_F_T_M_.table_F_F_T_M_ | None: ...
+ @overload
+ def get(self, tag: Literal["GDEF"]) -> G_D_E_F_.table_G_D_E_F_ | None: ...
+ @overload
+ def get(self, tag: Literal["GMAP"]) -> G_M_A_P_.table_G_M_A_P_ | None: ...
+ @overload
+ def get(self, tag: Literal["GPKG"]) -> G_P_K_G_.table_G_P_K_G_ | None: ...
+ @overload
+ def get(self, tag: Literal["GPOS"]) -> G_P_O_S_.table_G_P_O_S_ | None: ...
+ @overload
+ def get(self, tag: Literal["GSUB"]) -> G_S_U_B_.table_G_S_U_B_ | None: ...
+ @overload
+ def get(self, tag: Literal["GVAR"]) -> G_V_A_R_.table_G_V_A_R_ | None: ...
+ @overload
+ def get(self, tag: Literal["HVAR"]) -> H_V_A_R_.table_H_V_A_R_ | None: ...
+ @overload
+ def get(self, tag: Literal["JSTF"]) -> J_S_T_F_.table_J_S_T_F_ | None: ...
+ @overload
+ def get(self, tag: Literal["LTSH"]) -> L_T_S_H_.table_L_T_S_H_ | None: ...
+ @overload
+ def get(self, tag: Literal["MATH"]) -> M_A_T_H_.table_M_A_T_H_ | None: ...
+ @overload
+ def get(self, tag: Literal["META"]) -> M_E_T_A_.table_M_E_T_A_ | None: ...
+ @overload
+ def get(self, tag: Literal["MVAR"]) -> M_V_A_R_.table_M_V_A_R_ | None: ...
+ @overload
+ def get(self, tag: Literal["SING"]) -> S_I_N_G_.table_S_I_N_G_ | None: ...
+ @overload
+ def get(self, tag: Literal["STAT"]) -> S_T_A_T_.table_S_T_A_T_ | None: ...
+ @overload
+ def get(self, tag: Literal["SVG "]) -> S_V_G_.table_S_V_G_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSI0"]) -> T_S_I__0.table_T_S_I__0 | None: ...
+ @overload
+ def get(self, tag: Literal["TSI1"]) -> T_S_I__1.table_T_S_I__1 | None: ...
+ @overload
+ def get(self, tag: Literal["TSI2"]) -> T_S_I__2.table_T_S_I__2 | None: ...
+ @overload
+ def get(self, tag: Literal["TSI3"]) -> T_S_I__3.table_T_S_I__3 | None: ...
+ @overload
+ def get(self, tag: Literal["TSI5"]) -> T_S_I__5.table_T_S_I__5 | None: ...
+ @overload
+ def get(self, tag: Literal["TSIB"]) -> T_S_I_B_.table_T_S_I_B_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSIC"]) -> T_S_I_C_.table_T_S_I_C_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSID"]) -> T_S_I_D_.table_T_S_I_D_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSIJ"]) -> T_S_I_J_.table_T_S_I_J_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSIP"]) -> T_S_I_P_.table_T_S_I_P_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSIS"]) -> T_S_I_S_.table_T_S_I_S_ | None: ...
+ @overload
+ def get(self, tag: Literal["TSIV"]) -> T_S_I_V_.table_T_S_I_V_ | None: ...
+ @overload
+ def get(self, tag: Literal["TTFA"]) -> T_T_F_A_.table_T_T_F_A_ | None: ...
+ @overload
+ def get(self, tag: Literal["VARC"]) -> V_A_R_C_.table_V_A_R_C_ | None: ...
+ @overload
+ def get(self, tag: Literal["VDMX"]) -> V_D_M_X_.table_V_D_M_X_ | None: ...
+ @overload
+ def get(self, tag: Literal["VORG"]) -> V_O_R_G_.table_V_O_R_G_ | None: ...
+ @overload
+ def get(self, tag: Literal["VVAR"]) -> V_V_A_R_.table_V_V_A_R_ | None: ...
+ @overload
+ def get(self, tag: Literal["Debg"]) -> D__e_b_g.table_D__e_b_g | None: ...
+ @overload
+ def get(self, tag: Literal["Feat"]) -> F__e_a_t.table_F__e_a_t | None: ...
+ @overload
+ def get(self, tag: Literal["Glat"]) -> G__l_a_t.table_G__l_a_t | None: ...
+ @overload
+ def get(self, tag: Literal["Gloc"]) -> G__l_o_c.table_G__l_o_c | None: ...
+ @overload
+ def get(self, tag: Literal["OS/2"]) -> O_S_2f_2.table_O_S_2f_2 | None: ...
+ @overload
+ def get(self, tag: Literal["Silf"]) -> S__i_l_f.table_S__i_l_f | None: ...
+ @overload
+ def get(self, tag: Literal["Sill"]) -> S__i_l_l.table_S__i_l_l | None: ...
+ @overload
+ def get(self, tag: Literal["ankr"]) -> _a_n_k_r.table__a_n_k_r | None: ...
+ @overload
+ def get(self, tag: Literal["avar"]) -> _a_v_a_r.table__a_v_a_r | None: ...
+ @overload
+ def get(self, tag: Literal["bsln"]) -> _b_s_l_n.table__b_s_l_n | None: ...
+ @overload
+ def get(self, tag: Literal["cidg"]) -> _c_i_d_g.table__c_i_d_g | None: ...
+ @overload
+ def get(self, tag: Literal["cmap"]) -> _c_m_a_p.table__c_m_a_p | None: ...
+ @overload
+ def get(self, tag: Literal["cvar"]) -> _c_v_a_r.table__c_v_a_r | None: ...
+ @overload
+ def get(self, tag: Literal["cvt "]) -> _c_v_t.table__c_v_t | None: ...
+ @overload
+ def get(self, tag: Literal["feat"]) -> _f_e_a_t.table__f_e_a_t | None: ...
+ @overload
+ def get(self, tag: Literal["fpgm"]) -> _f_p_g_m.table__f_p_g_m | None: ...
+ @overload
+ def get(self, tag: Literal["fvar"]) -> _f_v_a_r.table__f_v_a_r | None: ...
+ @overload
+ def get(self, tag: Literal["gasp"]) -> _g_a_s_p.table__g_a_s_p | None: ...
+ @overload
+ def get(self, tag: Literal["gcid"]) -> _g_c_i_d.table__g_c_i_d | None: ...
+ @overload
+ def get(self, tag: Literal["glyf"]) -> _g_l_y_f.table__g_l_y_f | None: ...
+ @overload
+ def get(self, tag: Literal["gvar"]) -> _g_v_a_r.table__g_v_a_r | None: ...
+ @overload
+ def get(self, tag: Literal["hdmx"]) -> _h_d_m_x.table__h_d_m_x | None: ...
+ @overload
+ def get(self, tag: Literal["head"]) -> _h_e_a_d.table__h_e_a_d | None: ...
+ @overload
+ def get(self, tag: Literal["hhea"]) -> _h_h_e_a.table__h_h_e_a | None: ...
+ @overload
+ def get(self, tag: Literal["hmtx"]) -> _h_m_t_x.table__h_m_t_x | None: ...
+ @overload
+ def get(self, tag: Literal["kern"]) -> _k_e_r_n.table__k_e_r_n | None: ...
+ @overload
+ def get(self, tag: Literal["lcar"]) -> _l_c_a_r.table__l_c_a_r | None: ...
+ @overload
+ def get(self, tag: Literal["loca"]) -> _l_o_c_a.table__l_o_c_a | None: ...
+ @overload
+ def get(self, tag: Literal["ltag"]) -> _l_t_a_g.table__l_t_a_g | None: ...
+ @overload
+ def get(self, tag: Literal["maxp"]) -> _m_a_x_p.table__m_a_x_p | None: ...
+ @overload
+ def get(self, tag: Literal["meta"]) -> _m_e_t_a.table__m_e_t_a | None: ...
+ @overload
+ def get(self, tag: Literal["mort"]) -> _m_o_r_t.table__m_o_r_t | None: ...
+ @overload
+ def get(self, tag: Literal["morx"]) -> _m_o_r_x.table__m_o_r_x | None: ...
+ @overload
+ def get(self, tag: Literal["name"]) -> _n_a_m_e.table__n_a_m_e | None: ...
+ @overload
+ def get(self, tag: Literal["opbd"]) -> _o_p_b_d.table__o_p_b_d | None: ...
+ @overload
+ def get(self, tag: Literal["post"]) -> _p_o_s_t.table__p_o_s_t | None: ...
+ @overload
+ def get(self, tag: Literal["prep"]) -> _p_r_e_p.table__p_r_e_p | None: ...
+ @overload
+ def get(self, tag: Literal["prop"]) -> _p_r_o_p.table__p_r_o_p | None: ...
+ @overload
+ def get(self, tag: Literal["sbix"]) -> _s_b_i_x.table__s_b_i_x | None: ...
+ @overload
+ def get(self, tag: Literal["trak"]) -> _t_r_a_k.table__t_r_a_k | None: ...
+ @overload
+ def get(self, tag: Literal["vhea"]) -> _v_h_e_a.table__v_h_e_a | None: ...
+ @overload
+ def get(self, tag: Literal["vmtx"]) -> _v_m_t_x.table__v_m_t_x | None: ...
+ @overload
+ def get(self, tag: Literal["GlyphOrder"]) -> GlyphOrder: ...
+ @overload
+ def get(self, tag: str | bytes) -> DefaultTable | GlyphOrder | Any | None: ...
+ @overload
+ def get(
+ self, tag: str | bytes, default: _VT_co
+ ) -> DefaultTable | GlyphOrder | Any | _VT_co: ...
+
+ def get(
+ self, tag: str | bytes, default: Any | None = None
+ ) -> DefaultTable | GlyphOrder | Any | None:
+ """Returns the table if it exists or (optionally) a default if it doesn't."""
+ try:
+ return self[tag]
+ except KeyError:
+ return default
+
+ def setGlyphOrder(self, glyphOrder: list[str]) -> None:
+ """Set the glyph order
+
+ Args:
+ glyphOrder ([str]): List of glyph names in order.
+ """
+ self.glyphOrder = glyphOrder
+ if hasattr(self, "_reverseGlyphOrderDict"):
+ del self._reverseGlyphOrderDict
+ if self.isLoaded("glyf"):
+ self["glyf"].setGlyphOrder(glyphOrder)
+
+ def getGlyphOrder(self) -> list[str]:
+ """Returns a list of glyph names ordered by their position in the font."""
+ try:
+ return self.glyphOrder
+ except AttributeError:
+ pass
+ if "CFF " in self:
+ cff = self["CFF "]
+ self.glyphOrder = cff.getGlyphOrder()
+ elif "post" in self:
+ # TrueType font
+ glyphOrder = self["post"].getGlyphOrder()
+ if glyphOrder is None:
+ #
+ # No names found in the 'post' table.
+ # Try to create glyph names from the unicode cmap (if available)
+ # in combination with the Adobe Glyph List (AGL).
+ #
+ self._getGlyphNamesFromCmap()
+ elif len(glyphOrder) < self["maxp"].numGlyphs:
+ #
+ # Not enough names found in the 'post' table.
+ # Can happen when 'post' format 1 is improperly used on a font that
+ # has more than 258 glyphs (the length of 'standardGlyphOrder').
+ #
+ log.warning(
+ "Not enough names found in the 'post' table, generating them from cmap instead"
+ )
+ self._getGlyphNamesFromCmap()
+ else:
+ self.glyphOrder = glyphOrder
+ else:
+ self._getGlyphNamesFromCmap()
+ return self.glyphOrder
+
+ def _getGlyphNamesFromCmap(self) -> None:
+ #
+ # This is rather convoluted, but then again, it's an interesting problem:
+ # - we need to use the unicode values found in the cmap table to
+ # build glyph names (eg. because there is only a minimal post table,
+ # or none at all).
+ # - but the cmap parser also needs glyph names to work with...
+ # So here's what we do:
+ # - make up glyph names based on glyphID
+ # - load a temporary cmap table based on those names
+ # - extract the unicode values, build the "real" glyph names
+ # - unload the temporary cmap table
+ #
+ if self.isLoaded("cmap"):
+ # Bootstrapping: we're getting called by the cmap parser
+ # itself. This means self.tables['cmap'] contains a partially
+ # loaded cmap, making it impossible to get at a unicode
+ # subtable here. We remove the partially loaded cmap and
+ # restore it later.
+ # This only happens if the cmap table is loaded before any
+ # other table that does f.getGlyphOrder() or f.getGlyphName().
+ cmapLoading = self.tables["cmap"]
+ del self.tables["cmap"]
+ else:
+ cmapLoading = None
+ # Make up glyph names based on glyphID, which will be used by the
+ # temporary cmap and by the real cmap in case we don't find a unicode
+ # cmap.
+ numGlyphs = int(self["maxp"].numGlyphs)
+ glyphOrder = ["glyph%.5d" % i for i in range(numGlyphs)]
+ glyphOrder[0] = ".notdef"
+ # Set the glyph order, so the cmap parser has something
+ # to work with (so we don't get called recursively).
+ self.glyphOrder = glyphOrder
+
+ # Make up glyph names based on the reversed cmap table. Because some
+ # glyphs (eg. ligatures or alternates) may not be reachable via cmap,
+ # this naming table will usually not cover all glyphs in the font.
+ # If the font has no Unicode cmap table, reversecmap will be empty.
+ if "cmap" in self:
+ reversecmap = self["cmap"].buildReversedMin()
+ else:
+ reversecmap = {}
+ useCount = {}
+ for i, tempName in enumerate(glyphOrder):
+ if tempName in reversecmap:
+ # If a font maps both U+0041 LATIN CAPITAL LETTER A and
+ # U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
+ # we prefer naming the glyph as "A".
+ glyphName = self._makeGlyphName(reversecmap[tempName])
+ numUses = useCount[glyphName] = useCount.get(glyphName, 0) + 1
+ if numUses > 1:
+ glyphName = "%s.alt%d" % (glyphName, numUses - 1)
+ glyphOrder[i] = glyphName
+
+ if "cmap" in self:
+ # Delete the temporary cmap table from the cache, so it can
+ # be parsed again with the right names.
+ del self.tables["cmap"]
+ self.glyphOrder = glyphOrder
+ if cmapLoading:
+ # restore partially loaded cmap, so it can continue loading
+ # using the proper names.
+ self.tables["cmap"] = cmapLoading
+
+ @staticmethod
+ def _makeGlyphName(codepoint: int) -> str:
+ from fontTools import agl # Adobe Glyph List
+
+ if codepoint in agl.UV2AGL:
+ return agl.UV2AGL[codepoint]
+ elif codepoint <= 0xFFFF:
+ return "uni%04X" % codepoint
+ else:
+ return "u%X" % codepoint
+
+ def getGlyphNames(self) -> list[str]:
+ """Get a list of glyph names, sorted alphabetically."""
+ glyphNames = sorted(self.getGlyphOrder())
+ return glyphNames
+
+ def getGlyphNames2(self) -> list[str]:
+ """Get a list of glyph names, sorted alphabetically,
+ but not case sensitive.
+ """
+ from fontTools.misc import textTools
+
+ return textTools.caselessSort(self.getGlyphOrder())
+
+ def getGlyphName(self, glyphID: int) -> str:
+ """Returns the name for the glyph with the given ID.
+
+ If no name is available, synthesises one with the form ``glyphXXXXX``` where
+ ```XXXXX`` is the zero-padded glyph ID.
+ """
+ try:
+ return self.getGlyphOrder()[glyphID]
+ except IndexError:
+ return "glyph%.5d" % glyphID
+
+ def getGlyphNameMany(self, lst: Sequence[int]) -> list[str]:
+ """Converts a list of glyph IDs into a list of glyph names."""
+ glyphOrder = self.getGlyphOrder()
+ cnt = len(glyphOrder)
+ return [glyphOrder[gid] if gid < cnt else "glyph%.5d" % gid for gid in lst]
+
+ def getGlyphID(self, glyphName: str) -> int:
+ """Returns the ID of the glyph with the given name."""
+ try:
+ return self.getReverseGlyphMap()[glyphName]
+ except KeyError:
+ if glyphName[:5] == "glyph":
+ try:
+ return int(glyphName[5:])
+ except (NameError, ValueError):
+ raise KeyError(glyphName)
+ raise
+
+ def getGlyphIDMany(self, lst: Sequence[str]) -> list[int]:
+ """Converts a list of glyph names into a list of glyph IDs."""
+ d = self.getReverseGlyphMap()
+ try:
+ return [d[glyphName] for glyphName in lst]
+ except KeyError:
+ getGlyphID = self.getGlyphID
+ return [getGlyphID(glyphName) for glyphName in lst]
+
+ def getReverseGlyphMap(self, rebuild: bool = False) -> dict[str, int]:
+ """Returns a mapping of glyph names to glyph IDs."""
+ if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
+ self._buildReverseGlyphOrderDict()
+ return self._reverseGlyphOrderDict
+
+ def _buildReverseGlyphOrderDict(self) -> dict[str, int]:
+ self._reverseGlyphOrderDict = d = {}
+ for glyphID, glyphName in enumerate(self.getGlyphOrder()):
+ d[glyphName] = glyphID
+ return d
+
+ def _writeTable(
+ self,
+ tag: str | bytes,
+ writer: SFNTWriter,
+ done: list[str | bytes], # Use list as original
+ tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None = None,
+ ) -> None:
+ """Internal helper function for self.save(). Keeps track of
+ inter-table dependencies.
+ """
+ if tag in done:
+ return
+ tableClass = getTableClass(tag)
+ for masterTable in tableClass.dependencies:
+ if masterTable not in done:
+ if masterTable in self:
+ self._writeTable(masterTable, writer, done, tableCache)
+ else:
+ done.append(masterTable)
+ done.append(tag)
+ tabledata = self.getTableData(tag)
+ if tableCache is not None:
+ entry = tableCache.get((Tag(tag), tabledata))
+ if entry is not None:
+ log.debug("reusing '%s' table", tag)
+ writer.setEntry(tag, entry)
+ return
+ log.debug("Writing '%s' table to disk", tag)
+ writer[tag] = tabledata
+ if tableCache is not None:
+ tableCache[(Tag(tag), tabledata)] = writer[tag]
+
+ def getTableData(self, tag: str | bytes) -> bytes:
+ """Returns the binary representation of a table.
+
+ If the table is currently loaded and in memory, the data is compiled to
+ binary and returned; if it is not currently loaded, the binary data is
+ read from the font file and returned.
+ """
+ tag = Tag(tag)
+ if self.isLoaded(tag):
+ log.debug("Compiling '%s' table", tag)
+ return self.tables[tag].compile(self)
+ elif self.reader and tag in self.reader:
+ log.debug("Reading '%s' table from disk", tag)
+ return self.reader[tag]
+ else:
+ raise KeyError(tag)
+
+ def getGlyphSet(
+ self,
+ preferCFF: bool = True,
+ location: Mapping[str, _NumberT] | None = None,
+ normalized: bool = False,
+ recalcBounds: bool = True,
+ ) -> _TTGlyphSet:
+ """Return a generic GlyphSet, which is a dict-like object
+ mapping glyph names to glyph objects. The returned glyph objects
+ have a ``.draw()`` method that supports the Pen protocol, and will
+ have an attribute named 'width'.
+
+ If the font is CFF-based, the outlines will be taken from the ``CFF ``
+ or ``CFF2`` tables. Otherwise the outlines will be taken from the
+ ``glyf`` table.
+
+ If the font contains both a ``CFF ``/``CFF2`` and a ``glyf`` table, you
+ can use the ``preferCFF`` argument to specify which one should be taken.
+ If the font contains both a ``CFF `` and a ``CFF2`` table, the latter is
+ taken.
+
+ If the ``location`` parameter is set, it should be a dictionary mapping
+ four-letter variation tags to their float values, and the returned
+ glyph-set will represent an instance of a variable font at that
+ location.
+
+ If the ``normalized`` variable is set to True, that location is
+ interpreted as in the normalized (-1..+1) space, otherwise it is in the
+ font's defined axes space.
+ """
+ if location and "fvar" not in self:
+ location = None
+ if location and not normalized:
+ location = self.normalizeLocation(location)
+ glyphSet = None
+ if ("CFF " in self or "CFF2" in self) and (preferCFF or "glyf" not in self):
+ glyphSet = _TTGlyphSetCFF(self, location)
+ elif "glyf" in self:
+ glyphSet = _TTGlyphSetGlyf(self, location, recalcBounds=recalcBounds)
+ else:
+ raise TTLibError("Font contains no outlines")
+ if "VARC" in self:
+ glyphSet = _TTGlyphSetVARC(self, location, glyphSet)
+ return glyphSet
+
+ def normalizeLocation(self, location: Mapping[str, float]) -> dict[str, float]:
+ """Normalize a ``location`` from the font's defined axes space (also
+ known as user space) into the normalized (-1..+1) space. It applies
+ ``avar`` mapping if the font contains an ``avar`` table.
+
+ The ``location`` parameter should be a dictionary mapping four-letter
+ variation tags to their float values.
+
+ Raises ``TTLibError`` if the font is not a variable font.
+ """
+ from fontTools.varLib.models import normalizeLocation
+
+ if "fvar" not in self:
+ raise TTLibError("Not a variable font")
+
+ axes = self["fvar"].getAxes()
+ location = normalizeLocation(location, axes)
+ if "avar" in self:
+ location = self["avar"].renormalizeLocation(location, self)
+ return location
+
+ def getBestCmap(
+ self,
+ cmapPreferences: Sequence[tuple[int, int]] = (
+ (3, 10),
+ (0, 6),
+ (0, 4),
+ (3, 1),
+ (0, 3),
+ (0, 2),
+ (0, 1),
+ (0, 0),
+ ),
+ ) -> dict[int, str] | None:
+ """Returns the 'best' Unicode cmap dictionary available in the font
+ or ``None``, if no Unicode cmap subtable is available.
+
+ By default it will search for the following (platformID, platEncID)
+ pairs in order::
+
+ (3, 10), # Windows Unicode full repertoire
+ (0, 6), # Unicode full repertoire (format 13 subtable)
+ (0, 4), # Unicode 2.0 full repertoire
+ (3, 1), # Windows Unicode BMP
+ (0, 3), # Unicode 2.0 BMP
+ (0, 2), # Unicode ISO/IEC 10646
+ (0, 1), # Unicode 1.1
+ (0, 0) # Unicode 1.0
+
+ This particular order matches what HarfBuzz uses to choose what
+ subtable to use by default. This order prefers the largest-repertoire
+ subtable, and among those, prefers the Windows-platform over the
+ Unicode-platform as the former has wider support.
+
+ This order can be customized via the ``cmapPreferences`` argument.
+ """
+ return self["cmap"].getBestCmap(cmapPreferences=cmapPreferences)
+
+ def reorderGlyphs(self, new_glyph_order: list[str]) -> None:
+ from .reorderGlyphs import reorderGlyphs
+
+ reorderGlyphs(self, new_glyph_order)
+
+
+class GlyphOrder(object):
+ """A pseudo table. The glyph order isn't in the font as a separate
+ table, but it's nice to present it as such in the TTX format.
+ """
+
+ def __init__(self, tag: str | None = None) -> None:
+ pass
+
+ def toXML(self, writer: xmlWriter.XMLWriter, ttFont: TTFont) -> None:
+ glyphOrder = ttFont.getGlyphOrder()
+ writer.comment(
+ "The 'id' attribute is only for humans; it is ignored when parsed."
+ )
+ writer.newline()
+ for i, glyphName in enumerate(glyphOrder):
+ writer.simpletag("GlyphID", id=i, name=glyphName)
+ writer.newline()
+
+ def fromXML(
+ self, name: str, attrs: dict[str, str], content: list[Any], ttFont: TTFont
+ ) -> None:
+ if not hasattr(self, "glyphOrder"):
+ self.glyphOrder = []
+ if name == "GlyphID":
+ self.glyphOrder.append(attrs["name"])
+ ttFont.setGlyphOrder(self.glyphOrder)
+
+
+def getTableModule(tag: str | bytes) -> ModuleType | None:
+ """Fetch the packer/unpacker module for a table.
+ Return None when no module is found.
+ """
+ from . import tables
+
+ pyTag = tagToIdentifier(tag)
+ try:
+ __import__("fontTools.ttLib.tables." + pyTag)
+ except ImportError as err:
+ # If pyTag is found in the ImportError message,
+ # means table is not implemented. If it's not
+ # there, then some other module is missing, don't
+ # suppress the error.
+ if str(err).find(pyTag) >= 0:
+ return None
+ else:
+ raise err
+ else:
+ return getattr(tables, pyTag)
+
+
+# Registry for custom table packer/unpacker classes. Keys are table
+# tags, values are (moduleName, className) tuples.
+# See registerCustomTableClass() and getCustomTableClass()
+_customTableRegistry: dict[str | bytes, tuple[str, str]] = {}
+
+
+def registerCustomTableClass(
+ tag: str | bytes, moduleName: str, className: str | None = None
+) -> None:
+ """Register a custom packer/unpacker class for a table.
+
+ The 'moduleName' must be an importable module. If no 'className'
+ is given, it is derived from the tag, for example it will be
+ ``table_C_U_S_T_`` for a 'CUST' tag.
+
+ The registered table class should be a subclass of
+ :py:class:`fontTools.ttLib.tables.DefaultTable.DefaultTable`
+ """
+ if className is None:
+ className = "table_" + tagToIdentifier(tag)
+ _customTableRegistry[tag] = (moduleName, className)
+
+
+def unregisterCustomTableClass(tag: str | bytes) -> None:
+ """Unregister the custom packer/unpacker class for a table."""
+ del _customTableRegistry[tag]
+
+
+def getCustomTableClass(tag: str | bytes) -> type[DefaultTable] | None:
+ """Return the custom table class for tag, if one has been registered
+ with 'registerCustomTableClass()'. Else return None.
+ """
+ if tag not in _customTableRegistry:
+ return None
+ import importlib
+
+ moduleName, className = _customTableRegistry[tag]
+ module = importlib.import_module(moduleName)
+ return getattr(module, className)
+
+
+def getTableClass(tag: str | bytes) -> type[DefaultTable]:
+ """Fetch the packer/unpacker class for a table."""
+ tableClass = getCustomTableClass(tag)
+ if tableClass is not None:
+ return tableClass
+ module = getTableModule(tag)
+ if module is None:
+ from .tables.DefaultTable import DefaultTable
+
+ return DefaultTable
+ pyTag = tagToIdentifier(tag)
+ tableClass = getattr(module, "table_" + pyTag)
+ return tableClass
+
+
+def getClassTag(klass: type[DefaultTable]) -> str | bytes:
+ """Fetch the table tag for a class object."""
+ name = klass.__name__
+ assert name[:6] == "table_"
+ name = name[6:] # Chop 'table_'
+ return identifierToTag(name)
+
+
+def newTable(tag: str | bytes) -> DefaultTable:
+ """Return a new instance of a table."""
+ tableClass = getTableClass(tag)
+ return tableClass(tag)
+
+
+def _escapechar(c: str) -> str:
+ """Helper function for tagToIdentifier()"""
+ import re
+
+ if re.match("[a-z0-9]", c):
+ return "_" + c
+ elif re.match("[A-Z]", c):
+ return c + "_"
+ else:
+ return hex(byteord(c))[2:]
+
+
+def tagToIdentifier(tag: str | bytes) -> str:
+ """Convert a table tag to a valid (but UGLY) python identifier,
+ as well as a filename that's guaranteed to be unique even on a
+ caseless file system. Each character is mapped to two characters.
+ Lowercase letters get an underscore before the letter, uppercase
+ letters get an underscore after the letter. Trailing spaces are
+ trimmed. Illegal characters are escaped as two hex bytes. If the
+ result starts with a number (as the result of a hex escape), an
+ extra underscore is prepended. Examples:
+ .. code-block:: pycon
+
+ >>>
+ >> tagToIdentifier('glyf')
+ '_g_l_y_f'
+ >> tagToIdentifier('cvt ')
+ '_c_v_t'
+ >> tagToIdentifier('OS/2')
+ 'O_S_2f_2'
+ """
+ import re
+
+ tag = Tag(tag)
+ if tag == "GlyphOrder":
+ return tag
+ assert len(tag) == 4, "tag should be 4 characters long"
+ while len(tag) > 1 and tag[-1] == " ":
+ tag = tag[:-1]
+ ident = ""
+ for c in tag:
+ ident = ident + _escapechar(c)
+ if re.match("[0-9]", ident):
+ ident = "_" + ident
+ return ident
+
+
+def identifierToTag(ident: str) -> str:
+ """the opposite of tagToIdentifier()"""
+ if ident == "GlyphOrder":
+ return ident
+ if len(ident) % 2 and ident[0] == "_":
+ ident = ident[1:]
+ assert not (len(ident) % 2)
+ tag = ""
+ for i in range(0, len(ident), 2):
+ if ident[i] == "_":
+ tag = tag + ident[i + 1]
+ elif ident[i + 1] == "_":
+ tag = tag + ident[i]
+ else:
+ # assume hex
+ tag = tag + chr(int(ident[i : i + 2], 16))
+ # append trailing spaces
+ tag = tag + (4 - len(tag)) * " "
+ return Tag(tag)
+
+
+def tagToXML(tag: str | bytes) -> str:
+ """Similarly to tagToIdentifier(), this converts a TT tag
+ to a valid XML element name. Since XML element names are
+ case sensitive, this is a fairly simple/readable translation.
+ """
+ import re
+
+ tag = Tag(tag)
+ if tag == "OS/2":
+ return "OS_2"
+ elif tag == "GlyphOrder":
+ return tag
+ if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
+ return tag.strip()
+ else:
+ return tagToIdentifier(tag)
+
+
+def xmlToTag(tag: str) -> str:
+ """The opposite of tagToXML()"""
+ if tag == "OS_2":
+ return Tag("OS/2")
+ if len(tag) == 8:
+ return identifierToTag(tag)
+ else:
+ return Tag(tag + " " * (4 - len(tag)))
+
+
+# Table order as recommended in the OpenType specification 1.4
+TTFTableOrder = [
+ "head",
+ "hhea",
+ "maxp",
+ "OS/2",
+ "hmtx",
+ "LTSH",
+ "VDMX",
+ "hdmx",
+ "cmap",
+ "fpgm",
+ "prep",
+ "cvt ",
+ "loca",
+ "glyf",
+ "kern",
+ "name",
+ "post",
+ "gasp",
+ "PCLT",
+]
+
+OTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post", "CFF "]
+
+
+def sortedTagList(
+ tagList: Sequence[str], tableOrder: Sequence[str] | None = None
+) -> list[str]:
+ """Return a sorted copy of tagList, sorted according to the OpenType
+ specification, or according to a custom tableOrder. If given and not
+ None, tableOrder needs to be a list of tag names.
+ """
+ tagList = sorted(tagList)
+ if tableOrder is None:
+ if "DSIG" in tagList:
+ # DSIG should be last (XXX spec reference?)
+ tagList.remove("DSIG")
+ tagList.append("DSIG")
+ if "CFF " in tagList:
+ tableOrder = OTFTableOrder
+ else:
+ tableOrder = TTFTableOrder
+ orderedTables = []
+ for tag in tableOrder:
+ if tag in tagList:
+ orderedTables.append(tag)
+ tagList.remove(tag)
+ orderedTables.extend(tagList)
+ return orderedTables
+
+
+def reorderFontTables(
+ inFile: BinaryIO, # Takes file-like object as per original
+ outFile: BinaryIO, # Takes file-like object
+ tableOrder: Sequence[str] | None = None,
+ checkChecksums: bool = False, # Keep param even if reader handles it
+) -> None:
+ """Rewrite a font file, ordering the tables as recommended by the
+ OpenType specification 1.4.
+ """
+ inFile.seek(0)
+ outFile.seek(0)
+ reader = SFNTReader(inFile, checkChecksums=checkChecksums)
+ writer = SFNTWriter(
+ outFile,
+ len(reader.tables),
+ reader.sfntVersion,
+ reader.flavor,
+ reader.flavorData,
+ )
+ tables = list(reader.keys())
+ for tag in sortedTagList(tables, tableOrder):
+ writer[tag] = reader[tag]
+ writer.close()
+
+
+def maxPowerOfTwo(x: int) -> int:
+ """Return the highest exponent of two, so that
+ (2 ** exponent) <= x. Return 0 if x is 0.
+ """
+ exponent = 0
+ while x:
+ x = x >> 1
+ exponent = exponent + 1
+ return max(exponent - 1, 0)
+
+
+def getSearchRange(n: int, itemSize: int = 16) -> tuple[int, int, int]:
+ """Calculate searchRange, entrySelector, rangeShift."""
+ # itemSize defaults to 16, for backward compatibility
+ # with upstream fonttools.
+ exponent = maxPowerOfTwo(n)
+ searchRange = (2**exponent) * itemSize
+ entrySelector = exponent
+ rangeShift = max(0, n * itemSize - searchRange)
+ return searchRange, entrySelector, rangeShift
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/ttGlyphSet.py b/lib/python3.12/site-packages/fontTools/ttLib/ttGlyphSet.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a9a0cb9960434d28fd98bd6bf9fdd128522d89f
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/ttGlyphSet.py
@@ -0,0 +1,490 @@
+"""GlyphSets returned by a TTFont."""
+
+from abc import ABC, abstractmethod
+from collections.abc import Mapping
+from contextlib import contextmanager
+from copy import copy, deepcopy
+from types import SimpleNamespace
+from fontTools.misc.vector import Vector
+from fontTools.misc.fixedTools import otRound, fixedToFloat as fi2fl
+from fontTools.misc.loggingTools import deprecateFunction
+from fontTools.misc.transform import Transform, DecomposedTransform
+from fontTools.pens.transformPen import TransformPen, TransformPointPen
+from fontTools.pens.recordingPen import (
+ DecomposingRecordingPen,
+ lerpRecordings,
+ replayRecording,
+)
+
+
+class _TTGlyphSet(Mapping):
+ """Generic dict-like GlyphSet class that pulls metrics from hmtx and
+ glyph shape from TrueType or CFF.
+ """
+
+ def __init__(self, font, location, glyphsMapping, *, recalcBounds=True):
+ self.recalcBounds = recalcBounds
+ self.font = font
+ self.defaultLocationNormalized = (
+ {axis.axisTag: 0 for axis in self.font["fvar"].axes}
+ if "fvar" in self.font
+ else {}
+ )
+ self.location = location if location is not None else {}
+ self.rawLocation = {} # VarComponent-only location
+ self.originalLocation = location if location is not None else {}
+ self.depth = 0
+ self.locationStack = []
+ self.rawLocationStack = []
+ self.glyphsMapping = glyphsMapping
+ self.hMetrics = font["hmtx"].metrics
+ self.vMetrics = getattr(font.get("vmtx"), "metrics", None)
+ self.hvarTable = None
+ if location:
+ from fontTools.varLib.varStore import VarStoreInstancer
+
+ self.hvarTable = getattr(font.get("HVAR"), "table", None)
+ if self.hvarTable is not None:
+ self.hvarInstancer = VarStoreInstancer(
+ self.hvarTable.VarStore, font["fvar"].axes, location
+ )
+ # TODO VVAR, VORG
+
+ @contextmanager
+ def pushLocation(self, location, reset: bool):
+ self.locationStack.append(self.location)
+ self.rawLocationStack.append(self.rawLocation)
+ if reset:
+ self.location = self.originalLocation.copy()
+ self.rawLocation = self.defaultLocationNormalized.copy()
+ else:
+ self.location = self.location.copy()
+ self.rawLocation = {}
+ self.location.update(location)
+ self.rawLocation.update(location)
+
+ try:
+ yield None
+ finally:
+ self.location = self.locationStack.pop()
+ self.rawLocation = self.rawLocationStack.pop()
+
+ @contextmanager
+ def pushDepth(self):
+ try:
+ depth = self.depth
+ self.depth += 1
+ yield depth
+ finally:
+ self.depth -= 1
+
+ def __contains__(self, glyphName):
+ return glyphName in self.glyphsMapping
+
+ def __iter__(self):
+ return iter(self.glyphsMapping.keys())
+
+ def __len__(self):
+ return len(self.glyphsMapping)
+
+ @deprecateFunction(
+ "use 'glyphName in glyphSet' instead", category=DeprecationWarning
+ )
+ def has_key(self, glyphName):
+ return glyphName in self.glyphsMapping
+
+
+class _TTGlyphSetGlyf(_TTGlyphSet):
+ def __init__(self, font, location, recalcBounds=True):
+ self.glyfTable = font["glyf"]
+ super().__init__(font, location, self.glyfTable, recalcBounds=recalcBounds)
+ self.gvarTable = font.get("gvar")
+
+ def __getitem__(self, glyphName):
+ return _TTGlyphGlyf(self, glyphName, recalcBounds=self.recalcBounds)
+
+
+class _TTGlyphSetCFF(_TTGlyphSet):
+ def __init__(self, font, location):
+ tableTag = "CFF2" if "CFF2" in font else "CFF "
+ self.charStrings = list(font[tableTag].cff.values())[0].CharStrings
+ super().__init__(font, location, self.charStrings)
+ self.setLocation(location)
+
+ def __getitem__(self, glyphName):
+ return _TTGlyphCFF(self, glyphName)
+
+ def setLocation(self, location):
+ self.blender = None
+ if location:
+ # TODO Optimize by using instancer.setLocation()
+
+ from fontTools.varLib.varStore import VarStoreInstancer
+
+ varStore = getattr(self.charStrings, "varStore", None)
+ if varStore is not None:
+ instancer = VarStoreInstancer(
+ varStore.otVarStore, self.font["fvar"].axes, location
+ )
+ self.blender = instancer.interpolateFromDeltas
+ else:
+ self.blender = None
+
+ @contextmanager
+ def pushLocation(self, location, reset: bool):
+ self.setLocation(location)
+ with _TTGlyphSet.pushLocation(self, location, reset) as value:
+ try:
+ yield value
+ finally:
+ self.setLocation(self.location)
+
+
+class _TTGlyphSetVARC(_TTGlyphSet):
+ def __init__(self, font, location, glyphSet):
+ self.glyphSet = glyphSet
+ super().__init__(font, location, glyphSet)
+ self.varcTable = font["VARC"].table
+
+ def __getitem__(self, glyphName):
+ varc = self.varcTable
+ if glyphName not in varc.Coverage.glyphs:
+ return self.glyphSet[glyphName]
+ return _TTGlyphVARC(self, glyphName)
+
+
+class _TTGlyph(ABC):
+ """Glyph object that supports the Pen protocol, meaning that it has
+ .draw() and .drawPoints() methods that take a pen object as their only
+ argument. Additionally there are 'width' and 'lsb' attributes, read from
+ the 'hmtx' table.
+
+ If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
+ attributes.
+ """
+
+ def __init__(self, glyphSet, glyphName, *, recalcBounds=True):
+ self.glyphSet = glyphSet
+ self.name = glyphName
+ self.recalcBounds = recalcBounds
+ self.width, self.lsb = glyphSet.hMetrics[glyphName]
+ if glyphSet.vMetrics is not None:
+ self.height, self.tsb = glyphSet.vMetrics[glyphName]
+ else:
+ self.height, self.tsb = None, None
+ if glyphSet.location and glyphSet.hvarTable is not None:
+ varidx = (
+ glyphSet.font.getGlyphID(glyphName)
+ if glyphSet.hvarTable.AdvWidthMap is None
+ else glyphSet.hvarTable.AdvWidthMap.mapping[glyphName]
+ )
+ self.width += glyphSet.hvarInstancer[varidx]
+ # TODO: VVAR/VORG
+
+ @abstractmethod
+ def draw(self, pen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
+ how that works.
+ """
+ raise NotImplementedError
+
+ def drawPoints(self, pen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.pointPen for details
+ how that works.
+ """
+ from fontTools.pens.pointPen import SegmentToPointPen
+
+ self.draw(SegmentToPointPen(pen))
+
+
+class _TTGlyphGlyf(_TTGlyph):
+ def draw(self, pen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
+ how that works.
+ """
+ glyph, offset = self._getGlyphAndOffset()
+
+ with self.glyphSet.pushDepth() as depth:
+ if depth:
+ offset = 0 # Offset should only apply at top-level
+
+ glyph.draw(pen, self.glyphSet.glyfTable, offset)
+
+ def drawPoints(self, pen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.pointPen for details
+ how that works.
+ """
+ glyph, offset = self._getGlyphAndOffset()
+
+ with self.glyphSet.pushDepth() as depth:
+ if depth:
+ offset = 0 # Offset should only apply at top-level
+
+ glyph.drawPoints(pen, self.glyphSet.glyfTable, offset)
+
+ def _getGlyphAndOffset(self):
+ if self.glyphSet.location and self.glyphSet.gvarTable is not None:
+ glyph = self._getGlyphInstance()
+ else:
+ glyph = self.glyphSet.glyfTable[self.name]
+
+ offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+ return glyph, offset
+
+ def _getGlyphInstance(self):
+ from fontTools.varLib.iup import iup_delta
+ from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+ from fontTools.varLib.models import supportScalar
+
+ glyphSet = self.glyphSet
+ glyfTable = glyphSet.glyfTable
+ variations = glyphSet.gvarTable.variations[self.name]
+ hMetrics = glyphSet.hMetrics
+ vMetrics = glyphSet.vMetrics
+ coordinates, _ = glyfTable._getCoordinatesAndControls(
+ self.name, hMetrics, vMetrics
+ )
+ origCoords, endPts = None, None
+ for var in variations:
+ scalar = supportScalar(glyphSet.location, var.axes)
+ if not scalar:
+ continue
+ delta = var.coordinates
+ if None in delta:
+ if origCoords is None:
+ origCoords, control = glyfTable._getCoordinatesAndControls(
+ self.name, hMetrics, vMetrics
+ )
+ endPts = (
+ control[1] if control[0] >= 1 else list(range(len(control[1])))
+ )
+ delta = iup_delta(delta, origCoords, endPts)
+ coordinates += GlyphCoordinates(delta) * scalar
+
+ glyph = copy(glyfTable[self.name]) # Shallow copy
+ width, lsb, height, tsb = _setCoordinates(
+ glyph, coordinates, glyfTable, recalcBounds=self.recalcBounds
+ )
+ self.lsb = lsb
+ self.tsb = tsb
+ if glyphSet.hvarTable is None:
+ # no HVAR: let's set metrics from the phantom points
+ self.width = width
+ self.height = height
+ return glyph
+
+
+class _TTGlyphCFF(_TTGlyph):
+ def draw(self, pen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
+ how that works.
+ """
+ self.glyphSet.charStrings[self.name].draw(pen, self.glyphSet.blender)
+
+
+def _evaluateCondition(condition, fvarAxes, location, instancer):
+ if condition.Format == 1:
+ # ConditionAxisRange
+ axisIndex = condition.AxisIndex
+ axisTag = fvarAxes[axisIndex].axisTag
+ axisValue = location.get(axisTag, 0)
+ minValue = condition.FilterRangeMinValue
+ maxValue = condition.FilterRangeMaxValue
+ return minValue <= axisValue <= maxValue
+ elif condition.Format == 2:
+ # ConditionValue
+ value = condition.DefaultValue
+ value += instancer[condition.VarIdx][0]
+ return value > 0
+ elif condition.Format == 3:
+ # ConditionAnd
+ for subcondition in condition.ConditionTable:
+ if not _evaluateCondition(subcondition, fvarAxes, location, instancer):
+ return False
+ return True
+ elif condition.Format == 4:
+ # ConditionOr
+ for subcondition in condition.ConditionTable:
+ if _evaluateCondition(subcondition, fvarAxes, location, instancer):
+ return True
+ return False
+ elif condition.Format == 5:
+ # ConditionNegate
+ return not _evaluateCondition(
+ condition.conditionTable, fvarAxes, location, instancer
+ )
+ else:
+ return False # Unkonwn condition format
+
+
+class _TTGlyphVARC(_TTGlyph):
+ def _draw(self, pen, isPointPen):
+ """Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
+ how that works.
+ """
+ from fontTools.ttLib.tables.otTables import (
+ VarComponentFlags,
+ NO_VARIATION_INDEX,
+ )
+
+ glyphSet = self.glyphSet
+ varc = glyphSet.varcTable
+ idx = varc.Coverage.glyphs.index(self.name)
+ glyph = varc.VarCompositeGlyphs.VarCompositeGlyph[idx]
+
+ from fontTools.varLib.multiVarStore import MultiVarStoreInstancer
+ from fontTools.varLib.varStore import VarStoreInstancer
+
+ fvarAxes = glyphSet.font["fvar"].axes
+ instancer = MultiVarStoreInstancer(
+ varc.MultiVarStore, fvarAxes, self.glyphSet.location
+ )
+
+ for comp in glyph.components:
+ if comp.flags & VarComponentFlags.HAVE_CONDITION:
+ condition = varc.ConditionList.ConditionTable[comp.conditionIndex]
+ if not _evaluateCondition(
+ condition, fvarAxes, self.glyphSet.location, instancer
+ ):
+ continue
+
+ location = {}
+ if comp.axisIndicesIndex is not None:
+ axisIndices = varc.AxisIndicesList.Item[comp.axisIndicesIndex]
+ axisValues = Vector(comp.axisValues)
+ if comp.axisValuesVarIndex != NO_VARIATION_INDEX:
+ axisValues += fi2fl(instancer[comp.axisValuesVarIndex], 14)
+ assert len(axisIndices) == len(axisValues), (
+ len(axisIndices),
+ len(axisValues),
+ )
+ location = {
+ fvarAxes[i].axisTag: v for i, v in zip(axisIndices, axisValues)
+ }
+
+ if comp.transformVarIndex != NO_VARIATION_INDEX:
+ deltas = instancer[comp.transformVarIndex]
+ comp = deepcopy(comp)
+ comp.applyTransformDeltas(deltas)
+ transform = comp.transform
+
+ reset = comp.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES
+ with self.glyphSet.glyphSet.pushLocation(location, reset):
+ with self.glyphSet.pushLocation(location, reset):
+ shouldDecompose = self.name == comp.glyphName
+
+ if not shouldDecompose:
+ try:
+ pen.addVarComponent(
+ comp.glyphName, transform, self.glyphSet.rawLocation
+ )
+ except AttributeError:
+ shouldDecompose = True
+
+ if shouldDecompose:
+ t = transform.toTransform()
+ compGlyphSet = (
+ self.glyphSet
+ if comp.glyphName != self.name
+ else glyphSet.glyphSet
+ )
+ g = compGlyphSet[comp.glyphName]
+ if isPointPen:
+ tPen = TransformPointPen(pen, t)
+ g.drawPoints(tPen)
+ else:
+ tPen = TransformPen(pen, t)
+ g.draw(tPen)
+
+ def draw(self, pen):
+ self._draw(pen, False)
+
+ def drawPoints(self, pen):
+ self._draw(pen, True)
+
+
+def _setCoordinates(glyph, coord, glyfTable, *, recalcBounds=True):
+ # Handle phantom points for (left, right, top, bottom) positions.
+ assert len(coord) >= 4
+ leftSideX = coord[-4][0]
+ rightSideX = coord[-3][0]
+ topSideY = coord[-2][1]
+ bottomSideY = coord[-1][1]
+
+ for _ in range(4):
+ del coord[-1]
+
+ if glyph.isComposite():
+ assert len(coord) == len(glyph.components)
+ glyph.components = [copy(comp) for comp in glyph.components] # Shallow copy
+ for p, comp in zip(coord, glyph.components):
+ if hasattr(comp, "x"):
+ comp.x, comp.y = p
+ elif glyph.numberOfContours == 0:
+ assert len(coord) == 0
+ else:
+ assert len(coord) == len(glyph.coordinates)
+ glyph.coordinates = coord
+
+ if recalcBounds:
+ glyph.recalcBounds(glyfTable)
+
+ horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
+ verticalAdvanceWidth = otRound(topSideY - bottomSideY)
+ leftSideBearing = otRound(glyph.xMin - leftSideX)
+ topSideBearing = otRound(topSideY - glyph.yMax)
+ return (
+ horizontalAdvanceWidth,
+ leftSideBearing,
+ verticalAdvanceWidth,
+ topSideBearing,
+ )
+
+
+class LerpGlyphSet(Mapping):
+ """A glyphset that interpolates between two other glyphsets.
+
+ Factor is typically between 0 and 1. 0 means the first glyphset,
+ 1 means the second glyphset, and 0.5 means the average of the
+ two glyphsets. Other values are possible, and can be useful to
+ extrapolate. Defaults to 0.5.
+ """
+
+ def __init__(self, glyphset1, glyphset2, factor=0.5):
+ self.glyphset1 = glyphset1
+ self.glyphset2 = glyphset2
+ self.factor = factor
+
+ def __getitem__(self, glyphname):
+ if glyphname in self.glyphset1 and glyphname in self.glyphset2:
+ return LerpGlyph(glyphname, self)
+ raise KeyError(glyphname)
+
+ def __contains__(self, glyphname):
+ return glyphname in self.glyphset1 and glyphname in self.glyphset2
+
+ def __iter__(self):
+ set1 = set(self.glyphset1)
+ set2 = set(self.glyphset2)
+ return iter(set1.intersection(set2))
+
+ def __len__(self):
+ set1 = set(self.glyphset1)
+ set2 = set(self.glyphset2)
+ return len(set1.intersection(set2))
+
+
+class LerpGlyph:
+ def __init__(self, glyphname, glyphset):
+ self.glyphset = glyphset
+ self.glyphname = glyphname
+
+ def draw(self, pen):
+ recording1 = DecomposingRecordingPen(self.glyphset.glyphset1)
+ self.glyphset.glyphset1[self.glyphname].draw(recording1)
+ recording2 = DecomposingRecordingPen(self.glyphset.glyphset2)
+ self.glyphset.glyphset2[self.glyphname].draw(recording2)
+
+ factor = self.glyphset.factor
+
+ replayRecording(lerpRecordings(recording1.value, recording2.value, factor), pen)
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/ttVisitor.py b/lib/python3.12/site-packages/fontTools/ttLib/ttVisitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..54db61b1e0b1be5e2d36fd72008230de7fc35401
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/ttVisitor.py
@@ -0,0 +1,32 @@
+"""Specialization of fontTools.misc.visitor to work with TTFont."""
+
+from fontTools.misc.visitor import Visitor
+from fontTools.ttLib import TTFont
+
+
+class TTVisitor(Visitor):
+ def visitAttr(self, obj, attr, value, *args, **kwargs):
+ if isinstance(value, TTFont):
+ return False
+ super().visitAttr(obj, attr, value, *args, **kwargs)
+
+ def visit(self, obj, *args, **kwargs):
+ if hasattr(obj, "ensureDecompiled"):
+ obj.ensureDecompiled(recurse=False)
+ super().visit(obj, *args, **kwargs)
+
+
+@TTVisitor.register(TTFont)
+def visit(visitor, font, *args, **kwargs):
+ # Some objects have links back to TTFont; even though we
+ # have a check in visitAttr to stop them from recursing
+ # onto TTFont, sometimes they still do, for example when
+ # someone overrides visitAttr.
+ if hasattr(visitor, "font"):
+ return False
+
+ visitor.font = font
+ for tag in font.keys():
+ visitor.visit(font[tag], *args, **kwargs)
+ del visitor.font
+ return False
diff --git a/lib/python3.12/site-packages/fontTools/ttLib/woff2.py b/lib/python3.12/site-packages/fontTools/ttLib/woff2.py
new file mode 100644
index 0000000000000000000000000000000000000000..c11aeb2e54349b82f86213d598f98ee5eb763238
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ttLib/woff2.py
@@ -0,0 +1,1680 @@
+from io import BytesIO
+import sys
+import array
+import struct
+from collections import OrderedDict
+from fontTools.misc import sstruct
+from fontTools.misc.arrayTools import calcIntBounds
+from fontTools.misc.textTools import Tag, bytechr, byteord, bytesjoin, pad
+from fontTools.ttLib import (
+ TTFont,
+ TTLibError,
+ getTableModule,
+ getTableClass,
+ getSearchRange,
+)
+from fontTools.ttLib.sfnt import (
+ SFNTReader,
+ SFNTWriter,
+ DirectoryEntry,
+ WOFFFlavorData,
+ sfntDirectoryFormat,
+ sfntDirectorySize,
+ SFNTDirectoryEntry,
+ sfntDirectoryEntrySize,
+ calcChecksum,
+)
+from fontTools.ttLib.tables import ttProgram, _g_l_y_f
+import logging
+
+
+log = logging.getLogger("fontTools.ttLib.woff2")
+
+haveBrotli = False
+try:
+ try:
+ import brotlicffi as brotli
+ except ImportError:
+ import brotli
+ haveBrotli = True
+except ImportError:
+ pass
+
+
+class WOFF2Reader(SFNTReader):
+ flavor = "woff2"
+
+ def __init__(self, file, checkChecksums=0, fontNumber=-1):
+ if not haveBrotli:
+ log.error(
+ "The WOFF2 decoder requires the Brotli Python extension, available at: "
+ "https://github.com/google/brotli"
+ )
+ raise ImportError("No module named brotli")
+
+ self.file = file
+
+ signature = Tag(self.file.read(4))
+ if signature != b"wOF2":
+ raise TTLibError("Not a WOFF2 font (bad signature)")
+
+ self.file.seek(0)
+ self.DirectoryEntry = WOFF2DirectoryEntry
+ data = self.file.read(woff2DirectorySize)
+ if len(data) != woff2DirectorySize:
+ raise TTLibError("Not a WOFF2 font (not enough data)")
+ sstruct.unpack(woff2DirectoryFormat, data, self)
+
+ self.tables = OrderedDict()
+ offset = 0
+ for i in range(self.numTables):
+ entry = self.DirectoryEntry()
+ entry.fromFile(self.file)
+ tag = Tag(entry.tag)
+ self.tables[tag] = entry
+ entry.offset = offset
+ offset += entry.length
+
+ totalUncompressedSize = offset
+ compressedData = self.file.read(self.totalCompressedSize)
+ decompressedData = brotli.decompress(compressedData)
+ if len(decompressedData) != totalUncompressedSize:
+ raise TTLibError(
+ "unexpected size for decompressed font data: expected %d, found %d"
+ % (totalUncompressedSize, len(decompressedData))
+ )
+ self.transformBuffer = BytesIO(decompressedData)
+
+ self.file.seek(0, 2)
+ if self.length != self.file.tell():
+ raise TTLibError("reported 'length' doesn't match the actual file size")
+
+ self.flavorData = WOFF2FlavorData(self)
+
+ # make empty TTFont to store data while reconstructing tables
+ self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+
+ def __getitem__(self, tag):
+ """Fetch the raw table data. Reconstruct transformed tables."""
+ entry = self.tables[Tag(tag)]
+ if not hasattr(entry, "data"):
+ if entry.transformed:
+ entry.data = self.reconstructTable(tag)
+ else:
+ entry.data = entry.loadData(self.transformBuffer)
+ return entry.data
+
+ def reconstructTable(self, tag):
+ """Reconstruct table named 'tag' from transformed data."""
+ entry = self.tables[Tag(tag)]
+ rawData = entry.loadData(self.transformBuffer)
+ if tag == "glyf":
+ # no need to pad glyph data when reconstructing
+ padding = self.padding if hasattr(self, "padding") else None
+ data = self._reconstructGlyf(rawData, padding)
+ elif tag == "loca":
+ data = self._reconstructLoca()
+ elif tag == "hmtx":
+ data = self._reconstructHmtx(rawData)
+ else:
+ raise TTLibError("transform for table '%s' is unknown" % tag)
+ return data
+
+ def _reconstructGlyf(self, data, padding=None):
+ """Return recostructed glyf table data, and set the corresponding loca's
+ locations. Optionally pad glyph offsets to the specified number of bytes.
+ """
+ self.ttFont["loca"] = WOFF2LocaTable()
+ glyfTable = self.ttFont["glyf"] = WOFF2GlyfTable()
+ glyfTable.reconstruct(data, self.ttFont)
+ if padding:
+ glyfTable.padding = padding
+ data = glyfTable.compile(self.ttFont)
+ return data
+
+ def _reconstructLoca(self):
+ """Return reconstructed loca table data."""
+ if "loca" not in self.ttFont:
+ # make sure glyf is reconstructed first
+ self.tables["glyf"].data = self.reconstructTable("glyf")
+ locaTable = self.ttFont["loca"]
+ data = locaTable.compile(self.ttFont)
+ if len(data) != self.tables["loca"].origLength:
+ raise TTLibError(
+ "reconstructed 'loca' table doesn't match original size: "
+ "expected %d, found %d" % (self.tables["loca"].origLength, len(data))
+ )
+ return data
+
+ def _reconstructHmtx(self, data):
+ """Return reconstructed hmtx table data."""
+ # Before reconstructing 'hmtx' table we need to parse other tables:
+ # 'glyf' is required for reconstructing the sidebearings from the glyphs'
+ # bounding box; 'hhea' is needed for the numberOfHMetrics field.
+ if "glyf" in self.flavorData.transformedTables:
+ # transformed 'glyf' table is self-contained, thus 'loca' not needed
+ tableDependencies = ("maxp", "hhea", "glyf")
+ else:
+ # decompiling untransformed 'glyf' requires 'loca', which requires 'head'
+ tableDependencies = ("maxp", "head", "hhea", "loca", "glyf")
+ for tag in tableDependencies:
+ self._decompileTable(tag)
+ hmtxTable = self.ttFont["hmtx"] = WOFF2HmtxTable()
+ hmtxTable.reconstruct(data, self.ttFont)
+ data = hmtxTable.compile(self.ttFont)
+ return data
+
+ def _decompileTable(self, tag):
+ """Decompile table data and store it inside self.ttFont."""
+ data = self[tag]
+ if self.ttFont.isLoaded(tag):
+ return self.ttFont[tag]
+ tableClass = getTableClass(tag)
+ table = tableClass(tag)
+ self.ttFont.tables[tag] = table
+ table.decompile(data, self.ttFont)
+
+
+class WOFF2Writer(SFNTWriter):
+ flavor = "woff2"
+
+ def __init__(
+ self,
+ file,
+ numTables,
+ sfntVersion="\000\001\000\000",
+ flavor=None,
+ flavorData=None,
+ ):
+ if not haveBrotli:
+ log.error(
+ "The WOFF2 encoder requires the Brotli Python extension, available at: "
+ "https://github.com/google/brotli"
+ )
+ raise ImportError("No module named brotli")
+
+ self.file = file
+ self.numTables = numTables
+ self.sfntVersion = Tag(sfntVersion)
+ self.flavorData = WOFF2FlavorData(data=flavorData)
+
+ self.directoryFormat = woff2DirectoryFormat
+ self.directorySize = woff2DirectorySize
+ self.DirectoryEntry = WOFF2DirectoryEntry
+
+ self.signature = Tag("wOF2")
+
+ self.nextTableOffset = 0
+ self.transformBuffer = BytesIO()
+
+ self.tables = OrderedDict()
+
+ # make empty TTFont to store data while normalising and transforming tables
+ self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+
+ def __setitem__(self, tag, data):
+ """Associate new entry named 'tag' with raw table data."""
+ if tag in self.tables:
+ raise TTLibError("cannot rewrite '%s' table" % tag)
+ if tag == "DSIG":
+ # always drop DSIG table, since the encoding process can invalidate it
+ self.numTables -= 1
+ return
+
+ entry = self.DirectoryEntry()
+ entry.tag = Tag(tag)
+ entry.flags = getKnownTagIndex(entry.tag)
+ # WOFF2 table data are written to disk only on close(), after all tags
+ # have been specified
+ entry.data = data
+
+ self.tables[tag] = entry
+
+ def close(self):
+ """All tags must have been specified. Now write the table data and directory."""
+ if len(self.tables) != self.numTables:
+ raise TTLibError(
+ "wrong number of tables; expected %d, found %d"
+ % (self.numTables, len(self.tables))
+ )
+
+ if self.sfntVersion in ("\x00\x01\x00\x00", "true"):
+ isTrueType = True
+ elif self.sfntVersion == "OTTO":
+ isTrueType = False
+ else:
+ raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
+
+ # The WOFF2 spec no longer requires the glyph offsets to be 4-byte aligned.
+ # However, the reference WOFF2 implementation still fails to reconstruct
+ # 'unpadded' glyf tables, therefore we need to 'normalise' them.
+ # See:
+ # https://github.com/khaledhosny/ots/issues/60
+ # https://github.com/google/woff2/issues/15
+ if (
+ isTrueType
+ and "glyf" in self.flavorData.transformedTables
+ and "glyf" in self.tables
+ ):
+ self._normaliseGlyfAndLoca(padding=4)
+ self._setHeadTransformFlag()
+
+ # To pass the legacy OpenType Sanitiser currently included in browsers,
+ # we must sort the table directory and data alphabetically by tag.
+ # See:
+ # https://github.com/google/woff2/pull/3
+ # https://lists.w3.org/Archives/Public/public-webfonts-wg/2015Mar/0000.html
+ #
+ # 2023: We rely on this in _transformTables where we expect that
+ # "loca" comes after "glyf" table.
+ self.tables = OrderedDict(sorted(self.tables.items()))
+
+ self.totalSfntSize = self._calcSFNTChecksumsLengthsAndOffsets()
+
+ fontData = self._transformTables()
+ compressedFont = brotli.compress(fontData, mode=brotli.MODE_FONT)
+
+ self.totalCompressedSize = len(compressedFont)
+ self.length = self._calcTotalSize()
+ self.majorVersion, self.minorVersion = self._getVersion()
+ self.reserved = 0
+
+ directory = self._packTableDirectory()
+ self.file.seek(0)
+ self.file.write(pad(directory + compressedFont, size=4))
+ self._writeFlavorData()
+
+ def _normaliseGlyfAndLoca(self, padding=4):
+ """Recompile glyf and loca tables, aligning glyph offsets to multiples of
+ 'padding' size. Update the head table's 'indexToLocFormat' accordingly while
+ compiling loca.
+ """
+ if self.sfntVersion == "OTTO":
+ return
+
+ for tag in ("maxp", "head", "loca", "glyf", "fvar"):
+ if tag in self.tables:
+ self._decompileTable(tag)
+ self.ttFont["glyf"].padding = padding
+ for tag in ("glyf", "loca"):
+ self._compileTable(tag)
+
+ def _setHeadTransformFlag(self):
+ """Set bit 11 of 'head' table flags to indicate that the font has undergone
+ a lossless modifying transform. Re-compile head table data."""
+ self._decompileTable("head")
+ self.ttFont["head"].flags |= 1 << 11
+ self._compileTable("head")
+
+ def _decompileTable(self, tag):
+ """Fetch table data, decompile it, and store it inside self.ttFont."""
+ tag = Tag(tag)
+ if tag not in self.tables:
+ raise TTLibError("missing required table: %s" % tag)
+ if self.ttFont.isLoaded(tag):
+ return
+ data = self.tables[tag].data
+ if tag == "loca":
+ tableClass = WOFF2LocaTable
+ elif tag == "glyf":
+ tableClass = WOFF2GlyfTable
+ elif tag == "hmtx":
+ tableClass = WOFF2HmtxTable
+ else:
+ tableClass = getTableClass(tag)
+ table = tableClass(tag)
+ self.ttFont.tables[tag] = table
+ table.decompile(data, self.ttFont)
+
+ def _compileTable(self, tag):
+ """Compile table and store it in its 'data' attribute."""
+ self.tables[tag].data = self.ttFont[tag].compile(self.ttFont)
+
+ def _calcSFNTChecksumsLengthsAndOffsets(self):
+ """Compute the 'original' SFNT checksums, lengths and offsets for checksum
+ adjustment calculation. Return the total size of the uncompressed font.
+ """
+ offset = sfntDirectorySize + sfntDirectoryEntrySize * len(self.tables)
+ for tag, entry in self.tables.items():
+ data = entry.data
+ entry.origOffset = offset
+ entry.origLength = len(data)
+ if tag == "head":
+ entry.checkSum = calcChecksum(data[:8] + b"\0\0\0\0" + data[12:])
+ else:
+ entry.checkSum = calcChecksum(data)
+ offset += (entry.origLength + 3) & ~3
+ return offset
+
+ def _transformTables(self):
+ """Return transformed font data."""
+ transformedTables = self.flavorData.transformedTables
+ for tag, entry in self.tables.items():
+ data = None
+ if tag in transformedTables:
+ data = self.transformTable(tag)
+ if data is not None:
+ entry.transformed = True
+ if data is None:
+ if tag == "glyf":
+ # Currently we always sort table tags so
+ # 'loca' comes after 'glyf'.
+ transformedTables.discard("loca")
+ # pass-through the table data without transformation
+ data = entry.data
+ entry.transformed = False
+ entry.offset = self.nextTableOffset
+ entry.saveData(self.transformBuffer, data)
+ self.nextTableOffset += entry.length
+ self.writeMasterChecksum()
+ fontData = self.transformBuffer.getvalue()
+ return fontData
+
+ def transformTable(self, tag):
+ """Return transformed table data, or None if some pre-conditions aren't
+ met -- in which case, the non-transformed table data will be used.
+ """
+ if tag == "loca":
+ data = b""
+ elif tag == "glyf":
+ for tag in ("maxp", "head", "loca", "glyf"):
+ self._decompileTable(tag)
+ glyfTable = self.ttFont["glyf"]
+ data = glyfTable.transform(self.ttFont)
+ elif tag == "hmtx":
+ if "glyf" not in self.tables:
+ return
+ for tag in ("maxp", "head", "hhea", "loca", "glyf", "hmtx"):
+ self._decompileTable(tag)
+ hmtxTable = self.ttFont["hmtx"]
+ data = hmtxTable.transform(self.ttFont) # can be None
+ else:
+ raise TTLibError("Transform for table '%s' is unknown" % tag)
+ return data
+
+ def _calcMasterChecksum(self):
+ """Calculate checkSumAdjustment."""
+ checksums = []
+ for tag in self.tables.keys():
+ checksums.append(self.tables[tag].checkSum)
+
+ # Create a SFNT directory for checksum calculation purposes
+ self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(
+ self.numTables, 16
+ )
+ directory = sstruct.pack(sfntDirectoryFormat, self)
+ tables = sorted(self.tables.items())
+ for tag, entry in tables:
+ sfntEntry = SFNTDirectoryEntry()
+ sfntEntry.tag = entry.tag
+ sfntEntry.checkSum = entry.checkSum
+ sfntEntry.offset = entry.origOffset
+ sfntEntry.length = entry.origLength
+ directory = directory + sfntEntry.toString()
+
+ directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
+ assert directory_end == len(directory)
+
+ checksums.append(calcChecksum(directory))
+ checksum = sum(checksums) & 0xFFFFFFFF
+ # BiboAfba!
+ checksumadjustment = (0xB1B0AFBA - checksum) & 0xFFFFFFFF
+ return checksumadjustment
+
+ def writeMasterChecksum(self):
+ """Write checkSumAdjustment to the transformBuffer."""
+ checksumadjustment = self._calcMasterChecksum()
+ self.transformBuffer.seek(self.tables["head"].offset + 8)
+ self.transformBuffer.write(struct.pack(">L", checksumadjustment))
+
+ def _calcTotalSize(self):
+ """Calculate total size of WOFF2 font, including any meta- and/or private data."""
+ offset = self.directorySize
+ for entry in self.tables.values():
+ offset += len(entry.toString())
+ offset += self.totalCompressedSize
+ offset = (offset + 3) & ~3
+ offset = self._calcFlavorDataOffsetsAndSize(offset)
+ return offset
+
+ def _calcFlavorDataOffsetsAndSize(self, start):
+ """Calculate offsets and lengths for any meta- and/or private data."""
+ offset = start
+ data = self.flavorData
+ if data.metaData:
+ self.metaOrigLength = len(data.metaData)
+ self.metaOffset = offset
+ self.compressedMetaData = brotli.compress(
+ data.metaData, mode=brotli.MODE_TEXT
+ )
+ self.metaLength = len(self.compressedMetaData)
+ offset += self.metaLength
+ else:
+ self.metaOffset = self.metaLength = self.metaOrigLength = 0
+ self.compressedMetaData = b""
+ if data.privData:
+ # make sure private data is padded to 4-byte boundary
+ offset = (offset + 3) & ~3
+ self.privOffset = offset
+ self.privLength = len(data.privData)
+ offset += self.privLength
+ else:
+ self.privOffset = self.privLength = 0
+ return offset
+
+ def _getVersion(self):
+ """Return the WOFF2 font's (majorVersion, minorVersion) tuple."""
+ data = self.flavorData
+ if data.majorVersion is not None and data.minorVersion is not None:
+ return data.majorVersion, data.minorVersion
+ else:
+ # if None, return 'fontRevision' from 'head' table
+ if "head" in self.tables:
+ return struct.unpack(">HH", self.tables["head"].data[4:8])
+ else:
+ return 0, 0
+
+ def _packTableDirectory(self):
+ """Return WOFF2 table directory data."""
+ directory = sstruct.pack(self.directoryFormat, self)
+ for entry in self.tables.values():
+ directory = directory + entry.toString()
+ return directory
+
+ def _writeFlavorData(self):
+ """Write metadata and/or private data using appropiate padding."""
+ compressedMetaData = self.compressedMetaData
+ privData = self.flavorData.privData
+ if compressedMetaData and privData:
+ compressedMetaData = pad(compressedMetaData, size=4)
+ if compressedMetaData:
+ self.file.seek(self.metaOffset)
+ assert self.file.tell() == self.metaOffset
+ self.file.write(compressedMetaData)
+ if privData:
+ self.file.seek(self.privOffset)
+ assert self.file.tell() == self.privOffset
+ self.file.write(privData)
+
+ def reordersTables(self):
+ return True
+
+
+# -- woff2 directory helpers and cruft
+
+woff2DirectoryFormat = """
+ > # big endian
+ signature: 4s # "wOF2"
+ sfntVersion: 4s
+ length: L # total woff2 file size
+ numTables: H # number of tables
+ reserved: H # set to 0
+ totalSfntSize: L # uncompressed size
+ totalCompressedSize: L # compressed size
+ majorVersion: H # major version of WOFF file
+ minorVersion: H # minor version of WOFF file
+ metaOffset: L # offset to metadata block
+ metaLength: L # length of compressed metadata
+ metaOrigLength: L # length of uncompressed metadata
+ privOffset: L # offset to private data block
+ privLength: L # length of private data block
+"""
+
+woff2DirectorySize = sstruct.calcsize(woff2DirectoryFormat)
+
+woff2KnownTags = (
+ "cmap",
+ "head",
+ "hhea",
+ "hmtx",
+ "maxp",
+ "name",
+ "OS/2",
+ "post",
+ "cvt ",
+ "fpgm",
+ "glyf",
+ "loca",
+ "prep",
+ "CFF ",
+ "VORG",
+ "EBDT",
+ "EBLC",
+ "gasp",
+ "hdmx",
+ "kern",
+ "LTSH",
+ "PCLT",
+ "VDMX",
+ "vhea",
+ "vmtx",
+ "BASE",
+ "GDEF",
+ "GPOS",
+ "GSUB",
+ "EBSC",
+ "JSTF",
+ "MATH",
+ "CBDT",
+ "CBLC",
+ "COLR",
+ "CPAL",
+ "SVG ",
+ "sbix",
+ "acnt",
+ "avar",
+ "bdat",
+ "bloc",
+ "bsln",
+ "cvar",
+ "fdsc",
+ "feat",
+ "fmtx",
+ "fvar",
+ "gvar",
+ "hsty",
+ "just",
+ "lcar",
+ "mort",
+ "morx",
+ "opbd",
+ "prop",
+ "trak",
+ "Zapf",
+ "Silf",
+ "Glat",
+ "Gloc",
+ "Feat",
+ "Sill",
+)
+
+woff2FlagsFormat = """
+ > # big endian
+ flags: B # table type and flags
+"""
+
+woff2FlagsSize = sstruct.calcsize(woff2FlagsFormat)
+
+woff2UnknownTagFormat = """
+ > # big endian
+ tag: 4s # 4-byte tag (optional)
+"""
+
+woff2UnknownTagSize = sstruct.calcsize(woff2UnknownTagFormat)
+
+woff2UnknownTagIndex = 0x3F
+
+woff2Base128MaxSize = 5
+woff2DirectoryEntryMaxSize = (
+ woff2FlagsSize + woff2UnknownTagSize + 2 * woff2Base128MaxSize
+)
+
+woff2TransformedTableTags = ("glyf", "loca")
+
+woff2GlyfTableFormat = """
+ > # big endian
+ version: H # = 0x0000
+ optionFlags: H # Bit 0: we have overlapSimpleBitmap[], Bits 1-15: reserved
+ numGlyphs: H # Number of glyphs
+ indexFormat: H # Offset format for loca table
+ nContourStreamSize: L # Size of nContour stream
+ nPointsStreamSize: L # Size of nPoints stream
+ flagStreamSize: L # Size of flag stream
+ glyphStreamSize: L # Size of glyph stream
+ compositeStreamSize: L # Size of composite stream
+ bboxStreamSize: L # Comnined size of bboxBitmap and bboxStream
+ instructionStreamSize: L # Size of instruction stream
+"""
+
+woff2GlyfTableFormatSize = sstruct.calcsize(woff2GlyfTableFormat)
+
+bboxFormat = """
+ > # big endian
+ xMin: h
+ yMin: h
+ xMax: h
+ yMax: h
+"""
+
+woff2OverlapSimpleBitmapFlag = 0x0001
+
+
+def getKnownTagIndex(tag):
+ """Return index of 'tag' in woff2KnownTags list. Return 63 if not found."""
+ try:
+ return woff2KnownTags.index(tag)
+ except ValueError:
+ return woff2UnknownTagIndex
+
+
+class WOFF2DirectoryEntry(DirectoryEntry):
+ def fromFile(self, file):
+ pos = file.tell()
+ data = file.read(woff2DirectoryEntryMaxSize)
+ left = self.fromString(data)
+ consumed = len(data) - len(left)
+ file.seek(pos + consumed)
+
+ def fromString(self, data):
+ if len(data) < 1:
+ raise TTLibError("can't read table 'flags': not enough data")
+ dummy, data = sstruct.unpack2(woff2FlagsFormat, data, self)
+ if self.flags & 0x3F == 0x3F:
+ # if bits [0..5] of the flags byte == 63, read a 4-byte arbitrary tag value
+ if len(data) < woff2UnknownTagSize:
+ raise TTLibError("can't read table 'tag': not enough data")
+ dummy, data = sstruct.unpack2(woff2UnknownTagFormat, data, self)
+ else:
+ # otherwise, tag is derived from a fixed 'Known Tags' table
+ self.tag = woff2KnownTags[self.flags & 0x3F]
+ self.tag = Tag(self.tag)
+ self.origLength, data = unpackBase128(data)
+ self.length = self.origLength
+ if self.transformed:
+ self.length, data = unpackBase128(data)
+ if self.tag == "loca" and self.length != 0:
+ raise TTLibError("the transformLength of the 'loca' table must be 0")
+ # return left over data
+ return data
+
+ def toString(self):
+ data = bytechr(self.flags)
+ if (self.flags & 0x3F) == 0x3F:
+ data += struct.pack(">4s", self.tag.tobytes())
+ data += packBase128(self.origLength)
+ if self.transformed:
+ data += packBase128(self.length)
+ return data
+
+ @property
+ def transformVersion(self):
+ """Return bits 6-7 of table entry's flags, which indicate the preprocessing
+ transformation version number (between 0 and 3).
+ """
+ return self.flags >> 6
+
+ @transformVersion.setter
+ def transformVersion(self, value):
+ assert 0 <= value <= 3
+ self.flags |= value << 6
+
+ @property
+ def transformed(self):
+ """Return True if the table has any transformation, else return False."""
+ # For all tables in a font, except for 'glyf' and 'loca', the transformation
+ # version 0 indicates the null transform (where the original table data is
+ # passed directly to the Brotli compressor). For 'glyf' and 'loca' tables,
+ # transformation version 3 indicates the null transform
+ if self.tag in {"glyf", "loca"}:
+ return self.transformVersion != 3
+ else:
+ return self.transformVersion != 0
+
+ @transformed.setter
+ def transformed(self, booleanValue):
+ # here we assume that a non-null transform means version 0 for 'glyf' and
+ # 'loca' and 1 for every other table (e.g. hmtx); but that may change as
+ # new transformation formats are introduced in the future (if ever).
+ if self.tag in {"glyf", "loca"}:
+ self.transformVersion = 3 if not booleanValue else 0
+ else:
+ self.transformVersion = int(booleanValue)
+
+
+class WOFF2LocaTable(getTableClass("loca")):
+ """Same as parent class. The only difference is that it attempts to preserve
+ the 'indexFormat' as encoded in the WOFF2 glyf table.
+ """
+
+ def __init__(self, tag=None):
+ self.tableTag = Tag(tag or "loca")
+
+ def compile(self, ttFont):
+ try:
+ max_location = max(self.locations)
+ except AttributeError:
+ self.set([])
+ max_location = 0
+ if "glyf" in ttFont and hasattr(ttFont["glyf"], "indexFormat"):
+ # copile loca using the indexFormat specified in the WOFF2 glyf table
+ indexFormat = ttFont["glyf"].indexFormat
+ if indexFormat == 0:
+ if max_location >= 0x20000:
+ raise TTLibError("indexFormat is 0 but local offsets > 0x20000")
+ if not all(l % 2 == 0 for l in self.locations):
+ raise TTLibError(
+ "indexFormat is 0 but local offsets not multiples of 2"
+ )
+ locations = array.array("H")
+ for location in self.locations:
+ locations.append(location // 2)
+ else:
+ locations = array.array("I", self.locations)
+ if sys.byteorder != "big":
+ locations.byteswap()
+ data = locations.tobytes()
+ else:
+ # use the most compact indexFormat given the current glyph offsets
+ data = super(WOFF2LocaTable, self).compile(ttFont)
+ return data
+
+
+class WOFF2GlyfTable(getTableClass("glyf")):
+ """Decoder/Encoder for WOFF2 'glyf' table transform."""
+
+ subStreams = (
+ "nContourStream",
+ "nPointsStream",
+ "flagStream",
+ "glyphStream",
+ "compositeStream",
+ "bboxStream",
+ "instructionStream",
+ )
+
+ def __init__(self, tag=None):
+ self.tableTag = Tag(tag or "glyf")
+
+ def reconstruct(self, data, ttFont):
+ """Decompile transformed 'glyf' data."""
+ inputDataSize = len(data)
+
+ if inputDataSize < woff2GlyfTableFormatSize:
+ raise TTLibError("not enough 'glyf' data")
+ dummy, data = sstruct.unpack2(woff2GlyfTableFormat, data, self)
+ offset = woff2GlyfTableFormatSize
+
+ for stream in self.subStreams:
+ size = getattr(self, stream + "Size")
+ setattr(self, stream, data[:size])
+ data = data[size:]
+ offset += size
+
+ hasOverlapSimpleBitmap = self.optionFlags & woff2OverlapSimpleBitmapFlag
+ self.overlapSimpleBitmap = None
+ if hasOverlapSimpleBitmap:
+ overlapSimpleBitmapSize = (self.numGlyphs + 7) >> 3
+ self.overlapSimpleBitmap = array.array("B", data[:overlapSimpleBitmapSize])
+ offset += overlapSimpleBitmapSize
+
+ if offset != inputDataSize:
+ raise TTLibError(
+ "incorrect size of transformed 'glyf' table: expected %d, received %d bytes"
+ % (offset, inputDataSize)
+ )
+
+ bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
+ bboxBitmap = self.bboxStream[:bboxBitmapSize]
+ self.bboxBitmap = array.array("B", bboxBitmap)
+ self.bboxStream = self.bboxStream[bboxBitmapSize:]
+
+ self.nContourStream = array.array("h", self.nContourStream)
+ if sys.byteorder != "big":
+ self.nContourStream.byteswap()
+ assert len(self.nContourStream) == self.numGlyphs
+
+ if "head" in ttFont:
+ ttFont["head"].indexToLocFormat = self.indexFormat
+ try:
+ self.glyphOrder = ttFont.getGlyphOrder()
+ except:
+ self.glyphOrder = None
+ if self.glyphOrder is None:
+ self.glyphOrder = [".notdef"]
+ self.glyphOrder.extend(["glyph%.5d" % i for i in range(1, self.numGlyphs)])
+ else:
+ if len(self.glyphOrder) != self.numGlyphs:
+ raise TTLibError(
+ "incorrect glyphOrder: expected %d glyphs, found %d"
+ % (len(self.glyphOrder), self.numGlyphs)
+ )
+
+ glyphs = self.glyphs = {}
+ for glyphID, glyphName in enumerate(self.glyphOrder):
+ glyph = self._decodeGlyph(glyphID)
+ glyphs[glyphName] = glyph
+
+ def transform(self, ttFont):
+ """Return transformed 'glyf' data"""
+ self.numGlyphs = len(self.glyphs)
+ assert len(self.glyphOrder) == self.numGlyphs
+ if "maxp" in ttFont:
+ ttFont["maxp"].numGlyphs = self.numGlyphs
+ self.indexFormat = ttFont["head"].indexToLocFormat
+
+ for stream in self.subStreams:
+ setattr(self, stream, b"")
+ bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
+ self.bboxBitmap = array.array("B", [0] * bboxBitmapSize)
+
+ self.overlapSimpleBitmap = array.array("B", [0] * ((self.numGlyphs + 7) >> 3))
+ for glyphID in range(self.numGlyphs):
+ try:
+ self._encodeGlyph(glyphID)
+ except NotImplementedError:
+ return None
+ hasOverlapSimpleBitmap = any(self.overlapSimpleBitmap)
+
+ self.bboxStream = self.bboxBitmap.tobytes() + self.bboxStream
+ for stream in self.subStreams:
+ setattr(self, stream + "Size", len(getattr(self, stream)))
+ self.version = 0
+ self.optionFlags = 0
+ if hasOverlapSimpleBitmap:
+ self.optionFlags |= woff2OverlapSimpleBitmapFlag
+ data = sstruct.pack(woff2GlyfTableFormat, self)
+ data += bytesjoin([getattr(self, s) for s in self.subStreams])
+ if hasOverlapSimpleBitmap:
+ data += self.overlapSimpleBitmap.tobytes()
+ return data
+
+ def _decodeGlyph(self, glyphID):
+ glyph = getTableModule("glyf").Glyph()
+ glyph.numberOfContours = self.nContourStream[glyphID]
+ if glyph.numberOfContours == 0:
+ return glyph
+ elif glyph.isComposite():
+ self._decodeComponents(glyph)
+ else:
+ self._decodeCoordinates(glyph)
+ self._decodeOverlapSimpleFlag(glyph, glyphID)
+ self._decodeBBox(glyphID, glyph)
+ return glyph
+
+ def _decodeComponents(self, glyph):
+ data = self.compositeStream
+ glyph.components = []
+ more = 1
+ haveInstructions = 0
+ while more:
+ component = getTableModule("glyf").GlyphComponent()
+ more, haveInstr, data = component.decompile(data, self)
+ haveInstructions = haveInstructions | haveInstr
+ glyph.components.append(component)
+ self.compositeStream = data
+ if haveInstructions:
+ self._decodeInstructions(glyph)
+
+ def _decodeCoordinates(self, glyph):
+ data = self.nPointsStream
+ endPtsOfContours = []
+ endPoint = -1
+ for i in range(glyph.numberOfContours):
+ ptsOfContour, data = unpack255UShort(data)
+ endPoint += ptsOfContour
+ endPtsOfContours.append(endPoint)
+ glyph.endPtsOfContours = endPtsOfContours
+ self.nPointsStream = data
+ self._decodeTriplets(glyph)
+ self._decodeInstructions(glyph)
+
+ def _decodeOverlapSimpleFlag(self, glyph, glyphID):
+ if self.overlapSimpleBitmap is None or glyph.numberOfContours <= 0:
+ return
+ byte = glyphID >> 3
+ bit = glyphID & 7
+ if self.overlapSimpleBitmap[byte] & (0x80 >> bit):
+ glyph.flags[0] |= _g_l_y_f.flagOverlapSimple
+
+ def _decodeInstructions(self, glyph):
+ glyphStream = self.glyphStream
+ instructionStream = self.instructionStream
+ instructionLength, glyphStream = unpack255UShort(glyphStream)
+ glyph.program = ttProgram.Program()
+ glyph.program.fromBytecode(instructionStream[:instructionLength])
+ self.glyphStream = glyphStream
+ self.instructionStream = instructionStream[instructionLength:]
+
+ def _decodeBBox(self, glyphID, glyph):
+ haveBBox = bool(self.bboxBitmap[glyphID >> 3] & (0x80 >> (glyphID & 7)))
+ if glyph.isComposite() and not haveBBox:
+ raise TTLibError("no bbox values for composite glyph %d" % glyphID)
+ if haveBBox:
+ dummy, self.bboxStream = sstruct.unpack2(bboxFormat, self.bboxStream, glyph)
+ else:
+ glyph.recalcBounds(self)
+
+ def _decodeTriplets(self, glyph):
+ def withSign(flag, baseval):
+ assert 0 <= baseval and baseval < 65536, "integer overflow"
+ return baseval if flag & 1 else -baseval
+
+ nPoints = glyph.endPtsOfContours[-1] + 1
+ flagSize = nPoints
+ if flagSize > len(self.flagStream):
+ raise TTLibError("not enough 'flagStream' data")
+ flagsData = self.flagStream[:flagSize]
+ self.flagStream = self.flagStream[flagSize:]
+ flags = array.array("B", flagsData)
+
+ triplets = array.array("B", self.glyphStream)
+ nTriplets = len(triplets)
+ assert nPoints <= nTriplets
+
+ x = 0
+ y = 0
+ glyph.coordinates = getTableModule("glyf").GlyphCoordinates.zeros(nPoints)
+ glyph.flags = array.array("B")
+ tripletIndex = 0
+ for i in range(nPoints):
+ flag = flags[i]
+ onCurve = not bool(flag >> 7)
+ flag &= 0x7F
+ if flag < 84:
+ nBytes = 1
+ elif flag < 120:
+ nBytes = 2
+ elif flag < 124:
+ nBytes = 3
+ else:
+ nBytes = 4
+ assert (tripletIndex + nBytes) <= nTriplets
+ if flag < 10:
+ dx = 0
+ dy = withSign(flag, ((flag & 14) << 7) + triplets[tripletIndex])
+ elif flag < 20:
+ dx = withSign(flag, (((flag - 10) & 14) << 7) + triplets[tripletIndex])
+ dy = 0
+ elif flag < 84:
+ b0 = flag - 20
+ b1 = triplets[tripletIndex]
+ dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4))
+ dy = withSign(flag >> 1, 1 + ((b0 & 0x0C) << 2) + (b1 & 0x0F))
+ elif flag < 120:
+ b0 = flag - 84
+ dx = withSign(flag, 1 + ((b0 // 12) << 8) + triplets[tripletIndex])
+ dy = withSign(
+ flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + triplets[tripletIndex + 1]
+ )
+ elif flag < 124:
+ b2 = triplets[tripletIndex + 1]
+ dx = withSign(flag, (triplets[tripletIndex] << 4) + (b2 >> 4))
+ dy = withSign(
+ flag >> 1, ((b2 & 0x0F) << 8) + triplets[tripletIndex + 2]
+ )
+ else:
+ dx = withSign(
+ flag, (triplets[tripletIndex] << 8) + triplets[tripletIndex + 1]
+ )
+ dy = withSign(
+ flag >> 1,
+ (triplets[tripletIndex + 2] << 8) + triplets[tripletIndex + 3],
+ )
+ tripletIndex += nBytes
+ x += dx
+ y += dy
+ glyph.coordinates[i] = (x, y)
+ glyph.flags.append(int(onCurve))
+ bytesConsumed = tripletIndex
+ self.glyphStream = self.glyphStream[bytesConsumed:]
+
+ def _encodeGlyph(self, glyphID):
+ glyphName = self.getGlyphName(glyphID)
+ glyph = self[glyphName]
+ self.nContourStream += struct.pack(">h", glyph.numberOfContours)
+ if glyph.numberOfContours == 0:
+ return
+ elif glyph.isComposite():
+ self._encodeComponents(glyph)
+ else:
+ self._encodeCoordinates(glyph)
+ self._encodeOverlapSimpleFlag(glyph, glyphID)
+ self._encodeBBox(glyphID, glyph)
+
+ def _encodeComponents(self, glyph):
+ lastcomponent = len(glyph.components) - 1
+ more = 1
+ haveInstructions = 0
+ for i, component in enumerate(glyph.components):
+ if i == lastcomponent:
+ haveInstructions = hasattr(glyph, "program")
+ more = 0
+ self.compositeStream += component.compile(more, haveInstructions, self)
+ if haveInstructions:
+ self._encodeInstructions(glyph)
+
+ def _encodeCoordinates(self, glyph):
+ lastEndPoint = -1
+ if _g_l_y_f.flagCubic in glyph.flags:
+ raise NotImplementedError
+ for endPoint in glyph.endPtsOfContours:
+ ptsOfContour = endPoint - lastEndPoint
+ self.nPointsStream += pack255UShort(ptsOfContour)
+ lastEndPoint = endPoint
+ self._encodeTriplets(glyph)
+ self._encodeInstructions(glyph)
+
+ def _encodeOverlapSimpleFlag(self, glyph, glyphID):
+ if glyph.numberOfContours <= 0:
+ return
+ if glyph.flags[0] & _g_l_y_f.flagOverlapSimple:
+ byte = glyphID >> 3
+ bit = glyphID & 7
+ self.overlapSimpleBitmap[byte] |= 0x80 >> bit
+
+ def _encodeInstructions(self, glyph):
+ instructions = glyph.program.getBytecode()
+ self.glyphStream += pack255UShort(len(instructions))
+ self.instructionStream += instructions
+
+ def _encodeBBox(self, glyphID, glyph):
+ assert glyph.numberOfContours != 0, "empty glyph has no bbox"
+ if not glyph.isComposite():
+ # for simple glyphs, compare the encoded bounding box info with the calculated
+ # values, and if they match omit the bounding box info
+ currentBBox = glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax
+ calculatedBBox = calcIntBounds(glyph.coordinates)
+ if currentBBox == calculatedBBox:
+ return
+ self.bboxBitmap[glyphID >> 3] |= 0x80 >> (glyphID & 7)
+ self.bboxStream += sstruct.pack(bboxFormat, glyph)
+
+ def _encodeTriplets(self, glyph):
+ assert len(glyph.coordinates) == len(glyph.flags)
+ coordinates = glyph.coordinates.copy()
+ coordinates.absoluteToRelative()
+
+ flags = array.array("B")
+ triplets = array.array("B")
+ for i, (x, y) in enumerate(coordinates):
+ onCurve = glyph.flags[i] & _g_l_y_f.flagOnCurve
+ absX = abs(x)
+ absY = abs(y)
+ onCurveBit = 0 if onCurve else 128
+ xSignBit = 0 if (x < 0) else 1
+ ySignBit = 0 if (y < 0) else 1
+ xySignBits = xSignBit + 2 * ySignBit
+
+ if x == 0 and absY < 1280:
+ flags.append(onCurveBit + ((absY & 0xF00) >> 7) + ySignBit)
+ triplets.append(absY & 0xFF)
+ elif y == 0 and absX < 1280:
+ flags.append(onCurveBit + 10 + ((absX & 0xF00) >> 7) + xSignBit)
+ triplets.append(absX & 0xFF)
+ elif absX < 65 and absY < 65:
+ flags.append(
+ onCurveBit
+ + 20
+ + ((absX - 1) & 0x30)
+ + (((absY - 1) & 0x30) >> 2)
+ + xySignBits
+ )
+ triplets.append((((absX - 1) & 0xF) << 4) | ((absY - 1) & 0xF))
+ elif absX < 769 and absY < 769:
+ flags.append(
+ onCurveBit
+ + 84
+ + 12 * (((absX - 1) & 0x300) >> 8)
+ + (((absY - 1) & 0x300) >> 6)
+ + xySignBits
+ )
+ triplets.append((absX - 1) & 0xFF)
+ triplets.append((absY - 1) & 0xFF)
+ elif absX < 4096 and absY < 4096:
+ flags.append(onCurveBit + 120 + xySignBits)
+ triplets.append(absX >> 4)
+ triplets.append(((absX & 0xF) << 4) | (absY >> 8))
+ triplets.append(absY & 0xFF)
+ else:
+ flags.append(onCurveBit + 124 + xySignBits)
+ triplets.append(absX >> 8)
+ triplets.append(absX & 0xFF)
+ triplets.append(absY >> 8)
+ triplets.append(absY & 0xFF)
+
+ self.flagStream += flags.tobytes()
+ self.glyphStream += triplets.tobytes()
+
+
+class WOFF2HmtxTable(getTableClass("hmtx")):
+ def __init__(self, tag=None):
+ self.tableTag = Tag(tag or "hmtx")
+
+ def reconstruct(self, data, ttFont):
+ (flags,) = struct.unpack(">B", data[:1])
+ data = data[1:]
+ if flags & 0b11111100 != 0:
+ raise TTLibError("Bits 2-7 of '%s' flags are reserved" % self.tableTag)
+
+ # When bit 0 is _not_ set, the lsb[] array is present
+ hasLsbArray = flags & 1 == 0
+ # When bit 1 is _not_ set, the leftSideBearing[] array is present
+ hasLeftSideBearingArray = flags & 2 == 0
+ if hasLsbArray and hasLeftSideBearingArray:
+ raise TTLibError(
+ "either bits 0 or 1 (or both) must set in transformed '%s' flags"
+ % self.tableTag
+ )
+
+ glyfTable = ttFont["glyf"]
+ headerTable = ttFont["hhea"]
+ glyphOrder = glyfTable.glyphOrder
+ numGlyphs = len(glyphOrder)
+ numberOfHMetrics = min(int(headerTable.numberOfHMetrics), numGlyphs)
+
+ assert len(data) >= 2 * numberOfHMetrics
+ advanceWidthArray = array.array("H", data[: 2 * numberOfHMetrics])
+ if sys.byteorder != "big":
+ advanceWidthArray.byteswap()
+ data = data[2 * numberOfHMetrics :]
+
+ if hasLsbArray:
+ assert len(data) >= 2 * numberOfHMetrics
+ lsbArray = array.array("h", data[: 2 * numberOfHMetrics])
+ if sys.byteorder != "big":
+ lsbArray.byteswap()
+ data = data[2 * numberOfHMetrics :]
+ else:
+ # compute (proportional) glyphs' lsb from their xMin
+ lsbArray = array.array("h")
+ for i, glyphName in enumerate(glyphOrder):
+ if i >= numberOfHMetrics:
+ break
+ glyph = glyfTable[glyphName]
+ xMin = getattr(glyph, "xMin", 0)
+ lsbArray.append(xMin)
+
+ numberOfSideBearings = numGlyphs - numberOfHMetrics
+ if hasLeftSideBearingArray:
+ assert len(data) >= 2 * numberOfSideBearings
+ leftSideBearingArray = array.array("h", data[: 2 * numberOfSideBearings])
+ if sys.byteorder != "big":
+ leftSideBearingArray.byteswap()
+ data = data[2 * numberOfSideBearings :]
+ else:
+ # compute (monospaced) glyphs' leftSideBearing from their xMin
+ leftSideBearingArray = array.array("h")
+ for i, glyphName in enumerate(glyphOrder):
+ if i < numberOfHMetrics:
+ continue
+ glyph = glyfTable[glyphName]
+ xMin = getattr(glyph, "xMin", 0)
+ leftSideBearingArray.append(xMin)
+
+ if data:
+ raise TTLibError("too much '%s' table data" % self.tableTag)
+
+ self.metrics = {}
+ for i in range(numberOfHMetrics):
+ glyphName = glyphOrder[i]
+ advanceWidth, lsb = advanceWidthArray[i], lsbArray[i]
+ self.metrics[glyphName] = (advanceWidth, lsb)
+ lastAdvance = advanceWidthArray[-1]
+ for i in range(numberOfSideBearings):
+ glyphName = glyphOrder[i + numberOfHMetrics]
+ self.metrics[glyphName] = (lastAdvance, leftSideBearingArray[i])
+
+ def transform(self, ttFont):
+ glyphOrder = ttFont.getGlyphOrder()
+ glyf = ttFont["glyf"]
+ hhea = ttFont["hhea"]
+ numberOfHMetrics = hhea.numberOfHMetrics
+
+ # check if any of the proportional glyphs has left sidebearings that
+ # differ from their xMin bounding box values.
+ hasLsbArray = False
+ for i in range(numberOfHMetrics):
+ glyphName = glyphOrder[i]
+ lsb = self.metrics[glyphName][1]
+ if lsb != getattr(glyf[glyphName], "xMin", 0):
+ hasLsbArray = True
+ break
+
+ # do the same for the monospaced glyphs (if any) at the end of hmtx table
+ hasLeftSideBearingArray = False
+ for i in range(numberOfHMetrics, len(glyphOrder)):
+ glyphName = glyphOrder[i]
+ lsb = self.metrics[glyphName][1]
+ if lsb != getattr(glyf[glyphName], "xMin", 0):
+ hasLeftSideBearingArray = True
+ break
+
+ # if we need to encode both sidebearings arrays, then no transformation is
+ # applicable, and we must use the untransformed hmtx data
+ if hasLsbArray and hasLeftSideBearingArray:
+ return
+
+ # set bit 0 and 1 when the respective arrays are _not_ present
+ flags = 0
+ if not hasLsbArray:
+ flags |= 1 << 0
+ if not hasLeftSideBearingArray:
+ flags |= 1 << 1
+
+ data = struct.pack(">B", flags)
+
+ advanceWidthArray = array.array(
+ "H",
+ [
+ self.metrics[glyphName][0]
+ for i, glyphName in enumerate(glyphOrder)
+ if i < numberOfHMetrics
+ ],
+ )
+ if sys.byteorder != "big":
+ advanceWidthArray.byteswap()
+ data += advanceWidthArray.tobytes()
+
+ if hasLsbArray:
+ lsbArray = array.array(
+ "h",
+ [
+ self.metrics[glyphName][1]
+ for i, glyphName in enumerate(glyphOrder)
+ if i < numberOfHMetrics
+ ],
+ )
+ if sys.byteorder != "big":
+ lsbArray.byteswap()
+ data += lsbArray.tobytes()
+
+ if hasLeftSideBearingArray:
+ leftSideBearingArray = array.array(
+ "h",
+ [
+ self.metrics[glyphOrder[i]][1]
+ for i in range(numberOfHMetrics, len(glyphOrder))
+ ],
+ )
+ if sys.byteorder != "big":
+ leftSideBearingArray.byteswap()
+ data += leftSideBearingArray.tobytes()
+
+ return data
+
+
+class WOFF2FlavorData(WOFFFlavorData):
+ Flavor = "woff2"
+
+ def __init__(self, reader=None, data=None, transformedTables=None):
+ """Data class that holds the WOFF2 header major/minor version, any
+ metadata or private data (as bytes strings), and the set of
+ table tags that have transformations applied (if reader is not None),
+ or will have once the WOFF2 font is compiled.
+
+ Args:
+ reader: an SFNTReader (or subclass) object to read flavor data from.
+ data: another WOFFFlavorData object to initialise data from.
+ transformedTables: set of strings containing table tags to be transformed.
+
+ Raises:
+ ImportError if the brotli module is not installed.
+
+ NOTE: The 'reader' argument, on the one hand, and the 'data' and
+ 'transformedTables' arguments, on the other hand, are mutually exclusive.
+ """
+ if not haveBrotli:
+ raise ImportError("No module named brotli")
+
+ if reader is not None:
+ if data is not None:
+ raise TypeError("'reader' and 'data' arguments are mutually exclusive")
+ if transformedTables is not None:
+ raise TypeError(
+ "'reader' and 'transformedTables' arguments are mutually exclusive"
+ )
+
+ if transformedTables is not None and (
+ "glyf" in transformedTables
+ and "loca" not in transformedTables
+ or "loca" in transformedTables
+ and "glyf" not in transformedTables
+ ):
+ raise ValueError("'glyf' and 'loca' must be transformed (or not) together")
+ super(WOFF2FlavorData, self).__init__(reader=reader)
+ if reader:
+ transformedTables = [
+ tag for tag, entry in reader.tables.items() if entry.transformed
+ ]
+ elif data:
+ self.majorVersion = data.majorVersion
+ self.majorVersion = data.minorVersion
+ self.metaData = data.metaData
+ self.privData = data.privData
+ if transformedTables is None and hasattr(data, "transformedTables"):
+ transformedTables = data.transformedTables
+
+ if transformedTables is None:
+ transformedTables = woff2TransformedTableTags
+
+ self.transformedTables = set(transformedTables)
+
+ def _decompress(self, rawData):
+ return brotli.decompress(rawData)
+
+
+def unpackBase128(data):
+ r"""Read one to five bytes from UIntBase128-encoded input string, and return
+ a tuple containing the decoded integer plus any leftover data.
+
+ >>> unpackBase128(b'\x3f\x00\x00') == (63, b"\x00\x00")
+ True
+ >>> unpackBase128(b'\x8f\xff\xff\xff\x7f')[0] == 4294967295
+ True
+ >>> unpackBase128(b'\x80\x80\x3f') # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ File "", line 1, in ?
+ TTLibError: UIntBase128 value must not start with leading zeros
+ >>> unpackBase128(b'\x8f\xff\xff\xff\xff\x7f')[0] # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ File "", line 1, in ?
+ TTLibError: UIntBase128-encoded sequence is longer than 5 bytes
+ >>> unpackBase128(b'\x90\x80\x80\x80\x00')[0] # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ File "", line 1, in ?
+ TTLibError: UIntBase128 value exceeds 2**32-1
+ """
+ if len(data) == 0:
+ raise TTLibError("not enough data to unpack UIntBase128")
+ result = 0
+ if byteord(data[0]) == 0x80:
+ # font must be rejected if UIntBase128 value starts with 0x80
+ raise TTLibError("UIntBase128 value must not start with leading zeros")
+ for i in range(woff2Base128MaxSize):
+ if len(data) == 0:
+ raise TTLibError("not enough data to unpack UIntBase128")
+ code = byteord(data[0])
+ data = data[1:]
+ # if any of the top seven bits are set then we're about to overflow
+ if result & 0xFE000000:
+ raise TTLibError("UIntBase128 value exceeds 2**32-1")
+ # set current value = old value times 128 bitwise-or (byte bitwise-and 127)
+ result = (result << 7) | (code & 0x7F)
+ # repeat until the most significant bit of byte is false
+ if (code & 0x80) == 0:
+ # return result plus left over data
+ return result, data
+ # make sure not to exceed the size bound
+ raise TTLibError("UIntBase128-encoded sequence is longer than 5 bytes")
+
+
+def base128Size(n):
+ """Return the length in bytes of a UIntBase128-encoded sequence with value n.
+
+ >>> base128Size(0)
+ 1
+ >>> base128Size(24567)
+ 3
+ >>> base128Size(2**32-1)
+ 5
+ """
+ assert n >= 0
+ size = 1
+ while n >= 128:
+ size += 1
+ n >>= 7
+ return size
+
+
+def packBase128(n):
+ r"""Encode unsigned integer in range 0 to 2**32-1 (inclusive) to a string of
+ bytes using UIntBase128 variable-length encoding. Produce the shortest possible
+ encoding.
+
+ >>> packBase128(63) == b"\x3f"
+ True
+ >>> packBase128(2**32-1) == b'\x8f\xff\xff\xff\x7f'
+ True
+ """
+ if n < 0 or n >= 2**32:
+ raise TTLibError("UIntBase128 format requires 0 <= integer <= 2**32-1")
+ data = b""
+ size = base128Size(n)
+ for i in range(size):
+ b = (n >> (7 * (size - i - 1))) & 0x7F
+ if i < size - 1:
+ b |= 0x80
+ data += struct.pack("B", b)
+ return data
+
+
+def unpack255UShort(data):
+ """Read one to three bytes from 255UInt16-encoded input string, and return a
+ tuple containing the decoded integer plus any leftover data.
+
+ >>> unpack255UShort(bytechr(252))[0]
+ 252
+
+ Note that some numbers (e.g. 506) can have multiple encodings:
+ >>> unpack255UShort(struct.pack("BB", 254, 0))[0]
+ 506
+ >>> unpack255UShort(struct.pack("BB", 255, 253))[0]
+ 506
+ >>> unpack255UShort(struct.pack("BBB", 253, 1, 250))[0]
+ 506
+ """
+ code = byteord(data[:1])
+ data = data[1:]
+ if code == 253:
+ # read two more bytes as an unsigned short
+ if len(data) < 2:
+ raise TTLibError("not enough data to unpack 255UInt16")
+ (result,) = struct.unpack(">H", data[:2])
+ data = data[2:]
+ elif code == 254:
+ # read another byte, plus 253 * 2
+ if len(data) == 0:
+ raise TTLibError("not enough data to unpack 255UInt16")
+ result = byteord(data[:1])
+ result += 506
+ data = data[1:]
+ elif code == 255:
+ # read another byte, plus 253
+ if len(data) == 0:
+ raise TTLibError("not enough data to unpack 255UInt16")
+ result = byteord(data[:1])
+ result += 253
+ data = data[1:]
+ else:
+ # leave as is if lower than 253
+ result = code
+ # return result plus left over data
+ return result, data
+
+
+def pack255UShort(value):
+ r"""Encode unsigned integer in range 0 to 65535 (inclusive) to a bytestring
+ using 255UInt16 variable-length encoding.
+
+ >>> pack255UShort(252) == b'\xfc'
+ True
+ >>> pack255UShort(506) == b'\xfe\x00'
+ True
+ >>> pack255UShort(762) == b'\xfd\x02\xfa'
+ True
+ """
+ if value < 0 or value > 0xFFFF:
+ raise TTLibError("255UInt16 format requires 0 <= integer <= 65535")
+ if value < 253:
+ return struct.pack(">B", value)
+ elif value < 506:
+ return struct.pack(">BB", 255, value - 253)
+ elif value < 762:
+ return struct.pack(">BB", 254, value - 506)
+ else:
+ return struct.pack(">BH", 253, value)
+
+
+def compress(input_file, output_file, transform_tables=None):
+ """Compress OpenType font to WOFF2.
+
+ Args:
+ input_file: a file path, file or file-like object (open in binary mode)
+ containing an OpenType font (either CFF- or TrueType-flavored).
+ output_file: a file path, file or file-like object where to save the
+ compressed WOFF2 font.
+ transform_tables: Optional[Iterable[str]]: a set of table tags for which
+ to enable preprocessing transformations. By default, only 'glyf'
+ and 'loca' tables are transformed. An empty set means disable all
+ transformations.
+ """
+ log.info("Processing %s => %s" % (input_file, output_file))
+
+ font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False)
+ font.flavor = "woff2"
+
+ if transform_tables is not None:
+ font.flavorData = WOFF2FlavorData(
+ data=font.flavorData, transformedTables=transform_tables
+ )
+
+ font.save(output_file, reorderTables=False)
+
+
+def decompress(input_file, output_file):
+ """Decompress WOFF2 font to OpenType font.
+
+ Args:
+ input_file: a file path, file or file-like object (open in binary mode)
+ containing a compressed WOFF2 font.
+ output_file: a file path, file or file-like object where to save the
+ decompressed OpenType font.
+ """
+ log.info("Processing %s => %s" % (input_file, output_file))
+
+ font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False)
+ font.flavor = None
+ font.flavorData = None
+ font.save(output_file, reorderTables=True)
+
+
+def main(args=None):
+ """Compress and decompress WOFF2 fonts"""
+ import argparse
+ from fontTools import configLogger
+ from fontTools.ttx import makeOutputFileName
+
+ class _HelpAction(argparse._HelpAction):
+ def __call__(self, parser, namespace, values, option_string=None):
+ subparsers_actions = [
+ action
+ for action in parser._actions
+ if isinstance(action, argparse._SubParsersAction)
+ ]
+ for subparsers_action in subparsers_actions:
+ for choice, subparser in subparsers_action.choices.items():
+ print(subparser.format_help())
+ parser.exit()
+
+ class _NoGlyfTransformAction(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ namespace.transform_tables.difference_update({"glyf", "loca"})
+
+ class _HmtxTransformAction(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ namespace.transform_tables.add("hmtx")
+
+ parser = argparse.ArgumentParser(
+ prog="fonttools ttLib.woff2", description=main.__doc__, add_help=False
+ )
+
+ parser.add_argument(
+ "-h", "--help", action=_HelpAction, help="show this help message and exit"
+ )
+
+ parser_group = parser.add_subparsers(title="sub-commands")
+ parser_compress = parser_group.add_parser(
+ "compress", description="Compress a TTF or OTF font to WOFF2"
+ )
+ parser_decompress = parser_group.add_parser(
+ "decompress", description="Decompress a WOFF2 font to OTF"
+ )
+
+ for subparser in (parser_compress, parser_decompress):
+ group = subparser.add_mutually_exclusive_group(required=False)
+ group.add_argument(
+ "-v",
+ "--verbose",
+ action="store_true",
+ help="print more messages to console",
+ )
+ group.add_argument(
+ "-q",
+ "--quiet",
+ action="store_true",
+ help="do not print messages to console",
+ )
+
+ parser_compress.add_argument(
+ "input_file",
+ metavar="INPUT",
+ help="the input OpenType font (.ttf or .otf)",
+ )
+ parser_decompress.add_argument(
+ "input_file",
+ metavar="INPUT",
+ help="the input WOFF2 font",
+ )
+
+ parser_compress.add_argument(
+ "-o",
+ "--output-file",
+ metavar="OUTPUT",
+ help="the output WOFF2 font",
+ )
+ parser_decompress.add_argument(
+ "-o",
+ "--output-file",
+ metavar="OUTPUT",
+ help="the output OpenType font",
+ )
+
+ transform_group = parser_compress.add_argument_group()
+ transform_group.add_argument(
+ "--no-glyf-transform",
+ dest="transform_tables",
+ nargs=0,
+ action=_NoGlyfTransformAction,
+ help="Do not transform glyf (and loca) tables",
+ )
+ transform_group.add_argument(
+ "--hmtx-transform",
+ dest="transform_tables",
+ nargs=0,
+ action=_HmtxTransformAction,
+ help="Enable optional transformation for 'hmtx' table",
+ )
+
+ parser_compress.set_defaults(
+ subcommand=compress,
+ transform_tables={"glyf", "loca"},
+ )
+ parser_decompress.set_defaults(subcommand=decompress)
+
+ options = vars(parser.parse_args(args))
+
+ subcommand = options.pop("subcommand", None)
+ if not subcommand:
+ parser.print_help()
+ return
+
+ quiet = options.pop("quiet")
+ verbose = options.pop("verbose")
+ configLogger(
+ level=("ERROR" if quiet else "DEBUG" if verbose else "INFO"),
+ )
+
+ if not options["output_file"]:
+ if subcommand is compress:
+ extension = ".woff2"
+ elif subcommand is decompress:
+ # choose .ttf/.otf file extension depending on sfntVersion
+ with open(options["input_file"], "rb") as f:
+ f.seek(4) # skip 'wOF2' signature
+ sfntVersion = f.read(4)
+ assert len(sfntVersion) == 4, "not enough data"
+ extension = ".otf" if sfntVersion == b"OTTO" else ".ttf"
+ else:
+ raise AssertionError(subcommand)
+ options["output_file"] = makeOutputFileName(
+ options["input_file"], outputDir=None, extension=extension
+ )
+
+ try:
+ subcommand(**options)
+ except TTLibError as e:
+ parser.error(e)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__init__.py b/lib/python3.12/site-packages/fontTools/ufoLib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca409bdd5671238545175ed168609b9e67762dd8
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/__init__.py
@@ -0,0 +1,2575 @@
+"""
+A library for importing .ufo files and their descendants.
+Refer to http://unifiedfontobject.org for the UFO specification.
+
+The main interfaces are the :class:`.UFOReader` and :class:`.UFOWriter`
+classes, which support versions 1, 2, and 3 of the UFO specification.
+
+Set variables are available for external use that list the font
+info attribute names for the `fontinfo.plist` formats. These are:
+
+- :obj:`.fontInfoAttributesVersion1`
+- :obj:`.fontInfoAttributesVersion2`
+- :obj:`.fontInfoAttributesVersion3`
+
+A set listing the `fontinfo.plist` attributes that were deprecated
+in version 2 is available for external use:
+
+- :obj:`.deprecatedFontInfoAttributesVersion2`
+
+Functions that do basic validation on values for `fontinfo.plist`
+are available for external use. These are
+
+- :func:`.validateFontInfoVersion2ValueForAttribute`
+- :func:`.validateFontInfoVersion3ValueForAttribute`
+
+Value conversion functions are available for converting
+`fontinfo.plist` values between the possible format versions.
+
+- :func:`.convertFontInfoValueForAttributeFromVersion1ToVersion2`
+- :func:`.convertFontInfoValueForAttributeFromVersion2ToVersion1`
+- :func:`.convertFontInfoValueForAttributeFromVersion2ToVersion3`
+- :func:`.convertFontInfoValueForAttributeFromVersion3ToVersion2`
+"""
+
+from __future__ import annotations
+
+import enum
+import logging
+import os
+import zipfile
+from collections import OrderedDict
+from copy import deepcopy
+from os import fsdecode
+from typing import IO, TYPE_CHECKING, Any, Optional, Union, cast
+
+from fontTools.misc import filesystem as fs
+from fontTools.misc import plistlib
+from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
+from fontTools.ufoLib.errors import UFOLibError
+from fontTools.ufoLib.filenames import userNameToFileName
+from fontTools.ufoLib.utils import (
+ BaseFormatVersion,
+ normalizeFormatVersion,
+ numberTypes,
+)
+from fontTools.ufoLib.validators import *
+
+if TYPE_CHECKING:
+ from logging import Logger
+
+ from fontTools.annotations import (
+ GlyphNameToFileNameFunc,
+ K,
+ KerningDict,
+ KerningGroups,
+ KerningNested,
+ PathOrFS,
+ PathStr,
+ UFOFormatVersionInput,
+ V,
+ )
+ from fontTools.misc.filesystem._base import FS
+ from fontTools.ufoLib.glifLib import GlyphSet
+
+KerningGroupRenameMaps = dict[str, dict[str, str]]
+LibDict = dict[str, Any]
+LayerOrderList = Optional[list[Optional[str]]]
+AttributeDataDict = dict[str, Any]
+FontInfoAttributes = dict[str, AttributeDataDict]
+
+# client code can check this to see if the upstream `fs` package is being used
+haveFS = fs._haveFS
+
+__all__: list[str] = [
+ "haveFS",
+ "makeUFOPath",
+ "UFOLibError",
+ "UFOReader",
+ "UFOWriter",
+ "UFOReaderWriter",
+ "UFOFileStructure",
+ "fontInfoAttributesVersion1",
+ "fontInfoAttributesVersion2",
+ "fontInfoAttributesVersion3",
+ "deprecatedFontInfoAttributesVersion2",
+ "validateFontInfoVersion2ValueForAttribute",
+ "validateFontInfoVersion3ValueForAttribute",
+ "convertFontInfoValueForAttributeFromVersion1ToVersion2",
+ "convertFontInfoValueForAttributeFromVersion2ToVersion1",
+]
+
+__version__: str = "3.0.0"
+
+
+logger: Logger = logging.getLogger(__name__)
+
+
+# ---------
+# Constants
+# ---------
+
+DEFAULT_GLYPHS_DIRNAME: str = "glyphs"
+DATA_DIRNAME: str = "data"
+IMAGES_DIRNAME: str = "images"
+METAINFO_FILENAME: str = "metainfo.plist"
+FONTINFO_FILENAME: str = "fontinfo.plist"
+LIB_FILENAME: str = "lib.plist"
+GROUPS_FILENAME: str = "groups.plist"
+KERNING_FILENAME: str = "kerning.plist"
+FEATURES_FILENAME: str = "features.fea"
+LAYERCONTENTS_FILENAME: str = "layercontents.plist"
+LAYERINFO_FILENAME: str = "layerinfo.plist"
+
+DEFAULT_LAYER_NAME: str = "public.default"
+
+
+class UFOFormatVersion(BaseFormatVersion):
+ FORMAT_1_0 = (1, 0)
+ FORMAT_2_0 = (2, 0)
+ FORMAT_3_0 = (3, 0)
+
+
+class UFOFileStructure(enum.Enum):
+ ZIP = "zip"
+ PACKAGE = "package"
+
+
+# --------------
+# Shared Methods
+# --------------
+
+
+class _UFOBaseIO:
+ if TYPE_CHECKING:
+ fs: FS
+ _havePreviousFile: bool
+
+ def getFileModificationTime(self, path: PathStr) -> Optional[float]:
+ """
+ Returns the modification time for the file at the given path, as a
+ floating point number giving the number of seconds since the epoch.
+ The path must be relative to the UFO path.
+ Returns None if the file does not exist.
+ """
+ try:
+ dt = self.fs.getinfo(fsdecode(path), namespaces=["details"]).modified
+ except (fs.errors.MissingInfoNamespace, fs.errors.ResourceNotFound):
+ return None
+ else:
+ if dt is not None:
+ return dt.timestamp()
+ return None
+
+ def _getPlist(self, fileName: str, default: Optional[Any] = None) -> Any:
+ """
+ Read a property list relative to the UFO filesystem's root.
+ Raises UFOLibError if the file is missing and default is None,
+ otherwise default is returned.
+
+ The errors that could be raised during the reading of a plist are
+ unpredictable and/or too large to list, so, a blind try: except:
+ is done. If an exception occurs, a UFOLibError will be raised.
+ """
+ try:
+ with self.fs.open(fileName, "rb") as f:
+ return plistlib.load(f)
+ except fs.errors.ResourceNotFound:
+ if default is None:
+ raise UFOLibError(
+ "'%s' is missing on %s. This file is required" % (fileName, self.fs)
+ )
+ else:
+ return default
+ except Exception as e:
+ # TODO(anthrotype): try to narrow this down a little
+ raise UFOLibError(f"'{fileName}' could not be read on {self.fs}: {e}")
+
+ def _writePlist(self, fileName: str, obj: Any) -> None:
+ """
+ Write a property list to a file relative to the UFO filesystem's root.
+
+ Do this sort of atomically, making it harder to corrupt existing files,
+ for example when plistlib encounters an error halfway during write.
+ This also checks to see if text matches the text that is already in the
+ file at path. If so, the file is not rewritten so that the modification
+ date is preserved.
+
+ The errors that could be raised during the writing of a plist are
+ unpredictable and/or too large to list, so, a blind try: except: is done.
+ If an exception occurs, a UFOLibError will be raised.
+ """
+ if self._havePreviousFile:
+ try:
+ data = plistlib.dumps(obj)
+ except Exception as e:
+ raise UFOLibError(
+ "'%s' could not be written on %s because "
+ "the data is not properly formatted: %s" % (fileName, self.fs, e)
+ )
+ if self.fs.exists(fileName) and data == self.fs.readbytes(fileName):
+ return
+ self.fs.writebytes(fileName, data)
+ else:
+ with self.fs.open(fileName, mode="wb") as fp:
+ try:
+ plistlib.dump(obj, fp)
+ except Exception as e:
+ raise UFOLibError(
+ "'%s' could not be written on %s because "
+ "the data is not properly formatted: %s"
+ % (fileName, self.fs, e)
+ )
+
+
+# ----------
+# UFO Reader
+# ----------
+
+
+class UFOReader(_UFOBaseIO):
+ """Read the various components of a .ufo.
+
+ Attributes:
+ path: An :class:`os.PathLike` object pointing to the .ufo.
+ validate: A boolean indicating if the data read should be
+ validated. Defaults to `True`.
+
+ By default read data is validated. Set ``validate`` to
+ ``False`` to not validate the data.
+ """
+
+ def __init__(self, path: PathOrFS, validate: bool = True) -> None:
+ # Only call __fspath__ if path is not already a str or FS object
+ if not isinstance(path, (str, fs.base.FS)) and hasattr(path, "__fspath__"):
+ path = path.__fspath__()
+
+ if isinstance(path, str):
+ structure = _sniffFileStructure(path)
+ parentFS: FS
+ try:
+ if structure is UFOFileStructure.ZIP:
+ parentFS = fs.zipfs.ZipFS(path, write=False, encoding="utf-8") # type: ignore[abstract]
+ else:
+ parentFS = fs.osfs.OSFS(path)
+ except fs.errors.CreateFailed as e:
+ raise UFOLibError(f"unable to open '{path}': {e}")
+
+ if structure is UFOFileStructure.ZIP:
+ # .ufoz zip files must contain a single root directory, with arbitrary
+ # name, containing all the UFO files
+ rootDirs = [
+ p.name
+ for p in parentFS.scandir("/")
+ # exclude macOS metadata contained in zip file
+ if p.is_dir and p.name != "__MACOSX"
+ ]
+ if len(rootDirs) == 1:
+ # 'ClosingSubFS' ensures that the parent zip file is closed when
+ # its root subdirectory is closed
+ self.fs: FS = parentFS.opendir(
+ rootDirs[0], factory=fs.subfs.ClosingSubFS
+ )
+ else:
+ raise UFOLibError(
+ "Expected exactly 1 root directory, found %d" % len(rootDirs)
+ )
+ else:
+ # normal UFO 'packages' are just a single folder
+ self.fs = parentFS
+ # when passed a path string, we make sure we close the newly opened fs
+ # upon calling UFOReader.close method or context manager's __exit__
+ self._shouldClose: bool = True
+ self._fileStructure = structure
+ elif isinstance(path, fs.base.FS):
+ filesystem: FS = path
+ try:
+ filesystem.check()
+ except fs.errors.FilesystemClosed:
+ raise UFOLibError("the filesystem '%s' is closed" % path)
+ else:
+ self.fs = filesystem
+ try:
+ path = filesystem.getsyspath("/")
+ except fs.errors.NoSysPath:
+ # network or in-memory FS may not map to the local one
+ path = str(filesystem)
+ # when user passed an already initialized fs instance, it is her
+ # responsibility to close it, thus UFOReader.close/__exit__ are no-op
+ self._shouldClose = False
+ # default to a 'package' structure
+ self._fileStructure = UFOFileStructure.PACKAGE
+ else:
+ raise TypeError(
+ "Expected a path string or fs.base.FS object, found '%s'"
+ % type(path).__name__
+ )
+ self._path: str = fsdecode(path)
+ self._validate: bool = validate
+ self._upConvertedKerningData: Optional[dict[str, Any]] = None
+
+ try:
+ self.readMetaInfo(validate=validate)
+ except UFOLibError:
+ self.close()
+ raise
+
+ # properties
+
+ def _get_path(self) -> str:
+ import warnings
+
+ warnings.warn(
+ "The 'path' attribute is deprecated; use the 'fs' attribute instead",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return self._path
+
+ path: property = property(_get_path, doc="The path of the UFO (DEPRECATED).")
+
+ def _get_formatVersion(self) -> int:
+ import warnings
+
+ warnings.warn(
+ "The 'formatVersion' attribute is deprecated; use the 'formatVersionTuple'",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return self._formatVersion.major
+
+ formatVersion = property(
+ _get_formatVersion,
+ doc="The (major) format version of the UFO. DEPRECATED: Use formatVersionTuple",
+ )
+
+ @property
+ def formatVersionTuple(self) -> tuple[int, int]:
+ """The (major, minor) format version of the UFO.
+ This is determined by reading metainfo.plist during __init__.
+ """
+ return self._formatVersion
+
+ def _get_fileStructure(self) -> Any:
+ return self._fileStructure
+
+ fileStructure: property = property(
+ _get_fileStructure,
+ doc=(
+ "The file structure of the UFO: "
+ "either UFOFileStructure.ZIP or UFOFileStructure.PACKAGE"
+ ),
+ )
+
+ # up conversion
+
+ def _upConvertKerning(self, validate: bool) -> None:
+ """
+ Up convert kerning and groups in UFO 1 and 2.
+ The data will be held internally until each bit of data
+ has been retrieved. The conversion of both must be done
+ at once, so the raw data is cached and an error is raised
+ if one bit of data becomes obsolete before it is called.
+
+ ``validate`` will validate the data.
+ """
+ if self._upConvertedKerningData:
+ testKerning = self._readKerning()
+ if testKerning != self._upConvertedKerningData["originalKerning"]:
+ raise UFOLibError(
+ "The data in kerning.plist has been modified since it was converted to UFO 3 format."
+ )
+ testGroups = self._readGroups()
+ if testGroups != self._upConvertedKerningData["originalGroups"]:
+ raise UFOLibError(
+ "The data in groups.plist has been modified since it was converted to UFO 3 format."
+ )
+ else:
+ groups = self._readGroups()
+ if validate:
+ invalidFormatMessage = "groups.plist is not properly formatted."
+ if not isinstance(groups, dict):
+ raise UFOLibError(invalidFormatMessage)
+ for groupName, glyphList in groups.items():
+ if not isinstance(groupName, str):
+ raise UFOLibError(invalidFormatMessage)
+ elif not isinstance(glyphList, list):
+ raise UFOLibError(invalidFormatMessage)
+ for glyphName in glyphList:
+ if not isinstance(glyphName, str):
+ raise UFOLibError(invalidFormatMessage)
+ self._upConvertedKerningData = dict(
+ kerning={},
+ originalKerning=self._readKerning(),
+ groups={},
+ originalGroups=groups,
+ )
+ # convert kerning and groups
+ kerning, groups, conversionMaps = convertUFO1OrUFO2KerningToUFO3Kerning(
+ self._upConvertedKerningData["originalKerning"],
+ deepcopy(self._upConvertedKerningData["originalGroups"]),
+ self.getGlyphSet(),
+ )
+ # store
+ self._upConvertedKerningData["kerning"] = kerning
+ self._upConvertedKerningData["groups"] = groups
+ self._upConvertedKerningData["groupRenameMaps"] = conversionMaps
+
+ # support methods
+
+ def readBytesFromPath(self, path: PathStr) -> Optional[bytes]:
+ """
+ Returns the bytes in the file at the given path.
+ The path must be relative to the UFO's filesystem root.
+ Returns None if the file does not exist.
+ """
+ try:
+ return self.fs.readbytes(fsdecode(path))
+ except fs.errors.ResourceNotFound:
+ return None
+
+ def getReadFileForPath(
+ self, path: PathStr, encoding: Optional[str] = None
+ ) -> Optional[Union[IO[bytes], IO[str]]]:
+ """
+ Returns a file (or file-like) object for the file at the given path.
+ The path must be relative to the UFO path.
+ Returns None if the file does not exist.
+ By default the file is opened in binary mode (reads bytes).
+ If encoding is passed, the file is opened in text mode (reads str).
+
+ Note: The caller is responsible for closing the open file.
+ """
+ path = fsdecode(path)
+ try:
+ if encoding is None:
+ return self.fs.open(path, mode="rb")
+ else:
+ return self.fs.open(path, mode="r", encoding=encoding)
+ except fs.errors.ResourceNotFound:
+ return None
+
+ # metainfo.plist
+
+ def _readMetaInfo(self, validate: Optional[bool] = None) -> dict[str, Any]:
+ """
+ Read metainfo.plist and return raw data. Only used for internal operations.
+
+ ``validate`` will validate the read data, by default it is set
+ to the class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ data = self._getPlist(METAINFO_FILENAME)
+ if validate and not isinstance(data, dict):
+ raise UFOLibError("metainfo.plist is not properly formatted.")
+ try:
+ formatVersionMajor = data["formatVersion"]
+ except KeyError:
+ raise UFOLibError(
+ f"Missing required formatVersion in '{METAINFO_FILENAME}' on {self.fs}"
+ )
+ formatVersionMinor = data.setdefault("formatVersionMinor", 0)
+
+ try:
+ formatVersion = UFOFormatVersion((formatVersionMajor, formatVersionMinor))
+ except ValueError as e:
+ unsupportedMsg = (
+ f"Unsupported UFO format ({formatVersionMajor}.{formatVersionMinor}) "
+ f"in '{METAINFO_FILENAME}' on {self.fs}"
+ )
+ if validate:
+ from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+ raise UnsupportedUFOFormat(unsupportedMsg) from e
+
+ formatVersion = UFOFormatVersion.default()
+ logger.warning(
+ "%s. Assuming the latest supported version (%s). "
+ "Some data may be skipped or parsed incorrectly",
+ unsupportedMsg,
+ formatVersion,
+ )
+ data["formatVersionTuple"] = formatVersion
+ return data
+
+ def readMetaInfo(self, validate: Optional[bool] = None) -> None:
+ """
+ Read metainfo.plist and set formatVersion. Only used for internal operations.
+
+ ``validate`` will validate the read data, by default it is set
+ to the class's validate value, can be overridden.
+ """
+ data = self._readMetaInfo(validate=validate)
+ self._formatVersion = data["formatVersionTuple"]
+
+ # groups.plist
+
+ def _readGroups(self) -> dict[str, list[str]]:
+ groups = self._getPlist(GROUPS_FILENAME, {})
+ # remove any duplicate glyphs in a kerning group
+ for groupName, glyphList in groups.items():
+ if groupName.startswith(("public.kern1.", "public.kern2.")):
+ groups[groupName] = list(OrderedDict.fromkeys(glyphList))
+ return groups
+
+ def readGroups(self, validate: Optional[bool] = None) -> dict[str, list[str]]:
+ """
+ Read groups.plist. Returns a dict.
+ ``validate`` will validate the read data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ # handle up conversion
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ self._upConvertKerning(validate)
+ groups = cast(dict, self._upConvertedKerningData)["groups"]
+ # normal
+ else:
+ groups = self._readGroups()
+ if validate:
+ valid, message = groupsValidator(groups)
+ if not valid:
+ raise UFOLibError(message)
+ return groups
+
+ def getKerningGroupConversionRenameMaps(
+ self, validate: Optional[bool] = None
+ ) -> KerningGroupRenameMaps:
+ """
+ Get maps defining the renaming that was done during any
+ needed kerning group conversion. This method returns a
+ dictionary of this form::
+
+ {
+ "side1" : {"old group name" : "new group name"},
+ "side2" : {"old group name" : "new group name"}
+ }
+
+ When no conversion has been performed, the side1 and side2
+ dictionaries will be empty.
+
+ ``validate`` will validate the groups, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
+ return dict(side1={}, side2={})
+ # use the public group reader to force the load and
+ # conversion of the data if it hasn't happened yet.
+ self.readGroups(validate=validate)
+ return cast(dict, self._upConvertedKerningData)["groupRenameMaps"]
+
+ # fontinfo.plist
+
+ def _readInfo(self, validate: bool) -> dict[str, Any]:
+ data = self._getPlist(FONTINFO_FILENAME, {})
+ if validate and not isinstance(data, dict):
+ raise UFOLibError("fontinfo.plist is not properly formatted.")
+ return data
+
+ def readInfo(self, info: Any, validate: Optional[bool] = None) -> None:
+ """
+ Read fontinfo.plist. It requires an object that allows
+ setting attributes with names that follow the fontinfo.plist
+ version 3 specification. This will write the attributes
+ defined in the file into the object.
+
+ ``validate`` will validate the read data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ infoDict = self._readInfo(validate)
+ infoDataToSet = {}
+ # version 1
+ if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+ for attr in fontInfoAttributesVersion1:
+ value = infoDict.get(attr)
+ if value is not None:
+ infoDataToSet[attr] = value
+ infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet)
+ infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
+ # version 2
+ elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
+ for attr, dataValidationDict in list(
+ fontInfoAttributesVersion2ValueData.items()
+ ):
+ value = infoDict.get(attr)
+ if value is None:
+ continue
+ infoDataToSet[attr] = value
+ infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
+ # version 3.x
+ elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
+ for attr, dataValidationDict in list(
+ fontInfoAttributesVersion3ValueData.items()
+ ):
+ value = infoDict.get(attr)
+ if value is None:
+ continue
+ infoDataToSet[attr] = value
+ # unsupported version
+ else:
+ raise NotImplementedError(self._formatVersion)
+ # validate data
+ if validate:
+ infoDataToSet = validateInfoVersion3Data(infoDataToSet)
+ # populate the object
+ for attr, value in list(infoDataToSet.items()):
+ try:
+ setattr(info, attr, value)
+ except AttributeError:
+ raise UFOLibError(
+ "The supplied info object does not support setting a necessary attribute (%s)."
+ % attr
+ )
+
+ # kerning.plist
+
+ def _readKerning(self) -> KerningNested:
+ data = self._getPlist(KERNING_FILENAME, {})
+ return data
+
+ def readKerning(self, validate: Optional[bool] = None) -> KerningDict:
+ """
+ Read kerning.plist. Returns a dict.
+
+ ``validate`` will validate the kerning data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ # handle up conversion
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ self._upConvertKerning(validate)
+ kerningNested = cast(dict, self._upConvertedKerningData)["kerning"]
+ # normal
+ else:
+ kerningNested = self._readKerning()
+ if validate:
+ valid, message = kerningValidator(kerningNested)
+ if not valid:
+ raise UFOLibError(message)
+ # flatten
+ kerning = {}
+ for left in kerningNested:
+ for right in kerningNested[left]:
+ value = kerningNested[left][right]
+ kerning[left, right] = value
+ return kerning
+
+ # lib.plist
+
+ def readLib(self, validate: Optional[bool] = None) -> dict[str, Any]:
+ """
+ Read lib.plist. Returns a dict.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ data = self._getPlist(LIB_FILENAME, {})
+ if validate:
+ valid, message = fontLibValidator(data)
+ if not valid:
+ raise UFOLibError(message)
+ return data
+
+ # features.fea
+
+ def readFeatures(self) -> str:
+ """
+ Read features.fea. Return a string.
+ The returned string is empty if the file is missing.
+ """
+ try:
+ with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8-sig") as f:
+ return f.read()
+ except fs.errors.ResourceNotFound:
+ return ""
+
+ # glyph sets & layers
+
+ def _readLayerContents(self, validate: bool) -> list[tuple[str, str]]:
+ """
+ Rebuild the layer contents list by checking what glyphsets
+ are available on disk.
+
+ ``validate`` will validate the layer contents.
+ """
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ return [(DEFAULT_LAYER_NAME, DEFAULT_GLYPHS_DIRNAME)]
+ contents = self._getPlist(LAYERCONTENTS_FILENAME)
+ if validate:
+ valid, error = layerContentsValidator(contents, self.fs)
+ if not valid:
+ raise UFOLibError(error)
+ return contents
+
+ def getLayerNames(self, validate: Optional[bool] = None) -> list[str]:
+ """
+ Get the ordered layer names from layercontents.plist.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ layerContents = self._readLayerContents(validate)
+ layerNames = [layerName for layerName, directoryName in layerContents]
+ return layerNames
+
+ def getDefaultLayerName(self, validate: Optional[bool] = None) -> str:
+ """
+ Get the default layer name from layercontents.plist.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ layerContents = self._readLayerContents(validate)
+ for layerName, layerDirectory in layerContents:
+ if layerDirectory == DEFAULT_GLYPHS_DIRNAME:
+ return layerName
+ # this will already have been raised during __init__
+ raise UFOLibError("The default layer is not defined in layercontents.plist.")
+
+ def getGlyphSet(
+ self,
+ layerName: Optional[str] = None,
+ validateRead: Optional[bool] = None,
+ validateWrite: Optional[bool] = None,
+ ) -> GlyphSet:
+ """
+ Return the GlyphSet associated with the
+ glyphs directory mapped to layerName
+ in the UFO. If layerName is not provided,
+ the name retrieved with getDefaultLayerName
+ will be used.
+
+ ``validateRead`` will validate the read data, by default it is set to the
+ class's validate value, can be overridden.
+ ``validateWrite`` will validate the written data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ from fontTools.ufoLib.glifLib import GlyphSet
+
+ if validateRead is None:
+ validateRead = self._validate
+ if validateWrite is None:
+ validateWrite = self._validate
+ if layerName is None:
+ layerName = self.getDefaultLayerName(validate=validateRead)
+ directory = None
+ layerContents = self._readLayerContents(validateRead)
+ for storedLayerName, storedLayerDirectory in layerContents:
+ if layerName == storedLayerName:
+ directory = storedLayerDirectory
+ break
+ if directory is None:
+ raise UFOLibError('No glyphs directory is mapped to "%s".' % layerName)
+ try:
+ glyphSubFS = self.fs.opendir(directory)
+ except fs.errors.ResourceNotFound:
+ raise UFOLibError(f"No '{directory}' directory for layer '{layerName}'")
+ return GlyphSet(
+ glyphSubFS,
+ ufoFormatVersion=self._formatVersion,
+ validateRead=validateRead,
+ validateWrite=validateWrite,
+ expectContentsFile=True,
+ )
+
+ def getCharacterMapping(
+ self, layerName: Optional[str] = None, validate: Optional[bool] = None
+ ) -> dict[int, list[str]]:
+ """
+ Return a dictionary that maps unicode values (ints) to
+ lists of glyph names.
+ """
+ if validate is None:
+ validate = self._validate
+ glyphSet = self.getGlyphSet(
+ layerName, validateRead=validate, validateWrite=True
+ )
+ allUnicodes = glyphSet.getUnicodes()
+ cmap: dict[int, list[str]] = {}
+ for glyphName, unicodes in allUnicodes.items():
+ for code in unicodes:
+ if code in cmap:
+ cmap[code].append(glyphName)
+ else:
+ cmap[code] = [glyphName]
+ return cmap
+
+ # /data
+
+ def getDataDirectoryListing(self) -> list[str]:
+ """
+ Returns a list of all files in the data directory.
+ The returned paths will be relative to the UFO.
+ This will not list directory names, only file names.
+ Thus, empty directories will be skipped.
+ """
+ try:
+ self._dataFS = self.fs.opendir(DATA_DIRNAME)
+ except fs.errors.ResourceNotFound:
+ return []
+ except fs.errors.DirectoryExpected:
+ raise UFOLibError('The UFO contains a "data" file instead of a directory.')
+ try:
+ # fs Walker.files method returns "absolute" paths (in terms of the
+ # root of the 'data' SubFS), so we strip the leading '/' to make
+ # them relative
+ return [p.lstrip("/") for p in self._dataFS.walk.files()]
+ except fs.errors.ResourceError:
+ return []
+
+ def getImageDirectoryListing(self, validate: Optional[bool] = None) -> list[str]:
+ """
+ Returns a list of all image file names in
+ the images directory. Each of the images will
+ have been verified to have the PNG signature.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ return []
+ if validate is None:
+ validate = self._validate
+ try:
+ self._imagesFS = imagesFS = self.fs.opendir(IMAGES_DIRNAME)
+ except fs.errors.ResourceNotFound:
+ return []
+ except fs.errors.DirectoryExpected:
+ raise UFOLibError(
+ 'The UFO contains an "images" file instead of a directory.'
+ )
+ result = []
+ for path in imagesFS.scandir("/"):
+ if path.is_dir:
+ # silently skip this as version control
+ # systems often have hidden directories
+ continue
+ if validate:
+ with imagesFS.open(path.name, "rb") as fp:
+ valid, error = pngValidator(fileObj=fp)
+ if valid:
+ result.append(path.name)
+ else:
+ result.append(path.name)
+ return result
+
+ def readData(self, fileName: PathStr) -> bytes:
+ """
+ Return bytes for the file named 'fileName' inside the 'data/' directory.
+ """
+ fileName = fsdecode(fileName)
+ try:
+ try:
+ dataFS = self._dataFS
+ except AttributeError:
+ # in case readData is called before getDataDirectoryListing
+ dataFS = self.fs.opendir(DATA_DIRNAME)
+ data = dataFS.readbytes(fileName)
+ except fs.errors.ResourceNotFound:
+ raise UFOLibError(f"No data file named '{fileName}' on {self.fs}")
+ return data
+
+ def readImage(self, fileName: PathStr, validate: Optional[bool] = None) -> bytes:
+ """
+ Return image data for the file named fileName.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ raise UFOLibError(
+ f"Reading images is not allowed in UFO {self._formatVersion.major}."
+ )
+ fileName = fsdecode(fileName)
+ try:
+ try:
+ imagesFS = self._imagesFS
+ except AttributeError:
+ # in case readImage is called before getImageDirectoryListing
+ imagesFS = self.fs.opendir(IMAGES_DIRNAME)
+ data = imagesFS.readbytes(fileName)
+ except fs.errors.ResourceNotFound:
+ raise UFOLibError(f"No image file named '{fileName}' on {self.fs}")
+ if validate:
+ valid, error = pngValidator(data=data)
+ if not valid:
+ raise UFOLibError(error)
+ return data
+
+ def close(self) -> None:
+ if self._shouldClose:
+ self.fs.close()
+
+ def __enter__(self) -> UFOReader:
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
+ self.close()
+
+
+# ----------
+# UFO Writer
+# ----------
+
+
+class UFOWriter(UFOReader):
+ """Write the various components of a .ufo.
+
+ Attributes:
+ path: An :class:`os.PathLike` object pointing to the .ufo.
+ formatVersion: the UFO format version as a tuple of integers (major, minor),
+ or as a single integer for the major digit only (minor is implied to be 0).
+ By default, the latest formatVersion will be used; currently it is 3.0,
+ which is equivalent to formatVersion=(3, 0).
+ fileCreator: The creator of the .ufo file. Defaults to
+ `com.github.fonttools.ufoLib`.
+ structure: The internal structure of the .ufo file: either `ZIP` or `PACKAGE`.
+ validate: A boolean indicating if the data read should be validated. Defaults
+ to `True`.
+
+ By default, the written data will be validated before writing. Set ``validate`` to
+ ``False`` if you do not want to validate the data. Validation can also be overriden
+ on a per-method level if desired.
+
+ Raises:
+ UnsupportedUFOFormat: An exception indicating that the requested UFO
+ formatVersion is not supported.
+ """
+
+ def __init__(
+ self,
+ path: PathOrFS,
+ formatVersion: UFOFormatVersionInput = None,
+ fileCreator: str = "com.github.fonttools.ufoLib",
+ structure: Optional[UFOFileStructure] = None,
+ validate: bool = True,
+ ) -> None:
+ try:
+ formatVersion = normalizeFormatVersion(formatVersion, UFOFormatVersion)
+ except ValueError as e:
+ from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+ raise UnsupportedUFOFormat(
+ f"Unsupported UFO format: {formatVersion!r}"
+ ) from e
+
+ if hasattr(path, "__fspath__"): # support os.PathLike objects
+ path = path.__fspath__()
+
+ if isinstance(path, str):
+ # normalize path by removing trailing or double slashes
+ path = os.path.normpath(path)
+ havePreviousFile = os.path.exists(path)
+ if havePreviousFile:
+ # ensure we use the same structure as the destination
+ existingStructure = _sniffFileStructure(path)
+ if structure is not None:
+ try:
+ structure = UFOFileStructure(structure)
+ except ValueError:
+ raise UFOLibError(
+ "Invalid or unsupported structure: '%s'" % structure
+ )
+ if structure is not existingStructure:
+ raise UFOLibError(
+ "A UFO with a different structure (%s) already exists "
+ "at the given path: '%s'" % (existingStructure, path)
+ )
+ else:
+ structure = existingStructure
+ else:
+ # if not exists, default to 'package' structure
+ if structure is None:
+ structure = UFOFileStructure.PACKAGE
+ dirName = os.path.dirname(path)
+ if dirName and not os.path.isdir(dirName):
+ raise UFOLibError(
+ "Cannot write to '%s': directory does not exist" % path
+ )
+ if structure is UFOFileStructure.ZIP:
+ if havePreviousFile:
+ # we can't write a zip in-place, so we have to copy its
+ # contents to a temporary location and work from there, then
+ # upon closing UFOWriter we create the final zip file
+ parentFS: FS = fs.tempfs.TempFS()
+ with fs.zipfs.ZipFS(path, encoding="utf-8") as origFS: # type: ignore[abstract]
+ fs.copy.copy_fs(origFS, parentFS)
+ # if output path is an existing zip, we require that it contains
+ # one, and only one, root directory (with arbitrary name), in turn
+ # containing all the existing UFO contents
+ rootDirs = [
+ p.name
+ for p in parentFS.scandir("/")
+ # exclude macOS metadata contained in zip file
+ if p.is_dir and p.name != "__MACOSX"
+ ]
+ if len(rootDirs) != 1:
+ raise UFOLibError(
+ "Expected exactly 1 root directory, found %d"
+ % len(rootDirs)
+ )
+ else:
+ rootDir = rootDirs[0]
+ else:
+ # if the output zip file didn't exist, we create the root folder;
+ # we name it the same as input 'path', but with '.ufo' extension
+ rootDir = os.path.splitext(os.path.basename(path))[0] + ".ufo"
+ parentFS = fs.zipfs.ZipFS(path, write=True, encoding="utf-8") # type: ignore[abstract]
+ parentFS.makedir(rootDir)
+ # 'ClosingSubFS' ensures that the parent filesystem is closed
+ # when its root subdirectory is closed
+ self.fs = parentFS.opendir(rootDir, factory=fs.subfs.ClosingSubFS)
+ else:
+ self.fs = fs.osfs.OSFS(path, create=True)
+ self._fileStructure = structure
+ self._havePreviousFile = havePreviousFile
+ self._shouldClose = True
+ elif isinstance(path, fs.base.FS):
+ filesystem: FS = path
+ try:
+ filesystem.check()
+ except fs.errors.FilesystemClosed:
+ raise UFOLibError("the filesystem '%s' is closed" % path)
+ else:
+ self.fs = filesystem
+ try:
+ path = filesystem.getsyspath("/")
+ except fs.errors.NoSysPath:
+ # network or in-memory FS may not map to the local one
+ path = str(filesystem)
+ # if passed an FS object, always use 'package' structure
+ if structure and structure is not UFOFileStructure.PACKAGE:
+ import warnings
+
+ warnings.warn(
+ "The 'structure' argument is not used when input is an FS object",
+ UserWarning,
+ stacklevel=2,
+ )
+ self._fileStructure = UFOFileStructure.PACKAGE
+ # if FS contains a "metainfo.plist", we consider it non-empty
+ self._havePreviousFile = filesystem.exists(METAINFO_FILENAME)
+ # the user is responsible for closing the FS object
+ self._shouldClose = False
+ else:
+ raise TypeError(
+ "Expected a path string or fs object, found %s" % type(path).__name__
+ )
+
+ # establish some basic stuff
+ self._path = fsdecode(path)
+ self._formatVersion = formatVersion
+ self._fileCreator = fileCreator
+ self._downConversionKerningData: Optional[KerningGroupRenameMaps] = None
+ self._validate = validate
+ # if the file already exists, get the format version.
+ # this will be needed for up and down conversion.
+ previousFormatVersion = None
+ if self._havePreviousFile:
+ metaInfo = self._readMetaInfo(validate=validate)
+ previousFormatVersion = metaInfo["formatVersionTuple"]
+ # catch down conversion
+ if previousFormatVersion > formatVersion:
+ from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+ raise UnsupportedUFOFormat(
+ "The UFO located at this path is a higher version "
+ f"({previousFormatVersion}) than the version ({formatVersion}) "
+ "that is trying to be written. This is not supported."
+ )
+ # handle the layer contents
+ self.layerContents: Union[dict[str, str], OrderedDict[str, str]] = {}
+ if previousFormatVersion is not None and previousFormatVersion.major >= 3:
+ # already exists
+ self.layerContents = OrderedDict(self._readLayerContents(validate))
+ else:
+ # previous < 3
+ # imply the layer contents
+ if self.fs.exists(DEFAULT_GLYPHS_DIRNAME):
+ self.layerContents = {DEFAULT_LAYER_NAME: DEFAULT_GLYPHS_DIRNAME}
+ # write the new metainfo
+ self._writeMetaInfo()
+
+ # properties
+
+ def _get_fileCreator(self) -> str:
+ return self._fileCreator
+
+ fileCreator: property = property(
+ _get_fileCreator,
+ doc="The file creator of the UFO. This is set into metainfo.plist during __init__.",
+ )
+
+ # support methods for file system interaction
+
+ def copyFromReader(
+ self, reader: UFOReader, sourcePath: PathStr, destPath: PathStr
+ ) -> None:
+ """
+ Copy the sourcePath in the provided UFOReader to destPath
+ in this writer. The paths must be relative. This works with
+ both individual files and directories.
+ """
+ if not isinstance(reader, UFOReader):
+ raise UFOLibError("The reader must be an instance of UFOReader.")
+ sourcePath = fsdecode(sourcePath)
+ destPath = fsdecode(destPath)
+ if not reader.fs.exists(sourcePath):
+ raise UFOLibError(
+ 'The reader does not have data located at "%s".' % sourcePath
+ )
+ if self.fs.exists(destPath):
+ raise UFOLibError('A file named "%s" already exists.' % destPath)
+ # create the destination directory if it doesn't exist
+ self.fs.makedirs(fs.path.dirname(destPath), recreate=True)
+ if reader.fs.isdir(sourcePath):
+ fs.copy.copy_dir(reader.fs, sourcePath, self.fs, destPath)
+ else:
+ fs.copy.copy_file(reader.fs, sourcePath, self.fs, destPath)
+
+ def writeBytesToPath(self, path: PathStr, data: bytes) -> None:
+ """
+ Write bytes to a path relative to the UFO filesystem's root.
+ If writing to an existing UFO, check to see if data matches the data
+ that is already in the file at path; if so, the file is not rewritten
+ so that the modification date is preserved.
+ If needed, the directory tree for the given path will be built.
+ """
+ path = fsdecode(path)
+ if self._havePreviousFile:
+ if self.fs.isfile(path) and data == self.fs.readbytes(path):
+ return
+ try:
+ self.fs.writebytes(path, data)
+ except fs.errors.FileExpected:
+ raise UFOLibError("A directory exists at '%s'" % path)
+ except fs.errors.ResourceNotFound:
+ self.fs.makedirs(fs.path.dirname(path), recreate=True)
+ self.fs.writebytes(path, data)
+
+ def getFileObjectForPath(
+ self,
+ path: PathStr,
+ mode: str = "w",
+ encoding: Optional[str] = None,
+ ) -> Optional[IO[Any]]:
+ """
+ Returns a file (or file-like) object for the
+ file at the given path. The path must be relative
+ to the UFO path. Returns None if the file does
+ not exist and the mode is "r" or "rb.
+ An encoding may be passed if the file is opened in text mode.
+
+ Note: The caller is responsible for closing the open file.
+ """
+ path = fsdecode(path)
+ try:
+ return self.fs.open(path, mode=mode, encoding=encoding)
+ except fs.errors.ResourceNotFound as e:
+ m = mode[0]
+ if m == "r":
+ # XXX I think we should just let it raise. The docstring,
+ # however, says that this returns None if mode is 'r'
+ return None
+ elif m == "w" or m == "a" or m == "x":
+ self.fs.makedirs(fs.path.dirname(path), recreate=True)
+ return self.fs.open(path, mode=mode, encoding=encoding)
+ except fs.errors.ResourceError as e:
+ raise UFOLibError(f"unable to open '{path}' on {self.fs}: {e}")
+ return None
+
+ def removePath(
+ self, path: PathStr, force: bool = False, removeEmptyParents: bool = True
+ ) -> None:
+ """
+ Remove the file (or directory) at path. The path
+ must be relative to the UFO.
+ Raises UFOLibError if the path doesn't exist.
+ If force=True, ignore non-existent paths.
+ If the directory where 'path' is located becomes empty, it will
+ be automatically removed, unless 'removeEmptyParents' is False.
+ """
+ path = fsdecode(path)
+ try:
+ self.fs.remove(path)
+ except fs.errors.FileExpected:
+ self.fs.removetree(path)
+ except fs.errors.ResourceNotFound:
+ if not force:
+ raise UFOLibError(f"'{path}' does not exist on {self.fs}")
+ if removeEmptyParents:
+ parent = fs.path.dirname(path)
+ if parent:
+ fs.tools.remove_empty(self.fs, parent)
+
+ # alias kept for backward compatibility with old API
+ removeFileForPath = removePath
+
+ # UFO mod time
+
+ def setModificationTime(self) -> None:
+ """
+ Set the UFO modification time to the current time.
+ This is never called automatically. It is up to the
+ caller to call this when finished working on the UFO.
+ """
+ path = self._path
+ if path is not None and os.path.exists(path):
+ try:
+ # this may fail on some filesystems (e.g. SMB servers)
+ os.utime(path, None)
+ except OSError as e:
+ logger.warning("Failed to set modified time: %s", e)
+
+ # metainfo.plist
+
+ def _writeMetaInfo(self) -> None:
+ metaInfo = dict(
+ creator=self._fileCreator,
+ formatVersion=self._formatVersion.major,
+ )
+ if self._formatVersion.minor != 0:
+ metaInfo["formatVersionMinor"] = self._formatVersion.minor
+ self._writePlist(METAINFO_FILENAME, metaInfo)
+
+ # groups.plist
+
+ def setKerningGroupConversionRenameMaps(self, maps: KerningGroupRenameMaps) -> None:
+ """
+ Set maps defining the renaming that should be done
+ when writing groups and kerning in UFO 1 and UFO 2.
+ This will effectively undo the conversion done when
+ UFOReader reads this data. The dictionary should have
+ this form::
+
+ {
+ "side1" : {"group name to use when writing" : "group name in data"},
+ "side2" : {"group name to use when writing" : "group name in data"}
+ }
+
+ This is the same form returned by UFOReader's
+ getKerningGroupConversionRenameMaps method.
+ """
+ if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
+ return # XXX raise an error here
+ # flip the dictionaries
+ remap = {}
+ for side in ("side1", "side2"):
+ for writeName, dataName in list(maps[side].items()):
+ remap[dataName] = writeName
+ self._downConversionKerningData = dict(groupRenameMap=remap)
+
+ def writeGroups(
+ self, groups: KerningGroups, validate: Optional[bool] = None
+ ) -> None:
+ """
+ Write groups.plist. This method requires a
+ dict of glyph groups as an argument.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ # validate the data structure
+ if validate:
+ valid, message = groupsValidator(groups)
+ if not valid:
+ raise UFOLibError(message)
+ # down convert
+ if (
+ self._formatVersion < UFOFormatVersion.FORMAT_3_0
+ and self._downConversionKerningData is not None
+ ):
+ remap = self._downConversionKerningData["groupRenameMap"]
+ remappedGroups = {}
+ # there are some edge cases here that are ignored:
+ # 1. if a group is being renamed to a name that
+ # already exists, the existing group is always
+ # overwritten. (this is why there are two loops
+ # below.) there doesn't seem to be a logical
+ # solution to groups mismatching and overwriting
+ # with the specifiecd group seems like a better
+ # solution than throwing an error.
+ # 2. if side 1 and side 2 groups are being renamed
+ # to the same group name there is no check to
+ # ensure that the contents are identical. that
+ # is left up to the caller.
+ for name, contents in list(groups.items()):
+ if name in remap:
+ continue
+ remappedGroups[name] = contents
+ for name, contents in list(groups.items()):
+ if name not in remap:
+ continue
+ name = remap[name]
+ remappedGroups[name] = contents
+ groups = remappedGroups
+ # pack and write
+ groupsNew = {}
+ for key, value in groups.items():
+ groupsNew[key] = list(value)
+ if groupsNew:
+ self._writePlist(GROUPS_FILENAME, groupsNew)
+ elif self._havePreviousFile:
+ self.removePath(GROUPS_FILENAME, force=True, removeEmptyParents=False)
+
+ # fontinfo.plist
+
+ def writeInfo(self, info: Any, validate: Optional[bool] = None) -> None:
+ """
+ Write info.plist. This method requires an object
+ that supports getting attributes that follow the
+ fontinfo.plist version 2 specification. Attributes
+ will be taken from the given object and written
+ into the file.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ # gather version 3 data
+ infoData = {}
+ for attr in list(fontInfoAttributesVersion3ValueData.keys()):
+ if hasattr(info, attr):
+ try:
+ value = getattr(info, attr)
+ except AttributeError:
+ raise UFOLibError(
+ "The supplied info object does not support getting a necessary attribute (%s)."
+ % attr
+ )
+ if value is None:
+ continue
+ infoData[attr] = value
+ # down convert data if necessary and validate
+ if self._formatVersion == UFOFormatVersion.FORMAT_3_0:
+ if validate:
+ infoData = validateInfoVersion3Data(infoData)
+ elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
+ infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
+ if validate:
+ infoData = validateInfoVersion2Data(infoData)
+ elif self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+ infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
+ if validate:
+ infoData = validateInfoVersion2Data(infoData)
+ infoData = _convertFontInfoDataVersion2ToVersion1(infoData)
+ # write file if there is anything to write
+ if infoData:
+ self._writePlist(FONTINFO_FILENAME, infoData)
+
+ # kerning.plist
+
+ def writeKerning(
+ self, kerning: KerningDict, validate: Optional[bool] = None
+ ) -> None:
+ """
+ Write kerning.plist. This method requires a
+ dict of kerning pairs as an argument.
+
+ This performs basic structural validation of the kerning,
+ but it does not check for compliance with the spec in
+ regards to conflicting pairs. The assumption is that the
+ kerning data being passed is standards compliant.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ # validate the data structure
+ if validate:
+ invalidFormatMessage = "The kerning is not properly formatted."
+ if not isDictEnough(kerning):
+ raise UFOLibError(invalidFormatMessage)
+ for pair, value in list(kerning.items()):
+ if not isinstance(pair, (list, tuple)):
+ raise UFOLibError(invalidFormatMessage)
+ if not len(pair) == 2:
+ raise UFOLibError(invalidFormatMessage)
+ if not isinstance(pair[0], str):
+ raise UFOLibError(invalidFormatMessage)
+ if not isinstance(pair[1], str):
+ raise UFOLibError(invalidFormatMessage)
+ if not isinstance(value, numberTypes):
+ raise UFOLibError(invalidFormatMessage)
+ # down convert
+ if (
+ self._formatVersion < UFOFormatVersion.FORMAT_3_0
+ and self._downConversionKerningData is not None
+ ):
+ remap = self._downConversionKerningData["groupRenameMap"]
+ remappedKerning = {}
+ for (side1, side2), value in list(kerning.items()):
+ side1 = remap.get(side1, side1)
+ side2 = remap.get(side2, side2)
+ remappedKerning[side1, side2] = value
+ kerning = remappedKerning
+ # pack and write
+ kerningDict: KerningNested = {}
+ for left, right in kerning.keys():
+ value = kerning[left, right]
+ if left not in kerningDict:
+ kerningDict[left] = {}
+ kerningDict[left][right] = value
+ if kerningDict:
+ self._writePlist(KERNING_FILENAME, kerningDict)
+ elif self._havePreviousFile:
+ self.removePath(KERNING_FILENAME, force=True, removeEmptyParents=False)
+
+ # lib.plist
+
+ def writeLib(self, libDict: LibDict, validate: Optional[bool] = None) -> None:
+ """
+ Write lib.plist. This method requires a
+ lib dict as an argument.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's validate value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validate
+ if validate:
+ valid, message = fontLibValidator(libDict)
+ if not valid:
+ raise UFOLibError(message)
+ if libDict:
+ self._writePlist(LIB_FILENAME, libDict)
+ elif self._havePreviousFile:
+ self.removePath(LIB_FILENAME, force=True, removeEmptyParents=False)
+
+ # features.fea
+
+ def writeFeatures(self, features: str, validate: Optional[bool] = None) -> None:
+ """
+ Write features.fea. This method requires a
+ features string as an argument.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+ raise UFOLibError("features.fea is not allowed in UFO Format Version 1.")
+ if validate:
+ if not isinstance(features, str):
+ raise UFOLibError("The features are not text.")
+ if features:
+ self.writeBytesToPath(FEATURES_FILENAME, features.encode("utf8"))
+ elif self._havePreviousFile:
+ self.removePath(FEATURES_FILENAME, force=True, removeEmptyParents=False)
+
+ # glyph sets & layers
+
+ def writeLayerContents(
+ self, layerOrder: LayerOrderList = None, validate: Optional[bool] = None
+ ) -> None:
+ """
+ Write the layercontents.plist file. This method *must* be called
+ after all glyph sets have been written.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ return
+ if layerOrder is not None:
+ newOrder: list[Optional[str]] = []
+ for layerName in layerOrder:
+ if layerName is None:
+ layerName = DEFAULT_LAYER_NAME
+ newOrder.append(layerName)
+ layerOrder = newOrder
+ else:
+ layerOrder = list(self.layerContents.keys())
+ if validate and set(layerOrder) != set(self.layerContents.keys()):
+ raise UFOLibError(
+ "The layer order content does not match the glyph sets that have been created."
+ )
+ layerContents = [
+ (layerName, self.layerContents[layerName])
+ for layerName in layerOrder
+ if layerName is not None
+ ]
+ self._writePlist(LAYERCONTENTS_FILENAME, layerContents)
+
+ def _findDirectoryForLayerName(self, layerName: Optional[str]) -> str:
+ foundDirectory = None
+ for existingLayerName, directoryName in list(self.layerContents.items()):
+ if layerName is None and directoryName == DEFAULT_GLYPHS_DIRNAME:
+ foundDirectory = directoryName
+ break
+ elif existingLayerName == layerName:
+ foundDirectory = directoryName
+ break
+ if not foundDirectory:
+ raise UFOLibError(
+ "Could not locate a glyph set directory for the layer named %s."
+ % layerName
+ )
+ return foundDirectory
+
+ def getGlyphSet( # type: ignore[override]
+ self,
+ layerName: Optional[str] = None,
+ defaultLayer: bool = True,
+ glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
+ validateRead: Optional[bool] = None,
+ validateWrite: Optional[bool] = None,
+ expectContentsFile: bool = False,
+ ) -> GlyphSet:
+ """
+ Return the GlyphSet object associated with the
+ appropriate glyph directory in the .ufo.
+ If layerName is None, the default glyph set
+ will be used. The defaultLayer flag indictes
+ that the layer should be saved into the default
+ glyphs directory.
+
+ ``validateRead`` will validate the read data, by default it is set to the
+ class's validate value, can be overridden.
+ ``validateWrte`` will validate the written data, by default it is set to the
+ class's validate value, can be overridden.
+ ``expectContentsFile`` will raise a GlifLibError if a contents.plist file is
+ not found on the glyph set file system. This should be set to ``True`` if you
+ are reading an existing UFO and ``False`` if you use ``getGlyphSet`` to create
+ a fresh glyph set.
+ """
+ if validateRead is None:
+ validateRead = self._validate
+ if validateWrite is None:
+ validateWrite = self._validate
+ # only default can be written in < 3
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0 and (
+ not defaultLayer or layerName is not None
+ ):
+ raise UFOLibError(
+ f"Only the default layer can be writen in UFO {self._formatVersion.major}."
+ )
+ # locate a layer name when None has been given
+ if layerName is None and defaultLayer:
+ for existingLayerName, directory in self.layerContents.items():
+ if directory == DEFAULT_GLYPHS_DIRNAME:
+ layerName = existingLayerName
+ if layerName is None:
+ layerName = DEFAULT_LAYER_NAME
+ elif layerName is None and not defaultLayer:
+ raise UFOLibError("A layer name must be provided for non-default layers.")
+ # move along to format specific writing
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ return self._getDefaultGlyphSet(
+ validateRead,
+ validateWrite,
+ glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+ expectContentsFile=expectContentsFile,
+ )
+ elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
+ return self._getGlyphSetFormatVersion3(
+ validateRead,
+ validateWrite,
+ layerName=layerName,
+ defaultLayer=defaultLayer,
+ glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+ expectContentsFile=expectContentsFile,
+ )
+ else:
+ raise NotImplementedError(self._formatVersion)
+
+ def _getDefaultGlyphSet(
+ self,
+ validateRead: bool,
+ validateWrite: bool,
+ glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
+ expectContentsFile: bool = False,
+ ) -> GlyphSet:
+ from fontTools.ufoLib.glifLib import GlyphSet
+
+ glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
+ return GlyphSet(
+ glyphSubFS,
+ glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+ ufoFormatVersion=self._formatVersion,
+ validateRead=validateRead,
+ validateWrite=validateWrite,
+ expectContentsFile=expectContentsFile,
+ )
+
+ def _getGlyphSetFormatVersion3(
+ self,
+ validateRead: bool,
+ validateWrite: bool,
+ layerName: Optional[str] = None,
+ defaultLayer: bool = True,
+ glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
+ expectContentsFile: bool = False,
+ ) -> GlyphSet:
+ from fontTools.ufoLib.glifLib import GlyphSet
+
+ # if the default flag is on, make sure that the default in the file
+ # matches the default being written. also make sure that this layer
+ # name is not already linked to a non-default layer.
+ if defaultLayer:
+ for existingLayerName, directory in self.layerContents.items():
+ if directory == DEFAULT_GLYPHS_DIRNAME:
+ if existingLayerName != layerName:
+ raise UFOLibError(
+ "Another layer ('%s') is already mapped to the default directory."
+ % existingLayerName
+ )
+ elif existingLayerName == layerName:
+ raise UFOLibError(
+ "The layer name is already mapped to a non-default layer."
+ )
+
+ # handle layerName is None to avoid MyPy errors
+ if layerName is None:
+ raise TypeError("'leyerName' cannot be None.")
+
+ # get an existing directory name
+ if layerName in self.layerContents:
+ directory = self.layerContents[layerName]
+ # get a new directory name
+ else:
+ if defaultLayer:
+ directory = DEFAULT_GLYPHS_DIRNAME
+ else:
+ # not caching this could be slightly expensive,
+ # but caching it will be cumbersome
+ existing = {d.lower() for d in self.layerContents.values()}
+ directory = userNameToFileName(
+ layerName, existing=existing, prefix="glyphs."
+ )
+ # make the directory
+ glyphSubFS = self.fs.makedir(directory, recreate=True)
+ # store the mapping
+ self.layerContents[layerName] = directory
+ # load the glyph set
+ return GlyphSet(
+ glyphSubFS,
+ glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+ ufoFormatVersion=self._formatVersion,
+ validateRead=validateRead,
+ validateWrite=validateWrite,
+ expectContentsFile=expectContentsFile,
+ )
+
+ def renameGlyphSet(
+ self,
+ layerName: Optional[str],
+ newLayerName: Optional[str],
+ defaultLayer: bool = False,
+ ) -> None:
+ """
+ Rename a glyph set.
+
+ Note: if a GlyphSet object has already been retrieved for
+ layerName, it is up to the caller to inform that object that
+ the directory it represents has changed.
+ """
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ # ignore renaming glyph sets for UFO1 UFO2
+ # just write the data from the default layer
+ return
+ # the new and old names can be the same
+ # as long as the default is being switched
+ if layerName is not None and layerName == newLayerName:
+ # if the default is off and the layer is already not the default, skip
+ if (
+ self.layerContents[layerName] != DEFAULT_GLYPHS_DIRNAME
+ and not defaultLayer
+ ):
+ return
+ # if the default is on and the layer is already the default, skip
+ if self.layerContents[layerName] == DEFAULT_GLYPHS_DIRNAME and defaultLayer:
+ return
+ else:
+ # make sure the new layer name doesn't already exist
+ if newLayerName is None:
+ newLayerName = DEFAULT_LAYER_NAME
+ if newLayerName in self.layerContents:
+ raise UFOLibError("A layer named %s already exists." % newLayerName)
+ # make sure the default layer doesn't already exist
+ if defaultLayer and DEFAULT_GLYPHS_DIRNAME in self.layerContents.values():
+ raise UFOLibError("A default layer already exists.")
+ # get the paths
+ oldDirectory = self._findDirectoryForLayerName(layerName)
+ if defaultLayer:
+ newDirectory = DEFAULT_GLYPHS_DIRNAME
+ else:
+ existing = {name.lower() for name in self.layerContents.values()}
+ newDirectory = userNameToFileName(
+ newLayerName, existing=existing, prefix="glyphs."
+ )
+ # update the internal mapping
+ if layerName is not None:
+ del self.layerContents[layerName]
+ self.layerContents[newLayerName] = newDirectory
+ # do the file system copy
+ self.fs.movedir(oldDirectory, newDirectory, create=True)
+
+ def deleteGlyphSet(self, layerName: Optional[str]) -> None:
+ """
+ Remove the glyph set matching layerName.
+ """
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ # ignore deleting glyph sets for UFO1 UFO2 as there are no layers
+ # just write the data from the default layer
+ return
+ foundDirectory = self._findDirectoryForLayerName(layerName)
+ self.removePath(foundDirectory, removeEmptyParents=False)
+ if layerName is not None:
+ del self.layerContents[layerName]
+
+ def writeData(self, fileName: PathStr, data: bytes) -> None:
+ """
+ Write data to fileName in the 'data' directory.
+ The data must be a bytes string.
+ """
+ self.writeBytesToPath(f"{DATA_DIRNAME}/{fsdecode(fileName)}", data)
+
+ def removeData(self, fileName: PathStr) -> None:
+ """
+ Remove the file named fileName from the data directory.
+ """
+ self.removePath(f"{DATA_DIRNAME}/{fsdecode(fileName)}")
+
+ # /images
+
+ def writeImage(
+ self,
+ fileName: PathStr,
+ data: bytes,
+ validate: Optional[bool] = None,
+ ) -> None:
+ """
+ Write data to fileName in the images directory.
+ The data must be a valid PNG.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ raise UFOLibError(
+ f"Images are not allowed in UFO {self._formatVersion.major}."
+ )
+ fileName = fsdecode(fileName)
+ if validate:
+ valid, error = pngValidator(data=data)
+ if not valid:
+ raise UFOLibError(error)
+ self.writeBytesToPath(f"{IMAGES_DIRNAME}/{fileName}", data)
+
+ def removeImage(
+ self,
+ fileName: PathStr,
+ validate: Optional[bool] = None,
+ ) -> None: # XXX remove unused 'validate'?
+ """
+ Remove the file named fileName from the
+ images directory.
+ """
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ raise UFOLibError(
+ f"Images are not allowed in UFO {self._formatVersion.major}."
+ )
+ self.removePath(f"{IMAGES_DIRNAME}/{fsdecode(fileName)}")
+
+ def copyImageFromReader(
+ self,
+ reader: UFOReader,
+ sourceFileName: PathStr,
+ destFileName: PathStr,
+ validate: Optional[bool] = None,
+ ) -> None:
+ """
+ Copy the sourceFileName in the provided UFOReader to destFileName
+ in this writer. This uses the most memory efficient method possible
+ for copying the data possible.
+ """
+ if validate is None:
+ validate = self._validate
+ if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+ raise UFOLibError(
+ f"Images are not allowed in UFO {self._formatVersion.major}."
+ )
+ sourcePath = f"{IMAGES_DIRNAME}/{fsdecode(sourceFileName)}"
+ destPath = f"{IMAGES_DIRNAME}/{fsdecode(destFileName)}"
+ self.copyFromReader(reader, sourcePath, destPath)
+
+ def close(self) -> None:
+ if self._havePreviousFile and self._fileStructure is UFOFileStructure.ZIP:
+ # if we are updating an existing zip file, we can now compress the
+ # contents of the temporary filesystem in the destination path
+ rootDir = os.path.splitext(os.path.basename(self._path))[0] + ".ufo"
+ with fs.zipfs.ZipFS(self._path, write=True, encoding="utf-8") as destFS: # type: ignore[abstract]
+ fs.copy.copy_fs(self.fs, destFS.makedir(rootDir))
+ super().close()
+
+
+# just an alias, makes it more explicit
+UFOReaderWriter = UFOWriter
+
+
+# ----------------
+# Helper Functions
+# ----------------
+
+
+def _sniffFileStructure(ufo_path: PathStr) -> UFOFileStructure:
+ """Return UFOFileStructure.ZIP if the UFO at path 'ufo_path' (str)
+ is a zip file, else return UFOFileStructure.PACKAGE if 'ufo_path' is a
+ directory.
+ Raise UFOLibError if it is a file with unknown structure, or if the path
+ does not exist.
+ """
+ if zipfile.is_zipfile(ufo_path):
+ return UFOFileStructure.ZIP
+ elif os.path.isdir(ufo_path):
+ return UFOFileStructure.PACKAGE
+ elif os.path.isfile(ufo_path):
+ raise UFOLibError(
+ "The specified UFO does not have a known structure: '%s'" % ufo_path
+ )
+ else:
+ raise UFOLibError("No such file or directory: '%s'" % ufo_path)
+
+
+def makeUFOPath(path: PathStr) -> str:
+ """
+ Return a .ufo pathname.
+
+ >>> makeUFOPath("directory/something.ext") == (
+ ... os.path.join('directory', 'something.ufo'))
+ True
+ >>> makeUFOPath("directory/something.another.thing.ext") == (
+ ... os.path.join('directory', 'something.another.thing.ufo'))
+ True
+ """
+ dir, name = os.path.split(path)
+ name = ".".join([".".join(name.split(".")[:-1]), "ufo"])
+ return os.path.join(dir, name)
+
+
+# ----------------------
+# fontinfo.plist Support
+# ----------------------
+
+# Version Validators
+
+# There is no version 1 validator and there shouldn't be.
+# The version 1 spec was very loose and there were numerous
+# cases of invalid values.
+
+
+def validateFontInfoVersion2ValueForAttribute(attr: str, value: Any) -> bool:
+ """
+ This performs very basic validation of the value for attribute
+ following the UFO 2 fontinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the value
+ is of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ dataValidationDict = fontInfoAttributesVersion2ValueData[attr]
+ valueType = dataValidationDict.get("type")
+ validator = dataValidationDict.get("valueValidator", genericTypeValidator)
+ valueOptions = dataValidationDict.get("valueOptions")
+ # have specific options for the validator
+ if valueOptions is not None:
+ isValidValue = validator(value, valueOptions)
+ # no specific options
+ else:
+ if validator == genericTypeValidator:
+ isValidValue = validator(value, valueType)
+ else:
+ isValidValue = validator(value)
+ return isValidValue
+
+
+def validateInfoVersion2Data(infoData: dict[str, Any]) -> dict[str, Any]:
+ """
+ This performs very basic validation of the value for infoData
+ following the UFO 2 fontinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the values
+ are of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ validInfoData = {}
+ for attr, value in list(infoData.items()):
+ isValidValue = validateFontInfoVersion2ValueForAttribute(attr, value)
+ if not isValidValue:
+ raise UFOLibError(f"Invalid value for attribute {attr} ({value!r}).")
+ else:
+ validInfoData[attr] = value
+ return validInfoData
+
+
+def validateFontInfoVersion3ValueForAttribute(attr: str, value: Any) -> bool:
+ """
+ This performs very basic validation of the value for attribute
+ following the UFO 3 fontinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the value
+ is of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ dataValidationDict = fontInfoAttributesVersion3ValueData[attr]
+ valueType = dataValidationDict.get("type")
+ validator = dataValidationDict.get("valueValidator", genericTypeValidator)
+ valueOptions = dataValidationDict.get("valueOptions")
+ # have specific options for the validator
+ if valueOptions is not None:
+ isValidValue = validator(value, valueOptions)
+ # no specific options
+ else:
+ if validator == genericTypeValidator:
+ isValidValue = validator(value, valueType)
+ else:
+ isValidValue = validator(value)
+ return isValidValue
+
+
+def validateInfoVersion3Data(infoData: dict[str, Any]) -> dict[str, Any]:
+ """
+ This performs very basic validation of the value for infoData
+ following the UFO 3 fontinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the values
+ are of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ validInfoData = {}
+ for attr, value in list(infoData.items()):
+ isValidValue = validateFontInfoVersion3ValueForAttribute(attr, value)
+ if not isValidValue:
+ raise UFOLibError(f"Invalid value for attribute {attr} ({value!r}).")
+ else:
+ validInfoData[attr] = value
+ return validInfoData
+
+
+# Value Options
+
+fontInfoOpenTypeHeadFlagsOptions: list[int] = list(range(0, 15))
+fontInfoOpenTypeOS2SelectionOptions: list[int] = [1, 2, 3, 4, 7, 8, 9]
+fontInfoOpenTypeOS2UnicodeRangesOptions: list[int] = list(range(0, 128))
+fontInfoOpenTypeOS2CodePageRangesOptions: list[int] = list(range(0, 64))
+fontInfoOpenTypeOS2TypeOptions: list[int] = [0, 1, 2, 3, 8, 9]
+
+# Version Attribute Definitions
+# This defines the attributes, types and, in some
+# cases the possible values, that can exist is
+# fontinfo.plist.
+
+fontInfoAttributesVersion1: set[str] = {
+ "familyName",
+ "styleName",
+ "fullName",
+ "fontName",
+ "menuName",
+ "fontStyle",
+ "note",
+ "versionMajor",
+ "versionMinor",
+ "year",
+ "copyright",
+ "notice",
+ "trademark",
+ "license",
+ "licenseURL",
+ "createdBy",
+ "designer",
+ "designerURL",
+ "vendorURL",
+ "unitsPerEm",
+ "ascender",
+ "descender",
+ "capHeight",
+ "xHeight",
+ "defaultWidth",
+ "slantAngle",
+ "italicAngle",
+ "widthName",
+ "weightName",
+ "weightValue",
+ "fondName",
+ "otFamilyName",
+ "otStyleName",
+ "otMacName",
+ "msCharSet",
+ "fondID",
+ "uniqueID",
+ "ttVendor",
+ "ttUniqueID",
+ "ttVersion",
+}
+
+fontInfoAttributesVersion2ValueData: FontInfoAttributes = {
+ "familyName": dict(type=str),
+ "styleName": dict(type=str),
+ "styleMapFamilyName": dict(type=str),
+ "styleMapStyleName": dict(
+ type=str, valueValidator=fontInfoStyleMapStyleNameValidator
+ ),
+ "versionMajor": dict(type=int),
+ "versionMinor": dict(type=int),
+ "year": dict(type=int),
+ "copyright": dict(type=str),
+ "trademark": dict(type=str),
+ "unitsPerEm": dict(type=(int, float)),
+ "descender": dict(type=(int, float)),
+ "xHeight": dict(type=(int, float)),
+ "capHeight": dict(type=(int, float)),
+ "ascender": dict(type=(int, float)),
+ "italicAngle": dict(type=(float, int)),
+ "note": dict(type=str),
+ "openTypeHeadCreated": dict(
+ type=str, valueValidator=fontInfoOpenTypeHeadCreatedValidator
+ ),
+ "openTypeHeadLowestRecPPEM": dict(type=(int, float)),
+ "openTypeHeadFlags": dict(
+ type="integerList",
+ valueValidator=genericIntListValidator,
+ valueOptions=fontInfoOpenTypeHeadFlagsOptions,
+ ),
+ "openTypeHheaAscender": dict(type=(int, float)),
+ "openTypeHheaDescender": dict(type=(int, float)),
+ "openTypeHheaLineGap": dict(type=(int, float)),
+ "openTypeHheaCaretSlopeRise": dict(type=int),
+ "openTypeHheaCaretSlopeRun": dict(type=int),
+ "openTypeHheaCaretOffset": dict(type=(int, float)),
+ "openTypeNameDesigner": dict(type=str),
+ "openTypeNameDesignerURL": dict(type=str),
+ "openTypeNameManufacturer": dict(type=str),
+ "openTypeNameManufacturerURL": dict(type=str),
+ "openTypeNameLicense": dict(type=str),
+ "openTypeNameLicenseURL": dict(type=str),
+ "openTypeNameVersion": dict(type=str),
+ "openTypeNameUniqueID": dict(type=str),
+ "openTypeNameDescription": dict(type=str),
+ "openTypeNamePreferredFamilyName": dict(type=str),
+ "openTypeNamePreferredSubfamilyName": dict(type=str),
+ "openTypeNameCompatibleFullName": dict(type=str),
+ "openTypeNameSampleText": dict(type=str),
+ "openTypeNameWWSFamilyName": dict(type=str),
+ "openTypeNameWWSSubfamilyName": dict(type=str),
+ "openTypeOS2WidthClass": dict(
+ type=int, valueValidator=fontInfoOpenTypeOS2WidthClassValidator
+ ),
+ "openTypeOS2WeightClass": dict(
+ type=int, valueValidator=fontInfoOpenTypeOS2WeightClassValidator
+ ),
+ "openTypeOS2Selection": dict(
+ type="integerList",
+ valueValidator=genericIntListValidator,
+ valueOptions=fontInfoOpenTypeOS2SelectionOptions,
+ ),
+ "openTypeOS2VendorID": dict(type=str),
+ "openTypeOS2Panose": dict(
+ type="integerList", valueValidator=fontInfoVersion2OpenTypeOS2PanoseValidator
+ ),
+ "openTypeOS2FamilyClass": dict(
+ type="integerList", valueValidator=fontInfoOpenTypeOS2FamilyClassValidator
+ ),
+ "openTypeOS2UnicodeRanges": dict(
+ type="integerList",
+ valueValidator=genericIntListValidator,
+ valueOptions=fontInfoOpenTypeOS2UnicodeRangesOptions,
+ ),
+ "openTypeOS2CodePageRanges": dict(
+ type="integerList",
+ valueValidator=genericIntListValidator,
+ valueOptions=fontInfoOpenTypeOS2CodePageRangesOptions,
+ ),
+ "openTypeOS2TypoAscender": dict(type=(int, float)),
+ "openTypeOS2TypoDescender": dict(type=(int, float)),
+ "openTypeOS2TypoLineGap": dict(type=(int, float)),
+ "openTypeOS2WinAscent": dict(type=(int, float)),
+ "openTypeOS2WinDescent": dict(type=(int, float)),
+ "openTypeOS2Type": dict(
+ type="integerList",
+ valueValidator=genericIntListValidator,
+ valueOptions=fontInfoOpenTypeOS2TypeOptions,
+ ),
+ "openTypeOS2SubscriptXSize": dict(type=(int, float)),
+ "openTypeOS2SubscriptYSize": dict(type=(int, float)),
+ "openTypeOS2SubscriptXOffset": dict(type=(int, float)),
+ "openTypeOS2SubscriptYOffset": dict(type=(int, float)),
+ "openTypeOS2SuperscriptXSize": dict(type=(int, float)),
+ "openTypeOS2SuperscriptYSize": dict(type=(int, float)),
+ "openTypeOS2SuperscriptXOffset": dict(type=(int, float)),
+ "openTypeOS2SuperscriptYOffset": dict(type=(int, float)),
+ "openTypeOS2StrikeoutSize": dict(type=(int, float)),
+ "openTypeOS2StrikeoutPosition": dict(type=(int, float)),
+ "openTypeVheaVertTypoAscender": dict(type=(int, float)),
+ "openTypeVheaVertTypoDescender": dict(type=(int, float)),
+ "openTypeVheaVertTypoLineGap": dict(type=(int, float)),
+ "openTypeVheaCaretSlopeRise": dict(type=int),
+ "openTypeVheaCaretSlopeRun": dict(type=int),
+ "openTypeVheaCaretOffset": dict(type=(int, float)),
+ "postscriptFontName": dict(type=str),
+ "postscriptFullName": dict(type=str),
+ "postscriptSlantAngle": dict(type=(float, int)),
+ "postscriptUniqueID": dict(type=int),
+ "postscriptUnderlineThickness": dict(type=(int, float)),
+ "postscriptUnderlinePosition": dict(type=(int, float)),
+ "postscriptIsFixedPitch": dict(type=bool),
+ "postscriptBlueValues": dict(
+ type="integerList", valueValidator=fontInfoPostscriptBluesValidator
+ ),
+ "postscriptOtherBlues": dict(
+ type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator
+ ),
+ "postscriptFamilyBlues": dict(
+ type="integerList", valueValidator=fontInfoPostscriptBluesValidator
+ ),
+ "postscriptFamilyOtherBlues": dict(
+ type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator
+ ),
+ "postscriptStemSnapH": dict(
+ type="integerList", valueValidator=fontInfoPostscriptStemsValidator
+ ),
+ "postscriptStemSnapV": dict(
+ type="integerList", valueValidator=fontInfoPostscriptStemsValidator
+ ),
+ "postscriptBlueFuzz": dict(type=(int, float)),
+ "postscriptBlueShift": dict(type=(int, float)),
+ "postscriptBlueScale": dict(type=(float, int)),
+ "postscriptForceBold": dict(type=bool),
+ "postscriptDefaultWidthX": dict(type=(int, float)),
+ "postscriptNominalWidthX": dict(type=(int, float)),
+ "postscriptWeightName": dict(type=str),
+ "postscriptDefaultCharacter": dict(type=str),
+ "postscriptWindowsCharacterSet": dict(
+ type=int, valueValidator=fontInfoPostscriptWindowsCharacterSetValidator
+ ),
+ "macintoshFONDFamilyID": dict(type=int),
+ "macintoshFONDName": dict(type=str),
+}
+fontInfoAttributesVersion2: set[str] = set(fontInfoAttributesVersion2ValueData.keys())
+
+fontInfoAttributesVersion3ValueData: FontInfoAttributes = deepcopy(
+ fontInfoAttributesVersion2ValueData
+)
+fontInfoAttributesVersion3ValueData.update(
+ {
+ "versionMinor": dict(type=int, valueValidator=genericNonNegativeIntValidator),
+ "unitsPerEm": dict(
+ type=(int, float), valueValidator=genericNonNegativeNumberValidator
+ ),
+ "openTypeHeadLowestRecPPEM": dict(
+ type=int, valueValidator=genericNonNegativeNumberValidator
+ ),
+ "openTypeHheaAscender": dict(type=int),
+ "openTypeHheaDescender": dict(type=int),
+ "openTypeHheaLineGap": dict(type=int),
+ "openTypeHheaCaretOffset": dict(type=int),
+ "openTypeOS2Panose": dict(
+ type="integerList",
+ valueValidator=fontInfoVersion3OpenTypeOS2PanoseValidator,
+ ),
+ "openTypeOS2TypoAscender": dict(type=int),
+ "openTypeOS2TypoDescender": dict(type=int),
+ "openTypeOS2TypoLineGap": dict(type=int),
+ "openTypeOS2WinAscent": dict(
+ type=int, valueValidator=genericNonNegativeNumberValidator
+ ),
+ "openTypeOS2WinDescent": dict(
+ type=int, valueValidator=genericNonNegativeNumberValidator
+ ),
+ "openTypeOS2SubscriptXSize": dict(type=int),
+ "openTypeOS2SubscriptYSize": dict(type=int),
+ "openTypeOS2SubscriptXOffset": dict(type=int),
+ "openTypeOS2SubscriptYOffset": dict(type=int),
+ "openTypeOS2SuperscriptXSize": dict(type=int),
+ "openTypeOS2SuperscriptYSize": dict(type=int),
+ "openTypeOS2SuperscriptXOffset": dict(type=int),
+ "openTypeOS2SuperscriptYOffset": dict(type=int),
+ "openTypeOS2StrikeoutSize": dict(type=int),
+ "openTypeOS2StrikeoutPosition": dict(type=int),
+ "openTypeGaspRangeRecords": dict(
+ type="dictList", valueValidator=fontInfoOpenTypeGaspRangeRecordsValidator
+ ),
+ "openTypeNameRecords": dict(
+ type="dictList", valueValidator=fontInfoOpenTypeNameRecordsValidator
+ ),
+ "openTypeVheaVertTypoAscender": dict(type=int),
+ "openTypeVheaVertTypoDescender": dict(type=int),
+ "openTypeVheaVertTypoLineGap": dict(type=int),
+ "openTypeVheaCaretOffset": dict(type=int),
+ "woffMajorVersion": dict(
+ type=int, valueValidator=genericNonNegativeIntValidator
+ ),
+ "woffMinorVersion": dict(
+ type=int, valueValidator=genericNonNegativeIntValidator
+ ),
+ "woffMetadataUniqueID": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataUniqueIDValidator
+ ),
+ "woffMetadataVendor": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataVendorValidator
+ ),
+ "woffMetadataCredits": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataCreditsValidator
+ ),
+ "woffMetadataDescription": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataDescriptionValidator
+ ),
+ "woffMetadataLicense": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataLicenseValidator
+ ),
+ "woffMetadataCopyright": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataCopyrightValidator
+ ),
+ "woffMetadataTrademark": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataTrademarkValidator
+ ),
+ "woffMetadataLicensee": dict(
+ type=dict, valueValidator=fontInfoWOFFMetadataLicenseeValidator
+ ),
+ "woffMetadataExtensions": dict(
+ type=list, valueValidator=fontInfoWOFFMetadataExtensionsValidator
+ ),
+ "guidelines": dict(type=list, valueValidator=guidelinesValidator),
+ }
+)
+fontInfoAttributesVersion3: set[str] = set(fontInfoAttributesVersion3ValueData.keys())
+
+# insert the type validator for all attrs that
+# have no defined validator.
+for attr, dataDict in list(fontInfoAttributesVersion2ValueData.items()):
+ if "valueValidator" not in dataDict:
+ dataDict["valueValidator"] = genericTypeValidator
+
+for attr, dataDict in list(fontInfoAttributesVersion3ValueData.items()):
+ if "valueValidator" not in dataDict:
+ dataDict["valueValidator"] = genericTypeValidator
+
+# Version Conversion Support
+# These are used from converting from version 1
+# to version 2 or vice-versa.
+
+
+def _flipDict(d: dict[K, V]) -> dict[V, K]:
+ flipped = {}
+ for key, value in list(d.items()):
+ flipped[value] = key
+ return flipped
+
+
+fontInfoAttributesVersion1To2: dict[str, str] = {
+ "menuName": "styleMapFamilyName",
+ "designer": "openTypeNameDesigner",
+ "designerURL": "openTypeNameDesignerURL",
+ "createdBy": "openTypeNameManufacturer",
+ "vendorURL": "openTypeNameManufacturerURL",
+ "license": "openTypeNameLicense",
+ "licenseURL": "openTypeNameLicenseURL",
+ "ttVersion": "openTypeNameVersion",
+ "ttUniqueID": "openTypeNameUniqueID",
+ "notice": "openTypeNameDescription",
+ "otFamilyName": "openTypeNamePreferredFamilyName",
+ "otStyleName": "openTypeNamePreferredSubfamilyName",
+ "otMacName": "openTypeNameCompatibleFullName",
+ "weightName": "postscriptWeightName",
+ "weightValue": "openTypeOS2WeightClass",
+ "ttVendor": "openTypeOS2VendorID",
+ "uniqueID": "postscriptUniqueID",
+ "fontName": "postscriptFontName",
+ "fondID": "macintoshFONDFamilyID",
+ "fondName": "macintoshFONDName",
+ "defaultWidth": "postscriptDefaultWidthX",
+ "slantAngle": "postscriptSlantAngle",
+ "fullName": "postscriptFullName",
+ # require special value conversion
+ "fontStyle": "styleMapStyleName",
+ "widthName": "openTypeOS2WidthClass",
+ "msCharSet": "postscriptWindowsCharacterSet",
+}
+fontInfoAttributesVersion2To1 = _flipDict(fontInfoAttributesVersion1To2)
+deprecatedFontInfoAttributesVersion2 = set(fontInfoAttributesVersion1To2.keys())
+
+_fontStyle1To2: dict[int, str] = {
+ 64: "regular",
+ 1: "italic",
+ 32: "bold",
+ 33: "bold italic",
+}
+_fontStyle2To1: dict[str, int] = _flipDict(_fontStyle1To2)
+# Some UFO 1 files have 0
+_fontStyle1To2[0] = "regular"
+
+_widthName1To2: dict[str, int] = {
+ "Ultra-condensed": 1,
+ "Extra-condensed": 2,
+ "Condensed": 3,
+ "Semi-condensed": 4,
+ "Medium (normal)": 5,
+ "Semi-expanded": 6,
+ "Expanded": 7,
+ "Extra-expanded": 8,
+ "Ultra-expanded": 9,
+}
+_widthName2To1: dict[int, str] = _flipDict(_widthName1To2)
+# FontLab's default width value is "Normal".
+# Many format version 1 UFOs will have this.
+_widthName1To2["Normal"] = 5
+# FontLab has an "All" width value. In UFO 1
+# move this up to "Normal".
+_widthName1To2["All"] = 5
+# "medium" appears in a lot of UFO 1 files.
+_widthName1To2["medium"] = 5
+# "Medium" appears in a lot of UFO 1 files.
+_widthName1To2["Medium"] = 5
+
+_msCharSet1To2: dict[int, int] = {
+ 0: 1,
+ 1: 2,
+ 2: 3,
+ 77: 4,
+ 128: 5,
+ 129: 6,
+ 130: 7,
+ 134: 8,
+ 136: 9,
+ 161: 10,
+ 162: 11,
+ 163: 12,
+ 177: 13,
+ 178: 14,
+ 186: 15,
+ 200: 16,
+ 204: 17,
+ 222: 18,
+ 238: 19,
+ 255: 20,
+}
+_msCharSet2To1: dict[int, int] = _flipDict(_msCharSet1To2)
+
+# 1 <-> 2
+
+
+def convertFontInfoValueForAttributeFromVersion1ToVersion2(
+ attr: str, value: Any
+) -> tuple[str, Any]:
+ """
+ Convert value from version 1 to version 2 format.
+ Returns the new attribute name and the converted value.
+ If the value is None, None will be returned for the new value.
+ """
+ # convert floats to ints if possible
+ if isinstance(value, float):
+ if int(value) == value:
+ value = int(value)
+ if value is not None:
+ if attr == "fontStyle":
+ v: Optional[Union[str, int]] = _fontStyle1To2.get(value)
+ if v is None:
+ raise UFOLibError(
+ f"Cannot convert value ({value!r}) for attribute {attr}."
+ )
+ value = v
+ elif attr == "widthName":
+ v = _widthName1To2.get(value)
+ if v is None:
+ raise UFOLibError(
+ f"Cannot convert value ({value!r}) for attribute {attr}."
+ )
+ value = v
+ elif attr == "msCharSet":
+ v = _msCharSet1To2.get(value)
+ if v is None:
+ raise UFOLibError(
+ f"Cannot convert value ({value!r}) for attribute {attr}."
+ )
+ value = v
+ attr = fontInfoAttributesVersion1To2.get(attr, attr)
+ return attr, value
+
+
+def convertFontInfoValueForAttributeFromVersion2ToVersion1(
+ attr: str, value: Any
+) -> tuple[str, Any]:
+ """
+ Convert value from version 2 to version 1 format.
+ Returns the new attribute name and the converted value.
+ If the value is None, None will be returned for the new value.
+ """
+ if value is not None:
+ if attr == "styleMapStyleName":
+ value = _fontStyle2To1.get(value)
+ elif attr == "openTypeOS2WidthClass":
+ value = _widthName2To1.get(value)
+ elif attr == "postscriptWindowsCharacterSet":
+ value = _msCharSet2To1.get(value)
+ attr = fontInfoAttributesVersion2To1.get(attr, attr)
+ return attr, value
+
+
+def _convertFontInfoDataVersion1ToVersion2(data: dict[str, Any]) -> dict[str, Any]:
+ converted = {}
+ for attr, value in list(data.items()):
+ # FontLab gives -1 for the weightValue
+ # for fonts wil no defined value. Many
+ # format version 1 UFOs will have this.
+ if attr == "weightValue" and value == -1:
+ continue
+ newAttr, newValue = convertFontInfoValueForAttributeFromVersion1ToVersion2(
+ attr, value
+ )
+ # skip if the attribute is not part of version 2
+ if newAttr not in fontInfoAttributesVersion2:
+ continue
+ # catch values that can't be converted
+ if value is None:
+ raise UFOLibError(
+ f"Cannot convert value ({value!r}) for attribute {newAttr}."
+ )
+ # store
+ converted[newAttr] = newValue
+ return converted
+
+
+def _convertFontInfoDataVersion2ToVersion1(data: dict[str, Any]) -> dict[str, Any]:
+ converted = {}
+ for attr, value in list(data.items()):
+ newAttr, newValue = convertFontInfoValueForAttributeFromVersion2ToVersion1(
+ attr, value
+ )
+ # only take attributes that are registered for version 1
+ if newAttr not in fontInfoAttributesVersion1:
+ continue
+ # catch values that can't be converted
+ if value is None:
+ raise UFOLibError(
+ f"Cannot convert value ({value!r}) for attribute {newAttr}."
+ )
+ # store
+ converted[newAttr] = newValue
+ return converted
+
+
+# 2 <-> 3
+
+_ufo2To3NonNegativeInt: set[str] = {
+ "versionMinor",
+ "openTypeHeadLowestRecPPEM",
+ "openTypeOS2WinAscent",
+ "openTypeOS2WinDescent",
+}
+_ufo2To3NonNegativeIntOrFloat: set[str] = {
+ "unitsPerEm",
+}
+_ufo2To3FloatToInt: set[str] = {
+ "openTypeHeadLowestRecPPEM",
+ "openTypeHheaAscender",
+ "openTypeHheaDescender",
+ "openTypeHheaLineGap",
+ "openTypeHheaCaretOffset",
+ "openTypeOS2TypoAscender",
+ "openTypeOS2TypoDescender",
+ "openTypeOS2TypoLineGap",
+ "openTypeOS2WinAscent",
+ "openTypeOS2WinDescent",
+ "openTypeOS2SubscriptXSize",
+ "openTypeOS2SubscriptYSize",
+ "openTypeOS2SubscriptXOffset",
+ "openTypeOS2SubscriptYOffset",
+ "openTypeOS2SuperscriptXSize",
+ "openTypeOS2SuperscriptYSize",
+ "openTypeOS2SuperscriptXOffset",
+ "openTypeOS2SuperscriptYOffset",
+ "openTypeOS2StrikeoutSize",
+ "openTypeOS2StrikeoutPosition",
+ "openTypeVheaVertTypoAscender",
+ "openTypeVheaVertTypoDescender",
+ "openTypeVheaVertTypoLineGap",
+ "openTypeVheaCaretOffset",
+}
+
+
+def convertFontInfoValueForAttributeFromVersion2ToVersion3(
+ attr: str, value: Any
+) -> tuple[str, Any]:
+ """
+ Convert value from version 2 to version 3 format.
+ Returns the new attribute name and the converted value.
+ If the value is None, None will be returned for the new value.
+ """
+ if attr in _ufo2To3FloatToInt:
+ try:
+ value = round(value)
+ except (ValueError, TypeError):
+ raise UFOLibError("Could not convert value for %s." % attr)
+ if attr in _ufo2To3NonNegativeInt:
+ try:
+ value = int(abs(value))
+ except (ValueError, TypeError):
+ raise UFOLibError("Could not convert value for %s." % attr)
+ elif attr in _ufo2To3NonNegativeIntOrFloat:
+ try:
+ v = float(abs(value))
+ except (ValueError, TypeError):
+ raise UFOLibError("Could not convert value for %s." % attr)
+ if v == int(v):
+ v = int(v)
+ if v != value:
+ value = v
+ return attr, value
+
+
+def convertFontInfoValueForAttributeFromVersion3ToVersion2(
+ attr: str, value: Any
+) -> tuple[str, Any]:
+ """
+ Convert value from version 3 to version 2 format.
+ Returns the new attribute name and the converted value.
+ If the value is None, None will be returned for the new value.
+ """
+ return attr, value
+
+
+def _convertFontInfoDataVersion3ToVersion2(data: dict[str, Any]) -> dict[str, Any]:
+ converted = {}
+ for attr, value in list(data.items()):
+ newAttr, newValue = convertFontInfoValueForAttributeFromVersion3ToVersion2(
+ attr, value
+ )
+ if newAttr not in fontInfoAttributesVersion2:
+ continue
+ converted[newAttr] = newValue
+ return converted
+
+
+def _convertFontInfoDataVersion2ToVersion3(data: dict[str, Any]) -> dict[str, Any]:
+ converted = {}
+ for attr, value in list(data.items()):
+ attr, value = convertFontInfoValueForAttributeFromVersion2ToVersion3(
+ attr, value
+ )
+ converted[attr] = value
+ return converted
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..637044f0a77107d3e92a8f434a0238de79bc8851
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/converters.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/converters.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b1bf07bc88588d4e21f8440f30920db47f6b5ecd
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/converters.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/errors.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/errors.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f7aae8f95dd3f72a687eb15b61c290706ce6cd2c
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/errors.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/etree.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/etree.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b7820682cb8664bbeb3cdcc9383f6a87c3bd3498
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/etree.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/filenames.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/filenames.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2494bb189e62add08f2689f397d3ab3e0ca643fb
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/filenames.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/glifLib.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/glifLib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9456d6ade7e8354dcea111e53a7c1bc485479d5a
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/glifLib.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/kerning.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/kerning.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2957dadd8c5a57d87f14bef49661f1fa46359872
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/kerning.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/plistlib.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/plistlib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f3cd66b7f0f60027f760f9d37c39cbb1334e7fcb
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/plistlib.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/pointPen.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/pointPen.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0283176a669a2d3382c5d4d73bfd070c235c2400
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/pointPen.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/utils.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/utils.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d09195cc650b0dc969114998144736b644f20258
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/utils.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/validators.cpython-312.pyc b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/validators.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5e9d3af66a854201a2cdaa4bcb438b29f722b401
Binary files /dev/null and b/lib/python3.12/site-packages/fontTools/ufoLib/__pycache__/validators.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/converters.py b/lib/python3.12/site-packages/fontTools/ufoLib/converters.py
new file mode 100644
index 0000000000000000000000000000000000000000..94a229f48d2f89bf0fc51b3b1a2a0f083b97f81d
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/converters.py
@@ -0,0 +1,407 @@
+from __future__ import annotations
+
+from typing import Mapping, Any
+from collections.abc import Container
+
+from fontTools.annotations import KerningNested
+
+"""
+Functions for converting UFO1 or UFO2 files into UFO3 format.
+
+Currently provides functionality for converting kerning rules
+and kerning groups. Conversion is only supported _from_ UFO1
+or UFO2, and _to_ UFO3.
+"""
+
+# adapted from the UFO spec
+
+
+def convertUFO1OrUFO2KerningToUFO3Kerning(
+ kerning: KerningNested, groups: dict[str, list[str]], glyphSet: Container[str] = ()
+) -> tuple[KerningNested, dict[str, list[str]], dict[str, dict[str, str]]]:
+ """Convert kerning data in UFO1 or UFO2 syntax into UFO3 syntax.
+
+ Args:
+ kerning:
+ A dictionary containing the kerning rules defined in
+ the UFO font, as used in :class:`.UFOReader` objects.
+ groups:
+ A dictionary containing the groups defined in the UFO
+ font, as used in :class:`.UFOReader` objects.
+ glyphSet:
+ Optional; a set of glyph objects to skip (default: None).
+
+ Returns:
+ 1. A dictionary representing the converted kerning data.
+ 2. A copy of the groups dictionary, with all groups renamed to UFO3 syntax.
+ 3. A dictionary containing the mapping of old group names to new group names.
+
+ """
+ # gather known kerning groups based on the prefixes
+ firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
+ # Make lists of groups referenced in kerning pairs.
+ for first, seconds in list(kerning.items()):
+ if first in groups and first not in glyphSet:
+ if not first.startswith("public.kern1."):
+ firstReferencedGroups.add(first)
+ for second in list(seconds.keys()):
+ if second in groups and second not in glyphSet:
+ if not second.startswith("public.kern2."):
+ secondReferencedGroups.add(second)
+ # Create new names for these groups.
+ firstRenamedGroups: dict[str, str] = {}
+ for first in firstReferencedGroups:
+ # Make a list of existing group names.
+ existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
+ # Remove the old prefix from the name
+ newName = first.replace("@MMK_L_", "")
+ # Add the new prefix to the name.
+ newName = "public.kern1." + newName
+ # Make a unique group name.
+ newName = makeUniqueGroupName(newName, existingGroupNames)
+ # Store for use later.
+ firstRenamedGroups[first] = newName
+ secondRenamedGroups: dict[str, str] = {}
+ for second in secondReferencedGroups:
+ # Make a list of existing group names.
+ existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
+ # Remove the old prefix from the name
+ newName = second.replace("@MMK_R_", "")
+ # Add the new prefix to the name.
+ newName = "public.kern2." + newName
+ # Make a unique group name.
+ newName = makeUniqueGroupName(newName, existingGroupNames)
+ # Store for use later.
+ secondRenamedGroups[second] = newName
+ # Populate the new group names into the kerning dictionary as needed.
+ newKerning = {}
+ for first, seconds in list(kerning.items()):
+ first = firstRenamedGroups.get(first, first)
+ newSeconds = {}
+ for second, value in list(seconds.items()):
+ second = secondRenamedGroups.get(second, second)
+ newSeconds[second] = value
+ newKerning[first] = newSeconds
+ # Make copies of the referenced groups and store them
+ # under the new names in the overall groups dictionary.
+ allRenamedGroups = list(firstRenamedGroups.items())
+ allRenamedGroups += list(secondRenamedGroups.items())
+ for oldName, newName in allRenamedGroups:
+ group = list(groups[oldName])
+ groups[newName] = group
+ # Return the kerning and the groups.
+ return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
+
+
+def findKnownKerningGroups(groups: Mapping[str, Any]) -> tuple[set[str], set[str]]:
+ """Find all kerning groups in a UFO1 or UFO2 font that use known prefixes.
+
+ In some cases, not all kerning groups will be referenced
+ by the kerning pairs in a UFO. The algorithm for locating
+ groups in :func:`convertUFO1OrUFO2KerningToUFO3Kerning` will
+ miss these unreferenced groups. By scanning for known prefixes,
+ this function will catch all of the prefixed groups.
+
+ The prefixes and sides by this function are:
+
+ @MMK_L_ - side 1
+ @MMK_R_ - side 2
+
+ as defined in the UFO1 specification.
+
+ Args:
+ groups:
+ A dictionary containing the groups defined in the UFO
+ font, as read by :class:`.UFOReader`.
+
+ Returns:
+ Two sets; the first containing the names of all
+ first-side kerning groups identified in the ``groups``
+ dictionary, and the second containing the names of all
+ second-side kerning groups identified.
+
+ "First-side" and "second-side" are with respect to the
+ writing direction of the script.
+
+ Example::
+
+ >>> testGroups = {
+ ... "@MMK_L_1" : None,
+ ... "@MMK_L_2" : None,
+ ... "@MMK_L_3" : None,
+ ... "@MMK_R_1" : None,
+ ... "@MMK_R_2" : None,
+ ... "@MMK_R_3" : None,
+ ... "@MMK_l_1" : None,
+ ... "@MMK_r_1" : None,
+ ... "@MMK_X_1" : None,
+ ... "foo" : None,
+ ... }
+ >>> first, second = findKnownKerningGroups(testGroups)
+ >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
+ True
+ >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
+ True
+ """
+ knownFirstGroupPrefixes = ["@MMK_L_"]
+ knownSecondGroupPrefixes = ["@MMK_R_"]
+ firstGroups = set()
+ secondGroups = set()
+ for groupName in list(groups.keys()):
+ for firstPrefix in knownFirstGroupPrefixes:
+ if groupName.startswith(firstPrefix):
+ firstGroups.add(groupName)
+ break
+ for secondPrefix in knownSecondGroupPrefixes:
+ if groupName.startswith(secondPrefix):
+ secondGroups.add(groupName)
+ break
+ return firstGroups, secondGroups
+
+
+def makeUniqueGroupName(name: str, groupNames: list[str], counter: int = 0) -> str:
+ """Make a kerning group name that will be unique within the set of group names.
+
+ If the requested kerning group name already exists within the set, this
+ will return a new name by adding an incremented counter to the end
+ of the requested name.
+
+ Args:
+ name:
+ The requested kerning group name.
+ groupNames:
+ A list of the existing kerning group names.
+ counter:
+ Optional; a counter of group names already seen (default: 0). If
+ :attr:`.counter` is not provided, the function will recurse,
+ incrementing the value of :attr:`.counter` until it finds the
+ first unused ``name+counter`` combination, and return that result.
+
+ Returns:
+ A unique kerning group name composed of the requested name suffixed
+ by the smallest available integer counter.
+ """
+ # Add a number to the name if the counter is higher than zero.
+ newName = name
+ if counter > 0:
+ newName = "%s%d" % (newName, counter)
+ # If the new name is in the existing group names, recurse.
+ if newName in groupNames:
+ return makeUniqueGroupName(name, groupNames, counter + 1)
+ # Otherwise send back the new name.
+ return newName
+
+
+def test():
+ """
+ Tests for :func:`.convertUFO1OrUFO2KerningToUFO3Kerning`.
+
+ No known prefixes.
+
+ >>> testKerning = {
+ ... "A" : {
+ ... "A" : 1,
+ ... "B" : 2,
+ ... "CGroup" : 3,
+ ... "DGroup" : 4
+ ... },
+ ... "BGroup" : {
+ ... "A" : 5,
+ ... "B" : 6,
+ ... "CGroup" : 7,
+ ... "DGroup" : 8
+ ... },
+ ... "CGroup" : {
+ ... "A" : 9,
+ ... "B" : 10,
+ ... "CGroup" : 11,
+ ... "DGroup" : 12
+ ... },
+ ... }
+ >>> testGroups = {
+ ... "BGroup" : ["B"],
+ ... "CGroup" : ["C"],
+ ... "DGroup" : ["D"],
+ ... }
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+ ... testKerning, testGroups, [])
+ >>> expected = {
+ ... "A" : {
+ ... "A": 1,
+ ... "B": 2,
+ ... "public.kern2.CGroup": 3,
+ ... "public.kern2.DGroup": 4
+ ... },
+ ... "public.kern1.BGroup": {
+ ... "A": 5,
+ ... "B": 6,
+ ... "public.kern2.CGroup": 7,
+ ... "public.kern2.DGroup": 8
+ ... },
+ ... "public.kern1.CGroup": {
+ ... "A": 9,
+ ... "B": 10,
+ ... "public.kern2.CGroup": 11,
+ ... "public.kern2.DGroup": 12
+ ... }
+ ... }
+ >>> kerning == expected
+ True
+ >>> expected = {
+ ... "BGroup": ["B"],
+ ... "CGroup": ["C"],
+ ... "DGroup": ["D"],
+ ... "public.kern1.BGroup": ["B"],
+ ... "public.kern1.CGroup": ["C"],
+ ... "public.kern2.CGroup": ["C"],
+ ... "public.kern2.DGroup": ["D"],
+ ... }
+ >>> groups == expected
+ True
+
+ Known prefixes.
+
+ >>> testKerning = {
+ ... "A" : {
+ ... "A" : 1,
+ ... "B" : 2,
+ ... "@MMK_R_CGroup" : 3,
+ ... "@MMK_R_DGroup" : 4
+ ... },
+ ... "@MMK_L_BGroup" : {
+ ... "A" : 5,
+ ... "B" : 6,
+ ... "@MMK_R_CGroup" : 7,
+ ... "@MMK_R_DGroup" : 8
+ ... },
+ ... "@MMK_L_CGroup" : {
+ ... "A" : 9,
+ ... "B" : 10,
+ ... "@MMK_R_CGroup" : 11,
+ ... "@MMK_R_DGroup" : 12
+ ... },
+ ... }
+ >>> testGroups = {
+ ... "@MMK_L_BGroup" : ["B"],
+ ... "@MMK_L_CGroup" : ["C"],
+ ... "@MMK_L_XGroup" : ["X"],
+ ... "@MMK_R_CGroup" : ["C"],
+ ... "@MMK_R_DGroup" : ["D"],
+ ... "@MMK_R_XGroup" : ["X"],
+ ... }
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+ ... testKerning, testGroups, [])
+ >>> expected = {
+ ... "A" : {
+ ... "A": 1,
+ ... "B": 2,
+ ... "public.kern2.CGroup": 3,
+ ... "public.kern2.DGroup": 4
+ ... },
+ ... "public.kern1.BGroup": {
+ ... "A": 5,
+ ... "B": 6,
+ ... "public.kern2.CGroup": 7,
+ ... "public.kern2.DGroup": 8
+ ... },
+ ... "public.kern1.CGroup": {
+ ... "A": 9,
+ ... "B": 10,
+ ... "public.kern2.CGroup": 11,
+ ... "public.kern2.DGroup": 12
+ ... }
+ ... }
+ >>> kerning == expected
+ True
+ >>> expected = {
+ ... "@MMK_L_BGroup": ["B"],
+ ... "@MMK_L_CGroup": ["C"],
+ ... "@MMK_L_XGroup": ["X"],
+ ... "@MMK_R_CGroup": ["C"],
+ ... "@MMK_R_DGroup": ["D"],
+ ... "@MMK_R_XGroup": ["X"],
+ ... "public.kern1.BGroup": ["B"],
+ ... "public.kern1.CGroup": ["C"],
+ ... "public.kern1.XGroup": ["X"],
+ ... "public.kern2.CGroup": ["C"],
+ ... "public.kern2.DGroup": ["D"],
+ ... "public.kern2.XGroup": ["X"],
+ ... }
+ >>> groups == expected
+ True
+
+ >>> from .validators import kerningValidator
+ >>> kerningValidator(kerning)
+ (True, None)
+
+ Mixture of known prefixes and groups without prefixes.
+
+ >>> testKerning = {
+ ... "A" : {
+ ... "A" : 1,
+ ... "B" : 2,
+ ... "@MMK_R_CGroup" : 3,
+ ... "DGroup" : 4
+ ... },
+ ... "BGroup" : {
+ ... "A" : 5,
+ ... "B" : 6,
+ ... "@MMK_R_CGroup" : 7,
+ ... "DGroup" : 8
+ ... },
+ ... "@MMK_L_CGroup" : {
+ ... "A" : 9,
+ ... "B" : 10,
+ ... "@MMK_R_CGroup" : 11,
+ ... "DGroup" : 12
+ ... },
+ ... }
+ >>> testGroups = {
+ ... "BGroup" : ["B"],
+ ... "@MMK_L_CGroup" : ["C"],
+ ... "@MMK_R_CGroup" : ["C"],
+ ... "DGroup" : ["D"],
+ ... }
+ >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+ ... testKerning, testGroups, [])
+ >>> expected = {
+ ... "A" : {
+ ... "A": 1,
+ ... "B": 2,
+ ... "public.kern2.CGroup": 3,
+ ... "public.kern2.DGroup": 4
+ ... },
+ ... "public.kern1.BGroup": {
+ ... "A": 5,
+ ... "B": 6,
+ ... "public.kern2.CGroup": 7,
+ ... "public.kern2.DGroup": 8
+ ... },
+ ... "public.kern1.CGroup": {
+ ... "A": 9,
+ ... "B": 10,
+ ... "public.kern2.CGroup": 11,
+ ... "public.kern2.DGroup": 12
+ ... }
+ ... }
+ >>> kerning == expected
+ True
+ >>> expected = {
+ ... "BGroup": ["B"],
+ ... "@MMK_L_CGroup": ["C"],
+ ... "@MMK_R_CGroup": ["C"],
+ ... "DGroup": ["D"],
+ ... "public.kern1.BGroup": ["B"],
+ ... "public.kern1.CGroup": ["C"],
+ ... "public.kern2.CGroup": ["C"],
+ ... "public.kern2.DGroup": ["D"],
+ ... }
+ >>> groups == expected
+ True
+ """
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/errors.py b/lib/python3.12/site-packages/fontTools/ufoLib/errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..6cc9fec399471c1dff9141187650c2caabc53d58
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/errors.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+
+class UFOLibError(Exception):
+ pass
+
+
+class UnsupportedUFOFormat(UFOLibError):
+ pass
+
+
+class GlifLibError(UFOLibError):
+ """An error raised by glifLib.
+
+ This class is a loose backport of PEP 678, adding a :attr:`.note`
+ attribute that can hold additional context for errors encountered.
+
+ It will be maintained until only Python 3.11-and-later are supported.
+ """
+
+ def _add_note(self, note: str) -> None:
+ # Loose backport of PEP 678 until we only support Python 3.11+, used for
+ # adding additional context to errors.
+ # TODO: Replace with https://docs.python.org/3.11/library/exceptions.html#BaseException.add_note
+ (message, *rest) = self.args
+ self.args = ((message + "\n" + note), *rest)
+
+
+class UnsupportedGLIFFormat(GlifLibError):
+ pass
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/etree.py b/lib/python3.12/site-packages/fontTools/ufoLib/etree.py
new file mode 100644
index 0000000000000000000000000000000000000000..07b924ac8523c0680b175f0269c42fa1959abc47
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/etree.py
@@ -0,0 +1,6 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old ufoLib.etree module, which was moved to :mod:`fontTools.misc.etree`.
+Please use the latter instead.
+"""
+
+from fontTools.misc.etree import *
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/filenames.py b/lib/python3.12/site-packages/fontTools/ufoLib/filenames.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a4090a75536a0dc1e3d7b7e096982d3e6287726
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/filenames.py
@@ -0,0 +1,356 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+
+"""
+Convert user-provided internal UFO names to spec-compliant filenames.
+
+This module implements the algorithm for converting between a "user name" -
+something that a user can choose arbitrarily inside a font editor - and a file
+name suitable for use in a wide range of operating systems and filesystems.
+
+The `UFO 3 specification `_
+provides an example of an algorithm for such conversion, which avoids illegal
+characters, reserved file names, ambiguity between upper- and lower-case
+characters, and clashes with existing files.
+
+This code was originally copied from
+`ufoLib `_
+by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
+
+- Erik van Blokland
+- Tal Leming
+- Just van Rossum
+"""
+
+# Restrictions are taken mostly from
+# https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file#naming-conventions.
+#
+# 1. Integer value zero, sometimes referred to as the ASCII NUL character.
+# 2. Characters whose integer representations are in the range 1 to 31,
+# inclusive.
+# 3. Various characters that (mostly) Windows and POSIX-y filesystems don't
+# allow, plus "(" and ")", as per the specification.
+illegalCharacters: set[str] = {
+ "\x00",
+ "\x01",
+ "\x02",
+ "\x03",
+ "\x04",
+ "\x05",
+ "\x06",
+ "\x07",
+ "\x08",
+ "\t",
+ "\n",
+ "\x0b",
+ "\x0c",
+ "\r",
+ "\x0e",
+ "\x0f",
+ "\x10",
+ "\x11",
+ "\x12",
+ "\x13",
+ "\x14",
+ "\x15",
+ "\x16",
+ "\x17",
+ "\x18",
+ "\x19",
+ "\x1a",
+ "\x1b",
+ "\x1c",
+ "\x1d",
+ "\x1e",
+ "\x1f",
+ '"',
+ "*",
+ "+",
+ "/",
+ ":",
+ "<",
+ ">",
+ "?",
+ "[",
+ "\\",
+ "]",
+ "(",
+ ")",
+ "|",
+ "\x7f",
+}
+reservedFileNames: set[str] = {
+ "aux",
+ "clock$",
+ "com1",
+ "com2",
+ "com3",
+ "com4",
+ "com5",
+ "com6",
+ "com7",
+ "com8",
+ "com9",
+ "con",
+ "lpt1",
+ "lpt2",
+ "lpt3",
+ "lpt4",
+ "lpt5",
+ "lpt6",
+ "lpt7",
+ "lpt8",
+ "lpt9",
+ "nul",
+ "prn",
+}
+maxFileNameLength: int = 255
+
+
+class NameTranslationError(Exception):
+ pass
+
+
+def userNameToFileName(
+ userName: str, existing: Iterable[str] = (), prefix: str = "", suffix: str = ""
+) -> str:
+ """Converts from a user name to a file name.
+
+ Takes care to avoid illegal characters, reserved file names, ambiguity between
+ upper- and lower-case characters, and clashes with existing files.
+
+ Args:
+ userName (str): The input file name.
+ existing: A case-insensitive list of all existing file names.
+ prefix: Prefix to be prepended to the file name.
+ suffix: Suffix to be appended to the file name.
+
+ Returns:
+ A suitable filename.
+
+ Raises:
+ NameTranslationError: If no suitable name could be generated.
+
+ Examples::
+
+ >>> userNameToFileName("a") == "a"
+ True
+ >>> userNameToFileName("A") == "A_"
+ True
+ >>> userNameToFileName("AE") == "A_E_"
+ True
+ >>> userNameToFileName("Ae") == "A_e"
+ True
+ >>> userNameToFileName("ae") == "ae"
+ True
+ >>> userNameToFileName("aE") == "aE_"
+ True
+ >>> userNameToFileName("a.alt") == "a.alt"
+ True
+ >>> userNameToFileName("A.alt") == "A_.alt"
+ True
+ >>> userNameToFileName("A.Alt") == "A_.A_lt"
+ True
+ >>> userNameToFileName("A.aLt") == "A_.aL_t"
+ True
+ >>> userNameToFileName(u"A.alT") == "A_.alT_"
+ True
+ >>> userNameToFileName("T_H") == "T__H_"
+ True
+ >>> userNameToFileName("T_h") == "T__h"
+ True
+ >>> userNameToFileName("t_h") == "t_h"
+ True
+ >>> userNameToFileName("F_F_I") == "F__F__I_"
+ True
+ >>> userNameToFileName("f_f_i") == "f_f_i"
+ True
+ >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
+ True
+ >>> userNameToFileName(".notdef") == "_notdef"
+ True
+ >>> userNameToFileName("con") == "_con"
+ True
+ >>> userNameToFileName("CON") == "C_O_N_"
+ True
+ >>> userNameToFileName("con.alt") == "_con.alt"
+ True
+ >>> userNameToFileName("alt.con") == "alt._con"
+ True
+ """
+ # the incoming name must be a string
+ if not isinstance(userName, str):
+ raise ValueError("The value for userName must be a string.")
+ # establish the prefix and suffix lengths
+ prefixLength = len(prefix)
+ suffixLength = len(suffix)
+ # replace an initial period with an _
+ # if no prefix is to be added
+ if not prefix and userName[0] == ".":
+ userName = "_" + userName[1:]
+ # filter the user name
+ filteredUserName = []
+ for character in userName:
+ # replace illegal characters with _
+ if character in illegalCharacters:
+ character = "_"
+ # add _ to all non-lower characters
+ elif character != character.lower():
+ character += "_"
+ filteredUserName.append(character)
+ userName = "".join(filteredUserName)
+ # clip to 255
+ sliceLength = maxFileNameLength - prefixLength - suffixLength
+ userName = userName[:sliceLength]
+ # test for illegal files names
+ parts = []
+ for part in userName.split("."):
+ if part.lower() in reservedFileNames:
+ part = "_" + part
+ parts.append(part)
+ userName = ".".join(parts)
+ # test for clash
+ fullName = prefix + userName + suffix
+ if fullName.lower() in existing:
+ fullName = handleClash1(userName, existing, prefix, suffix)
+ # finished
+ return fullName
+
+
+def handleClash1(
+ userName: str, existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
+) -> str:
+ """A helper function that resolves collisions with existing names when choosing a filename.
+
+ This function attempts to append an unused integer counter to the filename.
+
+ Args:
+ userName (str): The input file name.
+ existing: A case-insensitive list of all existing file names.
+ prefix: Prefix to be prepended to the file name.
+ suffix: Suffix to be appended to the file name.
+
+ Returns:
+ A suitable filename.
+
+ >>> prefix = ("0" * 5) + "."
+ >>> suffix = "." + ("0" * 10)
+ >>> existing = ["a" * 5]
+
+ >>> e = list(existing)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000001.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000002.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
+ >>> handleClash1(userName="A" * 5, existing=e,
+ ... prefix=prefix, suffix=suffix) == (
+ ... '00000.AAAAA000000000000001.0000000000')
+ True
+ """
+ # if the prefix length + user name length + suffix length + 15 is at
+ # or past the maximum length, silce 15 characters off of the user name
+ prefixLength = len(prefix)
+ suffixLength = len(suffix)
+ if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
+ l = prefixLength + len(userName) + suffixLength + 15
+ sliceLength = maxFileNameLength - l
+ userName = userName[:sliceLength]
+ finalName = None
+ # try to add numbers to create a unique name
+ counter = 1
+ while finalName is None:
+ name = userName + str(counter).zfill(15)
+ fullName = prefix + name + suffix
+ if fullName.lower() not in existing:
+ finalName = fullName
+ break
+ else:
+ counter += 1
+ if counter >= 999999999999999:
+ break
+ # if there is a clash, go to the next fallback
+ if finalName is None:
+ finalName = handleClash2(existing, prefix, suffix)
+ # finished
+ return finalName
+
+
+def handleClash2(
+ existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
+) -> str:
+ """A helper function that resolves collisions with existing names when choosing a filename.
+
+ This function is a fallback to :func:`handleClash1`. It attempts to append an unused integer counter to the filename.
+
+ Args:
+ userName (str): The input file name.
+ existing: A case-insensitive list of all existing file names.
+ prefix: Prefix to be prepended to the file name.
+ suffix: Suffix to be appended to the file name.
+
+ Returns:
+ A suitable filename.
+
+ Raises:
+ NameTranslationError: If no suitable name could be generated.
+
+ Examples::
+
+ >>> prefix = ("0" * 5) + "."
+ >>> suffix = "." + ("0" * 10)
+ >>> existing = [prefix + str(i) + suffix for i in range(100)]
+
+ >>> e = list(existing)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.100.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.remove(prefix + "1" + suffix)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.1.0000000000')
+ True
+
+ >>> e = list(existing)
+ >>> e.remove(prefix + "2" + suffix)
+ >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+ ... '00000.2.0000000000')
+ True
+ """
+ # calculate the longest possible string
+ maxLength = maxFileNameLength - len(prefix) - len(suffix)
+ maxValue = int("9" * maxLength)
+ # try to find a number
+ finalName = None
+ counter = 1
+ while finalName is None:
+ fullName = prefix + str(counter) + suffix
+ if fullName.lower() not in existing:
+ finalName = fullName
+ break
+ else:
+ counter += 1
+ if counter >= maxValue:
+ break
+ # raise an error if nothing has been found
+ if finalName is None:
+ raise NameTranslationError("No unique name could be found.")
+ # finished
+ return finalName
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/glifLib.py b/lib/python3.12/site-packages/fontTools/ufoLib/glifLib.py
new file mode 100644
index 0000000000000000000000000000000000000000..040c31c4990b3cbb2cd631bdabceac6c7d868425
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/glifLib.py
@@ -0,0 +1,2120 @@
+"""
+Generic module for reading and writing the .glif format.
+
+More info about the .glif format (GLyphInterchangeFormat) can be found here:
+
+ http://unifiedfontobject.org
+
+The main class in this module is :class:`GlyphSet`. It manages a set of .glif files
+in a folder. It offers two ways to read glyph data, and one way to write
+glyph data. See the class doc string for details.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections import OrderedDict
+from typing import TYPE_CHECKING, Any, Optional, Union, cast
+from warnings import warn
+
+import fontTools.misc.filesystem as fs
+from fontTools.misc import etree, plistlib
+from fontTools.misc.textTools import tobytes
+from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen
+from fontTools.ufoLib import UFOFormatVersion, _UFOBaseIO
+from fontTools.ufoLib.errors import GlifLibError
+from fontTools.ufoLib.filenames import userNameToFileName
+from fontTools.ufoLib.utils import (
+ BaseFormatVersion,
+ normalizeFormatVersion,
+ numberTypes,
+)
+from fontTools.ufoLib.validators import (
+ anchorsValidator,
+ colorValidator,
+ genericTypeValidator,
+ glyphLibValidator,
+ guidelinesValidator,
+ identifierValidator,
+ imageValidator,
+)
+
+if TYPE_CHECKING:
+ from collections.abc import Callable, Iterable, Set
+ from logging import Logger
+
+ from fontTools.annotations import (
+ ElementType,
+ FormatVersion,
+ FormatVersions,
+ GLIFFormatVersionInput,
+ GlyphNameToFileNameFunc,
+ IntFloat,
+ PathOrFS,
+ UFOFormatVersionInput,
+ )
+ from fontTools.misc.filesystem._base import FS
+
+
+__all__: list[str] = [
+ "GlyphSet",
+ "GlifLibError",
+ "readGlyphFromString",
+ "writeGlyphToString",
+ "glyphNameToFileName",
+]
+
+logger: Logger = logging.getLogger(__name__)
+
+
+# ---------
+# Constants
+# ---------
+
+CONTENTS_FILENAME = "contents.plist"
+LAYERINFO_FILENAME = "layerinfo.plist"
+
+
+class GLIFFormatVersion(BaseFormatVersion):
+ """Class representing the versions of the .glif format supported by the UFO version in use.
+
+ For a given :mod:`fontTools.ufoLib.UFOFormatVersion`, the :func:`supported_versions` method will
+ return the supported versions of the GLIF file format. If the UFO version is unspecified, the
+ :func:`supported_versions` method will return all available GLIF format versions.
+ """
+
+ FORMAT_1_0 = (1, 0)
+ FORMAT_2_0 = (2, 0)
+
+ @classmethod
+ def default(
+ cls, ufoFormatVersion: Optional[UFOFormatVersion] = None
+ ) -> GLIFFormatVersion:
+ if ufoFormatVersion is not None:
+ return max(cls.supported_versions(ufoFormatVersion))
+ return super().default()
+
+ @classmethod
+ def supported_versions(
+ cls, ufoFormatVersion: Optional[UFOFormatVersion] = None
+ ) -> frozenset[GLIFFormatVersion]:
+ if ufoFormatVersion is None:
+ # if ufo format unspecified, return all the supported GLIF formats
+ return super().supported_versions()
+ # else only return the GLIF formats supported by the given UFO format
+ versions = {cls.FORMAT_1_0}
+ if ufoFormatVersion >= UFOFormatVersion.FORMAT_3_0:
+ versions.add(cls.FORMAT_2_0)
+ return frozenset(versions)
+
+
+# ------------
+# Simple Glyph
+# ------------
+
+
+class Glyph:
+ """
+ Minimal glyph object. It has no glyph attributes until either
+ the draw() or the drawPoints() method has been called.
+ """
+
+ def __init__(self, glyphName: str, glyphSet: GlyphSet) -> None:
+ self.glyphName: str = glyphName
+ self.glyphSet: GlyphSet = glyphSet
+
+ def draw(self, pen: Any, outputImpliedClosingLine: bool = False) -> None:
+ """
+ Draw this glyph onto a *FontTools* Pen.
+ """
+ pointPen = PointToSegmentPen(
+ pen, outputImpliedClosingLine=outputImpliedClosingLine
+ )
+ self.drawPoints(pointPen)
+
+ def drawPoints(self, pointPen: AbstractPointPen) -> None:
+ """
+ Draw this glyph onto a PointPen.
+ """
+ self.glyphSet.readGlyph(self.glyphName, self, pointPen)
+
+
+# ---------
+# Glyph Set
+# ---------
+
+
+class GlyphSet(_UFOBaseIO):
+ """
+ GlyphSet manages a set of .glif files inside one directory.
+
+ GlyphSet's constructor takes a path to an existing directory as it's
+ first argument. Reading glyph data can either be done through the
+ readGlyph() method, or by using GlyphSet's dictionary interface, where
+ the keys are glyph names and the values are (very) simple glyph objects.
+
+ To write a glyph to the glyph set, you use the writeGlyph() method.
+ The simple glyph objects returned through the dict interface do not
+ support writing, they are just a convenient way to get at the glyph data.
+ """
+
+ glyphClass = Glyph
+
+ def __init__(
+ self,
+ path: PathOrFS,
+ glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
+ ufoFormatVersion: UFOFormatVersionInput = None,
+ validateRead: bool = True,
+ validateWrite: bool = True,
+ expectContentsFile: bool = False,
+ ) -> None:
+ """
+ 'path' should be a path (string) to an existing local directory, or
+ an instance of fs.base.FS class.
+
+ The optional 'glyphNameToFileNameFunc' argument must be a callback
+ function that takes two arguments: a glyph name and a list of all
+ existing filenames (if any exist). It should return a file name
+ (including the .glif extension). The glyphNameToFileName function
+ is called whenever a file name is created for a given glyph name.
+
+ ``validateRead`` will validate read operations. Its default is ``True``.
+ ``validateWrite`` will validate write operations. Its default is ``True``.
+ ``expectContentsFile`` will raise a GlifLibError if a contents.plist file is
+ not found on the glyph set file system. This should be set to ``True`` if you
+ are reading an existing UFO and ``False`` if you create a fresh glyph set.
+ """
+ try:
+ ufoFormatVersion = normalizeFormatVersion(
+ ufoFormatVersion, UFOFormatVersion
+ )
+ except ValueError as e:
+ from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+ raise UnsupportedUFOFormat(
+ f"Unsupported UFO format: {ufoFormatVersion!r}"
+ ) from e
+
+ if hasattr(path, "__fspath__"): # support os.PathLike objects
+ path = path.__fspath__()
+
+ if isinstance(path, str):
+ try:
+ filesystem: FS = fs.osfs.OSFS(path)
+ except fs.errors.CreateFailed:
+ raise GlifLibError("No glyphs directory '%s'" % path)
+ self._shouldClose: bool = True
+ elif isinstance(path, fs.base.FS):
+ filesystem = path
+ try:
+ filesystem.check()
+ except fs.errors.FilesystemClosed:
+ raise GlifLibError("the filesystem '%s' is closed" % filesystem)
+ self._shouldClose = False
+ else:
+ raise TypeError(
+ "Expected a path string or fs object, found %s" % type(path).__name__
+ )
+ try:
+ path = filesystem.getsyspath("/")
+ except fs.errors.NoSysPath:
+ # network or in-memory FS may not map to the local one
+ path = str(filesystem)
+ # 'dirName' is kept for backward compatibility only, but it's DEPRECATED
+ # as it's not guaranteed that it maps to an existing OSFS directory.
+ # Client could use the FS api via the `self.fs` attribute instead.
+ self.dirName: str = fs.path.basename(path)
+ self.fs: FS = filesystem
+ # if glyphSet contains no 'contents.plist', we consider it empty
+ self._havePreviousFile: bool = filesystem.exists(CONTENTS_FILENAME)
+ if expectContentsFile and not self._havePreviousFile:
+ raise GlifLibError(f"{CONTENTS_FILENAME} is missing.")
+ # attribute kept for backward compatibility
+ self.ufoFormatVersion: int = ufoFormatVersion.major
+ self.ufoFormatVersionTuple: UFOFormatVersion = ufoFormatVersion
+ if glyphNameToFileNameFunc is None:
+ glyphNameToFileNameFunc = glyphNameToFileName
+ self.glyphNameToFileName: Callable[[str, set[str]], str] = (
+ glyphNameToFileNameFunc
+ )
+ self._validateRead: bool = validateRead
+ self._validateWrite: bool = validateWrite
+ self._existingFileNames: set[str] | None = None
+ self._reverseContents: Optional[dict[str, str]] = None
+
+ self.rebuildContents()
+
+ def rebuildContents(self, validateRead: bool = False) -> None:
+ """
+ Rebuild the contents dict by loading contents.plist.
+
+ ``validateRead`` will validate the data, by default it is set to the
+ class's ``validateRead`` value, can be overridden.
+ """
+ if validateRead is None:
+ validateRead = self._validateRead
+ contents = self._getPlist(CONTENTS_FILENAME, {})
+ # validate the contents
+ if validateRead:
+ invalidFormat = False
+ if not isinstance(contents, dict):
+ invalidFormat = True
+ else:
+ for name, fileName in contents.items():
+ if not isinstance(name, str):
+ invalidFormat = True
+ if not isinstance(fileName, str):
+ invalidFormat = True
+ elif not self.fs.exists(fileName):
+ raise GlifLibError(
+ "%s references a file that does not exist: %s"
+ % (CONTENTS_FILENAME, fileName)
+ )
+ if invalidFormat:
+ raise GlifLibError("%s is not properly formatted" % CONTENTS_FILENAME)
+ self.contents: dict[str, str] = contents
+ self._existingFileNames = None
+ self._reverseContents = None
+
+ def getReverseContents(self) -> dict[str, str]:
+ """
+ Return a reversed dict of self.contents, mapping file names to
+ glyph names. This is primarily an aid for custom glyph name to file
+ name schemes that want to make sure they don't generate duplicate
+ file names. The file names are converted to lowercase so we can
+ reliably check for duplicates that only differ in case, which is
+ important for case-insensitive file systems.
+ """
+ if self._reverseContents is None:
+ d = {}
+ for k, v in self.contents.items():
+ d[v.lower()] = k
+ self._reverseContents = d
+ return self._reverseContents
+
+ def writeContents(self) -> None:
+ """
+ Write the contents.plist file out to disk. Call this method when
+ you're done writing glyphs.
+ """
+ self._writePlist(CONTENTS_FILENAME, self.contents)
+
+ # layer info
+
+ def readLayerInfo(self, info: Any, validateRead: Optional[bool] = None) -> None:
+ """
+ ``validateRead`` will validate the data, by default it is set to the
+ class's ``validateRead`` value, can be overridden.
+ """
+ if validateRead is None:
+ validateRead = self._validateRead
+ infoDict = self._getPlist(LAYERINFO_FILENAME, {})
+ if validateRead:
+ if not isinstance(infoDict, dict):
+ raise GlifLibError("layerinfo.plist is not properly formatted.")
+ infoDict = validateLayerInfoVersion3Data(infoDict)
+ # populate the object
+ for attr, value in infoDict.items():
+ try:
+ setattr(info, attr, value)
+ except AttributeError:
+ raise GlifLibError(
+ "The supplied layer info object does not support setting a necessary attribute (%s)."
+ % attr
+ )
+
+ def writeLayerInfo(self, info: Any, validateWrite: Optional[bool] = None) -> None:
+ """
+ ``validateWrite`` will validate the data, by default it is set to the
+ class's ``validateWrite`` value, can be overridden.
+ """
+ if validateWrite is None:
+ validateWrite = self._validateWrite
+ if self.ufoFormatVersionTuple.major < 3:
+ raise GlifLibError(
+ "layerinfo.plist is not allowed in UFO %d."
+ % self.ufoFormatVersionTuple.major
+ )
+ # gather data
+ infoData = {}
+ for attr in layerInfoVersion3ValueData.keys():
+ if hasattr(info, attr):
+ try:
+ value = getattr(info, attr)
+ except AttributeError:
+ raise GlifLibError(
+ "The supplied info object does not support getting a necessary attribute (%s)."
+ % attr
+ )
+ if value is None or (attr == "lib" and not value):
+ continue
+ infoData[attr] = value
+ if infoData:
+ # validate
+ if validateWrite:
+ infoData = validateLayerInfoVersion3Data(infoData)
+ # write file
+ self._writePlist(LAYERINFO_FILENAME, infoData)
+ elif self._havePreviousFile and self.fs.exists(LAYERINFO_FILENAME):
+ # data empty, remove existing file
+ self.fs.remove(LAYERINFO_FILENAME)
+
+ def getGLIF(self, glyphName: str) -> bytes:
+ """
+ Get the raw GLIF text for a given glyph name. This only works
+ for GLIF files that are already on disk.
+
+ This method is useful in situations when the raw XML needs to be
+ read from a glyph set for a particular glyph before fully parsing
+ it into an object structure via the readGlyph method.
+
+ Raises KeyError if 'glyphName' is not in contents.plist, or
+ GlifLibError if the file associated with can't be found.
+ """
+ fileName = self.contents[glyphName]
+ try:
+ return self.fs.readbytes(fileName)
+ except fs.errors.ResourceNotFound:
+ raise GlifLibError(
+ "The file '%s' associated with glyph '%s' in contents.plist "
+ "does not exist on %s" % (fileName, glyphName, self.fs)
+ )
+
+ def getGLIFModificationTime(self, glyphName: str) -> Optional[float]:
+ """
+ Returns the modification time for the GLIF file with 'glyphName', as
+ a floating point number giving the number of seconds since the epoch.
+ Return None if the associated file does not exist or the underlying
+ filesystem does not support getting modified times.
+ Raises KeyError if the glyphName is not in contents.plist.
+ """
+ fileName = self.contents[glyphName]
+ return self.getFileModificationTime(fileName)
+
+ # reading/writing API
+
+ def readGlyph(
+ self,
+ glyphName: str,
+ glyphObject: Optional[Any] = None,
+ pointPen: Optional[AbstractPointPen] = None,
+ validate: Optional[bool] = None,
+ ) -> None:
+ """
+ Read a .glif file for 'glyphName' from the glyph set. The
+ 'glyphObject' argument can be any kind of object (even None);
+ the readGlyph() method will attempt to set the following
+ attributes on it:
+
+ width
+ the advance width of the glyph
+ height
+ the advance height of the glyph
+ unicodes
+ a list of unicode values for this glyph
+ note
+ a string
+ lib
+ a dictionary containing custom data
+ image
+ a dictionary containing image data
+ guidelines
+ a list of guideline data dictionaries
+ anchors
+ a list of anchor data dictionaries
+
+ All attributes are optional, in two ways:
+
+ 1) An attribute *won't* be set if the .glif file doesn't
+ contain data for it. 'glyphObject' will have to deal
+ with default values itself.
+ 2) If setting the attribute fails with an AttributeError
+ (for example if the 'glyphObject' attribute is read-
+ only), readGlyph() will not propagate that exception,
+ but ignore that attribute.
+
+ To retrieve outline information, you need to pass an object
+ conforming to the PointPen protocol as the 'pointPen' argument.
+ This argument may be None if you don't need the outline data.
+
+ readGlyph() will raise KeyError if the glyph is not present in
+ the glyph set.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's ``validateRead`` value, can be overridden.
+ """
+ if validate is None:
+ validate = self._validateRead
+ text = self.getGLIF(glyphName)
+ try:
+ tree = _glifTreeFromString(text)
+ formatVersions = GLIFFormatVersion.supported_versions(
+ self.ufoFormatVersionTuple
+ )
+ _readGlyphFromTree(
+ tree,
+ glyphObject,
+ pointPen,
+ formatVersions=formatVersions,
+ validate=validate,
+ )
+ except GlifLibError as glifLibError:
+ # Re-raise with a note that gives extra context, describing where
+ # the error occurred.
+ fileName = self.contents[glyphName]
+ try:
+ glifLocation = f"'{self.fs.getsyspath(fileName)}'"
+ except fs.errors.NoSysPath:
+ # Network or in-memory FS may not map to a local path, so use
+ # the best string representation we have.
+ glifLocation = f"'{fileName}' from '{str(self.fs)}'"
+
+ glifLibError._add_note(
+ f"The issue is in glyph '{glyphName}', located in {glifLocation}."
+ )
+ raise
+
+ def writeGlyph(
+ self,
+ glyphName: str,
+ glyphObject: Optional[Any] = None,
+ drawPointsFunc: Optional[Callable[[AbstractPointPen], None]] = None,
+ formatVersion: GLIFFormatVersionInput = None,
+ validate: Optional[bool] = None,
+ ) -> None:
+ """
+ Write a .glif file for 'glyphName' to the glyph set. The
+ 'glyphObject' argument can be any kind of object (even None);
+ the writeGlyph() method will attempt to get the following
+ attributes from it:
+
+ width
+ the advance width of the glyph
+ height
+ the advance height of the glyph
+ unicodes
+ a list of unicode values for this glyph
+ note
+ a string
+ lib
+ a dictionary containing custom data
+ image
+ a dictionary containing image data
+ guidelines
+ a list of guideline data dictionaries
+ anchors
+ a list of anchor data dictionaries
+
+ All attributes are optional: if 'glyphObject' doesn't
+ have the attribute, it will simply be skipped.
+
+ To write outline data to the .glif file, writeGlyph() needs
+ a function (any callable object actually) that will take one
+ argument: an object that conforms to the PointPen protocol.
+ The function will be called by writeGlyph(); it has to call the
+ proper PointPen methods to transfer the outline to the .glif file.
+
+ The GLIF format version will be chosen based on the ufoFormatVersion
+ passed during the creation of this object. If a particular format
+ version is desired, it can be passed with the formatVersion argument.
+ The formatVersion argument accepts either a tuple of integers for
+ (major, minor), or a single integer for the major digit only (with
+ minor digit implied as 0).
+
+ An UnsupportedGLIFFormat exception is raised if the requested GLIF
+ formatVersion is not supported.
+
+ ``validate`` will validate the data, by default it is set to the
+ class's ``validateWrite`` value, can be overridden.
+ """
+ if formatVersion is None:
+ formatVersion = GLIFFormatVersion.default(self.ufoFormatVersionTuple)
+ else:
+ try:
+ formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
+ except ValueError as e:
+ from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+ raise UnsupportedGLIFFormat(
+ f"Unsupported GLIF format version: {formatVersion!r}"
+ ) from e
+ if formatVersion not in GLIFFormatVersion.supported_versions(
+ self.ufoFormatVersionTuple
+ ):
+ from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+ raise UnsupportedGLIFFormat(
+ f"Unsupported GLIF format version ({formatVersion!s}) "
+ f"for UFO format version {self.ufoFormatVersionTuple!s}."
+ )
+ if validate is None:
+ validate = self._validateWrite
+ fileName = self.contents.get(glyphName)
+ if fileName is None:
+ if self._existingFileNames is None:
+ self._existingFileNames = {
+ fileName.lower() for fileName in self.contents.values()
+ }
+ fileName = self.glyphNameToFileName(glyphName, self._existingFileNames)
+ self.contents[glyphName] = fileName
+ self._existingFileNames.add(fileName.lower())
+ if self._reverseContents is not None:
+ self._reverseContents[fileName.lower()] = glyphName
+ data = _writeGlyphToBytes(
+ glyphName,
+ glyphObject,
+ drawPointsFunc,
+ formatVersion=formatVersion,
+ validate=validate,
+ )
+ if (
+ self._havePreviousFile
+ and self.fs.exists(fileName)
+ and data == self.fs.readbytes(fileName)
+ ):
+ return
+ self.fs.writebytes(fileName, data)
+
+ def deleteGlyph(self, glyphName: str) -> None:
+ """Permanently delete the glyph from the glyph set on disk. Will
+ raise KeyError if the glyph is not present in the glyph set.
+ """
+ fileName = self.contents[glyphName]
+ self.fs.remove(fileName)
+ if self._existingFileNames is not None:
+ self._existingFileNames.remove(fileName.lower())
+ if self._reverseContents is not None:
+ del self._reverseContents[fileName.lower()]
+ del self.contents[glyphName]
+
+ # dict-like support
+
+ def keys(self) -> list[str]:
+ return list(self.contents.keys())
+
+ def has_key(self, glyphName: str) -> bool:
+ return glyphName in self.contents
+
+ __contains__ = has_key
+
+ def __len__(self) -> int:
+ return len(self.contents)
+
+ def __getitem__(self, glyphName: str) -> Any:
+ if glyphName not in self.contents:
+ raise KeyError(glyphName)
+ return self.glyphClass(glyphName, self)
+
+ # quickly fetch unicode values
+
+ def getUnicodes(
+ self, glyphNames: Optional[Iterable[str]] = None
+ ) -> dict[str, list[int]]:
+ """
+ Return a dictionary that maps glyph names to lists containing
+ the unicode value[s] for that glyph, if any. This parses the .glif
+ files partially, so it is a lot faster than parsing all files completely.
+ By default this checks all glyphs, but a subset can be passed with glyphNames.
+ """
+ unicodes = {}
+ if glyphNames is None:
+ glyphNames = self.contents.keys()
+ for glyphName in glyphNames:
+ text = self.getGLIF(glyphName)
+ unicodes[glyphName] = _fetchUnicodes(text)
+ return unicodes
+
+ def getComponentReferences(
+ self, glyphNames: Optional[Iterable[str]] = None
+ ) -> dict[str, list[str]]:
+ """
+ Return a dictionary that maps glyph names to lists containing the
+ base glyph name of components in the glyph. This parses the .glif
+ files partially, so it is a lot faster than parsing all files completely.
+ By default this checks all glyphs, but a subset can be passed with glyphNames.
+ """
+ components = {}
+ if glyphNames is None:
+ glyphNames = self.contents.keys()
+ for glyphName in glyphNames:
+ text = self.getGLIF(glyphName)
+ components[glyphName] = _fetchComponentBases(text)
+ return components
+
+ def getImageReferences(
+ self, glyphNames: Optional[Iterable[str]] = None
+ ) -> dict[str, Optional[str]]:
+ """
+ Return a dictionary that maps glyph names to the file name of the image
+ referenced by the glyph. This parses the .glif files partially, so it is a
+ lot faster than parsing all files completely.
+ By default this checks all glyphs, but a subset can be passed with glyphNames.
+ """
+ images = {}
+ if glyphNames is None:
+ glyphNames = self.contents.keys()
+ for glyphName in glyphNames:
+ text = self.getGLIF(glyphName)
+ images[glyphName] = _fetchImageFileName(text)
+ return images
+
+ def close(self) -> None:
+ if self._shouldClose:
+ self.fs.close()
+
+ def __enter__(self) -> GlyphSet:
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
+ self.close()
+
+
+# -----------------------
+# Glyph Name to File Name
+# -----------------------
+
+
+def glyphNameToFileName(glyphName: str, existingFileNames: Optional[set[str]]) -> str:
+ """
+ Wrapper around the userNameToFileName function in filenames.py
+
+ Note that existingFileNames should be a set for large glyphsets
+ or performance will suffer.
+ """
+ if existingFileNames is None:
+ existingFileNames = set()
+ return userNameToFileName(glyphName, existing=existingFileNames, suffix=".glif")
+
+
+# -----------------------
+# GLIF To and From String
+# -----------------------
+
+
+def readGlyphFromString(
+ aString: Union[str, bytes],
+ glyphObject: Optional[Any] = None,
+ pointPen: Optional[Any] = None,
+ formatVersions: FormatVersions = None,
+ validate: bool = True,
+) -> None:
+ """
+ Read .glif data from a string into a glyph object.
+
+ The 'glyphObject' argument can be any kind of object (even None);
+ the readGlyphFromString() method will attempt to set the following
+ attributes on it:
+
+ width
+ the advance width of the glyph
+ height
+ the advance height of the glyph
+ unicodes
+ a list of unicode values for this glyph
+ note
+ a string
+ lib
+ a dictionary containing custom data
+ image
+ a dictionary containing image data
+ guidelines
+ a list of guideline data dictionaries
+ anchors
+ a list of anchor data dictionaries
+
+ All attributes are optional, in two ways:
+
+ 1) An attribute *won't* be set if the .glif file doesn't
+ contain data for it. 'glyphObject' will have to deal
+ with default values itself.
+ 2) If setting the attribute fails with an AttributeError
+ (for example if the 'glyphObject' attribute is read-
+ only), readGlyphFromString() will not propagate that
+ exception, but ignore that attribute.
+
+ To retrieve outline information, you need to pass an object
+ conforming to the PointPen protocol as the 'pointPen' argument.
+ This argument may be None if you don't need the outline data.
+
+ The formatVersions optional argument define the GLIF format versions
+ that are allowed to be read.
+ The type is Optional[Iterable[tuple[int, int], int]]. It can contain
+ either integers (for the major versions to be allowed, with minor
+ digits defaulting to 0), or tuples of integers to specify both
+ (major, minor) versions.
+ By default when formatVersions is None all the GLIF format versions
+ currently defined are allowed to be read.
+
+ ``validate`` will validate the read data. It is set to ``True`` by default.
+ """
+ tree = _glifTreeFromString(aString)
+
+ if formatVersions is None:
+ validFormatVersions: Set[GLIFFormatVersion] = (
+ GLIFFormatVersion.supported_versions()
+ )
+ else:
+ validFormatVersions, invalidFormatVersions = set(), set()
+ for v in formatVersions:
+ try:
+ formatVersion = normalizeFormatVersion(v, GLIFFormatVersion)
+ except ValueError:
+ invalidFormatVersions.add(v)
+ else:
+ validFormatVersions.add(formatVersion)
+ if not validFormatVersions:
+ raise ValueError(
+ "None of the requested GLIF formatVersions are supported: "
+ f"{formatVersions!r}"
+ )
+
+ _readGlyphFromTree(
+ tree,
+ glyphObject,
+ pointPen,
+ formatVersions=validFormatVersions,
+ validate=validate,
+ )
+
+
+def _writeGlyphToBytes(
+ glyphName: str,
+ glyphObject: Optional[Any] = None,
+ drawPointsFunc: Optional[Callable[[Any], None]] = None,
+ writer: Optional[Any] = None,
+ formatVersion: Optional[FormatVersion] = None,
+ validate: bool = True,
+) -> bytes:
+ """Return .glif data for a glyph as a UTF-8 encoded bytes string."""
+ try:
+ formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
+ except ValueError:
+ from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+ raise UnsupportedGLIFFormat(
+ "Unsupported GLIF format version: {formatVersion!r}"
+ )
+ # start
+ if validate and not isinstance(glyphName, str):
+ raise GlifLibError("The glyph name is not properly formatted.")
+ if validate and len(glyphName) == 0:
+ raise GlifLibError("The glyph name is empty.")
+ glyphAttrs = OrderedDict(
+ [("name", glyphName), ("format", repr(formatVersion.major))]
+ )
+ if formatVersion.minor != 0:
+ glyphAttrs["formatMinor"] = repr(formatVersion.minor)
+ root = etree.Element("glyph", glyphAttrs)
+ identifiers: set[str] = set()
+ # advance
+ _writeAdvance(glyphObject, root, validate)
+ # unicodes
+ if getattr(glyphObject, "unicodes", None):
+ _writeUnicodes(glyphObject, root, validate)
+ # note
+ if getattr(glyphObject, "note", None):
+ _writeNote(glyphObject, root, validate)
+ # image
+ if formatVersion.major >= 2 and getattr(glyphObject, "image", None):
+ _writeImage(glyphObject, root, validate)
+ # guidelines
+ if formatVersion.major >= 2 and getattr(glyphObject, "guidelines", None):
+ _writeGuidelines(glyphObject, root, identifiers, validate)
+ # anchors
+ anchors = getattr(glyphObject, "anchors", None)
+ if formatVersion.major >= 2 and anchors:
+ _writeAnchors(glyphObject, root, identifiers, validate)
+ # outline
+ if drawPointsFunc is not None:
+ outline = etree.SubElement(root, "outline")
+ pen = GLIFPointPen(outline, identifiers=identifiers, validate=validate)
+ drawPointsFunc(pen)
+ if formatVersion.major == 1 and anchors:
+ _writeAnchorsFormat1(pen, anchors, validate)
+ # prevent lxml from writing self-closing tags
+ if not len(outline):
+ outline.text = "\n "
+ # lib
+ if getattr(glyphObject, "lib", None):
+ _writeLib(glyphObject, root, validate)
+ # return the text
+ data = etree.tostring(
+ root, encoding="UTF-8", xml_declaration=True, pretty_print=True
+ )
+ return data
+
+
+def writeGlyphToString(
+ glyphName: str,
+ glyphObject: Optional[Any] = None,
+ drawPointsFunc: Optional[Callable[[Any], None]] = None,
+ formatVersion: Optional[FormatVersion] = None,
+ validate: bool = True,
+) -> str:
+ """
+ Return .glif data for a glyph as a string. The XML declaration's
+ encoding is always set to "UTF-8".
+ The 'glyphObject' argument can be any kind of object (even None);
+ the writeGlyphToString() method will attempt to get the following
+ attributes from it:
+
+ width
+ the advance width of the glyph
+ height
+ the advance height of the glyph
+ unicodes
+ a list of unicode values for this glyph
+ note
+ a string
+ lib
+ a dictionary containing custom data
+ image
+ a dictionary containing image data
+ guidelines
+ a list of guideline data dictionaries
+ anchors
+ a list of anchor data dictionaries
+
+ All attributes are optional: if 'glyphObject' doesn't
+ have the attribute, it will simply be skipped.
+
+ To write outline data to the .glif file, writeGlyphToString() needs
+ a function (any callable object actually) that will take one
+ argument: an object that conforms to the PointPen protocol.
+ The function will be called by writeGlyphToString(); it has to call the
+ proper PointPen methods to transfer the outline to the .glif file.
+
+ The GLIF format version can be specified with the formatVersion argument.
+ This accepts either a tuple of integers for (major, minor), or a single
+ integer for the major digit only (with minor digit implied as 0).
+ By default when formatVesion is None the latest GLIF format version will
+ be used; currently it's 2.0, which is equivalent to formatVersion=(2, 0).
+
+ An UnsupportedGLIFFormat exception is raised if the requested UFO
+ formatVersion is not supported.
+
+ ``validate`` will validate the written data. It is set to ``True`` by default.
+ """
+ data = _writeGlyphToBytes(
+ glyphName,
+ glyphObject=glyphObject,
+ drawPointsFunc=drawPointsFunc,
+ formatVersion=formatVersion,
+ validate=validate,
+ )
+ return data.decode("utf-8")
+
+
+def _writeAdvance(glyphObject: Any, element: ElementType, validate: bool) -> None:
+ width = getattr(glyphObject, "width", None)
+ if width is not None:
+ if validate and not isinstance(width, numberTypes):
+ raise GlifLibError("width attribute must be int or float")
+ if width == 0:
+ width = None
+ height = getattr(glyphObject, "height", None)
+ if height is not None:
+ if validate and not isinstance(height, numberTypes):
+ raise GlifLibError("height attribute must be int or float")
+ if height == 0:
+ height = None
+ if width is not None and height is not None:
+ etree.SubElement(
+ element,
+ "advance",
+ OrderedDict([("height", repr(height)), ("width", repr(width))]),
+ )
+ elif width is not None:
+ etree.SubElement(element, "advance", dict(width=repr(width)))
+ elif height is not None:
+ etree.SubElement(element, "advance", dict(height=repr(height)))
+
+
+def _writeUnicodes(glyphObject: Any, element: ElementType, validate: bool) -> None:
+ unicodes = getattr(glyphObject, "unicodes", [])
+ if validate and isinstance(unicodes, int):
+ unicodes = [unicodes]
+ seen = set()
+ for code in unicodes:
+ if validate and not isinstance(code, int):
+ raise GlifLibError("unicode values must be int")
+ if code in seen:
+ continue
+ seen.add(code)
+ hexCode = "%04X" % code
+ etree.SubElement(element, "unicode", dict(hex=hexCode))
+
+
+def _writeNote(glyphObject: Any, element: ElementType, validate: bool) -> None:
+ note = getattr(glyphObject, "note", None)
+ if validate and not isinstance(note, str):
+ raise GlifLibError("note attribute must be str")
+ if isinstance(note, str):
+ note = note.strip()
+ note = "\n" + note + "\n"
+ etree.SubElement(element, "note").text = note
+
+
+def _writeImage(glyphObject: Any, element: ElementType, validate: bool) -> None:
+ image = getattr(glyphObject, "image", None)
+ if image is None:
+ return
+
+ if validate and not imageValidator(image):
+ raise GlifLibError(
+ "image attribute must be a dict or dict-like object with the proper structure."
+ )
+ attrs = OrderedDict([("fileName", image["fileName"])])
+ for attr, default in _transformationInfo:
+ value = image.get(attr, default)
+ if value != default:
+ attrs[attr] = repr(value)
+ color = image.get("color")
+ if color is not None:
+ attrs["color"] = color
+ etree.SubElement(element, "image", attrs)
+
+
+def _writeGuidelines(
+ glyphObject: Any, element: ElementType, identifiers: set[str], validate: bool
+) -> None:
+ guidelines = getattr(glyphObject, "guidelines", [])
+ if validate and not guidelinesValidator(guidelines):
+ raise GlifLibError("guidelines attribute does not have the proper structure.")
+ for guideline in guidelines:
+ attrs = OrderedDict()
+ x = guideline.get("x")
+ if x is not None:
+ attrs["x"] = repr(x)
+ y = guideline.get("y")
+ if y is not None:
+ attrs["y"] = repr(y)
+ angle = guideline.get("angle")
+ if angle is not None:
+ attrs["angle"] = repr(angle)
+ name = guideline.get("name")
+ if name is not None:
+ attrs["name"] = name
+ color = guideline.get("color")
+ if color is not None:
+ attrs["color"] = color
+ identifier = guideline.get("identifier")
+ if identifier is not None:
+ if validate and identifier in identifiers:
+ raise GlifLibError("identifier used more than once: %s" % identifier)
+ attrs["identifier"] = identifier
+ identifiers.add(identifier)
+ etree.SubElement(element, "guideline", attrs)
+
+
+def _writeAnchorsFormat1(pen: Any, anchors: Any, validate: bool) -> None:
+ if validate and not anchorsValidator(anchors):
+ raise GlifLibError("anchors attribute does not have the proper structure.")
+ for anchor in anchors:
+ attrs = {}
+ x = anchor["x"]
+ attrs["x"] = repr(x)
+ y = anchor["y"]
+ attrs["y"] = repr(y)
+ name = anchor.get("name")
+ if name is not None:
+ attrs["name"] = name
+ pen.beginPath()
+ pen.addPoint((x, y), segmentType="move", name=name)
+ pen.endPath()
+
+
+def _writeAnchors(
+ glyphObject: Any,
+ element: ElementType,
+ identifiers: set[str],
+ validate: bool,
+) -> None:
+ anchors = getattr(glyphObject, "anchors", [])
+ if validate and not anchorsValidator(anchors):
+ raise GlifLibError("anchors attribute does not have the proper structure.")
+ for anchor in anchors:
+ attrs = OrderedDict()
+ x = anchor["x"]
+ attrs["x"] = repr(x)
+ y = anchor["y"]
+ attrs["y"] = repr(y)
+ name = anchor.get("name")
+ if name is not None:
+ attrs["name"] = name
+ color = anchor.get("color")
+ if color is not None:
+ attrs["color"] = color
+ identifier = anchor.get("identifier")
+ if identifier is not None:
+ if validate and identifier in identifiers:
+ raise GlifLibError("identifier used more than once: %s" % identifier)
+ attrs["identifier"] = identifier
+ identifiers.add(identifier)
+ etree.SubElement(element, "anchor", attrs)
+
+
+def _writeLib(glyphObject: Any, element: ElementType, validate: bool) -> None:
+ lib = getattr(glyphObject, "lib", None)
+ if not lib:
+ # don't write empty lib
+ return
+ if validate:
+ valid, message = glyphLibValidator(lib)
+ if not valid:
+ raise GlifLibError(message)
+ if not isinstance(lib, dict):
+ lib = dict(lib)
+ # plist inside GLIF begins with 2 levels of indentation
+ e = plistlib.totree(lib, indent_level=2)
+ etree.SubElement(element, "lib").append(e)
+
+
+# -----------------------
+# layerinfo.plist Support
+# -----------------------
+
+layerInfoVersion3ValueData = {
+ "color": dict(type=str, valueValidator=colorValidator),
+ "lib": dict(type=dict, valueValidator=genericTypeValidator),
+}
+
+
+def validateLayerInfoVersion3ValueForAttribute(attr: str, value: Any) -> bool:
+ """
+ This performs very basic validation of the value for attribute
+ following the UFO 3 fontinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the value
+ is of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ if attr not in layerInfoVersion3ValueData:
+ return False
+ dataValidationDict = layerInfoVersion3ValueData[attr]
+ valueType = dataValidationDict.get("type")
+ validator = dataValidationDict.get("valueValidator")
+ valueOptions = dataValidationDict.get("valueOptions")
+ # have specific options for the validator
+ assert callable(validator)
+ if valueOptions is not None:
+ isValidValue = validator(value, valueOptions)
+ # no specific options
+ else:
+ if validator == genericTypeValidator:
+ isValidValue = validator(value, valueType)
+ else:
+ isValidValue = validator(value)
+ return isValidValue
+
+
+def validateLayerInfoVersion3Data(infoData: dict[str, Any]) -> dict[str, Any]:
+ """
+ This performs very basic validation of the value for infoData
+ following the UFO 3 layerinfo.plist specification. The results
+ of this should not be interpretted as *correct* for the font
+ that they are part of. This merely indicates that the values
+ are of the proper type and, where the specification defines
+ a set range of possible values for an attribute, that the
+ value is in the accepted range.
+ """
+ for attr, value in infoData.items():
+ if attr not in layerInfoVersion3ValueData:
+ raise GlifLibError("Unknown attribute %s." % attr)
+ isValidValue = validateLayerInfoVersion3ValueForAttribute(attr, value)
+ if not isValidValue:
+ raise GlifLibError(f"Invalid value for attribute {attr} ({value!r}).")
+ return infoData
+
+
+# -----------------
+# GLIF Tree Support
+# -----------------
+
+
+def _glifTreeFromFile(aFile: Union[str, bytes, int]) -> ElementType:
+ if etree._have_lxml:
+ tree = etree.parse(aFile, parser=etree.XMLParser(remove_comments=True))
+ else:
+ tree = etree.parse(aFile)
+ root = tree.getroot()
+ if root.tag != "glyph":
+ raise GlifLibError("The GLIF is not properly formatted.")
+ if root.text and root.text.strip() != "":
+ raise GlifLibError("Invalid GLIF structure.")
+ return root
+
+
+def _glifTreeFromString(aString: Union[str, bytes]) -> ElementType:
+ data = tobytes(aString, encoding="utf-8")
+ try:
+ if etree._have_lxml:
+ root = etree.fromstring(data, parser=etree.XMLParser(remove_comments=True))
+ else:
+ root = etree.fromstring(data)
+ except Exception as etree_exception:
+ raise GlifLibError("GLIF contains invalid XML.") from etree_exception
+
+ if root.tag != "glyph":
+ raise GlifLibError("The GLIF is not properly formatted.")
+ if root.text and root.text.strip() != "":
+ raise GlifLibError("Invalid GLIF structure.")
+ return root
+
+
+def _readGlyphFromTree(
+ tree: ElementType,
+ glyphObject: Optional[Any] = None,
+ pointPen: Optional[AbstractPointPen] = None,
+ formatVersions: Set[GLIFFormatVersion] = GLIFFormatVersion.supported_versions(),
+ validate: bool = True,
+) -> None:
+ # check the format version
+ formatVersionMajor = tree.get("format")
+ if formatVersionMajor is None:
+ if validate:
+ raise GlifLibError("Unspecified format version in GLIF.")
+ formatVersionMajor = 0
+ formatVersionMinor = tree.get("formatMinor", 0)
+ try:
+ formatVersion = GLIFFormatVersion(
+ (int(formatVersionMajor), int(formatVersionMinor))
+ )
+ except ValueError as e:
+ msg = "Unsupported GLIF format: %s.%s" % (
+ formatVersionMajor,
+ formatVersionMinor,
+ )
+ if validate:
+ from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+ raise UnsupportedGLIFFormat(msg) from e
+ # warn but continue using the latest supported format
+ formatVersion = GLIFFormatVersion.default()
+ logger.warning(
+ "%s. Assuming the latest supported version (%s). "
+ "Some data may be skipped or parsed incorrectly.",
+ msg,
+ formatVersion,
+ )
+
+ if validate and formatVersion not in formatVersions:
+ raise GlifLibError(f"Forbidden GLIF format version: {formatVersion!s}")
+
+ try:
+ readGlyphFromTree = _READ_GLYPH_FROM_TREE_FUNCS[formatVersion]
+ except KeyError:
+ raise NotImplementedError(formatVersion)
+
+ readGlyphFromTree(
+ tree=tree,
+ glyphObject=glyphObject,
+ pointPen=pointPen,
+ validate=validate,
+ formatMinor=formatVersion.minor,
+ )
+
+
+def _readGlyphFromTreeFormat1(
+ tree: ElementType,
+ glyphObject: Optional[Any] = None,
+ pointPen: Optional[AbstractPointPen] = None,
+ validate: bool = False,
+ **kwargs: Any,
+) -> None:
+ # get the name
+ _readName(glyphObject, tree, validate)
+ # populate the sub elements
+ unicodes = []
+ haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False
+ for element in tree:
+ if glyphObject is None:
+ continue
+
+ if element.tag == "outline":
+ if validate:
+ if haveSeenOutline:
+ raise GlifLibError("The outline element occurs more than once.")
+ if element.attrib:
+ raise GlifLibError(
+ "The outline element contains unknown attributes."
+ )
+ if element.text and element.text.strip() != "":
+ raise GlifLibError("Invalid outline structure.")
+ haveSeenOutline = True
+ buildOutlineFormat1(glyphObject, pointPen, element, validate)
+ elif element.tag == "advance":
+ if validate and haveSeenAdvance:
+ raise GlifLibError("The advance element occurs more than once.")
+ haveSeenAdvance = True
+ _readAdvance(glyphObject, element)
+ elif element.tag == "unicode":
+ v = element.get("hex")
+ if v is None:
+ raise GlifLibError(
+ "A unicode element is missing its required hex attribute."
+ )
+ try:
+ v = int(v, 16)
+ if v not in unicodes:
+ unicodes.append(v)
+ except ValueError:
+ raise GlifLibError(
+ "Illegal value for hex attribute of unicode element."
+ )
+ elif element.tag == "note":
+ if validate and haveSeenNote:
+ raise GlifLibError("The note element occurs more than once.")
+ haveSeenNote = True
+ _readNote(glyphObject, element)
+ elif element.tag == "lib":
+ if validate and haveSeenLib:
+ raise GlifLibError("The lib element occurs more than once.")
+ haveSeenLib = True
+ _readLib(glyphObject, element, validate)
+ else:
+ raise GlifLibError("Unknown element in GLIF: %s" % element)
+ # set the collected unicodes
+ if unicodes:
+ _relaxedSetattr(glyphObject, "unicodes", unicodes)
+
+
+def _readGlyphFromTreeFormat2(
+ tree: ElementType,
+ glyphObject: Optional[Any] = None,
+ pointPen: Optional[AbstractPointPen] = None,
+ validate: bool = False,
+ formatMinor: int = 0,
+) -> None:
+ # get the name
+ _readName(glyphObject, tree, validate)
+ # populate the sub elements
+ unicodes = []
+ guidelines = []
+ anchors = []
+ haveSeenAdvance = haveSeenImage = haveSeenOutline = haveSeenLib = haveSeenNote = (
+ False
+ )
+ identifiers: set[str] = set()
+ for element in tree:
+ if glyphObject is None:
+ continue
+ if element.tag == "outline":
+ if validate:
+ if haveSeenOutline:
+ raise GlifLibError("The outline element occurs more than once.")
+ if element.attrib:
+ raise GlifLibError(
+ "The outline element contains unknown attributes."
+ )
+ if element.text and element.text.strip() != "":
+ raise GlifLibError("Invalid outline structure.")
+ haveSeenOutline = True
+ if pointPen is not None:
+ buildOutlineFormat2(
+ glyphObject, pointPen, element, identifiers, validate
+ )
+ elif element.tag == "advance":
+ if validate and haveSeenAdvance:
+ raise GlifLibError("The advance element occurs more than once.")
+ haveSeenAdvance = True
+ _readAdvance(glyphObject, element)
+ elif element.tag == "unicode":
+ v = element.get("hex")
+ if v is None:
+ raise GlifLibError(
+ "A unicode element is missing its required hex attribute."
+ )
+ try:
+ v = int(v, 16)
+ if v not in unicodes:
+ unicodes.append(v)
+ except ValueError:
+ raise GlifLibError(
+ "Illegal value for hex attribute of unicode element."
+ )
+ elif element.tag == "guideline":
+ if validate and len(element):
+ raise GlifLibError("Unknown children in guideline element.")
+ attrib = dict(element.attrib)
+ for attr in ("x", "y", "angle"):
+ if attr in attrib:
+ attrib[attr] = _number(attrib[attr])
+ guidelines.append(attrib)
+ elif element.tag == "anchor":
+ if validate and len(element):
+ raise GlifLibError("Unknown children in anchor element.")
+ attrib = dict(element.attrib)
+ for attr in ("x", "y"):
+ if attr in element.attrib:
+ attrib[attr] = _number(attrib[attr])
+ anchors.append(attrib)
+ elif element.tag == "image":
+ if validate:
+ if haveSeenImage:
+ raise GlifLibError("The image element occurs more than once.")
+ if len(element):
+ raise GlifLibError("Unknown children in image element.")
+ haveSeenImage = True
+ _readImage(glyphObject, element, validate)
+ elif element.tag == "note":
+ if validate and haveSeenNote:
+ raise GlifLibError("The note element occurs more than once.")
+ haveSeenNote = True
+ _readNote(glyphObject, element)
+ elif element.tag == "lib":
+ if validate and haveSeenLib:
+ raise GlifLibError("The lib element occurs more than once.")
+ haveSeenLib = True
+ _readLib(glyphObject, element, validate)
+ else:
+ raise GlifLibError("Unknown element in GLIF: %s" % element)
+ # set the collected unicodes
+ if unicodes:
+ _relaxedSetattr(glyphObject, "unicodes", unicodes)
+ # set the collected guidelines
+ if guidelines:
+ if validate and not guidelinesValidator(guidelines, identifiers):
+ raise GlifLibError("The guidelines are improperly formatted.")
+ _relaxedSetattr(glyphObject, "guidelines", guidelines)
+ # set the collected anchors
+ if anchors:
+ if validate and not anchorsValidator(anchors, identifiers):
+ raise GlifLibError("The anchors are improperly formatted.")
+ _relaxedSetattr(glyphObject, "anchors", anchors)
+
+
+_READ_GLYPH_FROM_TREE_FUNCS: dict[GLIFFormatVersion, Callable[..., Any]] = {
+ GLIFFormatVersion.FORMAT_1_0: _readGlyphFromTreeFormat1,
+ GLIFFormatVersion.FORMAT_2_0: _readGlyphFromTreeFormat2,
+}
+
+
+def _readName(glyphObject: Optional[Any], root: ElementType, validate: bool) -> None:
+ glyphName = root.get("name")
+ if validate and not glyphName:
+ raise GlifLibError("Empty glyph name in GLIF.")
+ if glyphName and glyphObject is not None:
+ _relaxedSetattr(glyphObject, "name", glyphName)
+
+
+def _readAdvance(glyphObject: Optional[Any], advance: ElementType) -> None:
+ width = _number(advance.get("width", 0))
+ _relaxedSetattr(glyphObject, "width", width)
+ height = _number(advance.get("height", 0))
+ _relaxedSetattr(glyphObject, "height", height)
+
+
+def _readNote(glyphObject: Optional[Any], note: ElementType) -> None:
+ if note.text is None:
+ return
+ lines = note.text.split("\n")
+ note = "\n".join(line.strip() for line in lines if line.strip())
+ _relaxedSetattr(glyphObject, "note", note)
+
+
+def _readLib(glyphObject: Optional[Any], lib: ElementType, validate: bool) -> None:
+ assert len(lib) == 1
+ child = lib[0]
+ plist = plistlib.fromtree(child)
+ if validate:
+ valid, message = glyphLibValidator(plist)
+ if not valid:
+ raise GlifLibError(message)
+ _relaxedSetattr(glyphObject, "lib", plist)
+
+
+def _readImage(glyphObject: Optional[Any], image: ElementType, validate: bool) -> None:
+ imageData = dict(image.attrib)
+ for attr, default in _transformationInfo:
+ value = imageData.get(attr, default)
+ imageData[attr] = _number(value)
+ if validate and not imageValidator(imageData):
+ raise GlifLibError("The image element is not properly formatted.")
+ _relaxedSetattr(glyphObject, "image", imageData)
+
+
+# ----------------
+# GLIF to PointPen
+# ----------------
+
+contourAttributesFormat2: set[str] = {"identifier"}
+componentAttributesFormat1: set[str] = {
+ "base",
+ "xScale",
+ "xyScale",
+ "yxScale",
+ "yScale",
+ "xOffset",
+ "yOffset",
+}
+componentAttributesFormat2: set[str] = componentAttributesFormat1 | {"identifier"}
+pointAttributesFormat1: set[str] = {"x", "y", "type", "smooth", "name"}
+pointAttributesFormat2: set[str] = pointAttributesFormat1 | {"identifier"}
+pointSmoothOptions: set[str] = {"no", "yes"}
+pointTypeOptions: set[str] = {"move", "line", "offcurve", "curve", "qcurve"}
+
+# format 1
+
+
+def buildOutlineFormat1(
+ glyphObject: Any,
+ pen: Optional[AbstractPointPen],
+ outline: Iterable[ElementType],
+ validate: bool,
+) -> None:
+ anchors = []
+ for element in outline:
+ if element.tag == "contour":
+ if len(element) == 1:
+ point = element[0]
+ if point.tag == "point":
+ anchor = _buildAnchorFormat1(point, validate)
+ if anchor is not None:
+ anchors.append(anchor)
+ continue
+ if pen is not None:
+ _buildOutlineContourFormat1(pen, element, validate)
+ elif element.tag == "component":
+ if pen is not None:
+ _buildOutlineComponentFormat1(pen, element, validate)
+ else:
+ raise GlifLibError("Unknown element in outline element: %s" % element)
+ if glyphObject is not None and anchors:
+ if validate and not anchorsValidator(anchors):
+ raise GlifLibError("GLIF 1 anchors are not properly formatted.")
+ _relaxedSetattr(glyphObject, "anchors", anchors)
+
+
+def _buildAnchorFormat1(point: ElementType, validate: bool) -> Optional[dict[str, Any]]:
+ if point.get("type") != "move":
+ return None
+ name = point.get("name")
+ if name is None:
+ return None
+ x = point.get("x")
+ y = point.get("y")
+ if validate and x is None:
+ raise GlifLibError("Required x attribute is missing in point element.")
+ assert x is not None
+ if validate and y is None:
+ raise GlifLibError("Required y attribute is missing in point element.")
+ assert y is not None
+ x = _number(x)
+ y = _number(y)
+ anchor = dict(x=x, y=y, name=name)
+ return anchor
+
+
+def _buildOutlineContourFormat1(
+ pen: AbstractPointPen, contour: ElementType, validate: bool
+) -> None:
+ if validate and contour.attrib:
+ raise GlifLibError("Unknown attributes in contour element.")
+ pen.beginPath()
+ if len(contour):
+ massaged = _validateAndMassagePointStructures(
+ contour,
+ pointAttributesFormat1,
+ openContourOffCurveLeniency=True,
+ validate=validate,
+ )
+ _buildOutlinePointsFormat1(pen, massaged)
+ pen.endPath()
+
+
+def _buildOutlinePointsFormat1(
+ pen: AbstractPointPen, contour: list[dict[str, Any]]
+) -> None:
+ for point in contour:
+ x = point["x"]
+ y = point["y"]
+ segmentType = point["segmentType"]
+ smooth = point["smooth"]
+ name = point["name"]
+ pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
+
+
+def _buildOutlineComponentFormat1(
+ pen: AbstractPointPen, component: ElementType, validate: bool
+) -> None:
+ if validate:
+ if len(component):
+ raise GlifLibError("Unknown child elements of component element.")
+ for attr in component.attrib.keys():
+ if attr not in componentAttributesFormat1:
+ raise GlifLibError("Unknown attribute in component element: %s" % attr)
+ baseGlyphName = component.get("base")
+ if validate and baseGlyphName is None:
+ raise GlifLibError("The base attribute is not defined in the component.")
+ assert baseGlyphName is not None
+ transformation = tuple(
+ _number(component.get(attr) or default) for attr, default in _transformationInfo
+ )
+ transformation = cast(
+ tuple[float, float, float, float, float, float], transformation
+ )
+ pen.addComponent(baseGlyphName, transformation)
+
+
+# format 2
+
+
+def buildOutlineFormat2(
+ glyphObject: Any,
+ pen: AbstractPointPen,
+ outline: Iterable[ElementType],
+ identifiers: set[str],
+ validate: bool,
+) -> None:
+ for element in outline:
+ if element.tag == "contour":
+ _buildOutlineContourFormat2(pen, element, identifiers, validate)
+ elif element.tag == "component":
+ _buildOutlineComponentFormat2(pen, element, identifiers, validate)
+ else:
+ raise GlifLibError("Unknown element in outline element: %s" % element.tag)
+
+
+def _buildOutlineContourFormat2(
+ pen: AbstractPointPen, contour: ElementType, identifiers: set[str], validate: bool
+) -> None:
+ if validate:
+ for attr in contour.attrib.keys():
+ if attr not in contourAttributesFormat2:
+ raise GlifLibError("Unknown attribute in contour element: %s" % attr)
+ identifier = contour.get("identifier")
+ if identifier is not None:
+ if validate:
+ if identifier in identifiers:
+ raise GlifLibError(
+ "The identifier %s is used more than once." % identifier
+ )
+ if not identifierValidator(identifier):
+ raise GlifLibError(
+ "The contour identifier %s is not valid." % identifier
+ )
+ identifiers.add(identifier)
+ try:
+ pen.beginPath(identifier=identifier)
+ except TypeError:
+ pen.beginPath()
+ warn(
+ "The beginPath method needs an identifier kwarg. The contour's identifier value has been discarded.",
+ DeprecationWarning,
+ )
+ if len(contour):
+ massaged = _validateAndMassagePointStructures(
+ contour, pointAttributesFormat2, validate=validate
+ )
+ _buildOutlinePointsFormat2(pen, massaged, identifiers, validate)
+ pen.endPath()
+
+
+def _buildOutlinePointsFormat2(
+ pen: AbstractPointPen,
+ contour: list[dict[str, Any]],
+ identifiers: set[str],
+ validate: bool,
+) -> None:
+ for point in contour:
+ x = point["x"]
+ y = point["y"]
+ segmentType = point["segmentType"]
+ smooth = point["smooth"]
+ name = point["name"]
+ identifier = point.get("identifier")
+ if identifier is not None:
+ if validate:
+ if identifier in identifiers:
+ raise GlifLibError(
+ "The identifier %s is used more than once." % identifier
+ )
+ if not identifierValidator(identifier):
+ raise GlifLibError("The identifier %s is not valid." % identifier)
+ identifiers.add(identifier)
+ try:
+ pen.addPoint(
+ (x, y),
+ segmentType=segmentType,
+ smooth=smooth,
+ name=name,
+ identifier=identifier,
+ )
+ except TypeError:
+ pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
+ warn(
+ "The addPoint method needs an identifier kwarg. The point's identifier value has been discarded.",
+ DeprecationWarning,
+ )
+
+
+def _buildOutlineComponentFormat2(
+ pen: AbstractPointPen, component: ElementType, identifiers: set[str], validate: bool
+) -> None:
+ if validate:
+ if len(component):
+ raise GlifLibError("Unknown child elements of component element.")
+ for attr in component.attrib.keys():
+ if attr not in componentAttributesFormat2:
+ raise GlifLibError("Unknown attribute in component element: %s" % attr)
+ baseGlyphName = component.get("base")
+ if validate and baseGlyphName is None:
+ raise GlifLibError("The base attribute is not defined in the component.")
+ assert baseGlyphName is not None
+ transformation = tuple(
+ _number(component.get(attr) or default) for attr, default in _transformationInfo
+ )
+ transformation = cast(
+ tuple[float, float, float, float, float, float], transformation
+ )
+ identifier = component.get("identifier")
+ if identifier is not None:
+ if validate:
+ if identifier in identifiers:
+ raise GlifLibError(
+ "The identifier %s is used more than once." % identifier
+ )
+ if validate and not identifierValidator(identifier):
+ raise GlifLibError("The identifier %s is not valid." % identifier)
+ identifiers.add(identifier)
+ try:
+ pen.addComponent(baseGlyphName, transformation, identifier=identifier)
+ except TypeError:
+ pen.addComponent(baseGlyphName, transformation)
+ warn(
+ "The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.",
+ DeprecationWarning,
+ )
+
+
+# all formats
+
+
+def _validateAndMassagePointStructures(
+ contour, pointAttributes, openContourOffCurveLeniency=False, validate=True
+):
+ if not len(contour):
+ return
+ # store some data for later validation
+ lastOnCurvePoint = None
+ haveOffCurvePoint = False
+ # validate and massage the individual point elements
+ massaged = []
+ for index, element in enumerate(contour):
+ # not
+ if element.tag != "point":
+ raise GlifLibError(
+ "Unknown child element (%s) of contour element." % element.tag
+ )
+ point = dict(element.attrib)
+ massaged.append(point)
+ if validate:
+ # unknown attributes
+ for attr in point.keys():
+ if attr not in pointAttributes:
+ raise GlifLibError("Unknown attribute in point element: %s" % attr)
+ # search for unknown children
+ if len(element):
+ raise GlifLibError("Unknown child elements in point element.")
+ # x and y are required
+ for attr in ("x", "y"):
+ try:
+ point[attr] = _number(point[attr])
+ except KeyError as e:
+ raise GlifLibError(
+ f"Required {attr} attribute is missing in point element."
+ ) from e
+ # segment type
+ pointType = point.pop("type", "offcurve")
+ if validate and pointType not in pointTypeOptions:
+ raise GlifLibError("Unknown point type: %s" % pointType)
+ if pointType == "offcurve":
+ pointType = None
+ point["segmentType"] = pointType
+ if pointType is None:
+ haveOffCurvePoint = True
+ else:
+ lastOnCurvePoint = index
+ # move can only occur as the first point
+ if validate and pointType == "move" and index != 0:
+ raise GlifLibError(
+ "A move point occurs after the first point in the contour."
+ )
+ # smooth is optional
+ smooth = point.get("smooth", "no")
+ if validate and smooth is not None:
+ if smooth not in pointSmoothOptions:
+ raise GlifLibError("Unknown point smooth value: %s" % smooth)
+ smooth = smooth == "yes"
+ point["smooth"] = smooth
+ # smooth can only be applied to curve and qcurve
+ if validate and smooth and pointType is None:
+ raise GlifLibError("smooth attribute set in an offcurve point.")
+ # name is optional
+ if "name" not in element.attrib:
+ point["name"] = None
+ if openContourOffCurveLeniency:
+ # remove offcurves that precede a move. this is technically illegal,
+ # but we let it slide because there are fonts out there in the wild like this.
+ if massaged[0]["segmentType"] == "move":
+ count = 0
+ for point in reversed(massaged):
+ if point["segmentType"] is None:
+ count += 1
+ else:
+ break
+ if count:
+ massaged = massaged[:-count]
+ # validate the off-curves in the segments
+ if validate and haveOffCurvePoint and lastOnCurvePoint is not None:
+ # we only care about how many offCurves there are before an onCurve
+ # filter out the trailing offCurves
+ offCurvesCount = len(massaged) - 1 - lastOnCurvePoint
+ for point in massaged:
+ segmentType = point["segmentType"]
+ if segmentType is None:
+ offCurvesCount += 1
+ else:
+ if offCurvesCount:
+ # move and line can't be preceded by off-curves
+ if segmentType == "move":
+ # this will have been filtered out already
+ raise GlifLibError("move can not have an offcurve.")
+ elif segmentType == "line":
+ raise GlifLibError("line can not have an offcurve.")
+ elif segmentType == "curve":
+ if offCurvesCount > 2:
+ raise GlifLibError("Too many offcurves defined for curve.")
+ elif segmentType == "qcurve":
+ pass
+ else:
+ # unknown segment type. it'll be caught later.
+ pass
+ offCurvesCount = 0
+ return massaged
+
+
+# ---------------------
+# Misc Helper Functions
+# ---------------------
+
+
+def _relaxedSetattr(object: Any, attr: str, value: Any) -> None:
+ try:
+ setattr(object, attr, value)
+ except AttributeError:
+ pass
+
+
+def _number(s: Union[str, int, float]) -> IntFloat:
+ """
+ Given a numeric string, return an integer or a float, whichever
+ the string indicates. _number("1") will return the integer 1,
+ _number("1.0") will return the float 1.0.
+
+ >>> _number("1")
+ 1
+ >>> _number("1.0")
+ 1.0
+ >>> _number("a") # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ GlifLibError: Could not convert a to an int or float.
+ """
+ try:
+ n: IntFloat = int(s)
+ return n
+ except ValueError:
+ pass
+ try:
+ n = float(s)
+ return n
+ except ValueError:
+ raise GlifLibError("Could not convert %s to an int or float." % s)
+
+
+# --------------------
+# Rapid Value Fetching
+# --------------------
+
+# base
+
+
+class _DoneParsing(Exception):
+ pass
+
+
+class _BaseParser:
+ def __init__(self) -> None:
+ self._elementStack: list[str] = []
+
+ def parse(self, text: bytes):
+ from xml.parsers.expat import ParserCreate
+
+ parser = ParserCreate()
+ parser.StartElementHandler = self.startElementHandler
+ parser.EndElementHandler = self.endElementHandler
+ parser.Parse(text, True)
+
+ def startElementHandler(self, name: str, attrs: Any) -> None:
+ self._elementStack.append(name)
+
+ def endElementHandler(self, name: str) -> None:
+ other = self._elementStack.pop(-1)
+ assert other == name
+
+
+# unicodes
+
+
+def _fetchUnicodes(glif: bytes) -> list[int]:
+ """
+ Get a list of unicodes listed in glif.
+ """
+ parser = _FetchUnicodesParser()
+ parser.parse(glif)
+ return parser.unicodes
+
+
+class _FetchUnicodesParser(_BaseParser):
+ def __init__(self) -> None:
+ self.unicodes: list[int] = []
+ super().__init__()
+
+ def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
+ if (
+ name == "unicode"
+ and self._elementStack
+ and self._elementStack[-1] == "glyph"
+ ):
+ value = attrs.get("hex")
+ if value is not None:
+ try:
+ intValue = int(value, 16)
+ if intValue not in self.unicodes:
+ self.unicodes.append(intValue)
+ except ValueError:
+ pass
+ super().startElementHandler(name, attrs)
+
+
+# image
+
+
+def _fetchImageFileName(glif: bytes) -> Optional[str]:
+ """
+ The image file name (if any) from glif.
+ """
+ parser = _FetchImageFileNameParser()
+ try:
+ parser.parse(glif)
+ except _DoneParsing:
+ pass
+ return parser.fileName
+
+
+class _FetchImageFileNameParser(_BaseParser):
+ def __init__(self) -> None:
+ self.fileName: Optional[str] = None
+ super().__init__()
+
+ def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
+ if name == "image" and self._elementStack and self._elementStack[-1] == "glyph":
+ self.fileName = attrs.get("fileName")
+ raise _DoneParsing
+ super().startElementHandler(name, attrs)
+
+
+# component references
+
+
+def _fetchComponentBases(glif: bytes) -> list[str]:
+ """
+ Get a list of component base glyphs listed in glif.
+ """
+ parser = _FetchComponentBasesParser()
+ try:
+ parser.parse(glif)
+ except _DoneParsing:
+ pass
+ return list(parser.bases)
+
+
+class _FetchComponentBasesParser(_BaseParser):
+ def __init__(self) -> None:
+ self.bases: list[str] = []
+ super().__init__()
+
+ def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
+ if (
+ name == "component"
+ and self._elementStack
+ and self._elementStack[-1] == "outline"
+ ):
+ base = attrs.get("base")
+ if base is not None:
+ self.bases.append(base)
+ super().startElementHandler(name, attrs)
+
+ def endElementHandler(self, name: str) -> None:
+ if name == "outline":
+ raise _DoneParsing
+ super().endElementHandler(name)
+
+
+# --------------
+# GLIF Point Pen
+# --------------
+
+_transformationInfo: list[tuple[str, int]] = [
+ # field name, default value
+ ("xScale", 1),
+ ("xyScale", 0),
+ ("yxScale", 0),
+ ("yScale", 1),
+ ("xOffset", 0),
+ ("yOffset", 0),
+]
+
+
+class GLIFPointPen(AbstractPointPen):
+ """
+ Helper class using the PointPen protocol to write the
+ part of .glif files.
+ """
+
+ def __init__(
+ self,
+ element: ElementType,
+ formatVersion: Optional[FormatVersion] = None,
+ identifiers: Optional[set[str]] = None,
+ validate: bool = True,
+ ) -> None:
+ if identifiers is None:
+ identifiers = set()
+ self.formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
+ self.identifiers = identifiers
+ self.outline = element
+ self.contour = None
+ self.prevOffCurveCount = 0
+ self.prevPointTypes: list[str] = []
+ self.validate = validate
+
+ def beginPath(self, identifier=None, **kwargs):
+ attrs = OrderedDict()
+ if identifier is not None and self.formatVersion.major >= 2:
+ if self.validate:
+ if identifier in self.identifiers:
+ raise GlifLibError(
+ "identifier used more than once: %s" % identifier
+ )
+ if not identifierValidator(identifier):
+ raise GlifLibError(
+ "identifier not formatted properly: %s" % identifier
+ )
+ attrs["identifier"] = identifier
+ self.identifiers.add(identifier)
+ self.contour = etree.SubElement(self.outline, "contour", attrs)
+ self.prevOffCurveCount = 0
+
+ def endPath(self):
+ if self.prevPointTypes and self.prevPointTypes[0] == "move":
+ if self.validate and self.prevPointTypes[-1] == "offcurve":
+ raise GlifLibError("open contour has loose offcurve point")
+ # prevent lxml from writing self-closing tags
+ if not len(self.contour):
+ self.contour.text = "\n "
+ self.contour = None
+ self.prevPointType = None
+ self.prevOffCurveCount = 0
+ self.prevPointTypes = []
+
+ def addPoint(
+ self, pt, segmentType=None, smooth=None, name=None, identifier=None, **kwargs
+ ):
+ attrs = OrderedDict()
+ # coordinates
+ if pt is not None:
+ if self.validate:
+ for coord in pt:
+ if not isinstance(coord, numberTypes):
+ raise GlifLibError("coordinates must be int or float")
+ attrs["x"] = repr(pt[0])
+ attrs["y"] = repr(pt[1])
+ # segment type
+ if segmentType == "offcurve":
+ segmentType = None
+ if self.validate:
+ if segmentType == "move" and self.prevPointTypes:
+ raise GlifLibError(
+ "move occurs after a point has already been added to the contour."
+ )
+ if (
+ segmentType in ("move", "line")
+ and self.prevPointTypes
+ and self.prevPointTypes[-1] == "offcurve"
+ ):
+ raise GlifLibError("offcurve occurs before %s point." % segmentType)
+ if segmentType == "curve" and self.prevOffCurveCount > 2:
+ raise GlifLibError("too many offcurve points before curve point.")
+ if segmentType is not None:
+ attrs["type"] = segmentType
+ else:
+ segmentType = "offcurve"
+ if segmentType == "offcurve":
+ self.prevOffCurveCount += 1
+ else:
+ self.prevOffCurveCount = 0
+ self.prevPointTypes.append(segmentType)
+ # smooth
+ if smooth:
+ if self.validate and segmentType == "offcurve":
+ raise GlifLibError("can't set smooth in an offcurve point.")
+ attrs["smooth"] = "yes"
+ # name
+ if name is not None:
+ attrs["name"] = name
+ # identifier
+ if identifier is not None and self.formatVersion.major >= 2:
+ if self.validate:
+ if identifier in self.identifiers:
+ raise GlifLibError(
+ "identifier used more than once: %s" % identifier
+ )
+ if not identifierValidator(identifier):
+ raise GlifLibError(
+ "identifier not formatted properly: %s" % identifier
+ )
+ attrs["identifier"] = identifier
+ self.identifiers.add(identifier)
+ etree.SubElement(self.contour, "point", attrs)
+
+ def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
+ attrs = OrderedDict([("base", glyphName)])
+ for (attr, default), value in zip(_transformationInfo, transformation):
+ if self.validate and not isinstance(value, numberTypes):
+ raise GlifLibError("transformation values must be int or float")
+ if value != default:
+ attrs[attr] = repr(value)
+ if identifier is not None and self.formatVersion.major >= 2:
+ if self.validate:
+ if identifier in self.identifiers:
+ raise GlifLibError(
+ "identifier used more than once: %s" % identifier
+ )
+ if self.validate and not identifierValidator(identifier):
+ raise GlifLibError(
+ "identifier not formatted properly: %s" % identifier
+ )
+ attrs["identifier"] = identifier
+ self.identifiers.add(identifier)
+ etree.SubElement(self.outline, "component", attrs)
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/kerning.py b/lib/python3.12/site-packages/fontTools/ufoLib/kerning.py
new file mode 100644
index 0000000000000000000000000000000000000000..01ae55c062cad4ef8e6f7c0a400c51f17cb29d58
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/kerning.py
@@ -0,0 +1,141 @@
+from __future__ import annotations
+
+from typing import Optional
+
+from fontTools.annotations import KerningPair, KerningDict, KerningGroups, IntFloat
+
+StrDict = dict[str, str]
+
+
+def lookupKerningValue(
+ pair: KerningPair,
+ kerning: KerningDict,
+ groups: KerningGroups,
+ fallback: IntFloat = 0,
+ glyphToFirstGroup: Optional[StrDict] = None,
+ glyphToSecondGroup: Optional[StrDict] = None,
+) -> IntFloat:
+ """Retrieve the kerning value (if any) between a pair of elements.
+
+ The elments can be either individual glyphs (by name) or kerning
+ groups (by name), or any combination of the two.
+
+ Args:
+ pair:
+ A tuple, in logical order (first, second) with respect
+ to the reading direction, to query the font for kerning
+ information on. Each element in the tuple can be either
+ a glyph name or a kerning group name.
+ kerning:
+ A dictionary of kerning pairs.
+ groups:
+ A set of kerning groups.
+ fallback:
+ The fallback value to return if no kern is found between
+ the elements in ``pair``. Defaults to 0.
+ glyphToFirstGroup:
+ A dictionary mapping glyph names to the first-glyph kerning
+ groups to which they belong. Defaults to ``None``.
+ glyphToSecondGroup:
+ A dictionary mapping glyph names to the second-glyph kerning
+ groups to which they belong. Defaults to ``None``.
+
+ Returns:
+ The kerning value between the element pair. If no kerning for
+ the pair is found, the fallback value is returned.
+
+ Note: This function expects the ``kerning`` argument to be a flat
+ dictionary of kerning pairs, not the nested structure used in a
+ kerning.plist file.
+
+ Examples::
+
+ >>> groups = {
+ ... "public.kern1.O" : ["O", "D", "Q"],
+ ... "public.kern2.E" : ["E", "F"]
+ ... }
+ >>> kerning = {
+ ... ("public.kern1.O", "public.kern2.E") : -100,
+ ... ("public.kern1.O", "F") : -200,
+ ... ("D", "F") : -300
+ ... }
+ >>> lookupKerningValue(("D", "F"), kerning, groups)
+ -300
+ >>> lookupKerningValue(("O", "F"), kerning, groups)
+ -200
+ >>> lookupKerningValue(("O", "E"), kerning, groups)
+ -100
+ >>> lookupKerningValue(("O", "O"), kerning, groups)
+ 0
+ >>> lookupKerningValue(("E", "E"), kerning, groups)
+ 0
+ >>> lookupKerningValue(("E", "O"), kerning, groups)
+ 0
+ >>> lookupKerningValue(("X", "X"), kerning, groups)
+ 0
+ >>> lookupKerningValue(("public.kern1.O", "public.kern2.E"),
+ ... kerning, groups)
+ -100
+ >>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups)
+ -200
+ >>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups)
+ -100
+ >>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups)
+ 0
+ """
+ # quickly check to see if the pair is in the kerning dictionary
+ if pair in kerning:
+ return kerning[pair]
+ # ensure both or no glyph-to-group mappings are provided
+ if (glyphToFirstGroup is None) != (glyphToSecondGroup is None):
+ raise ValueError(
+ "Must provide both 'glyphToFirstGroup' and 'glyphToSecondGroup', or neither."
+ )
+ # create glyph to group mapping
+ if glyphToFirstGroup is None:
+ glyphToFirstGroup = {}
+ glyphToSecondGroup = {}
+ for group, groupMembers in groups.items():
+ if group.startswith("public.kern1."):
+ for glyph in groupMembers:
+ glyphToFirstGroup[glyph] = group
+ elif group.startswith("public.kern2."):
+ for glyph in groupMembers:
+ glyphToSecondGroup[glyph] = group
+ # ensure type safety for mappings
+ assert glyphToFirstGroup is not None
+ assert glyphToSecondGroup is not None
+ # get group names and make sure first and second are glyph names
+ first, second = pair
+ firstGroup = secondGroup = None
+ if first.startswith("public.kern1."):
+ firstGroup = first
+ firstGlyph = None
+ else:
+ firstGroup = glyphToFirstGroup.get(first)
+ firstGlyph = first
+ if second.startswith("public.kern2."):
+ secondGroup = second
+ secondGlyph = None
+ else:
+ secondGroup = glyphToSecondGroup.get(second)
+ secondGlyph = second
+ # make an ordered list of pairs to look up
+ pairs = [
+ (a, b)
+ for a in (firstGlyph, firstGroup)
+ for b in (secondGlyph, secondGroup)
+ if a is not None and b is not None
+ ]
+ # look up the pairs and return any matches
+ for pair in pairs:
+ if pair in kerning:
+ return kerning[pair]
+ # use the fallback value
+ return fallback
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/plistlib.py b/lib/python3.12/site-packages/fontTools/ufoLib/plistlib.py
new file mode 100644
index 0000000000000000000000000000000000000000..0242e9d26fce6b5ec4def58ac6aee44a5738db98
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/plistlib.py
@@ -0,0 +1,47 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old `ufoLib.plistlib` module, which was moved to :class:`fontTools.misc.plistlib`.
+Please use the latter instead.
+"""
+
+from fontTools.misc.plistlib import dump, dumps, load, loads
+from fontTools.misc.textTools import tobytes
+
+# The following functions were part of the old py2-like ufoLib.plistlib API.
+# They are kept only for backward compatiblity.
+from fontTools.ufoLib.utils import deprecated
+
+
+@deprecated("Use 'fontTools.misc.plistlib.load' instead")
+def readPlist(path_or_file):
+ did_open = False
+ if isinstance(path_or_file, str):
+ path_or_file = open(path_or_file, "rb")
+ did_open = True
+ try:
+ return load(path_or_file, use_builtin_types=False)
+ finally:
+ if did_open:
+ path_or_file.close()
+
+
+@deprecated("Use 'fontTools.misc.plistlib.dump' instead")
+def writePlist(value, path_or_file):
+ did_open = False
+ if isinstance(path_or_file, str):
+ path_or_file = open(path_or_file, "wb")
+ did_open = True
+ try:
+ dump(value, path_or_file, use_builtin_types=False)
+ finally:
+ if did_open:
+ path_or_file.close()
+
+
+@deprecated("Use 'fontTools.misc.plistlib.loads' instead")
+def readPlistFromString(data):
+ return loads(tobytes(data, encoding="utf-8"), use_builtin_types=False)
+
+
+@deprecated("Use 'fontTools.misc.plistlib.dumps' instead")
+def writePlistToString(value):
+ return dumps(value, use_builtin_types=False)
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/pointPen.py b/lib/python3.12/site-packages/fontTools/ufoLib/pointPen.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a8126cd6481a39b5bfeec7348c8efd168118236
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/pointPen.py
@@ -0,0 +1,6 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old `ufoLib.pointPen` module, which was moved to :class:`fontTools.pens.pointPen`.
+Please use the latter instead.
+"""
+
+from fontTools.pens.pointPen import *
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/utils.py b/lib/python3.12/site-packages/fontTools/ufoLib/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..3930d8931da14db0815d885028304d3905c0d680
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/utils.py
@@ -0,0 +1,107 @@
+"""This module contains miscellaneous helpers.
+
+It is not considered part of the public ufoLib API. It does, however,
+define the :py:obj:`.deprecated` decorator that is used elsewhere in
+the module.
+"""
+
+from __future__ import annotations
+
+from typing import Optional, Type, TypeVar, Union, cast
+from collections.abc import Callable
+import enum
+import functools
+import warnings
+
+F = TypeVar("F", bound=Callable[..., object])
+FormatVersion = TypeVar("FormatVersion", bound="BaseFormatVersion")
+FormatVersionInput = Optional[Union[int, tuple[int, int], FormatVersion]]
+
+numberTypes = (int, float)
+
+
+def deprecated(msg: str = "") -> Callable[[F], F]:
+ """Decorator factory to mark functions as deprecated with given message.
+
+ >>> @deprecated("Enough!")
+ ... def some_function():
+ ... "I just print 'hello world'."
+ ... print("hello world")
+ >>> some_function()
+ hello world
+ >>> some_function.__doc__ == "I just print 'hello world'."
+ True
+ """
+
+ def deprecated_decorator(func: F) -> F:
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ warnings.warn(
+ f"{func.__name__} function is a deprecated. {msg}",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+ return func(*args, **kwargs)
+
+ return cast(F, wrapper)
+
+ return deprecated_decorator
+
+
+def normalizeFormatVersion(
+ value: FormatVersionInput, cls: Type[FormatVersion]
+) -> FormatVersion:
+ # Needed for type safety of UFOFormatVersion and GLIFFormatVersion input
+ if value is None:
+ return cls.default()
+ if isinstance(value, cls):
+ return value
+ if isinstance(value, int):
+ return cls((value, 0))
+ if isinstance(value, tuple) and len(value) == 2:
+ return cls(value)
+ raise ValueError(f"Unsupported format version: {value!r}")
+
+
+# Base class for UFOFormatVersion and GLIFFormatVersion
+class BaseFormatVersion(tuple[int, int], enum.Enum):
+ value: tuple[int, int]
+
+ def __new__(cls: Type[FormatVersion], value: tuple[int, int]) -> BaseFormatVersion:
+ return super().__new__(cls, value)
+
+ @property
+ def major(self) -> int:
+ return self.value[0]
+
+ @property
+ def minor(self) -> int:
+ return self.value[1]
+
+ @classmethod
+ def _missing_(cls, value: object) -> BaseFormatVersion:
+ # allow to initialize a version enum from a single (major) integer
+ if isinstance(value, int):
+ return cls((value, 0))
+ # or from None to obtain the current default version
+ if value is None:
+ return cls.default()
+ raise ValueError(f"{value!r} is not a valid {cls.__name__}")
+
+ def __str__(self) -> str:
+ return f"{self.major}.{self.minor}"
+
+ @classmethod
+ def default(cls: Type[FormatVersion]) -> FormatVersion:
+ # get the latest defined version (i.e. the max of all versions)
+ return max(cls.__members__.values())
+
+ @classmethod
+ def supported_versions(cls: Type[FormatVersion]) -> frozenset[FormatVersion]:
+ return frozenset(cls.__members__.values())
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
diff --git a/lib/python3.12/site-packages/fontTools/ufoLib/validators.py b/lib/python3.12/site-packages/fontTools/ufoLib/validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..54c65fb6cf6da81c98986a45b21102f8e476a8d6
--- /dev/null
+++ b/lib/python3.12/site-packages/fontTools/ufoLib/validators.py
@@ -0,0 +1,1208 @@
+"""Various low level data validators."""
+
+from __future__ import annotations
+
+import calendar
+from collections.abc import Mapping, Sequence
+from io import open
+
+import fontTools.misc.filesystem as fs
+from typing import Any, Type, Optional, Union
+
+from fontTools.annotations import IntFloat
+from fontTools.ufoLib.utils import numberTypes
+
+GenericDict = dict[str, tuple[Union[type, tuple[Type[Any], ...]], bool]]
+
+# -------
+# Generic
+# -------
+
+
+def isDictEnough(value: Any) -> bool:
+ """
+ Some objects will likely come in that aren't
+ dicts but are dict-ish enough.
+ """
+ if isinstance(value, Mapping):
+ return True
+ for attr in ("keys", "values", "items"):
+ if not hasattr(value, attr):
+ return False
+ return True
+
+
+def genericTypeValidator(value: Any, typ: Type[Any]) -> bool:
+ """
+ Generic. (Added at version 2.)
+ """
+ return isinstance(value, typ)
+
+
+def genericIntListValidator(values: Any, validValues: Sequence[int]) -> bool:
+ """
+ Generic. (Added at version 2.)
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ valuesSet = set(values)
+ validValuesSet = set(validValues)
+ if valuesSet - validValuesSet:
+ return False
+ for value in values:
+ if not isinstance(value, int):
+ return False
+ return True
+
+
+def genericNonNegativeIntValidator(value: Any) -> bool:
+ """
+ Generic. (Added at version 3.)
+ """
+ if not isinstance(value, int):
+ return False
+ if value < 0:
+ return False
+ return True
+
+
+def genericNonNegativeNumberValidator(value: Any) -> bool:
+ """
+ Generic. (Added at version 3.)
+ """
+ if not isinstance(value, numberTypes):
+ return False
+ if value < 0:
+ return False
+ return True
+
+
+def genericDictValidator(value: Any, prototype: GenericDict) -> bool:
+ """
+ Generic. (Added at version 3.)
+ """
+ # not a dict
+ if not isinstance(value, Mapping):
+ return False
+ # missing required keys
+ for key, (typ, required) in prototype.items():
+ if not required:
+ continue
+ if key not in value:
+ return False
+ # unknown keys
+ for key in value.keys():
+ if key not in prototype:
+ return False
+ # incorrect types
+ for key, v in value.items():
+ prototypeType, required = prototype[key]
+ if v is None and not required:
+ continue
+ if not isinstance(v, prototypeType):
+ return False
+ return True
+
+
+# --------------
+# fontinfo.plist
+# --------------
+
+# Data Validators
+
+
+def fontInfoStyleMapStyleNameValidator(value: Any) -> bool:
+ """
+ Version 2+.
+ """
+ options = ["regular", "italic", "bold", "bold italic"]
+ return value in options
+
+
+def fontInfoOpenTypeGaspRangeRecordsValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(value, list):
+ return False
+ if len(value) == 0:
+ return True
+ validBehaviors = [0, 1, 2, 3]
+ dictPrototype: GenericDict = dict(
+ rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True)
+ )
+ ppemOrder = []
+ for rangeRecord in value:
+ if not genericDictValidator(rangeRecord, dictPrototype):
+ return False
+ ppem = rangeRecord["rangeMaxPPEM"]
+ behavior = rangeRecord["rangeGaspBehavior"]
+ ppemValidity = genericNonNegativeIntValidator(ppem)
+ if not ppemValidity:
+ return False
+ behaviorValidity = genericIntListValidator(behavior, validBehaviors)
+ if not behaviorValidity:
+ return False
+ ppemOrder.append(ppem)
+ if ppemOrder != sorted(ppemOrder):
+ return False
+ return True
+
+
+def fontInfoOpenTypeHeadCreatedValidator(value: Any) -> bool:
+ """
+ Version 2+.
+ """
+ # format: 0000/00/00 00:00:00
+ if not isinstance(value, str):
+ return False
+ # basic formatting
+ if not len(value) == 19:
+ return False
+ if value.count(" ") != 1:
+ return False
+ strDate, strTime = value.split(" ")
+ if strDate.count("/") != 2:
+ return False
+ if strTime.count(":") != 2:
+ return False
+ # date
+ strYear, strMonth, strDay = strDate.split("/")
+ if len(strYear) != 4:
+ return False
+ if len(strMonth) != 2:
+ return False
+ if len(strDay) != 2:
+ return False
+ try:
+ intYear = int(strYear)
+ intMonth = int(strMonth)
+ intDay = int(strDay)
+ except ValueError:
+ return False
+ if intMonth < 1 or intMonth > 12:
+ return False
+ monthMaxDay = calendar.monthrange(intYear, intMonth)[1]
+ if intDay < 1 or intDay > monthMaxDay:
+ return False
+ # time
+ strHour, strMinute, strSecond = strTime.split(":")
+ if len(strHour) != 2:
+ return False
+ if len(strMinute) != 2:
+ return False
+ if len(strSecond) != 2:
+ return False
+ try:
+ intHour = int(strHour)
+ intMinute = int(strMinute)
+ intSecond = int(strSecond)
+ except ValueError:
+ return False
+ if intHour < 0 or intHour > 23:
+ return False
+ if intMinute < 0 or intMinute > 59:
+ return False
+ if intSecond < 0 or intSecond > 59:
+ return False
+ # fallback
+ return True
+
+
+def fontInfoOpenTypeNameRecordsValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(value, list):
+ return False
+ dictPrototype: GenericDict = dict(
+ nameID=(int, True),
+ platformID=(int, True),
+ encodingID=(int, True),
+ languageID=(int, True),
+ string=(str, True),
+ )
+ for nameRecord in value:
+ if not genericDictValidator(nameRecord, dictPrototype):
+ return False
+ return True
+
+
+def fontInfoOpenTypeOS2WeightClassValidator(value: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(value, int):
+ return False
+ if value < 0:
+ return False
+ return True
+
+
+def fontInfoOpenTypeOS2WidthClassValidator(value: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(value, int):
+ return False
+ if value < 1:
+ return False
+ if value > 9:
+ return False
+ return True
+
+
+def fontInfoVersion2OpenTypeOS2PanoseValidator(values: Any) -> bool:
+ """
+ Version 2.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) != 10:
+ return False
+ for value in values:
+ if not isinstance(value, int):
+ return False
+ # XXX further validation?
+ return True
+
+
+def fontInfoVersion3OpenTypeOS2PanoseValidator(values: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) != 10:
+ return False
+ for value in values:
+ if not isinstance(value, int):
+ return False
+ if value < 0:
+ return False
+ # XXX further validation?
+ return True
+
+
+def fontInfoOpenTypeOS2FamilyClassValidator(values: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) != 2:
+ return False
+ for value in values:
+ if not isinstance(value, int):
+ return False
+ classID, subclassID = values
+ if classID < 0 or classID > 14:
+ return False
+ if subclassID < 0 or subclassID > 15:
+ return False
+ return True
+
+
+def fontInfoPostscriptBluesValidator(values: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) > 14:
+ return False
+ if len(values) % 2:
+ return False
+ for value in values:
+ if not isinstance(value, numberTypes):
+ return False
+ return True
+
+
+def fontInfoPostscriptOtherBluesValidator(values: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) > 10:
+ return False
+ if len(values) % 2:
+ return False
+ for value in values:
+ if not isinstance(value, numberTypes):
+ return False
+ return True
+
+
+def fontInfoPostscriptStemsValidator(values: Any) -> bool:
+ """
+ Version 2+.
+ """
+ if not isinstance(values, (list, tuple)):
+ return False
+ if len(values) > 12:
+ return False
+ for value in values:
+ if not isinstance(value, numberTypes):
+ return False
+ return True
+
+
+def fontInfoPostscriptWindowsCharacterSetValidator(value: Any) -> bool:
+ """
+ Version 2+.
+ """
+ validValues = list(range(1, 21))
+ if value not in validValues:
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataUniqueIDValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(id=(str, True))
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataVendorValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = {
+ "name": (str, True),
+ "url": (str, False),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataCreditsValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(credits=(list, True))
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if not len(value["credits"]):
+ return False
+ dictPrototype = {
+ "name": (str, True),
+ "url": (str, False),
+ "role": (str, False),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ for credit in value["credits"]:
+ if not genericDictValidator(credit, dictPrototype):
+ return False
+ if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataDescriptionValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(url=(str, False), text=(list, True))
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ for text in value["text"]:
+ if not fontInfoWOFFMetadataTextValue(text):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataLicenseValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(
+ url=(str, False), text=(list, False), id=(str, False)
+ )
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "text" in value:
+ for text in value["text"]:
+ if not fontInfoWOFFMetadataTextValue(text):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataTrademarkValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(text=(list, True))
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ for text in value["text"]:
+ if not fontInfoWOFFMetadataTextValue(text):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataCopyrightValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(text=(list, True))
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ for text in value["text"]:
+ if not fontInfoWOFFMetadataTextValue(text):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataLicenseeValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = {
+ "name": (str, True),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataTextValue(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = {
+ "text": (str, True),
+ "language": (str, False),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataExtensionsValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(value, list):
+ return False
+ if not value:
+ return False
+ for extension in value:
+ if not fontInfoWOFFMetadataExtensionValidator(extension):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataExtensionValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(
+ names=(list, False), items=(list, True), id=(str, False)
+ )
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "names" in value:
+ for name in value["names"]:
+ if not fontInfoWOFFMetadataExtensionNameValidator(name):
+ return False
+ for item in value["items"]:
+ if not fontInfoWOFFMetadataExtensionItemValidator(item):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataExtensionItemValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = dict(
+ id=(str, False), names=(list, True), values=(list, True)
+ )
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ for name in value["names"]:
+ if not fontInfoWOFFMetadataExtensionNameValidator(name):
+ return False
+ for val in value["values"]:
+ if not fontInfoWOFFMetadataExtensionValueValidator(val):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataExtensionNameValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = {
+ "text": (str, True),
+ "language": (str, False),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+def fontInfoWOFFMetadataExtensionValueValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ dictPrototype: GenericDict = {
+ "text": (str, True),
+ "language": (str, False),
+ "dir": (str, False),
+ "class": (str, False),
+ }
+ if not genericDictValidator(value, dictPrototype):
+ return False
+ if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+ return False
+ return True
+
+
+# ----------
+# Guidelines
+# ----------
+
+
+def guidelinesValidator(value: Any, identifiers: Optional[set[str]] = None) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(value, list):
+ return False
+ if identifiers is None:
+ identifiers = set()
+ for guide in value:
+ if not guidelineValidator(guide):
+ return False
+ identifier = guide.get("identifier")
+ if identifier is not None:
+ if identifier in identifiers:
+ return False
+ identifiers.add(identifier)
+ return True
+
+
+_guidelineDictPrototype: GenericDict = dict(
+ x=((int, float), False),
+ y=((int, float), False),
+ angle=((int, float), False),
+ name=(str, False),
+ color=(str, False),
+ identifier=(str, False),
+)
+
+
+def guidelineValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not genericDictValidator(value, _guidelineDictPrototype):
+ return False
+ x = value.get("x")
+ y = value.get("y")
+ angle = value.get("angle")
+ # x or y must be present
+ if x is None and y is None:
+ return False
+ # if x or y are None, angle must not be present
+ if x is None or y is None:
+ if angle is not None:
+ return False
+ # if x and y are defined, angle must be defined
+ if x is not None and y is not None and angle is None:
+ return False
+ # angle must be between 0 and 360
+ if angle is not None:
+ if angle < 0:
+ return False
+ if angle > 360:
+ return False
+ # identifier must be 1 or more characters
+ identifier = value.get("identifier")
+ if identifier is not None and not identifierValidator(identifier):
+ return False
+ # color must follow the proper format
+ color = value.get("color")
+ if color is not None and not colorValidator(color):
+ return False
+ return True
+
+
+# -------
+# Anchors
+# -------
+
+
+def anchorsValidator(value: Any, identifiers: Optional[set[str]] = None) -> bool:
+ """
+ Version 3+.
+ """
+ if not isinstance(value, list):
+ return False
+ if identifiers is None:
+ identifiers = set()
+ for anchor in value:
+ if not anchorValidator(anchor):
+ return False
+ identifier = anchor.get("identifier")
+ if identifier is not None:
+ if identifier in identifiers:
+ return False
+ identifiers.add(identifier)
+ return True
+
+
+_anchorDictPrototype: GenericDict = dict(
+ x=((int, float), False),
+ y=((int, float), False),
+ name=(str, False),
+ color=(str, False),
+ identifier=(str, False),
+)
+
+
+def anchorValidator(value: Any) -> bool:
+ """
+ Version 3+.
+ """
+ if not genericDictValidator(value, _anchorDictPrototype):
+ return False
+ x = value.get("x")
+ y = value.get("y")
+ # x and y must be present
+ if x is None or y is None:
+ return False
+ # identifier must be 1 or more characters
+ identifier = value.get("identifier")
+ if identifier is not None and not identifierValidator(identifier):
+ return False
+ # color must follow the proper format
+ color = value.get("color")
+ if color is not None and not colorValidator(color):
+ return False
+ return True
+
+
+# ----------
+# Identifier
+# ----------
+
+
+def identifierValidator(value: Any) -> bool:
+ """
+ Version 3+.
+
+ >>> identifierValidator("a")
+ True
+ >>> identifierValidator("")
+ False
+ >>> identifierValidator("a" * 101)
+ False
+ """
+ validCharactersMin = 0x20
+ validCharactersMax = 0x7E
+ if not isinstance(value, str):
+ return False
+ if not value:
+ return False
+ if len(value) > 100:
+ return False
+ for c in value:
+ i = ord(c)
+ if i < validCharactersMin or i > validCharactersMax:
+ return False
+ return True
+
+
+# -----
+# Color
+# -----
+
+
+def colorValidator(value: Any) -> bool:
+ """
+ Version 3+.
+
+ >>> colorValidator("0,0,0,0")
+ True
+ >>> colorValidator(".5,.5,.5,.5")
+ True
+ >>> colorValidator("0.5,0.5,0.5,0.5")
+ True
+ >>> colorValidator("1,1,1,1")
+ True
+
+ >>> colorValidator("2,0,0,0")
+ False
+ >>> colorValidator("0,2,0,0")
+ False
+ >>> colorValidator("0,0,2,0")
+ False
+ >>> colorValidator("0,0,0,2")
+ False
+
+ >>> colorValidator("1r,1,1,1")
+ False
+ >>> colorValidator("1,1g,1,1")
+ False
+ >>> colorValidator("1,1,1b,1")
+ False
+ >>> colorValidator("1,1,1,1a")
+ False
+
+ >>> colorValidator("1 1 1 1")
+ False
+ >>> colorValidator("1 1,1,1")
+ False
+ >>> colorValidator("1,1 1,1")
+ False
+ >>> colorValidator("1,1,1 1")
+ False
+
+ >>> colorValidator("1, 1, 1, 1")
+ True
+ """
+ if not isinstance(value, str):
+ return False
+ parts = value.split(",")
+ if len(parts) != 4:
+ return False
+ for part in parts:
+ part = part.strip()
+ converted = False
+ number: IntFloat
+ try:
+ number = int(part)
+ converted = True
+ except ValueError:
+ pass
+ if not converted:
+ try:
+ number = float(part)
+ converted = True
+ except ValueError:
+ pass
+ if not converted:
+ return False
+ if not 0 <= number <= 1:
+ return False
+ return True
+
+
+# -----
+# image
+# -----
+
+pngSignature: bytes = b"\x89PNG\r\n\x1a\n"
+
+_imageDictPrototype: GenericDict = dict(
+ fileName=(str, True),
+ xScale=((int, float), False),
+ xyScale=((int, float), False),
+ yxScale=((int, float), False),
+ yScale=((int, float), False),
+ xOffset=((int, float), False),
+ yOffset=((int, float), False),
+ color=(str, False),
+)
+
+
+def imageValidator(value):
+ """
+ Version 3+.
+ """
+ if not genericDictValidator(value, _imageDictPrototype):
+ return False
+ # fileName must be one or more characters
+ if not value["fileName"]:
+ return False
+ # color must follow the proper format
+ color = value.get("color")
+ if color is not None and not colorValidator(color):
+ return False
+ return True
+
+
+def pngValidator(
+ path: Optional[str] = None,
+ data: Optional[bytes] = None,
+ fileObj: Optional[Any] = None,
+) -> tuple[bool, Any]:
+ """
+ Version 3+.
+
+ This checks the signature of the image data.
+ """
+ assert path is not None or data is not None or fileObj is not None
+ if path is not None:
+ with open(path, "rb") as f:
+ signature = f.read(8)
+ elif data is not None:
+ signature = data[:8]
+ elif fileObj is not None:
+ pos = fileObj.tell()
+ signature = fileObj.read(8)
+ fileObj.seek(pos)
+ if signature != pngSignature:
+ return False, "Image does not begin with the PNG signature."
+ return True, None
+
+
+# -------------------
+# layercontents.plist
+# -------------------
+
+
+def layerContentsValidator(
+ value: Any, ufoPathOrFileSystem: Union[str, fs.base.FS]
+) -> tuple[bool, Optional[str]]:
+ """
+ Check the validity of layercontents.plist.
+ Version 3+.
+ """
+ if isinstance(ufoPathOrFileSystem, fs.base.FS):
+ fileSystem = ufoPathOrFileSystem
+ else:
+ fileSystem = fs.osfs.OSFS(ufoPathOrFileSystem)
+
+ bogusFileMessage = "layercontents.plist in not in the correct format."
+ # file isn't in the right format
+ if not isinstance(value, list):
+ return False, bogusFileMessage
+ # work through each entry
+ usedLayerNames = set()
+ usedDirectories = set()
+ contents = {}
+ for entry in value:
+ # layer entry in the incorrect format
+ if not isinstance(entry, list):
+ return False, bogusFileMessage
+ if not len(entry) == 2:
+ return False, bogusFileMessage
+ for i in entry:
+ if not isinstance(i, str):
+ return False, bogusFileMessage
+ layerName, directoryName = entry
+ # check directory naming
+ if directoryName != "glyphs":
+ if not directoryName.startswith("glyphs."):
+ return (
+ False,
+ "Invalid directory name (%s) in layercontents.plist."
+ % directoryName,
+ )
+ if len(layerName) == 0:
+ return False, "Empty layer name in layercontents.plist."
+ # directory doesn't exist
+ if not fileSystem.exists(directoryName):
+ return False, "A glyphset does not exist at %s." % directoryName
+ # default layer name
+ if layerName == "public.default" and directoryName != "glyphs":
+ return (
+ False,
+ "The name public.default is being used by a layer that is not the default.",
+ )
+ # check usage
+ if layerName in usedLayerNames:
+ return (
+ False,
+ "The layer name %s is used by more than one layer." % layerName,
+ )
+ usedLayerNames.add(layerName)
+ if directoryName in usedDirectories:
+ return (
+ False,
+ "The directory %s is used by more than one layer." % directoryName,
+ )
+ usedDirectories.add(directoryName)
+ # store
+ contents[layerName] = directoryName
+ # missing default layer
+ foundDefault = "glyphs" in contents.values()
+ if not foundDefault:
+ return False, "The required default glyph set is not in the UFO."
+ return True, None
+
+
+# ------------
+# groups.plist
+# ------------
+
+
+def groupsValidator(value: Any) -> tuple[bool, Optional[str]]:
+ """
+ Check the validity of the groups.
+ Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+ >>> groups = {"A" : ["A", "A"], "A2" : ["A"]}
+ >>> groupsValidator(groups)
+ (True, None)
+
+ >>> groups = {"" : ["A"]}
+ >>> valid, msg = groupsValidator(groups)
+ >>> valid
+ False
+ >>> print(msg)
+ A group has an empty name.
+
+ >>> groups = {"public.awesome" : ["A"]}
+ >>> groupsValidator(groups)
+ (True, None)
+
+ >>> groups = {"public.kern1." : ["A"]}
+ >>> valid, msg = groupsValidator(groups)
+ >>> valid
+ False
+ >>> print(msg)
+ The group data contains a kerning group with an incomplete name.
+ >>> groups = {"public.kern2." : ["A"]}
+ >>> valid, msg = groupsValidator(groups)
+ >>> valid
+ False
+ >>> print(msg)
+ The group data contains a kerning group with an incomplete name.
+
+ >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]}
+ >>> groupsValidator(groups)
+ (True, None)
+
+ >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]}
+ >>> valid, msg = groupsValidator(groups)
+ >>> valid
+ False
+ >>> print(msg)
+ The glyph "A" occurs in too many kerning groups.
+ """
+ bogusFormatMessage = "The group data is not in the correct format."
+ if not isDictEnough(value):
+ return False, bogusFormatMessage
+ firstSideMapping: dict[str, str] = {}
+ secondSideMapping: dict[str, str] = {}
+ for groupName, glyphList in value.items():
+ if not isinstance(groupName, (str)):
+ return False, bogusFormatMessage
+ if not isinstance(glyphList, (list, tuple)):
+ return False, bogusFormatMessage
+ if not groupName:
+ return False, "A group has an empty name."
+ if groupName.startswith("public."):
+ if not groupName.startswith("public.kern1.") and not groupName.startswith(
+ "public.kern2."
+ ):
+ # unknown public.* name. silently skip.
+ continue
+ else:
+ if len("public.kernN.") == len(groupName):
+ return (
+ False,
+ "The group data contains a kerning group with an incomplete name.",
+ )
+ if groupName.startswith("public.kern1."):
+ d = firstSideMapping
+ else:
+ d = secondSideMapping
+ for glyphName in glyphList:
+ if not isinstance(glyphName, str):
+ return (
+ False,
+ "The group data %s contains an invalid member." % groupName,
+ )
+ if glyphName in d:
+ return (
+ False,
+ 'The glyph "%s" occurs in too many kerning groups.' % glyphName,
+ )
+ d[glyphName] = groupName
+ return True, None
+
+
+# -------------
+# kerning.plist
+# -------------
+
+
+def kerningValidator(data: Any) -> tuple[bool, Optional[str]]:
+ """
+ Check the validity of the kerning data structure.
+ Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+ >>> kerning = {"A" : {"B" : 100}}
+ >>> kerningValidator(kerning)
+ (True, None)
+
+ >>> kerning = {"A" : ["B"]}
+ >>> valid, msg = kerningValidator(kerning)
+ >>> valid
+ False
+ >>> print(msg)
+ The kerning data is not in the correct format.
+
+ >>> kerning = {"A" : {"B" : "100"}}
+ >>> valid, msg = kerningValidator(kerning)
+ >>> valid
+ False
+ >>> print(msg)
+ The kerning data is not in the correct format.
+ """
+ bogusFormatMessage = "The kerning data is not in the correct format."
+ if not isinstance(data, Mapping):
+ return False, bogusFormatMessage
+ for first, secondDict in data.items():
+ if not isinstance(first, str):
+ return False, bogusFormatMessage
+ elif not isinstance(secondDict, Mapping):
+ return False, bogusFormatMessage
+ for second, value in secondDict.items():
+ if not isinstance(second, str):
+ return False, bogusFormatMessage
+ elif not isinstance(value, numberTypes):
+ return False, bogusFormatMessage
+ return True, None
+
+
+# -------------
+# lib.plist/lib
+# -------------
+
+_bogusLibFormatMessage = "The lib data is not in the correct format: %s"
+
+
+def fontLibValidator(value: Any) -> tuple[bool, Optional[str]]:
+ """
+ Check the validity of the lib.
+ Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+ >>> lib = {"foo" : "bar"}
+ >>> fontLibValidator(lib)
+ (True, None)
+
+ >>> lib = {"public.awesome" : "hello"}
+ >>> fontLibValidator(lib)
+ (True, None)
+
+ >>> lib = {"public.glyphOrder" : ["A", "C", "B"]}
+ >>> fontLibValidator(lib)
+ (True, None)
+
+ >>> lib = "hello"
+ >>> valid, msg = fontLibValidator(lib)
+ >>> valid
+ False
+ >>> print(msg) # doctest: +ELLIPSIS
+ The lib data is not in the correct format: expected a dictionary, ...
+
+ >>> lib = {1: "hello"}
+ >>> valid, msg = fontLibValidator(lib)
+ >>> valid
+ False
+ >>> print(msg)
+ The lib key is not properly formatted: expected str, found int: 1
+
+ >>> lib = {"public.glyphOrder" : "hello"}
+ >>> valid, msg = fontLibValidator(lib)
+ >>> valid
+ False
+ >>> print(msg) # doctest: +ELLIPSIS
+ public.glyphOrder is not properly formatted: expected list or tuple,...
+
+ >>> lib = {"public.glyphOrder" : ["A", 1, "B"]}
+ >>> valid, msg = fontLibValidator(lib)
+ >>> valid
+ False
+ >>> print(msg) # doctest: +ELLIPSIS
+ public.glyphOrder is not properly formatted: expected str,...
+ """
+ if not isDictEnough(value):
+ reason = "expected a dictionary, found %s" % type(value).__name__
+ return False, _bogusLibFormatMessage % reason
+ for key, value in value.items():
+ if not isinstance(key, str):
+ return False, (
+ "The lib key is not properly formatted: expected str, found %s: %r"
+ % (type(key).__name__, key)
+ )
+ # public.glyphOrder
+ if key == "public.glyphOrder":
+ bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s"
+ if not isinstance(value, (list, tuple)):
+ reason = "expected list or tuple, found %s" % type(value).__name__
+ return False, bogusGlyphOrderMessage % reason
+ for glyphName in value:
+ if not isinstance(glyphName, str):
+ reason = "expected str, found %s" % type(glyphName).__name__
+ return False, bogusGlyphOrderMessage % reason
+ return True, None
+
+
+# --------
+# GLIF lib
+# --------
+
+
+def glyphLibValidator(value: Any) -> tuple[bool, Optional[str]]:
+ """
+ Check the validity of the lib.
+ Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+ >>> lib = {"foo" : "bar"}
+ >>> glyphLibValidator(lib)
+ (True, None)
+
+ >>> lib = {"public.awesome" : "hello"}
+ >>> glyphLibValidator(lib)
+ (True, None)
+
+ >>> lib = {"public.markColor" : "1,0,0,0.5"}
+ >>> glyphLibValidator(lib)
+ (True, None)
+
+ >>> lib = {"public.markColor" : 1}
+ >>> valid, msg = glyphLibValidator(lib)
+ >>> valid
+ False
+ >>> print(msg)
+ public.markColor is not properly formatted.
+ """
+ if not isDictEnough(value):
+ reason = "expected a dictionary, found %s" % type(value).__name__
+ return False, _bogusLibFormatMessage % reason
+ for key, value in value.items():
+ if not isinstance(key, str):
+ reason = "key (%s) should be a string" % key
+ return False, _bogusLibFormatMessage % reason
+ # public.markColor
+ if key == "public.markColor":
+ if not colorValidator(value):
+ return False, "public.markColor is not properly formatted."
+ return True, None
+
+
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()