Merge pull request #8579 from penpot/elenatorro-13619-fix-outer-non-closing-stroke

🐛 Fix stroke closing on outer strokes on paths
This commit is contained in:
Alejandro Alonso
2026-03-12 12:24:52 +01:00
committed by GitHub
6 changed files with 838 additions and 23 deletions

View File

@@ -0,0 +1,769 @@
{
"~:features": {
"~#set": [
"fdata/path-data",
"plugins/runtime",
"design-tokens/v1",
"variants/v1",
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"render-wasm/v1",
"components/v2",
"fdata/shape-data-type"
]
},
"~:team-id": "~uaaa00001-0001-0001-8007-000000000003",
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "outer-stroke-overlap-seam",
"~:revn": 1,
"~:modified-at": "~m1771855365377",
"~:vern": 0,
"~:id": "~uaaa00001-0001-0001-8007-000000000001",
"~:is-shared": false,
"~:migrations": {
"~#ordered-set": [
"legacy-2",
"legacy-3",
"legacy-5",
"legacy-6",
"legacy-7",
"legacy-8",
"legacy-9",
"legacy-10",
"legacy-11",
"legacy-12",
"legacy-13",
"legacy-14",
"legacy-16",
"legacy-17",
"legacy-18",
"legacy-19",
"legacy-25",
"legacy-26",
"legacy-27",
"legacy-28",
"legacy-29",
"legacy-31",
"legacy-32",
"legacy-33",
"legacy-34",
"legacy-36",
"legacy-37",
"legacy-38",
"legacy-39",
"legacy-40",
"legacy-41",
"legacy-42",
"legacy-43",
"legacy-44",
"legacy-45",
"legacy-46",
"legacy-47",
"legacy-48",
"legacy-49",
"legacy-50",
"legacy-51",
"legacy-52",
"legacy-53",
"legacy-54",
"legacy-55",
"legacy-56",
"legacy-57",
"legacy-59",
"legacy-62",
"legacy-65",
"legacy-66",
"legacy-67",
"0001-remove-tokens-from-groups",
"0002-normalize-bool-content-v2",
"0002-clean-shape-interactions",
"0003-fix-root-shape",
"0003-convert-path-content-v2",
"0005-deprecate-image-type",
"0006-fix-old-texts-fills",
"0008-fix-library-colors-v4",
"0009-clean-library-colors",
"0009-add-partial-text-touched-flags",
"0010-fix-swap-slots-pointing-non-existent-shapes",
"0011-fix-invalid-text-touched-flags",
"0012-fix-position-data",
"0013-fix-component-path",
"0013-clear-invalid-strokes-and-fills",
"0014-fix-tokens-lib-duplicate-ids",
"0014-clear-components-nil-objects",
"0015-fix-text-attrs-blank-strings",
"0015-clean-shadow-color",
"0016-copy-fills-from-position-data-to-text-node"
]
},
"~:version": 67,
"~:project-id": "~uaaa00001-0001-0001-8007-000000000004",
"~:created-at": "~m1771591980210",
"~:backend": "legacy-db",
"~:data": {
"~:pages": [
"~uaaa00001-0001-0001-8007-000000000002"
],
"~:pages-index": {
"~uaaa00001-0001-0001-8007-000000000002": {
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{"~#point": {"~:x": 0, "~:y": 0}},
{"~#point": {"~:x": 0.01, "~:y": 0}},
{"~#point": {"~:x": 0.01, "~:y": 0.01}},
{"~#point": {"~:x": 0, "~:y": 0.01}}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:r3": 0,
"~:r1": 0,
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {"~#rect": {"~:x": 0, "~:y": 0, "~:width": 0.01, "~:height": 0.01, "~:x1": 0, "~:y1": 0, "~:x2": 0.01, "~:y2": 0.01}},
"~:fills": [{"~:fill-color": "#FFFFFF", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992e4ab5"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146992e4ab5": {
"~#shape": {
"~:y": -237,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg",
"~:width": 86.215,
"~:type": "~:group",
"~:svg-attrs": {"~:width": "86.215", "~:height": "50"},
"~:points": [
{"~#point": {"~:x": 1079, "~:y": -237}},
{"~#point": {"~:x": 1165.215, "~:y": -237}},
{"~#point": {"~:x": 1165.215, "~:y": -187}},
{"~#point": {"~:x": 1079, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1079,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}},
"~:fills": [],
"~:flip-x": false,
"~:height": 50,
"~:flip-y": false,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992f0a36"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146992f0a36": {
"~#shape": {
"~:y": -237,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 86.215,
"~:type": "~:group",
"~:svg-attrs": {},
"~:points": [
{"~#point": {"~:x": 1079, "~:y": -237}},
{"~#point": {"~:x": 1165.215, "~:y": -237}},
{"~#point": {"~:x": 1165.215, "~:y": -187}},
{"~#point": {"~:x": 1079, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5",
"~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": -0.000003814697265625, "~:width": 86.21503414622225, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1079,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 50,
"~:flip-y": null,
"~:shapes": [
"~u8f89d4e8-0efd-804d-8007-b146992fbb39",
"~u8f89d4e8-0efd-804d-8007-b146992fde2e",
"~u8f89d4e8-0efd-804d-8007-b14699303fd3",
"~u8f89d4e8-0efd-804d-8007-b1469930583b",
"~u8f89d4e8-0efd-804d-8007-b146993083e1",
"~u8f89d4e8-0efd-804d-8007-b1469930dc39",
"~u8f89d4e8-0efd-804d-8007-b146993141e4",
"~u8f89d4e8-0efd-804d-8007-b146993141e5",
"~u8f89d4e8-0efd-804d-8007-b1469931a9d0",
"~u8f89d4e8-0efd-804d-8007-b1469931a9d1"
]
}
},
"~u8f89d4e8-0efd-804d-8007-b146992fbb39": {
"~#shape": {
"~:y": null,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:className": "fills"},
"~:points": [
{"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -187}},
{"~#point": {"~:x": 1112.4849232616168, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146992fbb39",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}},
"~:fills": [{"~:fill-color": "#70d19d", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b146992fde2e": {
"~#shape": {
"~:y": -237,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 19.244997226766372,
"~:type": "~:group",
"~:svg-attrs": {"~:className": "strokes"},
"~:points": [
{"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -187}},
{"~#point": {"~:x": 1112.4849232616168, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616814, "~:y": -0.000003814697265625, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1112.4849232616168,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.7299204883832, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 50,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992fde2f"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146992fde2f": {
"~#shape": {
"~:y": null,
"~:stroke-cap-start": "~:round",
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"},
"~:points": [
{"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}},
{"~#point": {"~:x": 1131.7299204883832, "~:y": -187}},
{"~#point": {"~:x": 1112.4849232616168, "~:y": -187}}
],
"~:proportion-lock": false,
"~:stroke-cap-end": "~:round",
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:stroke-linecap": "~:round",
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2f",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e",
"~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b14699303fd3": {
"~#shape": {
"~:y": null,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:className": "fills"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b14699303fd3",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}},
"~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b1469930583b": {
"~#shape": {
"~:y": -217.33076201036303,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 43.10745602122233,
"~:type": "~:group",
"~:svg-attrs": {"~:className": "strokes"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b1469930583b",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1122.107421875,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 30.33076201036303,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993083e0"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146993083e0": {
"~#shape": {
"~:y": null,
"~:stroke-cap-start": "~:round",
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1165.2148778962223, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:stroke-cap-end": "~:round",
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:stroke-linecap": "~:round",
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e0",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930583b",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b146993083e1": {
"~#shape": {
"~:y": null,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:className": "fills"},
"~:points": [
{"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1078.9999658537777, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e1",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b1469930dc39": {
"~#shape": {
"~:y": -217.33076201036303,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 43.10745602122233,
"~:type": "~:group",
"~:svg-attrs": {"~:className": "strokes"},
"~:points": [
{"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1078.9999658537777, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1079,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 30.33076201036303,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993110ec"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146993110ec": {
"~#shape": {
"~:y": null,
"~:stroke-cap-start": "~:round",
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"},
"~:points": [
{"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1078.9999658537777, "~:y": -187}}
],
"~:proportion-lock": false,
"~:stroke-cap-end": "~:round",
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:stroke-linecap": "~:round",
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993110ec",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39",
"~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b146993141e4": {
"~#shape": {
"~:y": null,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:className": "fills"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e4",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}},
"~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b146993141e5": {
"~#shape": {
"~:y": -231.66937927262296,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 27.81847881531712,
"~:type": "~:group",
"~:svg-attrs": {"~:className": "strokes"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e5",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1122.107421875,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 44.66937927262296,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993141e6"]
}
},
"~u8f89d4e8-0efd-804d-8007-b146993141e6": {
"~#shape": {
"~:y": null,
"~:stroke-cap-start": "~:round",
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"},
"~:points": [
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1149.9259006903171, "~:y": -187}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}}
],
"~:proportion-lock": false,
"~:stroke-cap-end": "~:round",
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:stroke-linecap": "~:round",
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e6",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146993141e5",
"~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b1469931a9d0": {
"~#shape": {
"~:y": null,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:className": "fills"},
"~:points": [
{"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1094.2889430596829, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d0",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
},
"~u8f89d4e8-0efd-804d-8007-b1469931a9d1": {
"~#shape": {
"~:y": -231.66937927262296,
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:name": "svg-g",
"~:width": 27.81847881531712,
"~:type": "~:group",
"~:svg-attrs": {"~:className": "strokes"},
"~:points": [
{"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1094.2889430596829, "~:y": -187}}
],
"~:proportion-lock": false,
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36",
"~:svg-viewbox": {"~#rect": {"~:x": 15.288943059682879, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1094.2889430596829,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": 44.66937927262296,
"~:flip-y": null,
"~:shapes": ["~u8f89d4e8-0efd-804d-8007-b1469931e08c"]
}
},
"~u8f89d4e8-0efd-804d-8007-b1469931e08c": {
"~#shape": {
"~:y": null,
"~:stroke-cap-start": "~:round",
"~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:rotation": 0,
"~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="},
"~:name": "svg-path",
"~:width": null,
"~:type": "~:path",
"~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"},
"~:points": [
{"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}},
{"~#point": {"~:x": 1122.107421875, "~:y": -187}},
{"~#point": {"~:x": 1094.2889430596829, "~:y": -187}}
],
"~:proportion-lock": false,
"~:stroke-cap-end": "~:round",
"~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:stroke-linecap": "~:round",
"~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}},
"~:id": "~u8f89d4e8-0efd-804d-8007-b1469931e08c",
"~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1",
"~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}},
"~:svg-defs": {},
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}],
"~:x": null,
"~:proportion": 1,
"~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}},
"~:fills": [],
"~:flip-x": null,
"~:height": null,
"~:flip-y": null
}
}
},
"~:id": "~uaaa00001-0001-0001-8007-000000000002",
"~:name": "Page 1"
}
},
"~:id": "~uaaa00001-0001-0001-8007-000000000001",
"~:options": {
"~:components-v2": true,
"~:base-font-size": "16px"
}
}
}

View File

@@ -485,6 +485,34 @@ test("No white seam at intersections of overlapping shapes with inner strokes",
});
});
test("Correct stroke closing at self-intersection of overlapping shapes with outer strokes", async ({
page,
}) => {
const workspace = new WasmWorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.mockGetFile("render-wasm/get-file-outer-stroke-overlap-seam.json");
await workspace.goToWorkspace({
id: "aaa00001-0001-0001-8007-000000000001",
pageId: "aaa00001-0001-0001-8007-000000000002",
});
await workspace.waitForFirstRender();
await workspace.viewport.click();
await page.keyboard.press("ControlOrMeta+A");
const previousRenderCount = await workspace.getRenderCount();
await page.keyboard.press("f");
await workspace.waitForNextRender(previousRenderCount);
await workspace.hideUI();
// Stricter comparison: white seam artifacts are very subtle
await expect(workspace.canvas).toHaveScreenshot({
maxDiffPixelRatio: 0,
threshold: 0.1,
});
});
test("BUG 13551 - Blurs affecting other elements", async ({
page,
}) => {

View File

@@ -365,11 +365,10 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec<Segment> {
let mut bm = init_bm(beziers);
while let Some(bezier) = pop_first(&mut bm) {
result.push(Segment::MoveTo((
bezier.1.start.x as f32,
bezier.1.start.y as f32,
)));
let start = (bezier.1.start.x as f32, bezier.1.start.y as f32);
result.push(Segment::MoveTo(start));
push_bezier(&mut result, &bezier.1);
let mut last_end = (bezier.1.end.x as f32, bezier.1.end.y as f32);
let mut next_p = BezierStart(bezier.0, bezier.1.end);
loop {
@@ -377,8 +376,24 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec<Segment> {
break;
};
push_bezier(&mut result, &next.1);
last_end = (next.1.end.x as f32, next.1.end.y as f32);
next_p = BezierStart(next.0, next.1.end);
}
// Close the subpath if the last point is close to the start.
if (last_end.0 - start.0).abs() < INTERSECT_THRESHOLD_SAME
&& (last_end.1 - start.1).abs() < INTERSECT_THRESHOLD_SAME
{
// Remove the redundant LineTo that goes back to start, if present.
if let Some(Segment::LineTo(p)) = result.last() {
if (p.0 - start.0).abs() < INTERSECT_THRESHOLD_SAME
&& (p.1 - start.1).abs() < INTERSECT_THRESHOLD_SAME
{
result.pop();
}
}
result.push(Segment::Close);
}
}
result
}

View File

@@ -32,34 +32,25 @@ impl Default for Path {
impl Path {
pub fn new(segments: Vec<Segment>) -> Self {
let mut pb = skia::PathBuilder::new();
let mut start = None;
// Don't auto-close the Skia path when start ≈ end.
// SVG treats these as open paths (caps apply at endpoints).
// Auto-closing changes stroke behavior from caps to joins,
// producing artifacts at self-intersection points.
// Only explicit Segment::Close should close the Skia path.
for segment in segments.iter() {
let destination = match *segment {
match *segment {
Segment::MoveTo(xy) => {
start = Some(xy);
pb.move_to(xy);
None
}
Segment::LineTo(xy) => {
pb.line_to(xy);
Some(xy)
}
Segment::CurveTo((c1, c2, xy)) => {
pb.cubic_to(c1, c2, xy);
Some(xy)
}
Segment::Close => {
pb.close();
None
}
};
if let (Some(start), Some(destination)) = (start, destination) {
if math::is_close_to(destination.0, start.0)
&& math::is_close_to(destination.1, start.1)
{
pb.close();
}
}
}

View File

@@ -213,12 +213,24 @@ impl Stroke {
paint.set_anti_alias(antialias);
if let Some(svg_attrs) = svg_attrs {
if svg_attrs.stroke_linecap == StrokeLineCap::Round {
paint.set_stroke_cap(skia::paint::Cap::Round);
match svg_attrs.stroke_linecap {
StrokeLineCap::Round => {
paint.set_stroke_cap(skia::paint::Cap::Round);
}
StrokeLineCap::Square => {
paint.set_stroke_cap(skia::paint::Cap::Square);
}
StrokeLineCap::Butt => {} // Skia default
}
if svg_attrs.stroke_linejoin == StrokeLineJoin::Round {
paint.set_stroke_join(skia::paint::Join::Round);
match svg_attrs.stroke_linejoin {
StrokeLineJoin::Round => {
paint.set_stroke_join(skia::paint::Join::Round);
}
StrokeLineJoin::Bevel => {
paint.set_stroke_join(skia::paint::Join::Bevel);
}
StrokeLineJoin::Miter => {} // Skia default
}
}