diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index dec832335..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,53 +42,46 @@ _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 + return colorStop -def _defaultColorIndex(): - colorIndex = ot.ColorIndex() - colorIndex.Alpha = _DEFAULT_ALPHA.value - return colorIndex - - -def _defaultVarColorIndex(): - colorIndex = ot.VarColorIndex() - colorIndex.Alpha = _DEFAULT_ALPHA - return colorIndex +def _defaultVarColorStop(): + colorStop = ot.VarColorStop() + colorStop.Alpha = _DEFAULT_ALPHA + return colorStop def _defaultColorLine(): @@ -113,6 +96,12 @@ def _defaultVarColorLine(): return colorLine +def _defaultPaintSolid(): + paint = ot.Paint() + paint.Alpha = _DEFAULT_ALPHA + return paint + + def _buildPaintCallbacks(): return { ( @@ -124,11 +113,21 @@ def _buildPaintCallbacks(): BuildCallback.BEFORE_BUILD, ot.Paint, ot.PaintFormat.PaintVarRadialGradient, - ): _beforeBuildPaintVarRadialGradient, - (BuildCallback.CREATE_DEFAULT, ot.ColorIndex): _defaultColorIndex, - (BuildCallback.CREATE_DEFAULT, ot.VarColorIndex): _defaultVarColorIndex, + ): _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.Paint, + ot.PaintFormat.PaintSolid, + ): _defaultPaintSolid, + ( + BuildCallback.CREATE_DEFAULT, + ot.Paint, + ot.PaintFormat.PaintVarSolid, + ): _defaultPaintSolid, } @@ -183,6 +182,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 +196,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 +230,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/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py index 6fba6b0fc..763115b96 100644 --- a/Lib/fontTools/colorLib/table_builder.py +++ b/Lib/fontTools/colorLib/table_builder.py @@ -17,10 +17,9 @@ from fontTools.ttLib.tables.otConverters import ( Short, UInt8, UShort, - VarInt16, - VarUInt16, IntValue, FloatValue, + OptionalValue, ) from fontTools.misc.roundTools import otRound @@ -39,7 +38,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. """ @@ -50,37 +49,29 @@ 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) -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: @@ -97,13 +88,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): @@ -140,6 +127,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 +142,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): @@ -182,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) @@ -210,11 +204,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/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/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py index 1976acaa7..058813a81 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 @@ -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): @@ -1700,104 +1715,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 @@ -1830,6 +1747,7 @@ converterMapping = { "uint32": ULong, "char64": Char64, "Flags32": Flags32, + "VarIndex": VarIndex, "Version": Version, "Tag": Tag, "GlyphID": GlyphID, @@ -1867,11 +1785,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/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 175fa958c..e33370829 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,30 +1618,25 @@ 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'), - ]), - - ('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.'), - ('VarF2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'), + ('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'), + ('VarIndex', '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', [ - ('VarF2Dot14', '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'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), ('ColorLine', [ @@ -1650,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'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintLinearGradient @@ -1673,12 +1686,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRadialGradient @@ -1696,12 +1710,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSweepGradient @@ -1717,10 +1732,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.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintGlyph @@ -1760,8 +1776,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.'), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScale @@ -1775,8 +1792,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleAroundCenter @@ -1792,10 +1810,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniform @@ -1808,7 +1827,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintScaleUniformAroundCenter @@ -1823,9 +1843,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotate @@ -1838,7 +1859,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintRotateAroundCenter @@ -1853,9 +1875,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkew @@ -1869,8 +1892,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, ''), + ('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'), ]), # PaintSkewAroundCenter @@ -1886,10 +1910,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, ''), + ('VarIndex', '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..339331873 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 diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 2c47b5fb8..987841aec 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,57 @@ 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): +def checkBuildAffine2x3(cls, variable=False): 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) + 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 +380,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 +413,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 +480,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 +527,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 +562,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 +583,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 +600,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 +627,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 +649,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 +680,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 +702,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 +733,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 +779,8 @@ def test_buildPaintComposite(): "Glyph": "a", "Paint": { "Format": ot.PaintFormat.PaintSolid, - "Color": (0, 1.0), + "PaletteIndex": 0, + "Alpha": 0.5, }, }, }, @@ -813,44 +791,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 +842,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 +850,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 +862,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 +938,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, @@ -984,10 +958,12 @@ def checkBuildPaintRotate(fmt): 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 +991,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 +1007,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 +1063,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 +1227,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 +1278,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 +1338,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 +1421,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 +1519,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 +1581,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", }, 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" diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 5da6f5939..f170dbe58 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"\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)"), @@ -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"\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)"), @@ -217,38 +219,41 @@ 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"\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)"), @@ -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 = [ ' ', ' ', ' ', - " ", - ' ', - ' ', - " ", + ' ', + ' ', " ", ' ', " ", @@ -492,6 +484,52 @@ COLR_V1_XML = [ "", ] +COLR_V1_VAR_XML = [ + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + "", + '', + ' ', + " ", + " ", + " ", + ' ', + ' ', + ' ', + ' ', + ' ', + " ", + " ", + " ", + " ", + ' ', + " ", + ' ', + " ", + ' ', + ' ', + ' ', + ' ', + " ", + ' ', + " ", + ' ', + " ", + ' ', + ' ', + ' ', + " ", + "", +] + class COLR_V1_Test(object): def test_decompile_and_compile(self, font): @@ -521,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