Merge pull request #2372 from fonttools/colrv1-var-idx-map

[COLRv1] add DeltaSetIndexMap, remove ColorIndex
This commit is contained in:
Cosimo Lupo 2021-07-26 12:06:15 +01:00 committed by GitHub
commit 2f1fbd6374
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 710 additions and 702 deletions

View File

@ -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

View File

@ -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:

View File

@ -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():

View File

@ -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,
}

View File

@ -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

View File

@ -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<<innerBits)-1))
if ored <= 0x000000FF:
entrySize = 1
elif ored <= 0x0000FFFF:
entrySize = 2
elif ored <= 0x00FFFFFF:
entrySize = 3
else:
entrySize = 4
return ((entrySize - 1) << 4) | (innerBits - 1)
def preWrite(self, font):
mapping = getattr(self, "mapping", None)
if mapping is None:
mapping = self.mapping = []
self.Format = 1 if len(mapping) > 0xFFFF else 0
rawTable = self.__dict__.copy()
rawTable['MappingCount'] = len(mapping)
rawTable['EntryFormat'] = self.getEntryFormat(mapping)
return rawTable
def toXML2(self, xmlWriter, font):
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<<innerBits)-1))
if ored <= 0x000000FF:
entrySize = 1
elif ored <= 0x0000FFFF:
entrySize = 2
elif ored <= 0x00FFFFFF:
entrySize = 3
else:
entrySize = 4
entryFormat = ((entrySize - 1) << 4) | (innerBits - 1)
rawTable['EntryFormat'] = entryFormat
rawTable['EntryFormat'] = DeltaSetIndexMap.getEntryFormat(mapping)
return rawTable
def toXML2(self, xmlWriter, font):
@ -1255,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

View File

@ -121,6 +121,14 @@ def buildVarIdxMap(varIdxes, glyphOrder):
self.mapping = {g:v for g,v in zip(glyphOrder, varIdxes)}
return self
def buildDeltaSetIndexMap(varIdxes):
self = ot.DeltaSetIndexMap()
self.mapping = list(varIdxes)
self.Format = 1 if len(varIdxes) > 0xFFFF else 0
return self
def buildVarDevTable(varIdx):
self = ot.Device()
self.DeltaFormat = 0x8000

View File

@ -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",
),
],

View File

@ -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",
},

View File

@ -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"

View File

@ -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 = [
' <yy value="1.0"/>',
' <dx value="300.0"/>',
' <dy value="0.0"/>',
' <VarIndexBase value="0"/>',
" </Transform>",
" </BackdropPaint>",
" </Paint>",
@ -359,17 +365,13 @@ COLR_V1_XML = [
" <!-- StopCount=2 -->",
' <ColorStop index="0">',
' <StopOffset value="0.0"/>',
" <Color>",
' <PaletteIndex value="3"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="3"/>',
' <Alpha value="1.0"/>',
" </ColorStop>",
' <ColorStop index="1">',
' <StopOffset value="1.0"/>',
" <Color>",
' <PaletteIndex value="5"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="5"/>',
' <Alpha value="1.0"/>',
" </ColorStop>",
" </ColorLine>",
' <centerX value="259"/>',
@ -385,10 +387,9 @@ COLR_V1_XML = [
" <!-- LayerCount=4 -->",
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
' <Paint Format="3"><!-- PaintVarSolid -->',
" <Color>",
' <PaletteIndex value="2"/>',
' <Alpha value="0.5"/>',
" </Color>",
' <PaletteIndex value="2"/>',
' <Alpha value="0.5"/>',
' <VarIndexBase value="6"/>',
" </Paint>",
' <Glyph value="glyph00011"/>',
" </Paint>",
@ -399,24 +400,18 @@ COLR_V1_XML = [
" <!-- StopCount=3 -->",
' <ColorStop index="0">',
' <StopOffset value="0.0"/>',
" <Color>",
' <PaletteIndex value="3"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="3"/>',
' <Alpha value="1.0"/>',
" </ColorStop>",
' <ColorStop index="1">',
' <StopOffset value="0.5"/>',
" <Color>",
' <PaletteIndex value="4"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="4"/>',
' <Alpha value="1.0"/>',
" </ColorStop>",
' <ColorStop index="2">',
' <StopOffset value="1.0"/>',
" <Color>",
' <PaletteIndex value="5"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="5"/>',
' <Alpha value="1.0"/>',
" </ColorStop>",
" </ColorLine>",
' <x0 value="1"/>',
@ -436,17 +431,15 @@ COLR_V1_XML = [
" <!-- StopCount=2 -->",
' <ColorStop index="0">',
' <StopOffset value="0.0"/>',
" <Color>",
' <PaletteIndex value="6"/>',
' <Alpha value="1.0"/>',
" </Color>",
' <PaletteIndex value="6"/>',
' <Alpha value="1.0"/>',
" <VarIndexBase/>",
" </ColorStop>",
' <ColorStop index="1">',
' <StopOffset value="1.0"/>',
" <Color>",
' <PaletteIndex value="7"/>',
' <Alpha value="0.4"/>',
" </Color>",
' <PaletteIndex value="7"/>',
' <Alpha value="0.4"/>',
' <VarIndexBase value="7"/>',
" </ColorStop>",
" </ColorLine>",
' <x0 value="7"/>',
@ -455,6 +448,7 @@ COLR_V1_XML = [
' <x1 value="10"/>',
' <y1 value="11"/>',
' <r1 value="12"/>',
" <VarIndexBase/>",
" </Paint>",
" <Transform>",
' <xx value="-13.0"/>',
@ -472,10 +466,8 @@ COLR_V1_XML = [
' <Paint Format="28"><!-- PaintSkew -->',
' <Paint Format="10"><!-- PaintGlyph -->',
' <Paint Format="2"><!-- PaintSolid -->',
" <Color>",
' <PaletteIndex value="2"/>',
' <Alpha value="0.5"/>',
" </Color>",
' <PaletteIndex value="2"/>',
' <Alpha value="0.5"/>',
" </Paint>",
' <Glyph value="glyph00011"/>',
" </Paint>",
@ -492,6 +484,52 @@ COLR_V1_XML = [
"</LayerList>",
]
COLR_V1_VAR_XML = [
'<VarIndexMap Format="0">',
' <Map index="0" outer="1" inner="1"/>',
' <Map index="1" outer="1" inner="0"/>',
' <Map index="2" outer="1" inner="0"/>',
' <Map index="3" outer="1" inner="1"/>',
' <Map index="4" outer="1" inner="0"/>',
' <Map index="5" outer="1" inner="0"/>',
' <Map index="6" outer="0" inner="2"/>',
' <Map index="7" outer="0" inner="0"/>',
' <Map index="8" outer="0" inner="1"/>',
"</VarIndexMap>",
'<VarStore Format="1">',
' <Format value="1"/>',
" <VarRegionList>",
" <!-- RegionAxisCount=1 -->",
" <!-- RegionCount=1 -->",
' <Region index="0">',
' <VarRegionAxis index="0">',
' <StartCoord value="0.0"/>',
' <PeakCoord value="1.0"/>',
' <EndCoord value="1.0"/>',
" </VarRegionAxis>",
" </Region>",
" </VarRegionList>",
" <!-- VarDataCount=2 -->",
' <VarData index="0">',
" <!-- ItemCount=3 -->",
' <NumShorts value="1"/>',
" <!-- VarRegionCount=1 -->",
' <VarRegionIndex index="0" value="0"/>',
' <Item index="0" value="[-3277]"/>',
' <Item index="1" value="[6553]"/>',
' <Item index="2" value="[8192]"/>',
" </VarData>",
' <VarData index="1">',
" <!-- ItemCount=2 -->",
' <NumShorts value="32769"/>',
" <!-- VarRegionCount=1 -->",
' <VarRegionIndex index="0" value="0"/>',
' <Item index="0" value="[0]"/>',
' <Item index="1" value="[65536]"/>',
" </VarData>",
"</VarStore>",
]
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