2022-06-24 18:16:28 +01:00
|
|
|
from copy import deepcopy
|
|
|
|
import string
|
2022-07-05 00:11:12 +01:00
|
|
|
from fontTools.colorLib.builder import LayerListBuilder, buildCOLR, buildClipList
|
2022-06-24 18:16:28 +01:00
|
|
|
from fontTools.misc.testTools import getXML
|
|
|
|
from fontTools.varLib.merger import COLRVariationMerger
|
|
|
|
from fontTools.varLib.models import VariationModel
|
|
|
|
from fontTools.ttLib import TTFont
|
|
|
|
from fontTools.ttLib.tables import otTables as ot
|
|
|
|
from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
NO_VARIATION_INDEX = ot.NO_VARIATION_INDEX
|
|
|
|
|
|
|
|
|
|
|
|
def dump_xml(table, ttFont=None):
|
|
|
|
xml = getXML(table.toXML, ttFont)
|
|
|
|
print("[")
|
|
|
|
for line in xml:
|
|
|
|
print(f" {line!r},")
|
|
|
|
print("]")
|
|
|
|
return xml
|
|
|
|
|
|
|
|
|
|
|
|
def compile_decompile(table, ttFont):
|
|
|
|
writer = OTTableWriter(tableTag="COLR")
|
2022-07-05 00:11:12 +01:00
|
|
|
# compile itself may modify a table, safer to copy it first
|
|
|
|
table = deepcopy(table)
|
2022-06-24 18:16:28 +01:00
|
|
|
table.compile(writer, ttFont)
|
|
|
|
data = writer.getAllData()
|
|
|
|
|
|
|
|
reader = OTTableReader(data, tableTag="COLR")
|
|
|
|
table2 = table.__class__()
|
|
|
|
table2.decompile(reader, ttFont)
|
|
|
|
|
|
|
|
return table2
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def ttFont():
|
|
|
|
font = TTFont()
|
|
|
|
font.setGlyphOrder([".notdef"] + list(string.ascii_letters))
|
|
|
|
return font
|
|
|
|
|
|
|
|
|
|
|
|
def build_paint(data):
|
|
|
|
return LayerListBuilder().buildPaint(data)
|
|
|
|
|
|
|
|
|
|
|
|
class COLRVariationMergerTest:
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"paints, expected_xml, expected_varIdxes",
|
|
|
|
[
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[],
|
|
|
|
id="solid-same",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[0],
|
|
|
|
id="solid-alpha",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.1, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 0.9, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": 2,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="5"><!-- PaintVarLinearGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="2"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <x0 value="0"/>',
|
|
|
|
' <y0 value="0"/>',
|
|
|
|
' <x1 value="1"/>',
|
|
|
|
' <y1 value="1"/>',
|
|
|
|
' <x2 value="2"/>',
|
|
|
|
' <y2 value="2"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[0, NO_VARIATION_INDEX, 1, NO_VARIATION_INDEX],
|
|
|
|
id="linear_grad-stop-offsets",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": 2,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="5"><!-- PaintVarLinearGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <x0 value="0"/>',
|
|
|
|
' <y0 value="0"/>',
|
|
|
|
' <x1 value="1"/>',
|
|
|
|
' <y1 value="1"/>',
|
|
|
|
' <x2 value="2"/>',
|
|
|
|
' <y2 value="2"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[NO_VARIATION_INDEX, 0],
|
|
|
|
id="linear_grad-stop[0].alpha",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintLinearGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": -0.5, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"x2": 2,
|
|
|
|
"y2": -200,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="5"><!-- PaintVarLinearGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <x0 value="0"/>',
|
|
|
|
' <y0 value="0"/>',
|
|
|
|
' <x1 value="1"/>',
|
|
|
|
' <y1 value="1"/>',
|
|
|
|
' <x2 value="2"/>',
|
|
|
|
' <y2 value="2"/>',
|
2022-06-28 14:11:28 +01:00
|
|
|
' <VarIndexBase value="1"/>',
|
2022-06-24 18:16:28 +01:00
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
1,
|
|
|
|
],
|
|
|
|
id="linear_grad-stop[0].offset-y2",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintRadialGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"r0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"r1": 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintRadialGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.1, "PaletteIndex": 0, "Alpha": 0.6},
|
|
|
|
{"StopOffset": 0.9, "PaletteIndex": 1, "Alpha": 0.7},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": -1,
|
|
|
|
"y0": -2,
|
|
|
|
"r0": 3,
|
|
|
|
"x1": -4,
|
|
|
|
"y1": -5,
|
|
|
|
"r1": 6,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="7"><!-- PaintVarRadialGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="2"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <x0 value="0"/>',
|
|
|
|
' <y0 value="0"/>',
|
|
|
|
' <r0 value="0"/>',
|
|
|
|
' <x1 value="1"/>',
|
|
|
|
' <y1 value="1"/>',
|
|
|
|
' <r1 value="1"/>',
|
|
|
|
' <VarIndexBase value="4"/>',
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
|
|
id="radial_grad-all-different",
|
|
|
|
),
|
2022-06-27 15:38:55 +01:00
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSweepGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.REPEAT),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.4, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 0.6, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 0,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": 0,
|
|
|
|
"endAngle": 180.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSweepGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.REPEAT),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.4, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 0.6, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 0,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": 90.0,
|
|
|
|
"endAngle": 180.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="9"><!-- PaintVarSweepGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="repeat"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.4"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="0.6"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <centerX value="0"/>',
|
|
|
|
' <centerY value="0"/>',
|
|
|
|
' <startAngle value="0.0"/>',
|
|
|
|
' <endAngle value="180.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[NO_VARIATION_INDEX, NO_VARIATION_INDEX, 0, NO_VARIATION_INDEX],
|
|
|
|
id="sweep_grad-startAngle",
|
|
|
|
),
|
2022-06-27 17:17:05 +01:00
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSweepGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 0,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": 0.0,
|
|
|
|
"endAngle": 180.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintSweepGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5},
|
|
|
|
{"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 0.5},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 0,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": 0.0,
|
|
|
|
"endAngle": 180.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="9"><!-- PaintVarSweepGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <centerX value="0"/>',
|
|
|
|
' <centerY value="0"/>',
|
|
|
|
' <startAngle value="0.0"/>',
|
|
|
|
' <endAngle value="180.0"/>',
|
2022-06-28 14:11:28 +01:00
|
|
|
" <VarIndexBase/>",
|
2022-06-27 17:17:05 +01:00
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[NO_VARIATION_INDEX, 0],
|
|
|
|
id="sweep_grad-stops-alpha-reuse-varidxbase",
|
|
|
|
),
|
2022-06-24 18:16:28 +01:00
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintTransform),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintRadialGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{
|
|
|
|
"StopOffset": 0.0,
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"StopOffset": 1.0,
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"r0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"r1": 1,
|
|
|
|
},
|
|
|
|
"Transform": {
|
|
|
|
"xx": 1.0,
|
|
|
|
"xy": 0.0,
|
|
|
|
"yx": 0.0,
|
|
|
|
"yy": 1.0,
|
|
|
|
"dx": 0.0,
|
|
|
|
"dy": 0.0,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintTransform),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintRadialGradient),
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": int(ot.ExtendMode.PAD),
|
|
|
|
"ColorStop": [
|
|
|
|
{
|
|
|
|
"StopOffset": 0.0,
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"StopOffset": 1.0,
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"x0": 0,
|
|
|
|
"y0": 0,
|
|
|
|
"r0": 0,
|
|
|
|
"x1": 1,
|
|
|
|
"y1": 1,
|
|
|
|
"r1": 1,
|
|
|
|
},
|
|
|
|
"Transform": {
|
|
|
|
"xx": 1.0,
|
|
|
|
"xy": 0.0,
|
|
|
|
"yx": 0.0,
|
|
|
|
"yy": 0.5,
|
|
|
|
"dx": 0.0,
|
|
|
|
"dy": -100.0,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="13"><!-- PaintVarTransform -->',
|
|
|
|
' <Paint Format="6"><!-- PaintRadialGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <x0 value="0"/>',
|
|
|
|
' <y0 value="0"/>',
|
|
|
|
' <r0 value="0"/>',
|
|
|
|
' <x1 value="1"/>',
|
|
|
|
' <y1 value="1"/>',
|
|
|
|
' <r1 value="1"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" <Transform>",
|
|
|
|
' <xx value="1.0"/>',
|
|
|
|
' <yx value="0.0"/>',
|
|
|
|
' <xy value="0.0"/>',
|
|
|
|
' <yy value="1.0"/>',
|
|
|
|
' <dx value="0.0"/>',
|
|
|
|
' <dy value="0.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Transform>",
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
0,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
1,
|
|
|
|
],
|
|
|
|
id="transform-yy-dy",
|
|
|
|
),
|
2022-06-28 14:11:28 +01:00
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"Format": ot.PaintFormat.PaintTransform,
|
|
|
|
"Paint": {
|
|
|
|
"Format": ot.PaintFormat.PaintSweepGradient,
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": ot.ExtendMode.PAD,
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0},
|
|
|
|
{
|
|
|
|
"StopOffset": 1.0,
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 0,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": -360,
|
|
|
|
"endAngle": 0,
|
|
|
|
},
|
|
|
|
"Transform": (1.0, 0, 0, 1.0, 0, 0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": ot.PaintFormat.PaintTransform,
|
|
|
|
"Paint": {
|
|
|
|
"Format": ot.PaintFormat.PaintSweepGradient,
|
|
|
|
"ColorLine": {
|
|
|
|
"Extend": ot.ExtendMode.PAD,
|
|
|
|
"ColorStop": [
|
|
|
|
{"StopOffset": 0.0, "PaletteIndex": 0},
|
|
|
|
{
|
|
|
|
"StopOffset": 1.0,
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"centerX": 256,
|
|
|
|
"centerY": 0,
|
|
|
|
"startAngle": -360,
|
|
|
|
"endAngle": 0,
|
|
|
|
},
|
|
|
|
# Transform.xx below produces the same VarStore delta as the
|
|
|
|
# above PaintSweepGradient's centerX because, when Fixed16.16
|
|
|
|
# is converted to integer, it becomes:
|
|
|
|
# floatToFixed(1.00390625, 16) == 256
|
|
|
|
# Because there is overlap between the varIdxes of the
|
|
|
|
# PaintVarTransform's Affine2x3 and the PaintSweepGradient's
|
|
|
|
# the VarIndexBase is reused (0 for both)
|
|
|
|
"Transform": (1.00390625, 0, 0, 1.0, 10, 0),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'<Paint Format="13"><!-- PaintVarTransform -->',
|
|
|
|
' <Paint Format="9"><!-- PaintVarSweepGradient -->',
|
|
|
|
" <ColorLine>",
|
|
|
|
' <Extend value="pad"/>',
|
|
|
|
" <!-- StopCount=2 -->",
|
|
|
|
' <ColorStop index="0">',
|
|
|
|
' <StopOffset value="0.0"/>',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
' <ColorStop index="1">',
|
|
|
|
' <StopOffset value="1.0"/>',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" <VarIndexBase/>",
|
|
|
|
" </ColorStop>",
|
|
|
|
" </ColorLine>",
|
|
|
|
' <centerX value="0"/>',
|
|
|
|
' <centerY value="0"/>',
|
|
|
|
' <startAngle value="-360.0"/>',
|
|
|
|
' <endAngle value="0.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" <Transform>",
|
|
|
|
' <xx value="1.0"/>',
|
|
|
|
' <yx value="0.0"/>',
|
|
|
|
' <xy value="0.0"/>',
|
|
|
|
' <yy value="1.0"/>',
|
|
|
|
' <dx value="0.0"/>',
|
|
|
|
' <dy value="0.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Transform>",
|
|
|
|
"</Paint>",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
1,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
],
|
|
|
|
id="transform-xx-sweep_grad-centerx-same-varidxbase",
|
|
|
|
),
|
2022-06-24 18:16:28 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_merge_Paint(self, paints, ttFont, expected_xml, expected_varIdxes):
|
|
|
|
paints = [build_paint(p) for p in paints]
|
|
|
|
out = deepcopy(paints[0])
|
|
|
|
|
|
|
|
model = VariationModel([{}, {"ZZZZ": 1.0}])
|
|
|
|
merger = COLRVariationMerger(model, ["ZZZZ"], ttFont)
|
|
|
|
|
|
|
|
merger.mergeThings(out, paints)
|
|
|
|
|
|
|
|
assert compile_decompile(out, ttFont) == out
|
|
|
|
assert dump_xml(out, ttFont) == expected_xml
|
|
|
|
assert merger.varIdxes == expected_varIdxes
|
2022-06-28 18:23:06 +01:00
|
|
|
|
|
|
|
def test_merge_ClipList(self, ttFont):
|
|
|
|
clipLists = [
|
|
|
|
buildClipList(clips)
|
|
|
|
for clips in [
|
|
|
|
{
|
|
|
|
"A": (0, 0, 1000, 1000),
|
|
|
|
"B": (0, 0, 1000, 1000),
|
|
|
|
"C": (0, 0, 1000, 1000),
|
|
|
|
"D": (0, 0, 1000, 1000),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
# non-default masters' clip boxes can be 'sparse'
|
|
|
|
# (i.e. can omit explicit clip box for some glyphs)
|
|
|
|
# "A": (0, 0, 1000, 1000),
|
|
|
|
"B": (10, 0, 1000, 1000),
|
|
|
|
"C": (20, 20, 1020, 1020),
|
|
|
|
"D": (20, 20, 1020, 1020),
|
|
|
|
},
|
|
|
|
]
|
|
|
|
]
|
|
|
|
out = deepcopy(clipLists[0])
|
|
|
|
|
|
|
|
model = VariationModel([{}, {"ZZZZ": 1.0}])
|
|
|
|
merger = COLRVariationMerger(model, ["ZZZZ"], ttFont)
|
|
|
|
|
|
|
|
merger.mergeThings(out, clipLists)
|
|
|
|
|
|
|
|
assert compile_decompile(out, ttFont) == out
|
|
|
|
assert dump_xml(out, ttFont) == [
|
|
|
|
'<ClipList Format="1">',
|
|
|
|
" <Clip>",
|
|
|
|
' <Glyph value="A"/>',
|
|
|
|
' <ClipBox Format="1">',
|
|
|
|
' <xMin value="0"/>',
|
|
|
|
' <yMin value="0"/>',
|
|
|
|
' <xMax value="1000"/>',
|
|
|
|
' <yMax value="1000"/>',
|
|
|
|
" </ClipBox>",
|
|
|
|
" </Clip>",
|
|
|
|
" <Clip>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
' <ClipBox Format="2">',
|
|
|
|
' <xMin value="0"/>',
|
|
|
|
' <yMin value="0"/>',
|
|
|
|
' <xMax value="1000"/>',
|
|
|
|
' <yMax value="1000"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </ClipBox>",
|
|
|
|
" </Clip>",
|
|
|
|
" <Clip>",
|
|
|
|
' <Glyph value="C"/>',
|
|
|
|
' <Glyph value="D"/>',
|
|
|
|
' <ClipBox Format="2">',
|
|
|
|
' <xMin value="0"/>',
|
|
|
|
' <yMin value="0"/>',
|
|
|
|
' <xMax value="1000"/>',
|
|
|
|
' <yMax value="1000"/>',
|
|
|
|
' <VarIndexBase value="4"/>',
|
|
|
|
" </ClipBox>",
|
|
|
|
" </Clip>",
|
|
|
|
"</ClipList>",
|
|
|
|
]
|
|
|
|
assert merger.varIdxes == [
|
|
|
|
0,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
NO_VARIATION_INDEX,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
]
|
2022-07-05 00:11:12 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2022-07-06 09:14:17 +01:00
|
|
|
"master_layer_reuse",
|
|
|
|
[
|
|
|
|
pytest.param(False, id="no-reuse"),
|
|
|
|
pytest.param(True, id="with-reuse"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"color_glyphs, output_layer_reuse, expected_xml, expected_varIdxes",
|
2022-07-05 00:11:12 +01:00
|
|
|
[
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": ot.PaintFormat.PaintColrLayers,
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
False,
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=1 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=2 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[],
|
|
|
|
id="no-variation",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"C": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 3,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
# NOTE: 'A' is missing from non-default master
|
|
|
|
"C": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 3,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
False,
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=2 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="1">',
|
|
|
|
' <BaseGlyph value="C"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="2"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=4 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="2" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="3" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="3"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[0],
|
|
|
|
id="sparse-masters",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"C": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
# 'C' reuses layers 1-3 from 'A'
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"D": { # identical to 'C'
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"E": { # superset of 'C' or 'D'
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 3,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
# NOTE: 'A' is missing from non-default master
|
|
|
|
"C": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"D": { # same as 'C'
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"E": { # first two layers vary the same way as 'C' or 'D'
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 3,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
True, # reuse
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=4 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="3"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="1">',
|
|
|
|
' <BaseGlyph value="C"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="2">',
|
|
|
|
' <BaseGlyph value="D"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="3">',
|
|
|
|
' <BaseGlyph value="E"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="5"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=7 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="2" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="3" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="4" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="5" Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="6" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="3"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[0],
|
|
|
|
id="sparse-masters-with-reuse",
|
|
|
|
),
|
2022-07-05 15:25:51 +01:00
|
|
|
pytest.param(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"C": { # 'C' shares layer 1 and 2 with 'A'
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 0.9,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"C": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 0.5,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
True,
|
|
|
|
[
|
|
|
|
# a different Alpha variation is applied to a shared layer between
|
|
|
|
# 'A' and 'C' and thus they are no longer shared.
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=2 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="3"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="1">',
|
|
|
|
' <BaseGlyph value="C"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=5 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="2" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="3" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="3"><!-- PaintVarSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
' <VarIndexBase value="1"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="4" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[0, 1],
|
|
|
|
id="shared-master-layers-different-variations",
|
|
|
|
),
|
2022-07-05 00:11:12 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_merge_full_table(
|
2022-07-06 09:14:17 +01:00
|
|
|
self,
|
|
|
|
color_glyphs,
|
|
|
|
ttFont,
|
|
|
|
expected_xml,
|
|
|
|
expected_varIdxes,
|
|
|
|
master_layer_reuse,
|
|
|
|
output_layer_reuse,
|
2022-07-05 00:11:12 +01:00
|
|
|
):
|
|
|
|
master_ttfs = [deepcopy(ttFont) for _ in range(len(color_glyphs))]
|
|
|
|
for ttf, glyphs in zip(master_ttfs, color_glyphs):
|
2022-07-06 09:14:17 +01:00
|
|
|
# merge algorithm is expected to work the same even if the master COLRs
|
|
|
|
# may differ as to the layer reuse, hence we try both ways
|
|
|
|
ttf["COLR"] = buildCOLR(glyphs, allowLayerReuse=master_layer_reuse)
|
2022-07-05 00:11:12 +01:00
|
|
|
vf = deepcopy(master_ttfs[0])
|
|
|
|
|
|
|
|
model = VariationModel([{}, {"ZZZZ": 1.0}])
|
2022-07-06 09:14:17 +01:00
|
|
|
merger = COLRVariationMerger(
|
|
|
|
model, ["ZZZZ"], vf, allowLayerReuse=output_layer_reuse
|
|
|
|
)
|
2022-07-05 00:11:12 +01:00
|
|
|
|
|
|
|
merger.mergeTables(vf, master_ttfs)
|
|
|
|
|
|
|
|
out = vf["COLR"].table
|
|
|
|
|
|
|
|
assert compile_decompile(out, vf) == out
|
|
|
|
assert dump_xml(out, vf) == expected_xml
|
|
|
|
assert merger.varIdxes == expected_varIdxes
|
2022-07-06 09:59:16 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"color_glyphs, before_xml, expected_xml",
|
|
|
|
[
|
|
|
|
pytest.param(
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "C",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "D",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"E": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrLayers),
|
|
|
|
"Layers": [
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 1,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "C",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 2,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "D",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 3,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "F",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"G": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintColrGlyph),
|
|
|
|
"Glyph": "E",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=3 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="3"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="1">',
|
|
|
|
' <BaseGlyph value="E"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="2">',
|
|
|
|
' <BaseGlyph value="G"/>',
|
|
|
|
' <Paint Format="11"><!-- PaintColrGlyph -->',
|
|
|
|
' <Glyph value="E"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=5 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="C"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="2" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="D"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="3" Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="2"/>',
|
|
|
|
' <FirstLayerIndex value="1"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="4" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="3"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="F"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=3 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="3"/>',
|
|
|
|
' <FirstLayerIndex value="0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="1">',
|
|
|
|
' <BaseGlyph value="E"/>',
|
|
|
|
' <Paint Format="1"><!-- PaintColrLayers -->',
|
|
|
|
' <NumLayers value="3"/>',
|
|
|
|
' <FirstLayerIndex value="3"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
' <BaseGlyphPaintRecord index="2">',
|
|
|
|
' <BaseGlyph value="G"/>',
|
|
|
|
' <Paint Format="11"><!-- PaintColrGlyph -->',
|
|
|
|
' <Glyph value="E"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
" <LayerList>",
|
|
|
|
" <!-- LayerCount=6 -->",
|
|
|
|
' <Paint index="0" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="1" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="C"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="2" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="D"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="3" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="1"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="C"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="4" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="2"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="D"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Paint index="5" Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="3"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="F"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </LayerList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
id="simple-reuse",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
{
|
|
|
|
"A": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintGlyph),
|
|
|
|
"Paint": {
|
|
|
|
"Format": int(ot.PaintFormat.PaintSolid),
|
|
|
|
"PaletteIndex": 0,
|
|
|
|
"Alpha": 1.0,
|
|
|
|
},
|
|
|
|
"Glyph": "B",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=1 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"<COLR>",
|
|
|
|
' <Version value="1"/>',
|
|
|
|
" <!-- BaseGlyphRecordCount=0 -->",
|
|
|
|
" <!-- LayerRecordCount=0 -->",
|
|
|
|
" <BaseGlyphList>",
|
|
|
|
" <!-- BaseGlyphCount=1 -->",
|
|
|
|
' <BaseGlyphPaintRecord index="0">',
|
|
|
|
' <BaseGlyph value="A"/>',
|
|
|
|
' <Paint Format="10"><!-- PaintGlyph -->',
|
|
|
|
' <Paint Format="2"><!-- PaintSolid -->',
|
|
|
|
' <PaletteIndex value="0"/>',
|
|
|
|
' <Alpha value="1.0"/>',
|
|
|
|
" </Paint>",
|
|
|
|
' <Glyph value="B"/>',
|
|
|
|
" </Paint>",
|
|
|
|
" </BaseGlyphPaintRecord>",
|
|
|
|
" </BaseGlyphList>",
|
|
|
|
"</COLR>",
|
|
|
|
],
|
|
|
|
id="no-layer-list",
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expandPaintColrLayers(
|
|
|
|
self, color_glyphs, ttFont, before_xml, expected_xml
|
|
|
|
):
|
|
|
|
colr = buildCOLR(color_glyphs, allowLayerReuse=True)
|
|
|
|
|
|
|
|
assert dump_xml(colr.table, ttFont) == before_xml
|
|
|
|
|
|
|
|
before_layer_count = 0
|
|
|
|
reuses_colr_layers = False
|
|
|
|
if colr.table.LayerList:
|
|
|
|
before_layer_count = len(colr.table.LayerList.Paint)
|
|
|
|
reuses_colr_layers = any(
|
|
|
|
p.Format == ot.PaintFormat.PaintColrLayers
|
|
|
|
for p in colr.table.LayerList.Paint
|
|
|
|
)
|
|
|
|
|
|
|
|
COLRVariationMerger.expandPaintColrLayers(colr.table)
|
|
|
|
|
|
|
|
assert dump_xml(colr.table, ttFont) == expected_xml
|
|
|
|
|
|
|
|
after_layer_count = (
|
|
|
|
0 if not colr.table.LayerList else len(colr.table.LayerList.Paint)
|
|
|
|
)
|
|
|
|
|
|
|
|
if reuses_colr_layers:
|
|
|
|
assert not any(
|
|
|
|
p.Format == ot.PaintFormat.PaintColrLayers
|
|
|
|
for p in colr.table.LayerList.Paint
|
|
|
|
)
|
|
|
|
assert after_layer_count > before_layer_count
|
|
|
|
else:
|
|
|
|
assert after_layer_count == before_layer_count
|
|
|
|
|
|
|
|
if colr.table.LayerList:
|
|
|
|
assert len({id(p) for p in colr.table.LayerList.Paint}) == after_layer_count
|