From 40897a1508cdaf040f940fda0ed784d30d8bcfaf Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 7 Jul 2021 19:06:38 +0100 Subject: [PATCH 1/8] Define DeltaSetIndexMap table used for COLR.VarIndexMap https://github.com/googlefonts/colr-gradients-spec/pull/316 --- Lib/fontTools/colorLib/builder.py | 3 + Lib/fontTools/ttLib/tables/otData.py | 122 +++++++++++++++---------- Lib/fontTools/ttLib/tables/otTables.py | 96 +++++++++++++------ Lib/fontTools/varLib/builder.py | 8 ++ 4 files changed, 156 insertions(+), 73 deletions(-) diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index dec832335..e12e741ea 100644 --- a/Lib/fontTools/colorLib/builder.py +++ b/Lib/fontTools/colorLib/builder.py @@ -183,6 +183,7 @@ def buildCOLR( version: Optional[int] = None, glyphMap: Optional[Mapping[str, int]] = None, varStore: Optional[ot.VarStore] = None, + varIndexMap: Optional[ot.DeltaSetIndexMap] = None, ) -> C_O_L_R_.table_C_O_L_R_: """Build COLR table from color layers mapping. Args: @@ -196,6 +197,7 @@ def buildCOLR( glyphMap: a map from glyph names to glyph indices, as returned from TTFont.getReverseGlyphMap(), to optionally sort base records by GID. varStore: Optional ItemVarationStore for deltas associated with v1 layer. + varIndexMap: Optional DeltaSetIndexMap for deltas associated with v1 layer. Return: A new COLR table. """ @@ -229,6 +231,7 @@ def buildCOLR( if version == 0: self.ColorLayers = self._decompileColorLayersV0(colr) else: + colr.VarIndexMap = varIndexMap colr.VarStore = varStore self.table = colr diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 175fa958c..31f2d0630 100755 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -988,6 +988,20 @@ otData = [ ('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'), + ]), + # Glyph advance variations ('HVAR', [ @@ -1548,6 +1562,7 @@ otData = [ ('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.'), + ('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)'), ]), @@ -1603,12 +1618,13 @@ otData = [ ('Fixed', 'dy', None, None, 'Translation in y direction'), ]), ('VarAffine2x3', [ - ('VarFixed', 'xx', None, None, 'x-part of x basis vector'), - ('VarFixed', 'yx', None, None, 'y-part of x basis vector'), - ('VarFixed', 'xy', None, None, 'x-part of y basis vector'), - ('VarFixed', 'yy', None, None, 'y-part of y basis vector'), - ('VarFixed', 'dx', None, None, 'Translation in x direction'), - ('VarFixed', 'dy', None, None, 'Translation in y direction'), + ('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'), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorIndex', [ @@ -1617,7 +1633,8 @@ otData = [ ]), ('VarColorIndex', [ ('uint16', 'PaletteIndex', None, None, 'Index value to use with a selected color palette.'), - ('VarF2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'), + ('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorStop', [ @@ -1625,8 +1642,9 @@ otData = [ ('ColorIndex', 'Color', None, None, ''), ]), ('VarColorStop', [ - ('VarF2Dot14', 'StopOffset', None, None, ''), + ('F2Dot14', 'StopOffset', None, None, ''), ('VarColorIndex', 'Color', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorLine', [ @@ -1673,12 +1691,13 @@ otData = [ ('PaintFormat5', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 5'), ('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarLinearGradient table) to VarColorLine subtable.'), - ('VarInt16', 'x0', None, None, ''), - ('VarInt16', 'y0', None, None, ''), - ('VarInt16', 'x1', None, None, ''), - ('VarInt16', 'y1', None, None, ''), - ('VarInt16', 'x2', None, None, ''), - ('VarInt16', 'y2', None, None, ''), + ('int16', 'x0', None, None, ''), + ('int16', 'y0', None, None, ''), + ('int16', 'x1', None, None, ''), + ('int16', 'y1', None, None, ''), + ('int16', 'x2', None, None, ''), + ('int16', 'y2', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRadialGradient @@ -1696,12 +1715,13 @@ otData = [ ('PaintFormat7', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 7'), ('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarRadialGradient table) to VarColorLine subtable.'), - ('VarInt16', 'x0', None, None, ''), - ('VarInt16', 'y0', None, None, ''), - ('VarUInt16', 'r0', None, None, ''), - ('VarInt16', 'x1', None, None, ''), - ('VarInt16', 'y1', None, None, ''), - ('VarUInt16', 'r1', None, None, ''), + ('int16', 'x0', None, None, ''), + ('int16', 'y0', None, None, ''), + ('uint16', 'r0', None, None, ''), + ('int16', 'x1', None, None, ''), + ('int16', 'y1', None, None, ''), + ('uint16', 'r1', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSweepGradient @@ -1717,10 +1737,11 @@ otData = [ ('PaintFormat9', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 9'), ('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarSweepGradient table) to VarColorLine subtable.'), - ('VarInt16', 'centerX', None, None, 'Center x coordinate.'), - ('VarInt16', 'centerY', None, None, 'Center y coordinate.'), - ('VarAngle', 'startAngle', None, None, 'Start of the angular range of the gradient.'), - ('VarAngle', 'endAngle', None, None, 'End of the angular range of the gradient.'), + ('int16', 'centerX', None, None, 'Center x coordinate.'), + ('int16', 'centerY', None, None, 'Center y coordinate.'), + ('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'), + ('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintGlyph @@ -1760,8 +1781,9 @@ otData = [ ('PaintFormat15', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 15'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarTranslate table) to Paint subtable.'), - ('VarInt16', 'dx', None, None, 'Translation in x direction.'), - ('VarInt16', 'dy', None, None, 'Translation in y direction.'), + ('int16', 'dx', None, None, 'Translation in x direction.'), + ('int16', 'dy', None, None, 'Translation in y direction.'), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScale @@ -1775,8 +1797,9 @@ otData = [ ('PaintFormat17', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 17'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScale table) to Paint subtable.'), - ('VarF2Dot14', 'scaleX', None, None, ''), - ('VarF2Dot14', 'scaleY', None, None, ''), + ('F2Dot14', 'scaleX', None, None, ''), + ('F2Dot14', 'scaleY', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleAroundCenter @@ -1792,10 +1815,11 @@ otData = [ ('PaintFormat19', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 19'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleAroundCenter table) to Paint subtable.'), - ('VarF2Dot14', 'scaleX', None, None, ''), - ('VarF2Dot14', 'scaleY', None, None, ''), - ('VarInt16', 'centerX', None, None, ''), - ('VarInt16', 'centerY', None, None, ''), + ('F2Dot14', 'scaleX', None, None, ''), + ('F2Dot14', 'scaleY', None, None, ''), + ('int16', 'centerX', None, None, ''), + ('int16', 'centerY', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniform @@ -1808,7 +1832,8 @@ otData = [ ('PaintFormat21', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 21'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniform table) to Paint subtable.'), - ('VarF2Dot14', 'scale', None, None, ''), + ('F2Dot14', 'scale', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniformAroundCenter @@ -1823,9 +1848,10 @@ otData = [ ('PaintFormat23', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 23'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniformAroundCenter table) to Paint subtable.'), - ('VarF2Dot14', 'scale', None, None, ''), - ('VarInt16', 'centerX', None, None, ''), - ('VarInt16', 'centerY', None, None, ''), + ('F2Dot14', 'scale', None, None, ''), + ('int16', 'centerX', None, None, ''), + ('int16', 'centerY', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotate @@ -1838,7 +1864,8 @@ otData = [ ('PaintFormat25', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 25'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotate table) to Paint subtable.'), - ('VarAngle', 'angle', None, None, ''), + ('Angle', 'angle', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotateAroundCenter @@ -1853,9 +1880,10 @@ otData = [ ('PaintFormat27', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 27'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotateAroundCenter table) to Paint subtable.'), - ('VarAngle', 'angle', None, None, ''), - ('VarInt16', 'centerX', None, None, ''), - ('VarInt16', 'centerY', None, None, ''), + ('Angle', 'angle', None, None, ''), + ('int16', 'centerX', None, None, ''), + ('int16', 'centerY', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkew @@ -1869,8 +1897,9 @@ otData = [ ('PaintFormat29', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 29'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkew table) to Paint subtable.'), - ('VarAngle', 'xSkewAngle', None, None, ''), - ('VarAngle', 'ySkewAngle', None, None, ''), + ('Angle', 'xSkewAngle', None, None, ''), + ('Angle', 'ySkewAngle', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkewAroundCenter @@ -1886,10 +1915,11 @@ otData = [ ('PaintFormat31', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 31'), ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkewAroundCenter table) to Paint subtable.'), - ('VarAngle', 'xSkewAngle', None, None, ''), - ('VarAngle', 'ySkewAngle', None, None, ''), - ('VarInt16', 'centerX', None, None, ''), - ('VarInt16', 'centerY', None, None, ''), + ('Angle', 'xSkewAngle', None, None, ''), + ('Angle', 'ySkewAngle', None, None, ''), + ('int16', 'centerX', None, None, ''), + ('int16', 'centerY', None, None, ''), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintComposite diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 18271eb83..e0ba3b64f 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -618,6 +618,73 @@ class Coverage(FormatSwitchingBaseTable): glyphs.append(attrs["value"]) +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< 0xFFFF else 0 + rawTable = self.__dict__.copy() + rawTable['MappingCount'] = len(mapping) + rawTable['EntryFormat'] = self.getEntryFormat(mapping) + return rawTable + + def toXML2(self, xmlWriter, font): + for i, value in enumerate(getattr(self, "mapping", [])): + attrs = ( + ('index', i), + ('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['outer']) + inner = safeEval(attrs['inner']) + assert inner <= 0xFFFF + mapping.insert(index, (outer << 16) | inner) + + class VarIdxMap(BaseTable): def populateDefaults(self, propagator=None): @@ -641,34 +708,9 @@ class VarIdxMap(BaseTable): while len(mapping) > 1 and mapping[-2] == mapping[-1]: del mapping[-1] - rawTable = { 'mapping': mapping } + rawTable = {'mapping': mapping} rawTable['MappingCount'] = len(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< 0xFFFF else 0 + return self + + def buildVarDevTable(varIdx): self = ot.Device() self.DeltaFormat = 0x8000 From 0eb27e987880f53388e1ce945ffc799b9dc52f1e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 20 Jul 2021 19:51:47 +0100 Subject: [PATCH 2/8] Remove and inline {Var,}ColorIndex --- Lib/fontTools/colorLib/builder.py | 29 +++++++++++++------- Lib/fontTools/colorLib/table_builder.py | 35 ++++++++++++++----------- Lib/fontTools/ttLib/tables/otData.py | 25 +++++++----------- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index e12e741ea..392fb106b 100644 --- a/Lib/fontTools/colorLib/builder.py +++ b/Lib/fontTools/colorLib/builder.py @@ -89,16 +89,16 @@ def _beforeBuildPaintRadialGradient(paint, source): return _beforeBuildPaintVarRadialGradient(paint, source, lambda v: v.value) -def _defaultColorIndex(): - colorIndex = ot.ColorIndex() - colorIndex.Alpha = _DEFAULT_ALPHA.value - return colorIndex +def _defaultColorStop(): + colorStop = ot.ColorStop() + colorStop.Alpha = _DEFAULT_ALPHA.value + return colorStop -def _defaultVarColorIndex(): - colorIndex = ot.VarColorIndex() - colorIndex.Alpha = _DEFAULT_ALPHA - return colorIndex +def _defaultVarColorStop(): + colorStop = ot.VarColorStop() + colorStop.Alpha = _DEFAULT_ALPHA.value + return colorStop def _defaultColorLine(): @@ -113,6 +113,12 @@ def _defaultVarColorLine(): return colorLine +def _defaultPaintSolid(): + paint = ot.Paint() + paint.Alpha = _DEFAULT_ALPHA.value + return paint + + def _buildPaintCallbacks(): return { ( @@ -125,10 +131,13 @@ def _buildPaintCallbacks(): ot.Paint, ot.PaintFormat.PaintVarRadialGradient, ): _beforeBuildPaintVarRadialGradient, - (BuildCallback.CREATE_DEFAULT, ot.ColorIndex): _defaultColorIndex, - (BuildCallback.CREATE_DEFAULT, ot.VarColorIndex): _defaultVarColorIndex, + (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop, + (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop, (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine, (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, + (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, + (BuildCallback.CREATE_DEFAULT, ot.Paint, ot.PaintFormat.PaintSolid): _defaultPaintSolid, + (BuildCallback.CREATE_DEFAULT, ot.Paint, ot.PaintFormat.PaintVarSolid): _defaultPaintSolid, } diff --git a/Lib/fontTools/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py index 6fba6b0fc..a9c994c39 100644 --- a/Lib/fontTools/colorLib/table_builder.py +++ b/Lib/fontTools/colorLib/table_builder.py @@ -39,7 +39,7 @@ class BuildCallback(enum.Enum): """ AFTER_BUILD = enum.auto() - """Keyed on (CREATE_DEFAULT, class). + """Keyed on (CREATE_DEFAULT, class[, Format if available]). Receives no arguments. Should return a new instance of class. """ @@ -62,25 +62,25 @@ def _isNonStrSequence(value): return isinstance(value, collections.abc.Sequence) and not isinstance(value, str) -def _set_format(dest, source): +def _split_format(cls, source): if _isNonStrSequence(source): - assert len(source) > 0, f"{type(dest)} needs at least format from {source}" - dest.Format = source[0] - source = source[1:] + assert len(source) > 0, f"{cls} needs at least format from {source}" + fmt, remainder = source[0], source[1:] elif isinstance(source, collections.abc.Mapping): - assert "Format" in source, f"{type(dest)} needs at least Format from {source}" - dest.Format = source["Format"] + assert "Format" in source, f"{cls} needs at least Format from {source}" + remainder = source.copy() + fmt = remainder.pop("Format") else: - raise ValueError(f"Not sure how to populate {type(dest)} from {source}") + raise ValueError(f"Not sure how to populate {cls} from {source}") assert isinstance( - dest.Format, collections.abc.Hashable - ), f"{type(dest)} Format is not hashable: {dest.Format}" + fmt, collections.abc.Hashable + ), f"{cls} Format is not hashable: {fmt!r}" assert ( - dest.Format in dest.convertersByName - ), f"{dest.Format} invalid Format of {cls}" + fmt in cls.convertersByName + ), f"{cls} invalid Format: {fmt!r}" - return source + return fmt, remainder class TableBuilder: @@ -140,6 +140,11 @@ class TableBuilder: return source callbackKey = (cls,) + fmt = None + if issubclass(cls, FormatSwitchingBaseTable): + fmt, source = _split_format(cls, source) + callbackKey = (cls, fmt) + dest = self._callbackTable.get( (BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls() )() @@ -150,11 +155,9 @@ class TableBuilder: # For format switchers we need to resolve converters based on format if issubclass(cls, FormatSwitchingBaseTable): - source = _set_format(dest, source) - + dest.Format = fmt convByName = _assignable(convByName[dest.Format]) skippedFields.add("Format") - callbackKey = (cls, dest.Format) # Convert sequence => mapping so before thunk only has to handle one format if _isNonStrSequence(source): diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 31f2d0630..056b68117 100755 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -1627,23 +1627,15 @@ otData = [ ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), - ('ColorIndex', [ - ('uint16', 'PaletteIndex', None, None, 'Index value to use with a selected color palette.'), - ('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'), - ]), - ('VarColorIndex', [ - ('uint16', 'PaletteIndex', None, None, 'Index value to use with a selected color palette.'), - ('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), - ]), - ('ColorStop', [ ('F2Dot14', 'StopOffset', None, None, ''), - ('ColorIndex', 'Color', 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, ''), - ('VarColorIndex', 'Color', None, None, ''), + ('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'), ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), @@ -1668,12 +1660,15 @@ otData = [ # PaintSolid ('PaintFormat2', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 2'), - ('ColorIndex', 'Color', None, None, 'A solid color paint.'), + ('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'), - ('VarColorIndex', 'Color', None, None, 'A solid color paint.'), + ('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'), + ('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 0'), + ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintLinearGradient From e8e2aa530b8a658886b8375a70268caf6d437daf Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 22 Jul 2021 11:51:25 +0100 Subject: [PATCH 3/8] remove unused Variable{Float,Int} namedtuples from otTables/otConverters --- Lib/fontTools/colorLib/builder.py | 76 +++++++------- Lib/fontTools/colorLib/table_builder.py | 21 +--- Lib/fontTools/ttLib/tables/otConverters.py | 109 +-------------------- Lib/fontTools/ttLib/tables/otTables.py | 37 ------- 4 files changed, 37 insertions(+), 206 deletions(-) diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index 392fb106b..57f757e0c 100644 --- a/Lib/fontTools/colorLib/builder.py +++ b/Lib/fontTools/colorLib/builder.py @@ -26,20 +26,10 @@ from fontTools.ttLib.tables import C_O_L_R_ from fontTools.ttLib.tables import C_P_A_L_ from fontTools.ttLib.tables import _n_a_m_e from fontTools.ttLib.tables import otTables as ot -from fontTools.ttLib.tables.otTables import ( - ExtendMode, - CompositeMode, - VariableValue, - VariableFloat, - VariableInt, -) +from fontTools.ttLib.tables.otTables import ExtendMode, CompositeMode from .errors import ColorLibError from .geometry import round_start_circle_stable_containment -from .table_builder import ( - convertTupleClass, - BuildCallback, - TableBuilder, -) +from .table_builder import BuildCallback, TableBuilder # TODO move type aliases to colorLib.types? @@ -52,52 +42,45 @@ _ColorGlyphsV0Dict = Dict[str, Sequence[Tuple[str, int]]] MAX_PAINT_COLR_LAYER_COUNT = 255 -_DEFAULT_ALPHA = VariableFloat(1.0) +_DEFAULT_ALPHA = 1.0 _MAX_REUSE_LEN = 32 -def _beforeBuildPaintVarRadialGradient(paint, source, srcMapFn=lambda v: v): - # normalize input types (which may or may not specify a varIdx) - x0 = convertTupleClass(VariableFloat, source["x0"]) - y0 = convertTupleClass(VariableFloat, source["y0"]) - r0 = convertTupleClass(VariableFloat, source["r0"]) - x1 = convertTupleClass(VariableFloat, source["x1"]) - y1 = convertTupleClass(VariableFloat, source["y1"]) - r1 = convertTupleClass(VariableFloat, source["r1"]) +def _beforeBuildPaintRadialGradient(paint, source): + x0 = source["x0"] + y0 = source["y0"] + r0 = source["r0"] + x1 = source["x1"] + y1 = source["y1"] + r1 = source["r1"] # TODO apparently no builder_test confirms this works (?) # avoid abrupt change after rounding when c0 is near c1's perimeter - c = round_start_circle_stable_containment( - (x0.value, y0.value), r0.value, (x1.value, y1.value), r1.value - ) - x0, y0 = x0._replace(value=c.centre[0]), y0._replace(value=c.centre[1]) - r0 = r0._replace(value=c.radius) + c = round_start_circle_stable_containment((x0, y0), r0, (x1, y1), r1) + x0, y0 = c.centre + r0 = c.radius # update source to ensure paint is built with corrected values - source["x0"] = srcMapFn(x0) - source["y0"] = srcMapFn(y0) - source["r0"] = srcMapFn(r0) - source["x1"] = srcMapFn(x1) - source["y1"] = srcMapFn(y1) - source["r1"] = srcMapFn(r1) + source["x0"] = x0 + source["y0"] = y0 + source["r0"] = r0 + source["x1"] = x1 + source["y1"] = y1 + source["r1"] = r1 return paint, source -def _beforeBuildPaintRadialGradient(paint, source): - return _beforeBuildPaintVarRadialGradient(paint, source, lambda v: v.value) - - def _defaultColorStop(): colorStop = ot.ColorStop() - colorStop.Alpha = _DEFAULT_ALPHA.value + colorStop.Alpha = _DEFAULT_ALPHA return colorStop def _defaultVarColorStop(): colorStop = ot.VarColorStop() - colorStop.Alpha = _DEFAULT_ALPHA.value + colorStop.Alpha = _DEFAULT_ALPHA return colorStop @@ -115,7 +98,7 @@ def _defaultVarColorLine(): def _defaultPaintSolid(): paint = ot.Paint() - paint.Alpha = _DEFAULT_ALPHA.value + paint.Alpha = _DEFAULT_ALPHA return paint @@ -130,14 +113,21 @@ def _buildPaintCallbacks(): BuildCallback.BEFORE_BUILD, ot.Paint, ot.PaintFormat.PaintVarRadialGradient, - ): _beforeBuildPaintVarRadialGradient, + ): _beforeBuildPaintRadialGradient, (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop, (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop, (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine, (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, - (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, - (BuildCallback.CREATE_DEFAULT, ot.Paint, ot.PaintFormat.PaintSolid): _defaultPaintSolid, - (BuildCallback.CREATE_DEFAULT, ot.Paint, ot.PaintFormat.PaintVarSolid): _defaultPaintSolid, + ( + BuildCallback.CREATE_DEFAULT, + ot.Paint, + ot.PaintFormat.PaintSolid, + ): _defaultPaintSolid, + ( + BuildCallback.CREATE_DEFAULT, + ot.Paint, + ot.PaintFormat.PaintVarSolid, + ): _defaultPaintSolid, } diff --git a/Lib/fontTools/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py index a9c994c39..98a27e479 100644 --- a/Lib/fontTools/colorLib/table_builder.py +++ b/Lib/fontTools/colorLib/table_builder.py @@ -17,8 +17,6 @@ from fontTools.ttLib.tables.otConverters import ( Short, UInt8, UShort, - VarInt16, - VarUInt16, IntValue, FloatValue, ) @@ -50,14 +48,6 @@ def _assignable(convertersByName): return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)} -def convertTupleClass(tupleClass, value): - if isinstance(value, tupleClass): - return value - if isinstance(value, tuple): - return tupleClass(*value) - return tupleClass(value) - - def _isNonStrSequence(value): return isinstance(value, collections.abc.Sequence) and not isinstance(value, str) @@ -97,13 +87,9 @@ class TableBuilder: self._callbackTable = callbackTable def _convert(self, dest, field, converter, value): - tupleClass = getattr(converter, "tupleClass", None) enumClass = getattr(converter, "enumClass", None) - if tupleClass: - value = convertTupleClass(tupleClass, value) - - elif enumClass: + if enumClass: if isinstance(value, enumClass): pass elif isinstance(value, str): @@ -213,11 +199,8 @@ class TableUnbuilder: continue value = getattr(table, converter.name) - tupleClass = getattr(converter, "tupleClass", None) enumClass = getattr(converter, "enumClass", None) - if tupleClass: - source[converter.name] = tuple(value) - elif enumClass: + if enumClass: source[converter.name] = value.name.lower() elif isinstance(converter, Struct): if converter.repeat: diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py index 1976acaa7..ecc1057a4 100644 --- a/Lib/fontTools/ttLib/tables/otConverters.py +++ b/Lib/fontTools/ttLib/tables/otConverters.py @@ -14,8 +14,8 @@ from .otBase import (CountReference, FormatSwitchingBaseTable, OTTableReader, OTTableWriter, ValueRecordFactory) from .otTables import (lookupTypes, AATStateTable, AATState, AATAction, ContextualMorphAction, LigatureMorphAction, - InsertionMorphAction, MorxSubtable, VariableFloat, - VariableInt, ExtendMode as _ExtendMode, + InsertionMorphAction, MorxSubtable, + ExtendMode as _ExtendMode, CompositeMode as _CompositeMode) from itertools import zip_longest from functools import partial @@ -1700,104 +1700,6 @@ class LookupFlag(UShort): xmlWriter.comment(" ".join(flags)) xmlWriter.newline() -def _issubclass_namedtuple(x): - return ( - issubclass(x, tuple) - and getattr(x, "_fields", None) is not None - ) - - -class _NamedTupleConverter(BaseConverter): - # subclasses must override this - tupleClass = NotImplemented - # List[SimpleValue] - converterClasses = NotImplemented - - def __init__(self, name, repeat, aux, tableClass=None): - # we expect all converters to be subclasses of SimpleValue - assert all(issubclass(klass, SimpleValue) for klass in self.converterClasses) - assert _issubclass_namedtuple(self.tupleClass), repr(self.tupleClass) - assert len(self.tupleClass._fields) == len(self.converterClasses) - assert tableClass is None # tableClass is unused by SimplValues - BaseConverter.__init__(self, name, repeat, aux) - self.converters = [ - klass(name=name, repeat=None, aux=None) - for name, klass in zip(self.tupleClass._fields, self.converterClasses) - ] - self.convertersByName = {conv.name: conv for conv in self.converters} - # returned by getRecordSize method - self.staticSize = sum(c.staticSize for c in self.converters) - - def read(self, reader, font, tableDict): - kwargs = { - conv.name: conv.read(reader, font, tableDict) - for conv in self.converters - } - return self.tupleClass(**kwargs) - - def write(self, writer, font, tableDict, value, repeatIndex=None): - for conv in self.converters: - v = getattr(value, conv.name) - # repeatIndex is unused for SimpleValues - conv.write(writer, font, tableDict, v, repeatIndex=None) - - def xmlWrite(self, xmlWriter, font, value, name, attrs): - assert value is not None - defaults = value.__new__.__defaults__ or () - assert len(self.converters) >= len(defaults) - values = {} - required = object() - for conv, default in zip_longest( - reversed(self.converters), - reversed(defaults), - fillvalue=required, - ): - v = getattr(value, conv.name) - if default is required or v != default: - values[conv.name] = conv.toString(v) - if attrs is None: - attrs = [] - attrs.extend( - (conv.name, values[conv.name]) - for conv in self.converters - if conv.name in values - ) - xmlWriter.simpletag(name, attrs) - xmlWriter.newline() - - def xmlRead(self, attrs, content, font): - converters = self.convertersByName - kwargs = { - k: converters[k].fromString(v) - for k, v in attrs.items() - } - return self.tupleClass(**kwargs) - - -class VarFixed(_NamedTupleConverter): - tupleClass = VariableFloat - converterClasses = [Fixed, ULong] - - -class VarF2Dot14(_NamedTupleConverter): - tupleClass = VariableFloat - converterClasses = [F2Dot14, ULong] - - -class VarInt16(_NamedTupleConverter): - tupleClass = VariableInt - converterClasses = [Short, ULong] - - -class VarUInt16(_NamedTupleConverter): - tupleClass = VariableInt - converterClasses = [UShort, ULong] - - -class VarAngle(_NamedTupleConverter): - tupleClass = VariableFloat - converterClasses = [F2Dot14, ULong] - class _UInt8Enum(UInt8): enumClass = NotImplemented @@ -1867,11 +1769,4 @@ converterMapping = { "OffsetTo": lambda C: partial(Table, tableClass=C), "LOffsetTo": lambda C: partial(LTable, tableClass=C), "LOffset24To": lambda C: partial(Table24, tableClass=C), - - # Variable types - "VarFixed": VarFixed, - "VarF2Dot14": VarF2Dot14, - "VarInt16": VarInt16, - "VarUInt16": VarUInt16, - "VarAngle": VarAngle, } diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index e0ba3b64f..339331873 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -1297,43 +1297,6 @@ class BaseGlyphList(BaseTable): return self.__dict__.copy() - -class VariableValue(namedtuple("VariableValue", ["value", "varIdx"])): - __slots__ = () - - _value_mapper = None - - def __new__(cls, value, varIdx=0xFFFFFFFF): - return super().__new__( - cls, - cls._value_mapper(value) if cls._value_mapper else value, - varIdx - ) - - @classmethod - def _make(cls, iterable): - if cls._value_mapper: - it = iter(iterable) - try: - value = next(it) - except StopIteration: - pass - else: - value = cls._value_mapper(value) - iterable = itertools.chain((value,), it) - return super()._make(iterable) - - -class VariableFloat(VariableValue): - __slots__ = () - _value_mapper = float - - -class VariableInt(VariableValue): - __slots__ = () - _value_mapper = otRound - - class ExtendMode(IntEnum): PAD = 0 REPEAT = 1 From e5d0c00fab406c0d37f2e88d9bd56ce98ff04f23 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 22 Jul 2021 11:53:53 +0100 Subject: [PATCH 4/8] fix colorLib tests after adding VarIndexBase, ColorIndex removed, etc. --- Tests/colorLib/builder_test.py | 520 +++++++++++++++---------------- Tests/colorLib/unbuilder_test.py | 107 ++++--- 2 files changed, 323 insertions(+), 304 deletions(-) diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 2c47b5fb8..8d30ca5ee 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -231,92 +231,63 @@ def test_buildCPAL_invalid_color(): builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, -1, 2)]]) -def test_buildColorIndex_Minimal(): - c = _build(ot.ColorIndex, 1) - assert c.PaletteIndex == 1 - assert c.Alpha == 1.0 - - -def test_buildVarColorIndex_Minimal(): - c = _build(ot.VarColorIndex, 1) - assert c.PaletteIndex == 1 - assert c.Alpha.value == 1.0 - assert c.Alpha.varIdx == 0xFFFFFFFF - - -def test_buildColorIndex(): - c = _build(ot.ColorIndex, (1, 0.5)) - assert c.PaletteIndex == 1 - assert c.Alpha == 0.5 - - -def test_buildVarColorIndex(): - c = _build(ot.VarColorIndex, (3, builder.VariableFloat(0.5, varIdx=2))) - assert c.PaletteIndex == 3 - assert c.Alpha.value == 0.5 - assert c.Alpha.varIdx == 2 - - def test_buildPaintSolid(): p = _buildPaint((ot.PaintFormat.PaintSolid, 0)) assert p.Format == ot.PaintFormat.PaintSolid - assert p.Color.PaletteIndex == 0 - assert p.Color.Alpha == 1.0 + assert p.PaletteIndex == 0 + assert p.Alpha == 1.0 def test_buildPaintSolid_Alpha(): - p = _buildPaint((ot.PaintFormat.PaintSolid, (1, 0.5))) + p = _buildPaint((ot.PaintFormat.PaintSolid, 1, 0.5)) assert p.Format == ot.PaintFormat.PaintSolid - assert p.Color.PaletteIndex == 1 - assert p.Color.Alpha == 0.5 + assert p.PaletteIndex == 1 + assert p.Alpha == 0.5 def test_buildPaintVarSolid(): - p = _buildPaint( - (ot.PaintFormat.PaintVarSolid, (3, builder.VariableFloat(0.5, varIdx=2))) - ) + p = _buildPaint((ot.PaintFormat.PaintVarSolid, 3, 0.5, 2)) assert p.Format == ot.PaintFormat.PaintVarSolid - assert p.Color.PaletteIndex == 3 - assert p.Color.Alpha.value == 0.5 - assert p.Color.Alpha.varIdx == 2 + assert p.PaletteIndex == 3 + assert p.Alpha == 0.5 + assert p.VarIndexBase == 2 def test_buildVarColorStop_DefaultAlpha(): s = _build(ot.ColorStop, (0.1, 2)) assert s.StopOffset == 0.1 - assert s.Color.PaletteIndex == 2 - assert s.Color.Alpha == builder._DEFAULT_ALPHA.value + assert s.PaletteIndex == 2 + assert s.Alpha == builder._DEFAULT_ALPHA def test_buildVarColorStop_DefaultAlpha(): s = _build(ot.VarColorStop, (0.1, 2)) - assert s.StopOffset == builder.VariableFloat(0.1) - assert s.Color.PaletteIndex == 2 - assert s.Color.Alpha == builder._DEFAULT_ALPHA + assert s.StopOffset == 0.1 + assert s.PaletteIndex == 2 + assert s.Alpha == builder._DEFAULT_ALPHA def test_buildColorStop(): - s = _build( - ot.ColorStop, {"StopOffset": 0.2, "Color": {"PaletteIndex": 3, "Alpha": 0.4}} - ) + s = _build(ot.ColorStop, {"StopOffset": 0.2, "PaletteIndex": 3, "Alpha": 0.4}) assert s.StopOffset == 0.2 - assert s.Color == _build(ot.ColorIndex, (3, 0.4)) + assert s.PaletteIndex == 3 + assert s.Alpha == 0.4 def test_buildColorStop_Variable(): s = _build( ot.VarColorStop, { - "StopOffset": builder.VariableFloat(0.0, varIdx=1), - "Color": { - "PaletteIndex": 0, - "Alpha": builder.VariableFloat(0.3, varIdx=2), - }, + "StopOffset": 0.0, + "PaletteIndex": 0, + "Alpha": 0.3, + "VarIndexBase": 1, }, ) - assert s.StopOffset == builder.VariableFloat(0.0, varIdx=1) - assert s.Color.PaletteIndex == 0 - assert s.Color.Alpha == builder.VariableFloat(0.3, varIdx=2) + assert s.StopOffset == 0.0 + assert s.PaletteIndex == 0 + assert s.Alpha == 0.3 + assert s.VarIndexBase == 1 def test_buildColorLine_StopList(): @@ -325,7 +296,7 @@ def test_buildColorLine_StopList(): cline = _build(ot.ColorLine, {"ColorStop": stops}) assert cline.Extend == builder.ExtendMode.PAD assert cline.StopCount == 3 - assert [(cs.StopOffset, cs.Color.PaletteIndex) for cs in cline.ColorStop] == stops + assert [(cs.StopOffset, cs.PaletteIndex) for cs in cline.ColorStop] == stops cline = _build(ot.ColorLine, {"Extend": "pad", "ColorStop": stops}) assert cline.Extend == builder.ExtendMode.PAD @@ -343,51 +314,59 @@ def test_buildColorLine_StopList(): cline = _build( ot.ColorLine, {"ColorStop": [_build(ot.ColorStop, s) for s in stops]} ) - assert [(cs.StopOffset, cs.Color.PaletteIndex) for cs in cline.ColorStop] == stops + assert [(cs.StopOffset, cs.PaletteIndex) for cs in cline.ColorStop] == stops def test_buildVarColorLine_StopMap(): stops = [ - {"StopOffset": (0.0, (1,)), "Color": {"PaletteIndex": 0, "Alpha": (0.5, 2)}}, - {"StopOffset": (1.0, (3,)), "Color": {"PaletteIndex": 1, "Alpha": (0.3, 4)}}, + {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5, "VarIndexBase": 1}, + {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 0.3, "VarIndexBase": 3}, ] cline = _build(ot.VarColorLine, {"ColorStop": stops}) assert [ { "StopOffset": cs.StopOffset, - "Color": { - "PaletteIndex": cs.Color.PaletteIndex, - "Alpha": cs.Color.Alpha, - }, + "PaletteIndex": cs.PaletteIndex, + "Alpha": cs.Alpha, + "VarIndexBase": cs.VarIndexBase, } for cs in cline.ColorStop ] == stops -def checkBuildAffine2x3(cls, resultMapFn): - matrix = _build(cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0)) - assert matrix.xx == resultMapFn(1.5) - assert matrix.yx == resultMapFn(0.0) - assert matrix.xy == resultMapFn(0.5) - assert matrix.yy == resultMapFn(2.0) - assert matrix.dx == resultMapFn(1.0) - assert matrix.dy == resultMapFn(-3.0) +def checkBuildAffine2x3(cls, variable=False): + matrix = _build( + cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0) + ((0xFFFFFFFF,) if variable else ()) + ) + assert matrix.xx == 1.5 + assert matrix.yx == 0.0 + assert matrix.xy == 0.5 + assert matrix.yy == 2.0 + assert matrix.dx == 1.0 + assert matrix.dy == -3.0 + if variable: + assert matrix.VarIndexBase == 0xFFFFFFFF def test_buildAffine2x3(): - checkBuildAffine2x3(ot.Affine2x3, lambda v: v) + checkBuildAffine2x3(ot.Affine2x3) def test_buildVarAffine2x3(): - checkBuildAffine2x3(ot.VarAffine2x3, builder.VariableFloat) + checkBuildAffine2x3(ot.VarAffine2x3, variable=True) -def _sample_stops(cls): - return [ - _build(cls, (0.0, 0)), - _build(cls, (0.5, 1)), - _build(cls, (1.0, (2, 0.8))), +def _sample_stops(variable): + cls = ot.ColorStop if not variable else ot.VarColorStop + stop_sources = [ + {"StopOffset": 0.0, "PaletteIndex": 0}, + {"StopOffset": 0.5, "PaletteIndex": 1}, + {"StopOffset": 1.0, "PaletteIndex": 2, "Alpha": 0.8}, ] + if variable: + for i, src in enumerate(stop_sources, start=123): + src["VarIndexBase"] = i + return [_build(cls, src) for src in stop_sources] def _is_var(fmt): @@ -403,34 +382,32 @@ def _is_uniform_scale(fmt): def checkBuildPaintLinearGradient(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v.value - color_stops = _sample_stops(ot.VarColorStop) - else: - inputMapFn = outputMapFn = lambda v: v - color_stops = _sample_stops(ot.ColorStop) + variable = _is_var(fmt) + color_stops = _sample_stops(variable) - x0, y0, x1, y1, x2, y2 = tuple(inputMapFn(v) for v in (1, 2, 3, 4, 5, 6)) - gradient = _buildPaint( - { - "Format": fmt, - "ColorLine": {"ColorStop": color_stops}, - "x0": x0, - "y0": y0, - "x1": x1, - "y1": y1, - "x2": x2, - "y2": y2, - }, - ) + x0, y0, x1, y1, x2, y2 = (1, 2, 3, 4, 5, 6) + source = { + "Format": fmt, + "ColorLine": {"ColorStop": color_stops}, + "x0": x0, + "y0": y0, + "x1": x1, + "y1": y1, + "x2": x2, + "y2": y2, + } + if variable: + source["VarIndexBase"] = 7 + gradient = _buildPaint(source) assert gradient.ColorLine.Extend == builder.ExtendMode.PAD assert gradient.ColorLine.ColorStop == color_stops gradient = _buildPaint(gradient) - assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == (1, 2) - assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == (3, 4) - assert (outputMapFn(gradient.x2), outputMapFn(gradient.y2)) == (5, 6) + assert (gradient.x0, gradient.y0) == (1, 2) + assert (gradient.x1, gradient.y1) == (3, 4) + assert (gradient.x2, gradient.y2) == (5, 6) + if variable: + assert gradient.VarIndexBase == 7 def test_buildPaintLinearGradient(): @@ -438,57 +415,60 @@ def test_buildPaintLinearGradient(): checkBuildPaintLinearGradient(ot.PaintFormat.PaintLinearGradient) -def test_buildVarPaintLinearGradient(): +def test_buildPaintVarLinearGradient(): assert _is_var(ot.PaintFormat.PaintVarLinearGradient) checkBuildPaintLinearGradient(ot.PaintFormat.PaintVarLinearGradient) def checkBuildPaintRadialGradient(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v - color_stops = _sample_stops(ot.VarColorStop) - line_cls = ot.VarColorLine - else: - inputMapFn = outputMapFn = lambda v: v - color_stops = _sample_stops(ot.ColorStop) - line_cls = ot.ColorLine + variable = _is_var(fmt) + color_stops = _sample_stops(variable) + line_cls = ot.VarColorLine if variable else ot.ColorLine color_line = _build( line_cls, {"ColorStop": color_stops, "Extend": builder.ExtendMode.REPEAT} ) - c0 = (inputMapFn(100), inputMapFn(200)) - c1 = (inputMapFn(150), inputMapFn(250)) - r0 = inputMapFn(10) - r1 = inputMapFn(5) + c0 = (100, 200) + c1 = (150, 250) + r0 = 10 + r1 = 5 + varIndexBase = 0 - gradient = _build(ot.Paint, (fmt, color_line, *c0, r0, *c1, r1)) + source = [fmt, color_line, *c0, r0, *c1, r1] + if variable: + source.append(varIndexBase) + + gradient = _build(ot.Paint, tuple(source)) assert gradient.Format == fmt assert gradient.ColorLine == color_line - assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == c0 - assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == c1 - assert outputMapFn(gradient.r0) == r0 - assert outputMapFn(gradient.r1) == r1 + assert (gradient.x0, gradient.y0) == c0 + assert (gradient.x1, gradient.y1) == c1 + assert gradient.r0 == r0 + assert gradient.r1 == r1 + if variable: + assert gradient.VarIndexBase == varIndexBase - gradient = _build( - ot.Paint, - { - "Format": fmt, - "ColorLine": {"ColorStop": color_stops}, - "x0": c0[0], - "y0": c0[1], - "x1": c1[0], - "y1": c1[1], - "r0": r0, - "r1": r1, - }, - ) + source = { + "Format": fmt, + "ColorLine": {"ColorStop": color_stops}, + "x0": c0[0], + "y0": c0[1], + "x1": c1[0], + "y1": c1[1], + "r0": r0, + "r1": r1, + } + if variable: + source["VarIndexBase"] = varIndexBase + gradient = _build(ot.Paint, source) assert gradient.ColorLine.Extend == builder.ExtendMode.PAD assert gradient.ColorLine.ColorStop == color_stops - assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == c0 - assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == c1 - assert outputMapFn(gradient.r0) == r0 - assert outputMapFn(gradient.r1) == r1 + assert (gradient.x0, gradient.y0) == c0 + assert (gradient.x1, gradient.y1) == c1 + assert gradient.r0 == r0 + assert gradient.r1 == r1 + if variable: + assert gradient.VarIndexBase == varIndexBase def test_buildPaintRadialGradient(): @@ -502,33 +482,26 @@ def test_buildPaintVarRadialGradient(): def checkPaintSweepGradient(fmt): - if _is_var(fmt): - outputMapFn = lambda v: v.value - else: - outputMapFn = lambda v: v - - paint = _buildPaint( - { - "Format": fmt, - "ColorLine": { - "ColorStop": ( - (0.0, 0), - (0.5, 1), - (1.0, (2, 0.8)), - ) - }, - "centerX": 127, - "centerY": 129, - "startAngle": 15, - "endAngle": 42, - } - ) + variable = _is_var(fmt) + source = { + "Format": fmt, + "ColorLine": {"ColorStop": _sample_stops(variable)}, + "centerX": 127, + "centerY": 129, + "startAngle": 15, + "endAngle": 42, + } + if variable: + source["VarIndexBase"] = 666 + paint = _buildPaint(source) assert paint.Format == fmt - assert outputMapFn(paint.centerX) == 127 - assert outputMapFn(paint.centerY) == 129 - assert outputMapFn(paint.startAngle) == 15 - assert outputMapFn(paint.endAngle) == 42 + assert paint.centerX == 127 + assert paint.centerY == 129 + assert paint.startAngle == 15 + assert paint.endAngle == 42 + if variable: + assert paint.VarIndexBase == 666 def test_buildPaintSweepGradient(): @@ -556,22 +529,19 @@ def test_buildPaintGlyph_Solid(): assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" assert layer.Paint.Format == ot.PaintFormat.PaintSolid - assert layer.Paint.Color.PaletteIndex == 2 + assert layer.Paint.PaletteIndex == 2 layer = _build( ot.Paint, ( ot.PaintFormat.PaintGlyph, - ( - ot.PaintFormat.PaintSolid, - (3, 0.9), - ), + (ot.PaintFormat.PaintSolid, 3, 0.9), "a", ), ) assert layer.Paint.Format == ot.PaintFormat.PaintSolid - assert layer.Paint.Color.PaletteIndex == 3 - assert layer.Paint.Color.Alpha == 0.9 + assert layer.Paint.PaletteIndex == 3 + assert layer.Paint.Alpha == 0.9 def test_buildPaintGlyph_VarLinearGradient(): @@ -594,14 +564,14 @@ def test_buildPaintGlyph_VarLinearGradient(): assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient - assert layer.Paint.ColorLine.ColorStop[0].StopOffset.value == 0.0 - assert layer.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 3 - assert layer.Paint.ColorLine.ColorStop[1].StopOffset.value == 1.0 - assert layer.Paint.ColorLine.ColorStop[1].Color.PaletteIndex == 4 - assert layer.Paint.x0.value == 100 - assert layer.Paint.y0.value == 200 - assert layer.Paint.x1.value == 150 - assert layer.Paint.y1.value == 250 + assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0 + assert layer.Paint.ColorLine.ColorStop[0].PaletteIndex == 3 + assert layer.Paint.ColorLine.ColorStop[1].StopOffset == 1.0 + assert layer.Paint.ColorLine.ColorStop[1].PaletteIndex == 4 + assert layer.Paint.x0 == 100 + assert layer.Paint.y0 == 200 + assert layer.Paint.x1 == 150 + assert layer.Paint.y1 == 250 def test_buildPaintGlyph_RadialGradient(): @@ -615,7 +585,7 @@ def test_buildPaintGlyph_RadialGradient(): "pad", [ (0.0, 5), - {"StopOffset": 0.5, "Color": {"PaletteIndex": 6, "Alpha": 0.8}}, + {"StopOffset": 0.5, "PaletteIndex": 6, "Alpha": 0.8}, (1.0, 7), ], ), @@ -632,12 +602,12 @@ def test_buildPaintGlyph_RadialGradient(): assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Paint.Format == ot.PaintFormat.PaintRadialGradient assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0 - assert layer.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 5 + assert layer.Paint.ColorLine.ColorStop[0].PaletteIndex == 5 assert layer.Paint.ColorLine.ColorStop[1].StopOffset == 0.5 - assert layer.Paint.ColorLine.ColorStop[1].Color.PaletteIndex == 6 - assert layer.Paint.ColorLine.ColorStop[1].Color.Alpha == 0.8 + assert layer.Paint.ColorLine.ColorStop[1].PaletteIndex == 6 + assert layer.Paint.ColorLine.ColorStop[1].Alpha == 0.8 assert layer.Paint.ColorLine.ColorStop[2].StopOffset == 1.0 - assert layer.Paint.ColorLine.ColorStop[2].Color.PaletteIndex == 7 + assert layer.Paint.ColorLine.ColorStop[2].PaletteIndex == 7 assert layer.Paint.x0 == 50 assert layer.Paint.y0 == 50 assert layer.Paint.r0 == 30 @@ -659,7 +629,7 @@ def test_buildPaintGlyph_Dict_Solid(): assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" assert layer.Paint.Format == ot.PaintFormat.PaintSolid - assert layer.Paint.Color.PaletteIndex == 1 + assert layer.Paint.PaletteIndex == 1 def test_buildPaintGlyph_Dict_VarLinearGradient(): @@ -681,7 +651,7 @@ def test_buildPaintGlyph_Dict_VarLinearGradient(): assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient - assert layer.Paint.ColorLine.ColorStop[0].StopOffset.value == 0.0 + assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0 def test_buildPaintGlyph_Dict_RadialGradient(): @@ -712,19 +682,21 @@ def test_buildPaintColrGlyph(): def checkBuildPaintTransform(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableFloat - outputMapFn = lambda v: v.value + variable = _is_var(fmt) + if variable: affine_cls = ot.VarAffine2x3 else: - inputMapFn = outputMapFn = lambda v: v affine_cls = ot.Affine2x3 + affine_src = [1, 2, 3, 4, 5, 6] + if variable: + affine_src.append(7) + paint = _buildPaint( ( int(fmt), - (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, (0, 1.0)), "a"), - _build(affine_cls, (1, 2, 3, 4, 5, 6)), + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0, 1.0), "a"), + _build(affine_cls, tuple(affine_src)), ), ) @@ -732,18 +704,23 @@ def checkBuildPaintTransform(fmt): assert paint.Paint.Format == ot.PaintFormat.PaintGlyph assert paint.Paint.Paint.Format == ot.PaintFormat.PaintSolid - assert outputMapFn(paint.Transform.xx) == 1.0 - assert outputMapFn(paint.Transform.yx) == 2.0 - assert outputMapFn(paint.Transform.xy) == 3.0 - assert outputMapFn(paint.Transform.yy) == 4.0 - assert outputMapFn(paint.Transform.dx) == 5.0 - assert outputMapFn(paint.Transform.dy) == 6.0 + assert paint.Transform.xx == 1.0 + assert paint.Transform.yx == 2.0 + assert paint.Transform.xy == 3.0 + assert paint.Transform.yy == 4.0 + assert paint.Transform.dx == 5.0 + assert paint.Transform.dy == 6.0 + if variable: + assert paint.Transform.VarIndexBase == 7 + affine_src = [1, 2, 3, 0.3333, 10, 10] + if variable: + affine_src.append(456) # VarIndexBase paint = _build( ot.Paint, { "Format": fmt, - "Transform": (1, 2, 3, 0.3333, 10, 10), + "Transform": tuple(affine_src), "Paint": { "Format": int(ot.PaintFormat.PaintRadialGradient), "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]}, @@ -758,12 +735,14 @@ def checkBuildPaintTransform(fmt): ) assert paint.Format == fmt - assert outputMapFn(paint.Transform.xx) == 1.0 - assert outputMapFn(paint.Transform.yx) == 2.0 - assert outputMapFn(paint.Transform.xy) == 3.0 - assert outputMapFn(paint.Transform.yy) == 0.3333 - assert outputMapFn(paint.Transform.dx) == 10 - assert outputMapFn(paint.Transform.dy) == 10 + assert paint.Transform.xx == 1.0 + assert paint.Transform.yx == 2.0 + assert paint.Transform.xy == 3.0 + assert paint.Transform.yy == 0.3333 + assert paint.Transform.dx == 10 + assert paint.Transform.dy == 10 + if variable: + assert paint.Transform.VarIndexBase == 456 assert paint.Paint.Format == ot.PaintFormat.PaintRadialGradient @@ -802,7 +781,8 @@ def test_buildPaintComposite(): "Glyph": "a", "Paint": { "Format": ot.PaintFormat.PaintSolid, - "Color": (0, 1.0), + "PaletteIndex": 0, + "Alpha": 0.5, }, }, }, @@ -813,44 +793,44 @@ def test_buildPaintComposite(): assert composite.SourcePaint.SourcePaint.Format == ot.PaintFormat.PaintGlyph assert composite.SourcePaint.SourcePaint.Glyph == "c" assert composite.SourcePaint.SourcePaint.Paint.Format == ot.PaintFormat.PaintSolid - assert composite.SourcePaint.SourcePaint.Paint.Color.PaletteIndex == 2 + assert composite.SourcePaint.SourcePaint.Paint.PaletteIndex == 2 assert composite.SourcePaint.CompositeMode == ot.CompositeMode.SRC_OVER assert composite.SourcePaint.BackdropPaint.Format == ot.PaintFormat.PaintGlyph assert composite.SourcePaint.BackdropPaint.Glyph == "b" assert composite.SourcePaint.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid - assert composite.SourcePaint.BackdropPaint.Paint.Color.PaletteIndex == 1 + assert composite.SourcePaint.BackdropPaint.Paint.PaletteIndex == 1 assert composite.CompositeMode == ot.CompositeMode.SRC_OVER assert composite.BackdropPaint.Format == ot.PaintFormat.PaintGlyph assert composite.BackdropPaint.Glyph == "a" assert composite.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid - assert composite.BackdropPaint.Paint.Color.PaletteIndex == 0 + assert composite.BackdropPaint.Paint.PaletteIndex == 0 + assert composite.BackdropPaint.Paint.Alpha == 0.5 def checkBuildPaintTranslate(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v.value - else: - inputMapFn = outputMapFn = lambda v: v + variable = _is_var(fmt) - paint = _build( - ot.Paint, - { - "Format": fmt, - "Paint": ( - ot.PaintFormat.PaintGlyph, - (ot.PaintFormat.PaintSolid, (0, 1.0)), - "a", - ), - "dx": 123, - "dy": -345, - }, - ) + source = { + "Format": fmt, + "Paint": ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, 0, 1.0), + "a", + ), + "dx": 123, + "dy": -345, + } + if variable: + source["VarIndexBase"] = 678 + + paint = _build(ot.Paint, source) assert paint.Format == fmt assert paint.Paint.Format == ot.PaintFormat.PaintGlyph - assert outputMapFn(paint.dx) == 123 - assert outputMapFn(paint.dy) == -345 + assert paint.dx == 123 + assert paint.dy == -345 + if variable: + assert paint.VarIndexBase == 678 def test_buildPaintTranslate(): @@ -864,11 +844,7 @@ def test_buildPaintVarTranslate(): def checkBuildPaintScale(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v.value - else: - inputMapFn = outputMapFn = lambda v: v + variable = _is_var(fmt) around_center = _is_around_center(fmt) uniform = _is_uniform_scale(fmt) @@ -876,7 +852,7 @@ def checkBuildPaintScale(fmt): "Format": fmt, "Paint": ( ot.PaintFormat.PaintGlyph, - (ot.PaintFormat.PaintSolid, (0, 1.0)), + (ot.PaintFormat.PaintSolid, 0, 1.0), "a", ), } @@ -888,19 +864,23 @@ def checkBuildPaintScale(fmt): if around_center: source["centerX"] = 127 source["centerY"] = 129 + if variable: + source["VarIndexBase"] = 666 paint = _build(ot.Paint, source) assert paint.Format == fmt assert paint.Paint.Format == ot.PaintFormat.PaintGlyph if uniform: - assert outputMapFn(paint.scale) == 1.5 + assert paint.scale == 1.5 else: - assert outputMapFn(paint.scaleX) == 1.0 - assert outputMapFn(paint.scaleY) == 2.0 + assert paint.scaleX == 1.0 + assert paint.scaleY == 2.0 if around_center: - assert outputMapFn(paint.centerX) == 127 - assert outputMapFn(paint.centerY) == 129 + assert paint.centerX == 127 + assert paint.centerY == 129 + if variable: + assert paint.VarIndexBase == 666 def test_buildPaintScale(): @@ -960,18 +940,14 @@ def test_buildPaintVarScaleUniformAroundCenter(): def checkBuildPaintRotate(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v.value - else: - inputMapFn = outputMapFn = lambda v: v + variable = _is_var(fmt) around_center = _is_around_center(fmt) source = { "Format": fmt, "Paint": ( ot.PaintFormat.PaintGlyph, - (ot.PaintFormat.PaintSolid, (0, 1.0)), + (ot.PaintFormat.PaintSolid, 0, 1.0), "a", ), "angle": 15, @@ -979,15 +955,19 @@ def checkBuildPaintRotate(fmt): if around_center: source["centerX"] = 127 source["centerY"] = 129 + if variable: + source["VarIndexBase"] = 0xFFFFFFFF paint = _build(ot.Paint, source) assert paint.Format == fmt assert paint.Paint.Format == ot.PaintFormat.PaintGlyph - assert outputMapFn(paint.angle) == 15 + assert paint.angle == 15 if around_center: - assert outputMapFn(paint.centerX) == 127 - assert outputMapFn(paint.centerY) == 129 + assert paint.centerX == 127 + assert paint.centerY == 129 + if variable: + assert paint.VarIndexBase == 0xFFFFFFFF def test_buildPaintRotate(): @@ -1015,18 +995,14 @@ def test_buildPaintVarRotateAroundCenter(): def checkBuildPaintSkew(fmt): - if _is_var(fmt): - inputMapFn = builder.VariableInt - outputMapFn = lambda v: v.value - else: - inputMapFn = outputMapFn = lambda v: v + variable = _is_var(fmt) around_center = _is_around_center(fmt) source = { "Format": fmt, "Paint": ( ot.PaintFormat.PaintGlyph, - (ot.PaintFormat.PaintSolid, (0, 1.0)), + (ot.PaintFormat.PaintSolid, 0, 1.0), "a", ), "xSkewAngle": 15, @@ -1035,16 +1011,20 @@ def checkBuildPaintSkew(fmt): if around_center: source["centerX"] = 127 source["centerY"] = 129 + if variable: + source["VarIndexBase"] = 0 paint = _build(ot.Paint, source) assert paint.Format == fmt assert paint.Paint.Format == ot.PaintFormat.PaintGlyph - assert outputMapFn(paint.xSkewAngle) == 15 - assert outputMapFn(paint.ySkewAngle) == 42 + assert paint.xSkewAngle == 15 + assert paint.ySkewAngle == 42 if around_center: - assert outputMapFn(paint.centerX) == 127 - assert outputMapFn(paint.centerY) == 129 + assert paint.centerX == 127 + assert paint.centerY == 129 + if variable: + assert paint.VarIndexBase == 0 def test_buildPaintSkew(): @@ -1087,7 +1067,8 @@ def test_buildColrV1(): ot.PaintFormat.PaintGlyph, { "Format": int(ot.PaintFormat.PaintSolid), - "Color": {"PaletteIndex": 2, "Alpha": 0.8}, + "PaletteIndex": 2, + "Alpha": 0.8, }, "e", ), @@ -1250,7 +1231,7 @@ def test_build_layerv1list_empty(): # BaseGlyph, tuple form "a": ( int(ot.PaintFormat.PaintGlyph), - (2, (2, 0.8)), + (int(ot.PaintFormat.PaintSolid), 2, 0.8), "b", ), # BaseGlyph, map form @@ -1301,7 +1282,11 @@ def _paint_names(paints) -> List[str]: def test_build_layerv1list_simple(): # Two colr glyphs, each with two layers the first of which is common # All layers use the same solid paint - solid_paint = {"Format": 2, "Color": {"PaletteIndex": 2, "Alpha": 0.8}} + solid_paint = { + "Format": int(ot.PaintFormat.PaintSolid), + "PaletteIndex": 2, + "Alpha": 0.8, + } backdrop = { "Format": int(ot.PaintFormat.PaintGlyph), "Paint": solid_paint, @@ -1357,7 +1342,11 @@ def test_build_layerv1list_simple(): def test_build_layerv1list_with_sharing(): # Three colr glyphs, each with two layers in common - solid_paint = {"Format": 2, "Color": (2, 0.8)} + solid_paint = { + "Format": int(ot.PaintFormat.PaintSolid), + "PaletteIndex": 2, + "Alpha": 0.8, + } backdrop = [ { "Format": int(ot.PaintFormat.PaintGlyph), @@ -1436,7 +1425,8 @@ def test_build_layerv1list_with_overlaps(): "Format": ot.PaintFormat.PaintGlyph, "Paint": { "Format": ot.PaintFormat.PaintSolid, - "Color": {"PaletteIndex": 2, "Alpha": 0.8}, + "PaletteIndex": 2, + "Alpha": 0.8, }, "Glyph": c, } @@ -1533,7 +1523,11 @@ class BuildCOLRTest(object): ), ( ot.PaintFormat.PaintGlyph, - {"Format": 2, "Color": {"PaletteIndex": 2, "Alpha": 0.8}}, + { + "Format": ot.PaintFormat.PaintSolid, + "PaletteIndex": 2, + "Alpha": 0.8, + }, "c", ), ], @@ -1591,7 +1585,7 @@ class BuildCOLRTest(object): ), ( ot.PaintFormat.PaintGlyph, - (ot.PaintFormat.PaintSolid, (2, 0.8)), + (ot.PaintFormat.PaintSolid, 2, 0.8), "f", ), ], diff --git a/Tests/colorLib/unbuilder_test.py b/Tests/colorLib/unbuilder_test.py index 7babb768f..b5af732f7 100644 --- a/Tests/colorLib/unbuilder_test.py +++ b/Tests/colorLib/unbuilder_test.py @@ -12,7 +12,8 @@ TEST_COLOR_GLYPHS = { "Format": int(ot.PaintFormat.PaintGlyph), "Paint": { "Format": int(ot.PaintFormat.PaintSolid), - "Color": {"PaletteIndex": 2, "Alpha": 0.5}, + "PaletteIndex": 2, + "Alpha": 0.5, }, "Glyph": "glyph00011", }, @@ -24,25 +25,32 @@ TEST_COLOR_GLYPHS = { "Extend": "repeat", "ColorStop": [ { - "StopOffset": (0.0, 0), - "Color": {"PaletteIndex": 3, "Alpha": (1.0, 0)}, + "StopOffset": 0.0, + "PaletteIndex": 3, + "Alpha": 1.0, + "VarIndexBase": 0, }, { - "StopOffset": (0.5, 0), - "Color": {"PaletteIndex": 4, "Alpha": (1.0, 0)}, + "StopOffset": 0.5, + "PaletteIndex": 4, + "Alpha": 1.0, + "VarIndexBase": 1, }, { - "StopOffset": (1.0, 0), - "Color": {"PaletteIndex": 5, "Alpha": (1.0, 0)}, + "StopOffset": 1.0, + "PaletteIndex": 5, + "Alpha": 1.0, + "VarIndexBase": 2, }, ], }, - "x0": (1, 0), - "y0": (2, 0), - "x1": (-3, 0), - "y1": (-4, 0), - "x2": (5, 0), - "y2": (6, 0), + "x0": 1, + "y0": 2, + "x1": -3, + "y1": -4, + "x2": 5, + "y2": 6, + "VarIndexBase": 0xFFFFFFFF, }, "Glyph": "glyph00012", }, @@ -57,11 +65,13 @@ TEST_COLOR_GLYPHS = { "ColorStop": [ { "StopOffset": 0, - "Color": {"PaletteIndex": 6, "Alpha": 1.0}, + "PaletteIndex": 6, + "Alpha": 1.0, }, { "StopOffset": 1.0, - "Color": {"PaletteIndex": 7, "Alpha": 0.4}, + "PaletteIndex": 7, + "Alpha": 0.4, }, ], }, @@ -73,12 +83,13 @@ TEST_COLOR_GLYPHS = { "r1": 12, }, "Transform": { - "xx": (-13.0, 0), - "yx": (14.0, 0), - "xy": (15.0, 0), - "yy": (-17.0, 0), - "dx": (18.0, 0), - "dy": (19.0, 0), + "xx": -13.0, + "yx": 14.0, + "xy": 15.0, + "yy": -17.0, + "dx": 18.0, + "dy": 19.0, + "VarIndexBase": 3, }, }, "Glyph": "glyph00013", @@ -93,17 +104,20 @@ TEST_COLOR_GLYPHS = { "Format": int(ot.PaintFormat.PaintGlyph), "Paint": { "Format": int(ot.PaintFormat.PaintSolid), - "Color": {"PaletteIndex": 2, "Alpha": 0.5}, + "PaletteIndex": 2, + "Alpha": 0.5, }, "Glyph": "glyph00011", }, - "xSkewAngle": (-11.0, 0), - "ySkewAngle": (5.0, 0), + "xSkewAngle": -11.0, + "ySkewAngle": 5.0, + "VarIndexBase": 4, }, "angle": 45.0, }, - "dx": (257.0, 0), - "dy": (258.0, 0), + "dx": 257.0, + "dy": 258.0, + "VarIndexBase": 5, }, ], }, @@ -139,11 +153,13 @@ TEST_COLOR_GLYPHS = { "ColorStop": [ { "StopOffset": 0.0, - "Color": {"PaletteIndex": 3, "Alpha": 1.0}, + "PaletteIndex": 3, + "Alpha": 1.0, }, { "StopOffset": 1.0, - "Color": {"PaletteIndex": 5, "Alpha": 1.0}, + "PaletteIndex": 5, + "Alpha": 1.0, }, ], }, @@ -161,7 +177,9 @@ TEST_COLOR_GLYPHS = { "Format": int(ot.PaintFormat.PaintGlyph), "Paint": { "Format": int(ot.PaintFormat.PaintVarSolid), - "Color": {"PaletteIndex": 2, "Alpha": (0.5, 0)}, + "PaletteIndex": 2, + "Alpha": 0.5, + "VarIndexBase": 6, }, "Glyph": "glyph00011", }, @@ -173,25 +191,32 @@ TEST_COLOR_GLYPHS = { "Extend": "repeat", "ColorStop": [ { - "StopOffset": (0.0, 0), - "Color": {"PaletteIndex": 3, "Alpha": (1.0, 0)}, + "StopOffset": 0.0, + "PaletteIndex": 3, + "Alpha": 1.0, + "VarIndexBase": 7, }, { - "StopOffset": (0.5, 0), - "Color": {"PaletteIndex": 4, "Alpha": (1.0, 0)}, + "StopOffset": 0.5, + "PaletteIndex": 4, + "Alpha": 1.0, + "VarIndexBase": 8, }, { - "StopOffset": (1.0, 0), - "Color": {"PaletteIndex": 5, "Alpha": (1.0, 0)}, + "StopOffset": 1.0, + "PaletteIndex": 5, + "Alpha": 1.0, + "VarIndexBase": 9, }, ], }, - "x0": (1, 0), - "y0": (2, 0), - "x1": (-3, 0), - "y1": (-4, 0), - "x2": (5, 0), - "y2": (6, 0), + "x0": 1, + "y0": 2, + "x1": -3, + "y1": -4, + "x2": 5, + "y2": 6, + "VarIndexBase": 0xFFFFFFFF, }, "Glyph": "glyph00012", }, From 6ba0163614ae25a957391d51acffcb5759a50838 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 22 Jul 2021 12:44:23 +0100 Subject: [PATCH 5/8] fix C_O_L_R_test.py following recent changes --- Tests/ttLib/tables/C_O_L_R_test.py | 152 ++++++++++++++--------------- 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 5da6f5939..35075ced5 100644 --- a/Tests/ttLib/tables/C_O_L_R_test.py +++ b/Tests/ttLib/tables/C_O_L_R_test.py @@ -104,13 +104,14 @@ COLR_V1_SAMPLE = ( (b"\x00\x01", "Version (1)"), (b"\x00\x01", "BaseGlyphRecordCount (1)"), ( - b"\x00\x00\x00\x1a", - "Offset to BaseGlyphRecordArray from beginning of table (26)", + b"\x00\x00\x00\x1e", + "Offset to BaseGlyphRecordArray from beginning of table (30)", ), - (b"\x00\x00\x00 ", "Offset to LayerRecordArray from beginning of table (32)"), + (b"\x00\x00\x00\x24", "Offset to LayerRecordArray from beginning of table (36)"), (b"\x00\x03", "LayerRecordCount (3)"), - (b"\x00\x00\x00,", "Offset to BaseGlyphList from beginning of table (44)"), - (b"\x00\x00\x00\xab", "Offset to LayerList from beginning of table (171)"), + (b"\x00\x00\x00\x30", "Offset to BaseGlyphList from beginning of table (48)"), + (b"\x00\x00\x00\x9b", "Offset to LayerList from beginning of table (155)"), + (b"\x00\x00\x00\x00", "Offset to DeltaSetIndexMap (NULL)"), (b"\x00\x00\x00\x00", "Offset to VarStore (NULL)"), (b"\x00\x06", "BaseGlyphRecord[0].BaseGlyph (6)"), (b"\x00\x00", "BaseGlyphRecord[0].FirstLayerIndex (0)"), @@ -135,8 +136,8 @@ COLR_V1_SAMPLE = ( ), (b"\x00\x0f", "BaseGlyphList.BaseGlyphPaintRecord[2].BaseGlyph (15)"), ( - b"\x00\x00\x00\x5e", - "Offset to Paint table from beginning of BaseGlyphList (94)", + b"\x00\x00\x00\x4a", + "Offset to Paint table from beginning of BaseGlyphList (74)", ), # BaseGlyphPaintRecord[0] (b"\x01", "BaseGlyphPaintRecord[0].Paint.Format (1)"), @@ -152,12 +153,13 @@ COLR_V1_SAMPLE = ( (b"\x00\x00\x0a", "Offset to VarAffine2x3 from beginning of PaintVarTransform (10)"), (b"\x0b", "BaseGlyphPaintRecord[1].Paint.BackdropPaint.Format (11)"), (b"\x00\x0a", "BaseGlyphPaintRecord[1].Paint.BackdropPaint.Glyph (10)"), - (b"\x00\x01\x00\x00\xff\xff\xff\xff", "VarAffine2x3.xx (1.0)"), - (b"\x00\x00\x00\x00\xff\xff\xff\xff", "VarAffine2x3.xy (0.0)"), - (b"\x00\x00\x00\x00\xff\xff\xff\xff", "VarAffine2x3.yx (0.0)"), - (b"\x00\x01\x00\x00\xff\xff\xff\xff", "VarAffine2x3.yy (1.0)"), - (b"\x01\x2c\x00\x00\xff\xff\xff\xff", "VarAffine2x3.dx (300.0)"), - (b"\x00\x00\x00\x00\xff\xff\xff\xff", "VarAffine2x3.dy (0.0)"), + (b"\x00\x01\x00\x00", "VarAffine2x3.xx (1.0)"), + (b"\x00\x00\x00\x00", "VarAffine2x3.xy (0.0)"), + (b"\x00\x00\x00\x00", "VarAffine2x3.yx (0.0)"), + (b"\x00\x01\x00\x00", "VarAffine2x3.yy (1.0)"), + (b"\x01\x2c\x00\x00", "VarAffine2x3.dx (300.0)"), + (b"\x00\x00\x00\x00", "VarAffine2x3.dy (0.0)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFF)"), (b"\x0a", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Format (10)"), (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), (b"\x00\x0b", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Glyph (11)"), @@ -170,11 +172,11 @@ COLR_V1_SAMPLE = ( (b"\x00", "ColorLine.Extend (0; pad)"), (b"\x00\x02", "ColorLine.StopCount (2)"), (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset (0.0)"), - (b"\x00\x03", "ColorLine.ColorStop[0].Color.PaletteIndex (3)"), - (b"@\x00", "ColorLine.ColorStop[0].Color.Alpha (1.0)"), + (b"\x00\x03", "ColorLine.ColorStop[0].PaletteIndex (3)"), + (b"@\x00", "ColorLine.ColorStop[0].Alpha (1.0)"), (b"@\x00", "ColorLine.ColorStop[1].StopOffset (1.0)"), - (b"\x00\x05", "ColorLine.ColorStop[1].Color.PaletteIndex (5)"), - (b"@\x00", "ColorLine.ColorStop[1].Color.Alpha (1.0)"), + (b"\x00\x05", "ColorLine.ColorStop[1].PaletteIndex (5)"), + (b"@\x00", "ColorLine.ColorStop[1].Alpha (1.0)"), # LayerList (b"\x00\x00\x00\x04", "LayerList.LayerCount (4)"), ( @@ -190,8 +192,8 @@ COLR_V1_SAMPLE = ( "Third Offset to Paint table from beginning of LayerList (78)", ), ( - b"\x00\x00\x00\xba", - "Fourth Offset to Paint table from beginning of LayerList (186)", + b"\x00\x00\x00\x9e", + "Fourth Offset to Paint table from beginning of LayerList (158)", ), # BaseGlyphPaintRecord[2] (b"\x0a", "BaseGlyphPaintRecord[2].Paint.Format (10)"), @@ -199,9 +201,9 @@ COLR_V1_SAMPLE = ( (b"\x00\x0b", "BaseGlyphPaintRecord[2].Paint.Glyph (11)"), # PaintVarSolid (b"\x03", "LayerList.Paint[0].Paint.Format (3)"), - (b"\x00\x02", "Paint.Color.PaletteIndex (2)"), - (b" \x00", "Paint.Color.Alpha.value (0.5)"), - (b"\xff\xff\xff\xff", "Paint.Color.Alpha.varIdx (0xFFFFFFFF)"), + (b"\x00\x02", "Paint.PaletteIndex (2)"), + (b" \x00", "Paint.Alpha.value (0.5)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), # PaintGlyph glyph00012 (b"\x0a", "LayerList.Paint[1].Format (10)"), (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), @@ -217,37 +219,40 @@ COLR_V1_SAMPLE = ( (b"\x01", "ColorLine.Extend (1; repeat)"), (b"\x00\x03", "ColorLine.StopCount (3)"), (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset (0.0)"), - (b"\x00\x03", "ColorLine.ColorStop[0].Color.PaletteIndex (3)"), - (b"@\x00", "ColorLine.ColorStop[0].Color.Alpha (1.0)"), + (b"\x00\x03", "ColorLine.ColorStop[0].PaletteIndex (3)"), + (b"@\x00", "ColorLine.ColorStop[0].Alpha (1.0)"), (b" \x00", "ColorLine.ColorStop[1].StopOffset (0.5)"), - (b"\x00\x04", "ColorLine.ColorStop[1].Color.PaletteIndex (4)"), - (b"@\x00", "ColorLine.ColorStop[1].Color.Alpha (1.0)"), + (b"\x00\x04", "ColorLine.ColorStop[1].PaletteIndex (4)"), + (b"@\x00", "ColorLine.ColorStop[1].Alpha (1.0)"), (b"@\x00", "ColorLine.ColorStop[2].StopOffset (1.0)"), - (b"\x00\x05", "ColorLine.ColorStop[2].Color.PaletteIndex (5)"), - (b"@\x00", "ColorLine.ColorStop[2].Color.Alpha (1.0)"), + (b"\x00\x05", "ColorLine.ColorStop[2].PaletteIndex (5)"), + (b"@\x00", "ColorLine.ColorStop[2].Alpha (1.0)"), # PaintGlyph glyph00013 (b"\x0a", "LayerList.Paint[2].Format (10)"), (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), (b"\x00\x0d", "LayerList.Paint[2].Glyph (13)"), (b"\x0c", "LayerList.Paint[2].Paint.Format (12)"), (b"\x00\x00\x07", "Offset to Paint subtable from beginning of PaintTransform (7)"), - (b"\x00\x00\x4e", "Offset to Affine2x3 subtable from beginning of PaintTransform (78)"), + (b"\x00\x00\x32", "Offset to Affine2x3 subtable from beginning of PaintTransform (50)"), (b"\x07", "LayerList.Paint[2].Paint.Paint.Format (7)"), - (b"\x00\x00(", "Offset to ColorLine from beginning of PaintVarRadialGradient (40)"), - (b"\x00\x07\xff\xff\xff\xff", "Paint.x0.value (7)"), - (b"\x00\x08\xff\xff\xff\xff", "Paint.y0.value (8)"), - (b"\x00\t\xff\xff\xff\xff", "Paint.r0.value (9)"), - (b"\x00\n\xff\xff\xff\xff", "Paint.x1.value (10)"), - (b"\x00\x0b\xff\xff\xff\xff", "Paint.y1.value (11)"), - (b"\x00\x0c\xff\xff\xff\xff", "Paint.r1.value (12)"), + (b"\x00\x00\x14", "Offset to ColorLine from beginning of PaintVarRadialGradient (20)"), + (b"\x00\x07", "Paint.x0.value (7)"), + (b"\x00\x08", "Paint.y0.value (8)"), + (b"\x00\t", "Paint.r0.value (9)"), + (b"\x00\n", "Paint.x1.value (10)"), + (b"\x00\x0b", "Paint.y1.value (11)"), + (b"\x00\x0c", "Paint.r1.value (12)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), (b"\x00", "ColorLine.Extend (0; pad)"), (b"\x00\x02", "ColorLine.StopCount (2)"), - (b"\x00\x00\xff\xff\xff\xff", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), - (b"\x00\x06", "ColorLine.ColorStop[0].Color.PaletteIndex (6)"), - (b"@\x00\xff\xff\xff\xff", "ColorLine.ColorStop[0].Color.Alpha.value (1.0)"), - (b"@\x00\xff\xff\xff\xff", "ColorLine.ColorStop[1].StopOffset.value (1.0)"), - (b"\x00\x07", "ColorLine.ColorStop[1].Color.PaletteIndex (7)"), - (b"\x19\x9a\xff\xff\xff\xff", "ColorLine.ColorStop[1].Color.Alpha.value (0.4)"), + (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), + (b"\x00\x06", "ColorLine.ColorStop[0].PaletteIndex (6)"), + (b"@\x00", "ColorLine.ColorStop[0].Alpha.value (1.0)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), + (b"@\x00", "ColorLine.ColorStop[1].StopOffset.value (1.0)"), + (b"\x00\x07", "ColorLine.ColorStop[1].PaletteIndex (7)"), + (b"\x19\x9a", "ColorLine.ColorStop[1].Alpha.value (0.4)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), (b"\xff\xf3\x00\x00", "Affine2x3.xx (-13)"), (b"\x00\x0e\x00\x00", "Affine2x3.xy (14)"), @@ -288,8 +293,8 @@ COLR_V1_SAMPLE = ( # PaintSolid (b"\x02", "LayerList.Paint[0].Paint.Paint.Paint.Paint.Format (2)"), - (b"\x00\x02", "Paint.Color.PaletteIndex (2)"), - (b" \x00", "Paint.Color.Alpha (0.5)"), + (b"\x00\x02", "Paint.PaletteIndex (2)"), + (b" \x00", "Paint.Alpha (0.5)"), ) COLR_V1_DATA = b"".join(t[0] for t in COLR_V1_SAMPLE) @@ -346,6 +351,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', + ' ', " ", " ", " ", @@ -359,17 +365,13 @@ COLR_V1_XML = [ " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", " ", ' ', @@ -385,10 +387,9 @@ COLR_V1_XML = [ " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', + ' ', " ", ' ', " ", @@ -399,24 +400,18 @@ COLR_V1_XML = [ " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", " ", ' ', @@ -436,17 +431,15 @@ COLR_V1_XML = [ " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', + ' ', " ", ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', + ' ', " ", " ", ' ', @@ -455,6 +448,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', + ' ', " ", " ", ' ', @@ -472,10 +466,8 @@ COLR_V1_XML = [ ' ', ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", ' ', " ", From 11871673ff68f36f4e9e8379ab62e8551d57e802 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 22 Jul 2021 12:56:22 +0100 Subject: [PATCH 6/8] [subset] fixed CPAL pruning after removal of ColorIndex --- Lib/fontTools/subset/__init__.py | 6 +++--- Tests/subset/subset_test.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index 8fa98f40e..8616053b6 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -2117,11 +2117,11 @@ def prune_post_subset(self, font, options): colors_by_index = defaultdict(list) def collect_colors_by_index(paint): - if hasattr(paint, "Color"): # either solid colors... - colors_by_index[paint.Color.PaletteIndex].append(paint.Color) + if hasattr(paint, "PaletteIndex"): # either solid colors... + colors_by_index[paint.PaletteIndex].append(paint) elif hasattr(paint, "ColorLine"): # ... or gradient color stops for stop in paint.ColorLine.ColorStop: - colors_by_index[stop.Color.PaletteIndex].append(stop.Color) + colors_by_index[stop.PaletteIndex].append(stop) if colr.version == 0: for layers in colr.ColorLayers.values(): diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py index feee47b24..aa96fb7cb 100644 --- a/Tests/subset/subset_test.py +++ b/Tests/subset/subset_test.py @@ -1013,7 +1013,7 @@ def colrv1_path(tmp_path): }, { "Format": ot.PaintFormat.PaintGlyph, - "Paint": (ot.PaintFormat.PaintSolid, (2, 0.3)), + "Paint": (ot.PaintFormat.PaintSolid, 2, 0.3), "Glyph": "glyph00011", }, ], @@ -1044,7 +1044,7 @@ def colrv1_path(tmp_path): }, { "Format": ot.PaintFormat.PaintGlyph, - "Paint": (ot.PaintFormat.PaintSolid, (1, 0.5)), + "Paint": (ot.PaintFormat.PaintSolid, 1, 0.5), "Glyph": "glyph00013", }, ], @@ -1159,9 +1159,9 @@ def test_subset_COLRv1_and_CPAL(colrv1_path): ] assert len(layers) == 2 # check v1 palette indices were remapped - assert layers[0].Paint.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 0 - assert layers[0].Paint.Paint.ColorLine.ColorStop[1].Color.PaletteIndex == 1 - assert layers[1].Paint.Color.PaletteIndex == 0 + assert layers[0].Paint.Paint.ColorLine.ColorStop[0].PaletteIndex == 0 + assert layers[0].Paint.Paint.ColorLine.ColorStop[1].PaletteIndex == 1 + assert layers[1].Paint.PaletteIndex == 0 baseRecV0 = colr.BaseGlyphRecordArray.BaseGlyphRecord[0] assert baseRecV0.BaseGlyph == "uniE004" From 29a2ebf813665fb9a2bbd773a925675505e9ee42 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 22 Jul 2021 19:25:29 +0100 Subject: [PATCH 7/8] omit default VarIndexBase 4294967295 (0xFFFFFFFF in decimal form) is not very memorable. Still, using hex notation for all VarIndexBases would make the non-default values less readable (when interpreted as an index into the DeltaSetIndexMap array, decimal makes more sense). Since 0xFFFFFFFF means 'no variation data', it makes sense to omit it from the ttx dump and write an empty element with no value. We also allow to build Var tables without needing to pass "VarIndexBase": 0xFFFFFFFF in the source dict. --- Lib/fontTools/colorLib/table_builder.py | 5 ++++ Lib/fontTools/ttLib/tables/otConverters.py | 16 ++++++++++++ Lib/fontTools/ttLib/tables/otData.py | 30 +++++++++++----------- Tests/colorLib/builder_test.py | 6 +---- Tests/ttLib/tables/C_O_L_R_test.py | 14 +++++----- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/Lib/fontTools/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py index 98a27e479..763115b96 100644 --- a/Lib/fontTools/colorLib/table_builder.py +++ b/Lib/fontTools/colorLib/table_builder.py @@ -19,6 +19,7 @@ from fontTools.ttLib.tables.otConverters import ( UShort, IntValue, FloatValue, + OptionalValue, ) from fontTools.misc.roundTools import otRound @@ -171,6 +172,10 @@ class TableBuilder: # let's try as a 1-tuple dest = self.build(cls, (source,)) + for field, conv in convByName.items(): + if not hasattr(dest, field) and isinstance(conv, OptionalValue): + setattr(dest, field, conv.DEFAULT) + dest = self._callbackTable.get( (BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d )(dest) diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py index ecc1057a4..058813a81 100644 --- a/Lib/fontTools/ttLib/tables/otConverters.py +++ b/Lib/fontTools/ttLib/tables/otConverters.py @@ -226,6 +226,18 @@ class SimpleValue(BaseConverter): 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): @@ -258,6 +270,9 @@ class Flags32(ULong): def toString(value): return "0x%08X" % value +class VarIndex(OptionalValue, ULong): + DEFAULT = 0xFFFFFFFF + class Short(IntValue): staticSize = 2 def read(self, reader, font, tableDict): @@ -1732,6 +1747,7 @@ converterMapping = { "uint32": ULong, "char64": Char64, "Flags32": Flags32, + "VarIndex": VarIndex, "Version": Version, "Tag": Tag, "GlyphID": GlyphID, diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 056b68117..e33370829 100755 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -1624,7 +1624,7 @@ otData = [ ('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'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorStop', [ @@ -1636,7 +1636,7 @@ otData = [ ('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'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorLine', [ @@ -1668,7 +1668,7 @@ otData = [ ('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'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintLinearGradient @@ -1692,7 +1692,7 @@ otData = [ ('int16', 'y1', None, None, ''), ('int16', 'x2', None, None, ''), ('int16', 'y2', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRadialGradient @@ -1716,7 +1716,7 @@ otData = [ ('int16', 'x1', None, None, ''), ('int16', 'y1', None, None, ''), ('uint16', 'r1', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSweepGradient @@ -1736,7 +1736,7 @@ otData = [ ('int16', 'centerY', None, None, 'Center y coordinate.'), ('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'), ('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintGlyph @@ -1778,7 +1778,7 @@ otData = [ ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarTranslate table) to Paint subtable.'), ('int16', 'dx', None, None, 'Translation in x direction.'), ('int16', 'dy', None, None, 'Translation in y direction.'), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScale @@ -1794,7 +1794,7 @@ otData = [ ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScale table) to Paint subtable.'), ('F2Dot14', 'scaleX', None, None, ''), ('F2Dot14', 'scaleY', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleAroundCenter @@ -1814,7 +1814,7 @@ otData = [ ('F2Dot14', 'scaleY', None, None, ''), ('int16', 'centerX', None, None, ''), ('int16', 'centerY', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniform @@ -1828,7 +1828,7 @@ otData = [ ('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, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniformAroundCenter @@ -1846,7 +1846,7 @@ otData = [ ('F2Dot14', 'scale', None, None, ''), ('int16', 'centerX', None, None, ''), ('int16', 'centerY', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotate @@ -1860,7 +1860,7 @@ otData = [ ('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, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotateAroundCenter @@ -1878,7 +1878,7 @@ otData = [ ('Angle', 'angle', None, None, ''), ('int16', 'centerX', None, None, ''), ('int16', 'centerY', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkew @@ -1894,7 +1894,7 @@ otData = [ ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkew table) to Paint subtable.'), ('Angle', 'xSkewAngle', None, None, ''), ('Angle', 'ySkewAngle', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkewAroundCenter @@ -1914,7 +1914,7 @@ otData = [ ('Angle', 'ySkewAngle', None, None, ''), ('int16', 'centerX', None, None, ''), ('int16', 'centerY', None, None, ''), - ('uint32', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintComposite diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 8d30ca5ee..987841aec 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -335,9 +335,7 @@ def test_buildVarColorLine_StopMap(): def checkBuildAffine2x3(cls, variable=False): - matrix = _build( - cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0) + ((0xFFFFFFFF,) if variable else ()) - ) + matrix = _build(cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0)) assert matrix.xx == 1.5 assert matrix.yx == 0.0 assert matrix.xy == 0.5 @@ -955,8 +953,6 @@ def checkBuildPaintRotate(fmt): if around_center: source["centerX"] = 127 source["centerY"] = 129 - if variable: - source["VarIndexBase"] = 0xFFFFFFFF paint = _build(ot.Paint, source) diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 35075ced5..73ba74d75 100644 --- a/Tests/ttLib/tables/C_O_L_R_test.py +++ b/Tests/ttLib/tables/C_O_L_R_test.py @@ -248,11 +248,11 @@ COLR_V1_SAMPLE = ( (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), (b"\x00\x06", "ColorLine.ColorStop[0].PaletteIndex (6)"), (b"@\x00", "ColorLine.ColorStop[0].Alpha.value (1.0)"), - (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), + (b"\x00\x00\x00\x00", "VarIndexBase (0)"), (b"@\x00", "ColorLine.ColorStop[1].StopOffset.value (1.0)"), (b"\x00\x07", "ColorLine.ColorStop[1].PaletteIndex (7)"), (b"\x19\x9a", "ColorLine.ColorStop[1].Alpha.value (0.4)"), - (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), + (b"\x00\x00\x00\x01", "VarIndexBase (1)"), (b"\xff\xf3\x00\x00", "Affine2x3.xx (-13)"), (b"\x00\x0e\x00\x00", "Affine2x3.xy (14)"), @@ -351,7 +351,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", " ", " ", @@ -389,7 +389,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", ' ', " ", @@ -433,13 +433,13 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", ' ', ' ', ' ', ' ', - ' ', + ' ', " ", " ", ' ', @@ -448,7 +448,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", " ", ' ', From b74ffb4cda018c05cf8002e6718041d8b2eb6469 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 23 Jul 2021 17:25:05 +0100 Subject: [PATCH 8/8] test roundtripping variable COLRv1 with a working VarIndexMap and VarStore --- Tests/ttLib/tables/C_O_L_R_test.py | 77 ++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 73ba74d75..f170dbe58 100644 --- a/Tests/ttLib/tables/C_O_L_R_test.py +++ b/Tests/ttLib/tables/C_O_L_R_test.py @@ -159,7 +159,7 @@ COLR_V1_SAMPLE = ( (b"\x00\x01\x00\x00", "VarAffine2x3.yy (1.0)"), (b"\x01\x2c\x00\x00", "VarAffine2x3.dx (300.0)"), (b"\x00\x00\x00\x00", "VarAffine2x3.dy (0.0)"), - (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFF)"), + (b"\x00\x00\x00\x00", "VarIndexBase (0)"), (b"\x0a", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Format (10)"), (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), (b"\x00\x0b", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Glyph (11)"), @@ -203,7 +203,7 @@ COLR_V1_SAMPLE = ( (b"\x03", "LayerList.Paint[0].Paint.Format (3)"), (b"\x00\x02", "Paint.PaletteIndex (2)"), (b" \x00", "Paint.Alpha.value (0.5)"), - (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), + (b"\x00\x00\x00\x06", "VarIndexBase (6)"), # PaintGlyph glyph00012 (b"\x0a", "LayerList.Paint[1].Format (10)"), (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), @@ -248,12 +248,12 @@ COLR_V1_SAMPLE = ( (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), (b"\x00\x06", "ColorLine.ColorStop[0].PaletteIndex (6)"), (b"@\x00", "ColorLine.ColorStop[0].Alpha.value (1.0)"), - (b"\x00\x00\x00\x00", "VarIndexBase (0)"), + (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"), (b"@\x00", "ColorLine.ColorStop[1].StopOffset.value (1.0)"), (b"\x00\x07", "ColorLine.ColorStop[1].PaletteIndex (7)"), (b"\x19\x9a", "ColorLine.ColorStop[1].Alpha.value (0.4)"), - (b"\x00\x00\x00\x01", "VarIndexBase (1)"), + (b"\x00\x00\x00\x07", "VarIndexBase (7)"), (b"\xff\xf3\x00\x00", "Affine2x3.xx (-13)"), (b"\x00\x0e\x00\x00", "Affine2x3.xy (14)"), (b"\x00\x0f\x00\x00", "Affine2x3.yx (15)"), @@ -351,7 +351,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", " ", " ", @@ -389,7 +389,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", ' ', " ", @@ -433,13 +433,13 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + " ", " ", ' ', ' ', ' ', ' ', - ' ', + ' ', " ", " ", ' ', @@ -448,7 +448,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + " ", " ", " ", ' ', @@ -484,6 +484,52 @@ COLR_V1_XML = [ "", ] +COLR_V1_VAR_XML = [ + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + "", + '', + ' ', + " ", + " ", + " ", + ' ', + ' ', + ' ', + ' ', + ' ', + " ", + " ", + " ", + " ", + ' ', + " ", + ' ', + " ", + ' ', + ' ', + ' ', + ' ', + " ", + ' ', + " ", + ' ', + " ", + ' ', + ' ', + ' ', + " ", + "", +] + class COLR_V1_Test(object): def test_decompile_and_compile(self, font): @@ -513,3 +559,16 @@ class COLR_V1_Test(object): colr = table_C_O_L_R_() colr.decompile(compiled, font) assert getXML(colr.toXML, font) == COLR_V1_XML + + +class COLR_V1_Variable_Test(object): + def test_round_trip_xml(self, font): + colr = table_C_O_L_R_() + xml = COLR_V1_XML + COLR_V1_VAR_XML + for name, attrs, content in parseXML(xml): + colr.fromXML(name, attrs, content, font) + compiled = colr.compile(font) + + colr = table_C_O_L_R_() + colr.decompile(compiled, font) + assert getXML(colr.toXML, font) == xml