From 92fbac8a64af0e7c59b3b766904a2922b2c42ba7 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Mar 2023 19:03:50 +0000 Subject: [PATCH 1/6] [arrayTools] add quantizeRect --- Lib/fontTools/misc/arrayTools.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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( From 7433c5dbb92099b5b4008d776ff9b959b80d5ead Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Mar 2023 19:13:48 +0000 Subject: [PATCH 2/6] [otTraverse] allow to use custom callback to iterate over subtables --- Lib/fontTools/ttLib/tables/otTraverse.py | 31 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) 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) From 17f431b9c272c2ae0a9a5f1363a1e94262f3b57e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Mar 2023 19:19:50 +0000 Subject: [PATCH 3/6] [otTables] switch to generic table traversal for Paint.traverse --- Lib/fontTools/ttLib/tables/otTables.py | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 1200ad0d7..9bf02b85e 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -9,6 +9,7 @@ import copy from enum import IntEnum import itertools from collections import defaultdict, namedtuple +from fontTools.ttLib.tables.otTraverse import dfs_base_table from fontTools.misc.roundTools import otRound from fontTools.misc.textTools import bytesjoin, pad, safeEval from .otBase import ( @@ -21,6 +22,7 @@ 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 log = logging.getLogger(__name__) @@ -1557,41 +1559,47 @@ 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) # For each subtable format there is a class. However, we don't really distinguish From 55cc41a24e4d15ef0e3c5a9dec12fc4c9105ffde Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Mar 2023 19:21:13 +0000 Subject: [PATCH 4/6] [otTables] implement Paint.getTransform, Paint.computeClipBox & COLR.computeClipBoxes This for now only works for static COLR table, not variable. --- Lib/fontTools/ttLib/tables/otTables.py | 102 +++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 9bf02b85e..27440f7b9 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -7,11 +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, @@ -24,6 +30,9 @@ 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__) @@ -1220,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 @@ -1601,6 +1636,73 @@ class Paint(getFormatSwitchingBaseTableClass("uint8")): 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 From c15e77cbc9180b66ac232972823806910d860050 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 10 Mar 2023 12:35:52 +0000 Subject: [PATCH 5/6] ot-spec says skewX angle should be counter-clockwise so we must negate Transform.skew method assumes skewX angle goes clockwise. verified using test_glyphs-glyf_colr_1.ttf test font from googlefonts/color-fonts repo. Will add that to the tests. --- Lib/fontTools/ttLib/tables/otTables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 27440f7b9..c023f8844 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -1667,11 +1667,11 @@ class Paint(getFormatSwitchingBaseTableClass("uint8")): .translate(-self.centerX, -self.centerY) ) elif self.Format == PaintFormat.PaintSkew: - return Identity.skew(radians(self.xSkewAngle), radians(self.ySkewAngle)) + 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)) + .skew(radians(-self.xSkewAngle), radians(self.ySkewAngle)) .translate(-self.centerX, -self.centerY) ) if PaintFormat(self.Format).is_variable(): From c5e464ad8f9e13a3bfd8a6b120edbbe896cebce1 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 10 Mar 2023 15:23:30 +0000 Subject: [PATCH 6/6] C_O_L_R_test: add tests for computeClipBoxes using a subset from test_glyphs font from the googlefonts/color-fonts repository --- Tests/ttLib/tables/C_O_L_R_test.py | 25 + .../tables/data/COLRv1-clip-boxes-cff.ttx | 1213 ++++++++++++++ .../tables/data/COLRv1-clip-boxes-glyf.ttx | 1414 +++++++++++++++++ .../data/COLRv1-clip-boxes-q1-expected.ttx | 919 +++++++++++ .../data/COLRv1-clip-boxes-q10-expected.ttx | 911 +++++++++++ .../data/COLRv1-clip-boxes-q100-expected.ttx | 863 ++++++++++ 6 files changed, 5345 insertions(+) create mode 100644 Tests/ttLib/tables/data/COLRv1-clip-boxes-cff.ttx create mode 100644 Tests/ttLib/tables/data/COLRv1-clip-boxes-glyf.ttx create mode 100644 Tests/ttLib/tables/data/COLRv1-clip-boxes-q1-expected.ttx create mode 100644 Tests/ttLib/tables/data/COLRv1-clip-boxes-q10-expected.ttx create mode 100644 Tests/ttLib/tables/data/COLRv1-clip-boxes-q100-expected.ttx 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +