diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py index f5a5a705c..5fb01a838 100644 --- a/Lib/fontTools/misc/arrayTools.py +++ b/Lib/fontTools/misc/arrayTools.py @@ -282,6 +282,27 @@ def intRect(rect): 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( diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 1200ad0d7..c023f8844 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -7,10 +7,17 @@ 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.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 from fontTools.misc.textTools import bytesjoin, pad, safeEval +from fontTools.pens.boundsPen import ControlBoundsPen +from fontTools.pens.transformPen import TransformPen from .otBase import ( BaseTable, FormatSwitchingBaseTable, @@ -21,6 +28,10 @@ from .otBase import ( from fontTools.feaLib.lookupDebugInfo import LookupDebugInfo, LOOKUP_DEBUG_INFO_KEY import logging import struct +from typing import TYPE_CHECKING, Iterator, List, Optional, Set + +if TYPE_CHECKING: + from fontTools.ttLib.ttGlyphSet import _TTGlyphSet log = logging.getLogger(__name__) @@ -1218,6 +1229,32 @@ class COLR(BaseTable): "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: + raise TTLibError( + "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 @@ -1557,41 +1594,114 @@ class Paint(getFormatSwitchingBaseTableClass("uint8")): xmlWriter.endtag(tableName) xmlWriter.newline() - def getChildren(self, colr): + 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 - return layers[self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers] + 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: - return [record.Paint] + yield BaseTable.SubTableEntry(name="BaseGlyph", value=record.Paint) + return else: raise KeyError(f"{self.Glyph!r} not in colr.BaseGlyphList") - children = [] for conv in self.getConverters(): if conv.tableClass is not None and issubclass(conv.tableClass, type(self)): - children.append(getattr(self, conv.name)) + value = getattr(self, conv.name) + yield BaseTable.SubTableEntry(name=conv.name, value=value) - return children + 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") - stack = [self] - visited = set() - while stack: - current = stack.pop() - if id(current) in visited: - continue - callback(current) - visited.add(id(current)) - stack.extend(reversed(current.getChildren(colr))) + + 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 diff --git a/Lib/fontTools/ttLib/tables/otTraverse.py b/Lib/fontTools/ttLib/tables/otTraverse.py index 412f89f26..bf22dcfdb 100644 --- a/Lib/fontTools/ttLib/tables/otTraverse.py +++ b/Lib/fontTools/ttLib/tables/otTraverse.py @@ -31,6 +31,9 @@ def dfs_base_table( 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. @@ -43,6 +46,9 @@ def dfs_base_table( 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 @@ -56,6 +62,7 @@ def dfs_base_table( skip_root, predicate, lambda frontier, new: frontier.extendleft(reversed(new)), + iter_subtables_fn, ) @@ -64,11 +71,14 @@ def bfs_base_table( 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 (BaseTable): the root of the tree. + 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 @@ -76,6 +86,9 @@ def bfs_base_table( 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 @@ -89,6 +102,7 @@ def bfs_base_table( skip_root, predicate, lambda frontier, new: frontier.extend(new), + iter_subtables_fn, ) @@ -98,6 +112,9 @@ def _traverse_ot_data( 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: @@ -108,6 +125,11 @@ def _traverse_ot_data( 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) @@ -116,7 +138,10 @@ def _traverse_ot_data( else: add_to_frontier_fn( frontier, - [(root_entry, subtable_entry) for subtable_entry in root.iterSubTables()], + [ + (root_entry, subtable_entry) + for subtable_entry in iter_subtables_fn(root) + ], ) while frontier: @@ -130,7 +155,7 @@ def _traverse_ot_data( yield SubTablePath(path) new_entries = [ - path + (subtable_entry,) for subtable_entry in current.iterSubTables() + path + (subtable_entry,) for subtable_entry in iter_subtables_fn(current) ] add_to_frontier_fn(frontier, new_entries) diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 18e41dc8e..71789a4de 100644 --- a/Tests/ttLib/tables/C_O_L_R_test.py +++ b/Tests/ttLib/tables/C_O_L_R_test.py @@ -1,11 +1,16 @@ from fontTools import ttLib from fontTools.misc.testTools import getXML, parseXML +from fontTools.ttLib import TTFont from fontTools.ttLib.tables.C_O_L_R_ import table_C_O_L_R_ +from pathlib import Path import binascii import pytest +TEST_DATA_DIR = Path(__file__).parent / "data" + + COLR_V0_SAMPLE = ( (b"\x00\x00", "Version (0)"), (b"\x00\x01", "BaseGlyphRecordCount (1)"), @@ -611,6 +616,26 @@ class COLR_V1_Test(object): colr.decompile(compiled, font) assert getXML(colr.toXML, font) == COLR_V1_XML + @pytest.mark.parametrize("quantization", [1, 10, 100]) + @pytest.mark.parametrize("flavor", ["glyf", "cff"]) + def test_computeClipBoxes(self, flavor, quantization): + font = TTFont() + font.importXML(TEST_DATA_DIR / f"COLRv1-clip-boxes-{flavor}.ttx") + assert font["COLR"].table.ClipList is None + + font["COLR"].table.computeClipBoxes(font.getGlyphSet(), quantization) + + clipList = font["COLR"].table.ClipList + assert len(clipList.clips) > 0 + + expected = TTFont() + expected.importXML( + TEST_DATA_DIR / f"COLRv1-clip-boxes-q{quantization}-expected.ttx" + ) + expectedClipList = expected["COLR"].table.ClipList + + assert getXML(clipList.toXML) == getXML(expectedClipList.toXML) + class COLR_V1_Variable_Test(object): def test_round_trip_xml(self, font): diff --git a/Tests/ttLib/tables/data/COLRv1-clip-boxes-cff.ttx b/Tests/ttLib/tables/data/COLRv1-clip-boxes-cff.ttx new file mode 100644 index 000000000..05172caef --- /dev/null +++ b/Tests/ttLib/tables/data/COLRv1-clip-boxes-cff.ttx @@ -0,0 +1,1213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2023-03-10T15:01:34.955294 + + + COLRv1 Static Test Glyphs Regular + + + 2023-03-10T15:01:34.955294 + + + COLRv1StaticTestGlyphs-Regular + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2023-03-10T15:01:34.955294 + + + COLRv1 Static Test Glyphs Regular + + + 2023-03-10T15:01:34.955294 + + + COLRv1StaticTestGlyphs-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 600 endchar + + + 0 endchar + + + 1000 475 525 rmoveto + 225 50 -225 225 -50 -225 -225 -50 225 -225 50 vlineto + endchar + + + 1000 296 543 rmoveto + -293 -37 247 vlineto + -75 -31 0 37 106 40 rlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 0 hmoveto + 1000 1000 -1000 vlineto + endchar + + + 1000 357 374 rmoveto + -47 -8 -34 -17 -19 vhcurveto + -19 -16 -23 -9 -28 hhcurveto + -28 -22 9 19 -17 hvcurveto + -17 19 -8 34 47 vvcurveto + 45 vlineto + 47 8 33 17 19 vhcurveto + 18 17 22 9 28 hhcurveto + 28 23 -9 -18 16 hvcurveto + 17 -19 8 -33 -47 vvcurveto + -37 6 rmoveto + 33 -5 23 -9 14 vhcurveto + 13 -10 -13 7 -18 hhcurveto + -18 -13 -7 -13 -10 hvcurveto + -9 -14 -5 -23 -33 vvcurveto + -57 vlineto + -32 5 -24 10 -14 vhcurveto + -14 9 14 -8 17 hhcurveto + 18 14 8 14 9 hvcurveto + 9 14 5 24 32 vvcurveto + endchar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ttLib/tables/data/COLRv1-clip-boxes-glyf.ttx b/Tests/ttLib/tables/data/COLRv1-clip-boxes-glyf.ttx new file mode 100644 index 000000000..2f1c14c40 --- /dev/null +++ b/Tests/ttLib/tables/data/COLRv1-clip-boxes-glyf.ttx @@ -0,0 +1,1414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2023-03-10T15:07:35.658876 + + + COLRv1 Static Test Glyphs Regular + + + 2023-03-10T15:07:35.658876 + + + COLRv1StaticTestGlyphs-Regular + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2023-03-10T15:07:35.658876 + + + COLRv1 Static Test Glyphs Regular + + + 2023-03-10T15:07:35.658876 + + + COLRv1StaticTestGlyphs-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ttLib/tables/data/COLRv1-clip-boxes-q1-expected.ttx b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q1-expected.ttx new file mode 100644 index 000000000..c3b8ef62b --- /dev/null +++ b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q1-expected.ttx @@ -0,0 +1,919 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ttLib/tables/data/COLRv1-clip-boxes-q10-expected.ttx b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q10-expected.ttx new file mode 100644 index 000000000..f6b66f5c6 --- /dev/null +++ b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q10-expected.ttx @@ -0,0 +1,911 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ttLib/tables/data/COLRv1-clip-boxes-q100-expected.ttx b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q100-expected.ttx new file mode 100644 index 000000000..f4fee78ac --- /dev/null +++ b/Tests/ttLib/tables/data/COLRv1-clip-boxes-q100-expected.ttx @@ -0,0 +1,863 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +