@emasoft/svg-matrix 1.0.28 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +325 -0
  2. package/bin/svg-matrix.js +985 -378
  3. package/bin/svglinter.cjs +4172 -433
  4. package/bin/svgm.js +723 -180
  5. package/package.json +16 -4
  6. package/src/animation-references.js +71 -52
  7. package/src/arc-length.js +160 -96
  8. package/src/bezier-analysis.js +257 -117
  9. package/src/bezier-intersections.js +411 -148
  10. package/src/browser-verify.js +240 -100
  11. package/src/clip-path-resolver.js +350 -142
  12. package/src/convert-path-data.js +279 -134
  13. package/src/css-specificity.js +78 -70
  14. package/src/flatten-pipeline.js +751 -263
  15. package/src/geometry-to-path.js +511 -182
  16. package/src/index.js +191 -46
  17. package/src/inkscape-support.js +18 -7
  18. package/src/marker-resolver.js +278 -164
  19. package/src/mask-resolver.js +209 -98
  20. package/src/matrix.js +147 -67
  21. package/src/mesh-gradient.js +187 -96
  22. package/src/off-canvas-detection.js +201 -104
  23. package/src/path-analysis.js +187 -107
  24. package/src/path-data-plugins.js +628 -167
  25. package/src/path-simplification.js +0 -1
  26. package/src/pattern-resolver.js +125 -88
  27. package/src/polygon-clip.js +111 -66
  28. package/src/svg-boolean-ops.js +194 -118
  29. package/src/svg-collections.js +22 -18
  30. package/src/svg-flatten.js +282 -164
  31. package/src/svg-parser.js +427 -200
  32. package/src/svg-rendering-context.js +147 -104
  33. package/src/svg-toolbox.js +16381 -3370
  34. package/src/svg2-polyfills.js +93 -224
  35. package/src/transform-decomposition.js +46 -41
  36. package/src/transform-optimization.js +89 -68
  37. package/src/transforms2d.js +49 -16
  38. package/src/transforms3d.js +58 -22
  39. package/src/use-symbol-resolver.js +150 -110
  40. package/src/vector.js +67 -15
  41. package/src/vendor/README.md +110 -0
  42. package/src/vendor/inkscape-hatch-polyfill.js +401 -0
  43. package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
  44. package/src/vendor/inkscape-mesh-polyfill.js +843 -0
  45. package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
  46. package/src/verification.js +288 -124
@@ -0,0 +1,110 @@
1
+ # Vendor Dependencies
2
+
3
+ This directory contains third-party code used by SVG-MATRIX for SVG 2.0 browser compatibility.
4
+
5
+ ## Files
6
+
7
+ | File | Size | License | Author |
8
+ |------|------|---------|--------|
9
+ | inkscape-mesh-polyfill.js | ~35KB | GPLv3 | Tavmjong Bah |
10
+ | inkscape-mesh-polyfill.min.js | ~16KB | GPLv3 | Tavmjong Bah |
11
+ | inkscape-hatch-polyfill.js | ~10KB | CC0 | Valentin Ionita |
12
+ | inkscape-hatch-polyfill.min.js | ~5KB | CC0 | Valentin Ionita |
13
+
14
+ ---
15
+
16
+ ## inkscape-mesh-polyfill.js / inkscape-mesh-polyfill.min.js
17
+
18
+ **Source:** https://gitlab.com/Tavmjong/mesh.js/
19
+ **Author:** Tavmjong Bah
20
+ **License:** GNU General Public License version 3 or later (GPLv3+)
21
+ **Copyright:** 2017 Tavmjong Bah
22
+
23
+ ### Description
24
+
25
+ JavaScript polyfill for SVG 2.0 mesh gradients. Renders mesh gradients to HTML5 Canvas and converts them to images for browser compatibility.
26
+
27
+ ### Features
28
+
29
+ - Multi-patch grid support (multiple meshrows and meshpatches)
30
+ - Bezier curve edge parsing (l, L, c, C path commands)
31
+ - Adaptive tessellation via de Casteljau subdivision
32
+ - gradientTransform support (translate, rotate, scale, skew, matrix)
33
+ - Proper shape clipping via clipPath
34
+ - objectBoundingBox and userSpaceOnUse gradientUnits
35
+
36
+ ### License Notice
37
+
38
+ This file is distributed under the GNU General Public License version 3 or later.
39
+ See https://www.gnu.org/licenses/gpl-3.0.html for the full license text.
40
+
41
+ **IMPORTANT:** When using SVG-MATRIX with the `--svg2-polyfills` option on SVGs
42
+ containing mesh gradients, the generated SVG files will contain this GPLv3-licensed
43
+ polyfill code embedded in a `<script>` element. Distribution of such files must
44
+ comply with GPLv3 terms.
45
+
46
+ ### Usage in SVG-MATRIX
47
+
48
+ The polyfill is automatically embedded when:
49
+ 1. The SVG contains `<meshgradient>` elements
50
+ 2. The `--svg2-polyfills` CLI option is used
51
+
52
+ The polyfill runs in the browser at page load and:
53
+ 1. Detects elements using mesh gradient fills
54
+ 2. Rasterizes each mesh gradient to a Canvas
55
+ 3. Converts the Canvas to a PNG data URL
56
+ 4. Creates an `<image>` element with proper clipping
57
+ 5. Replaces the original fill with the rasterized image
58
+
59
+ ---
60
+
61
+ ## inkscape-hatch-polyfill.js / inkscape-hatch-polyfill.min.js
62
+
63
+ **Source:** https://gitlab.com/inkscape/inkscape/-/tree/master/src/extension/internal/polyfill
64
+ **Author:** Valentin Ionita (2019)
65
+ **License:** CC0 / Public Domain
66
+ **Copyright:** None (dedicated to public domain)
67
+
68
+ ### Description
69
+
70
+ JavaScript polyfill for SVG 2.0 hatch patterns. Converts SVG 2 `<hatch>` elements
71
+ to SVG 1.1 `<pattern>` elements for browser compatibility.
72
+
73
+ ### Features
74
+
75
+ - Full SVG path command support (M, L, C, S, Q, T, A, H, V, Z)
76
+ - Both relative and absolute path coordinates
77
+ - objectBoundingBox and userSpaceOnUse coordinate systems
78
+ - Hatch rotation and transform support
79
+ - Multiple hatchpath elements with offset values
80
+ - Proper pitch and spacing calculations
81
+ - Pattern tiling and repetition
82
+
83
+ ### License Notice
84
+
85
+ This file is dedicated to the public domain under CC0 (Creative Commons Zero).
86
+ See https://creativecommons.org/publicdomain/zero/1.0/ for details.
87
+
88
+ There are no restrictions on distribution or use of this polyfill.
89
+
90
+ ### Usage in SVG-MATRIX
91
+
92
+ The polyfill is automatically embedded when:
93
+ 1. The SVG contains `<hatch>` elements
94
+ 2. The `--svg2-polyfills` CLI option is used
95
+
96
+ The polyfill runs in the browser at page load and:
97
+ 1. Detects elements using hatch pattern fills
98
+ 2. Calculates bounding boxes and pitch values
99
+ 3. Generates equivalent `<pattern>` elements
100
+ 4. Updates fill references to use the generated patterns
101
+
102
+ ---
103
+
104
+ ## Minified vs Full Versions
105
+
106
+ By default, SVG-MATRIX uses the minified polyfills for smaller file sizes:
107
+ - Mesh: ~16KB minified vs ~35KB full
108
+ - Hatch: ~5KB minified vs ~10KB full
109
+
110
+ Use `--no-minify-polyfills` to embed the full, readable versions (useful for debugging).
@@ -0,0 +1,401 @@
1
+ // SPDX-License-Identifier: CC0
2
+ /** @file
3
+ * Use patterns to render a hatch paint server via this polyfill
4
+ *//*
5
+ * Authors:
6
+ * - Valentin Ionita (2019)
7
+ * License: CC0 / Public Domain
8
+ */
9
+
10
+ (function () {
11
+ // Name spaces -----------------------------------
12
+ const svgNS = 'http://www.w3.org/2000/svg';
13
+ const xlinkNS = 'http://www.w3.org/1999/xlink';
14
+ const unitObjectBoundingBox = 'objectBoundingBox';
15
+ const unitUserSpace = 'userSpaceOnUse';
16
+
17
+ // Set multiple attributes to an element
18
+ const setAttributes = (el, attrs) => {
19
+ for (let key in attrs) {
20
+ el.setAttribute(key, attrs[key]);
21
+ }
22
+ };
23
+
24
+ // Copy attributes from the hatch with 'id' to the current element
25
+ const setReference = (el, id) => {
26
+ const attr = [
27
+ 'x', 'y', 'pitch', 'rotate',
28
+ 'hatchUnits', 'hatchContentUnits', 'transform'
29
+ ];
30
+ const template = document.getElementById(id.slice(1));
31
+
32
+ if (template && template.nodeName === 'hatch') {
33
+ attr.forEach(a => {
34
+ let t = template.getAttribute(a);
35
+ if (el.getAttribute(a) === null && t !== null) {
36
+ el.setAttribute(a, t);
37
+ }
38
+ });
39
+
40
+ if (el.children.length === 0) {
41
+ Array.from(template.children).forEach(c => {
42
+ el.appendChild(c.cloneNode(true));
43
+ });
44
+ }
45
+ }
46
+ };
47
+
48
+ // Order pain-order of hatchpaths relative to their pitch
49
+ const orderHatchPaths = (paths) => {
50
+ const nodeArray = [];
51
+ paths.forEach(p => nodeArray.push(p));
52
+
53
+ return nodeArray.sort((a, b) =>
54
+ // (pitch - a.offset) - (pitch - b.offset)
55
+ Number(b.getAttribute('offset')) - Number(a.getAttribute('offset'))
56
+ );
57
+ };
58
+
59
+ // Generate x-axis coordinates for the pattern paths
60
+ const generatePositions = (width, diagonal, initial, distance) => {
61
+ const offset = (diagonal - width) / 2;
62
+ const leftDistance = initial + offset;
63
+ const rightDistance = width + offset + distance;
64
+ const units = Math.round(leftDistance / distance) + 1;
65
+ let array = [];
66
+
67
+ for (let i = initial - units * distance; i < rightDistance; i += distance) {
68
+ array.push(i);
69
+ }
70
+
71
+ return array;
72
+ };
73
+
74
+ // Turn a path array into a tokenized version of it
75
+ const parsePath = (data) => {
76
+ let array = [];
77
+ let i = 0;
78
+ let len = data.length;
79
+ let last = 0;
80
+
81
+ /*
82
+ * Last state (last) index map
83
+ * 0 => ()
84
+ * 1 => (x y)
85
+ * 2 => (x)
86
+ * 3 => (y)
87
+ * 4 => (x1 y1 x2 y2 x y)
88
+ * 5 => (x2 y2 x y)
89
+ * 6 => (_ _ _ _ _ x y)
90
+ * 7 => (_)
91
+ */
92
+
93
+ while (i < len) {
94
+ switch (data[i].toUpperCase()) {
95
+ case 'Z':
96
+ array.push(data[i]);
97
+ i += 1;
98
+ last = 0;
99
+ break;
100
+ case 'M':
101
+ case 'L':
102
+ case 'T':
103
+ array.push(data[i], new Point(Number(data[i + 1]), Number(data[i + 2])));
104
+ i += 3;
105
+ last = 1;
106
+ break;
107
+ case 'H':
108
+ array.push(data[i], new Point(Number(data[i + 1]), null));
109
+ i += 2;
110
+ last = 2;
111
+ break;
112
+ case 'V':
113
+ array.push(data[i], new Point(null, Number(data[i + 1])));
114
+ i += 2;
115
+ last = 3;
116
+ break;
117
+ case 'C':
118
+ array.push(
119
+ data[i], new Point(Number(data[i + 1]), Number(data[i + 2])),
120
+ new Point(Number(data[i + 3]), Number(data[i + 4])),
121
+ new Point(Number(data[i + 5]), Number(data[i + 6]))
122
+ );
123
+ i += 7;
124
+ last = 4;
125
+ break;
126
+ case 'S':
127
+ case 'Q':
128
+ array.push(
129
+ data[i], new Point(Number(data[i + 1]), Number(data[i + 2])),
130
+ new Point(Number(data[i + 3]), Number(data[i + 4]))
131
+ );
132
+ i += 5;
133
+ last = 5;
134
+ break;
135
+ case 'A':
136
+ array.push(
137
+ data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
138
+ data[i + 5], new Point(Number(data[i + 6]), Number(data[i + 7]))
139
+ );
140
+ i += 8;
141
+ last = 6;
142
+ break;
143
+ case 'B':
144
+ array.push(data[i], data[i + 1]);
145
+ i += 2;
146
+ last = 7;
147
+ break;
148
+ default:
149
+ switch (last) {
150
+ case 1:
151
+ array.push(new Point(Number(data[i]), Number(data[i + 1])));
152
+ i += 2;
153
+ break;
154
+ case 2:
155
+ array.push(new Point(Number(data[i]), null));
156
+ i += 1;
157
+ break;
158
+ case 3:
159
+ array.push(new Point(null, Number(data[i])));
160
+ i += 1;
161
+ break;
162
+ case 4:
163
+ array.push(
164
+ new Point(Number(data[i]), Number(data[i + 1])),
165
+ new Point(Number(data[i + 2]), Number(data[i + 3])),
166
+ new Point(Number(data[i + 4]), Number(data[i + 5]))
167
+ );
168
+ i += 6;
169
+ break;
170
+ case 5:
171
+ array.push(
172
+ new Point(Number(data[i]), Number(data[i + 1])),
173
+ new Point(Number(data[i + 2]), Number(data[i + 3]))
174
+ );
175
+ i += 4;
176
+ break;
177
+ case 6:
178
+ array.push(
179
+ data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
180
+ new Point(Number(data[i + 5]), Number(data[i + 6]))
181
+ );
182
+ i += 7;
183
+ break;
184
+ default:
185
+ array.push(data[i]);
186
+ i += 1;
187
+ }
188
+ }
189
+ }
190
+
191
+ return array;
192
+ };
193
+
194
+ const getYDistance = (hatchpath) => {
195
+ const path = document.createElementNS(svgNS, 'path');
196
+ let d = hatchpath.getAttribute('d');
197
+
198
+ if (d[0].toUpperCase() !== 'M') {
199
+ d = `M 0,0 ${d}`;
200
+ }
201
+
202
+ path.setAttribute('d', d);
203
+
204
+ return path.getPointAtLength(path.getTotalLength()).y -
205
+ path.getPointAtLength(0).y;
206
+ };
207
+
208
+ // Point class --------------------------------------
209
+ class Point {
210
+ constructor (x, y) {
211
+ this.x = x;
212
+ this.y = y;
213
+ }
214
+
215
+ toString () {
216
+ return `${this.x} ${this.y}`;
217
+ }
218
+
219
+ isPoint () {
220
+ return true;
221
+ }
222
+
223
+ clone () {
224
+ return new Point(this.x, this.y);
225
+ }
226
+
227
+ add (v) {
228
+ return new Point(this.x + v.x, this.y + v.y);
229
+ }
230
+
231
+ distSquared (v) {
232
+ let x = this.x - v.x;
233
+ let y = this.y - v.y;
234
+ return (x * x + y * y);
235
+ }
236
+ }
237
+
238
+ // Start of document processing ---------------------
239
+ const shapes = document.querySelectorAll('rect,circle,ellipse,path,text');
240
+
241
+ shapes.forEach((shape, i) => {
242
+ // Get id. If no id, create one.
243
+ let shapeId = shape.getAttribute('id');
244
+ if (!shapeId) {
245
+ shapeId = 'hatch_shape_' + i;
246
+ shape.setAttribute('id', shapeId);
247
+ }
248
+
249
+ const fill = shape.getAttribute('fill') || shape.style.fill;
250
+ const fillURL = fill.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/);
251
+
252
+ if (fillURL && fillURL[1]) {
253
+ const hatch = document.getElementById(fillURL[1]);
254
+
255
+ if (hatch && hatch.nodeName === 'hatch') {
256
+ const href = hatch.getAttributeNS(xlinkNS, 'href');
257
+
258
+ if (href !== null && href !== '') {
259
+ setReference(hatch, href);
260
+ }
261
+
262
+ // Degenerate hatch, with no hatchpath children
263
+ if (hatch.children.length === 0) {
264
+ return;
265
+ }
266
+
267
+ const bbox = shape.getBBox();
268
+ const hatchDiag = Math.ceil(Math.sqrt(
269
+ bbox.width * bbox.width + bbox.height * bbox.height
270
+ ));
271
+
272
+ // Hatch variables
273
+ const units = hatch.getAttribute('hatchUnits') || unitObjectBoundingBox;
274
+ const contentUnits = hatch.getAttribute('hatchContentUnits') || unitUserSpace;
275
+ const rotate = Number(hatch.getAttribute('rotate')) || 0;
276
+ const transform = hatch.getAttribute('transform') ||
277
+ hatch.getAttribute('hatchTransform') || '';
278
+ const hatchpaths = orderHatchPaths(hatch.querySelectorAll('hatchpath,hatchPath'));
279
+ const x = units === unitObjectBoundingBox
280
+ ? (Number(hatch.getAttribute('x')) * bbox.width) || 0
281
+ : Number(hatch.getAttribute('x')) || 0;
282
+ const y = units === unitObjectBoundingBox
283
+ ? (Number(hatch.getAttribute('y')) * bbox.width) || 0
284
+ : Number(hatch.getAttribute('y')) || 0;
285
+ let pitch = units === unitObjectBoundingBox
286
+ ? (Number(hatch.getAttribute('pitch')) * bbox.width) || 0
287
+ : Number(hatch.getAttribute('pitch')) || 0;
288
+
289
+ if (contentUnits === unitObjectBoundingBox && bbox.height) {
290
+ pitch /= bbox.height;
291
+ }
292
+
293
+ // A negative value is an error.
294
+ // A value of zero disables rendering of the element
295
+ if (pitch <= 0) {
296
+ console.error('Non-positive pitch');
297
+ return;
298
+ }
299
+
300
+ // Pattern variables
301
+ const pattern = document.createElementNS(svgNS, 'pattern');
302
+ const patternId = `${fillURL[1]}_pattern`;
303
+ let patternWidth = bbox.width - bbox.width % pitch;
304
+ let patternHeight = 0;
305
+
306
+ const xPositions = generatePositions(patternWidth, hatchDiag, x, pitch);
307
+
308
+ hatchpaths.forEach(hatchpath => {
309
+ let offset = Number(hatchpath.getAttribute('offset')) || 0;
310
+ offset = offset > pitch ? (offset % pitch) : offset;
311
+ const currentXPositions = xPositions.map(p => p + offset);
312
+
313
+ const path = document.createElementNS(svgNS, 'path');
314
+ let d = '';
315
+
316
+ for (let j = 0; j < hatchpath.attributes.length; ++j) {
317
+ const attr = hatchpath.attributes.item(j);
318
+ if (attr.name !== 'd') {
319
+ path.setAttribute(attr.name, attr.value);
320
+ }
321
+ }
322
+
323
+ if (hatchpath.getAttribute('d') === null) {
324
+ d += currentXPositions.reduce(
325
+ (acc, xPos) => `${acc}M ${xPos} ${y} V ${hatchDiag} `, ''
326
+ );
327
+ patternHeight = hatchDiag;
328
+ } else {
329
+ const hatchData = hatchpath.getAttribute('d');
330
+ const data = parsePath(
331
+ hatchData.match(/([+-]?(\d+(\.\d+)?))|[MmZzLlHhVvCcSsQqTtAaBb]/g)
332
+ );
333
+ const len = data.length;
334
+ const startsWithM = data[0] === 'M';
335
+ const relative = data[0].toLowerCase() === data[0];
336
+ const point = new Point(0, 0);
337
+ let yOffset = getYDistance(hatchpath);
338
+
339
+ if (data[len - 1].y !== undefined && yOffset < data[len - 1].y) {
340
+ yOffset = data[len - 1].y;
341
+ }
342
+
343
+ // The offset must be positive
344
+ if (yOffset <= 0) {
345
+ console.error('y offset is non-positive');
346
+ return;
347
+ }
348
+ patternHeight = bbox.height - bbox.height % yOffset;
349
+
350
+ const currentYPositions = generatePositions(
351
+ patternHeight, hatchDiag, y, yOffset
352
+ );
353
+
354
+ currentXPositions.forEach(xPos => {
355
+ point.x = xPos;
356
+
357
+ if (!startsWithM && !relative) {
358
+ d += `M ${xPos} 0`;
359
+ }
360
+
361
+ currentYPositions.forEach(yPos => {
362
+ point.y = yPos;
363
+
364
+ if (relative) {
365
+ // Path is relative, set the first point in each path render
366
+ d += `M ${xPos} ${yPos} ${hatchData}`;
367
+ } else {
368
+ // Path is absolute, translate every point
369
+ d += data.map(e => e.isPoint && e.isPoint() ? e.add(point) : e)
370
+ .map(e => e.isPoint && e.isPoint() ? e.toString() : e)
371
+ .reduce((acc, e) => `${acc} ${e}`, '');
372
+ }
373
+ });
374
+ });
375
+
376
+ // The hatchpaths are infinite, so they have no fill
377
+ path.style.fill = 'none';
378
+ }
379
+
380
+ path.setAttribute('d', d);
381
+ pattern.appendChild(path);
382
+ });
383
+
384
+ setAttributes(pattern, {
385
+ 'id': patternId,
386
+ 'patternUnits': unitUserSpace,
387
+ 'patternContentUnits': contentUnits,
388
+ 'width': patternWidth,
389
+ 'height': patternHeight,
390
+ 'x': bbox.x,
391
+ 'y': bbox.y,
392
+ 'patternTransform': `rotate(${rotate} ${0} ${0}) ${transform}`
393
+ });
394
+ hatch.parentElement.insertBefore(pattern, hatch);
395
+
396
+ shape.style.fill = `url(#${patternId})`;
397
+ shape.setAttribute('fill', `url(#${patternId})`);
398
+ }
399
+ }
400
+ });
401
+ })();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Inkscape Hatch Pattern Polyfill (Minified)
3
+ * Source: https://gitlab.com/inkscape/inkscape/-/blob/master/src/extension/internal/polyfill/hatch_compressed.include
4
+ * License: CC0 (Public Domain)
5
+ *
6
+ * Converts SVG 2 hatch elements to SVG 1.1 pattern elements for browser compatibility.
7
+ */
8
+ !function(){const t="http://www.w3.org/2000/svg",e=(t,e,r,n)=>{const u=(e-t)/2,i=r+u,s=t+u+n;let h=[];for(let t=r-(Math.round(i/n)+1)*n;t<s;t+=n)h.push(t);return h};class r{constructor(t,e){this.x=t,this.y=e}toString(){return`${this.x} ${this.y}`}isPoint(){return!0}clone(){return new r(this.x,this.y)}add(t){return new r(this.x+t.x,this.y+t.y)}distSquared(t){let e=this.x-t.x,r=this.y-t.y;return e*e+r*r}}document.querySelectorAll("rect,circle,ellipse,path,text").forEach((n,u)=>{let i=n.getAttribute("id");i||(i="hatch_shape_"+u,n.setAttribute("id",i));const s=(n.getAttribute("fill")||n.style.fill).match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/);if(s&&s[1]){const u=document.getElementById(s[1]);if(u&&"hatch"===u.nodeName){const i=u.getAttributeNS("http://www.w3.org/1999/xlink","href");if(null!==i&&""!==i&&((t,e)=>{const r=["x","y","pitch","rotate","hatchUnits","hatchContentUnits","transform"],n=document.getElementById(e.slice(1));n&&"hatch"===n.nodeName&&(r.forEach(e=>{let r=n.getAttribute(e);null===t.getAttribute(e)&&null!==r&&t.setAttribute(e,r)}),0===t.children.length&&Array.from(n.children).forEach(e=>{t.appendChild(e.cloneNode(!0))}))})(u,i),0===u.children.length)return;const h=n.getBBox(),o=Math.ceil(Math.sqrt(h.width*h.width+h.height*h.height)),a=u.getAttribute("hatchUnits")||"objectBoundingBox",c=u.getAttribute("hatchContentUnits")||"userSpaceOnUse",b=Number(u.getAttribute("rotate"))||0,l=u.getAttribute("transform")||u.getAttribute("hatchTransform")||"",m=(t=>{const e=[];return t.forEach(t=>e.push(t)),e.sort((t,e)=>Number(e.getAttribute("offset"))-Number(t.getAttribute("offset")))})(u.querySelectorAll("hatchpath,hatchPath")),d="objectBoundingBox"===a?Number(u.getAttribute("x"))*h.width||0:Number(u.getAttribute("x"))||0,g="objectBoundingBox"===a?Number(u.getAttribute("y"))*h.width||0:Number(u.getAttribute("y"))||0;let p="objectBoundingBox"===a?Number(u.getAttribute("pitch"))*h.width||0:Number(u.getAttribute("pitch"))||0;if("objectBoundingBox"===c&&h.height&&(p/=h.height),p<=0)return void console.error("Non-positive pitch");const N=document.createElementNS(t,"pattern"),f=`${s[1]}_pattern`;let w=h.width-h.width%p,A=0;const y=e(w,o,d,p);m.forEach(n=>{let u=Number(n.getAttribute("offset"))||0;u=u>p?u%p:u;const i=y.map(t=>t+u),s=document.createElementNS(t,"path");let a="";for(let t=0;t<n.attributes.length;++t){const e=n.attributes.item(t);"d"!==e.name&&s.setAttribute(e.name,e.value)}if(null===n.getAttribute("d"))a+=i.reduce((t,e)=>`${t}M ${e} ${g} V ${o} `,""),A=o;else{const u=n.getAttribute("d"),c=(t=>{let e=[],n=0,u=t.length,i=0;for(;n<u;)switch(t[n].toUpperCase()){case"Z":e.push(t[n]),n+=1,i=0;break;case"M":case"L":case"T":e.push(t[n],new r(Number(t[n+1]),Number(t[n+2]))),n+=3,i=1;break;case"H":e.push(t[n],new r(Number(t[n+1]),null)),n+=2,i=2;break;case"V":e.push(t[n],new r(null,Number(t[n+1]))),n+=2,i=3;break;case"C":e.push(t[n],new r(Number(t[n+1]),Number(t[n+2])),new r(Number(t[n+3]),Number(t[n+4])),new r(Number(t[n+5]),Number(t[n+6]))),n+=7,i=4;break;case"S":case"Q":e.push(t[n],new r(Number(t[n+1]),Number(t[n+2])),new r(Number(t[n+3]),Number(t[n+4]))),n+=5,i=5;break;case"A":e.push(t[n],t[n+1],t[n+2],t[n+3],t[n+4],t[n+5],new r(Number(t[n+6]),Number(t[n+7]))),n+=8,i=6;break;case"B":e.push(t[n],t[n+1]),n+=2,i=7;break;default:switch(i){case 1:e.push(new r(Number(t[n]),Number(t[n+1]))),n+=2;break;case 2:e.push(new r(Number(t[n]),null)),n+=1;break;case 3:e.push(new r(null,Number(t[n]))),n+=1;break;case 4:e.push(new r(Number(t[n]),Number(t[n+1])),new r(Number(t[n+2]),Number(t[n+3])),new r(Number(t[n+4]),Number(t[n+5]))),n+=6;break;case 5:e.push(new r(Number(t[n]),Number(t[n+1])),new r(Number(t[n+2]),Number(t[n+3]))),n+=4;break;case 6:e.push(t[n],t[n+1],t[n+2],t[n+3],t[n+4],new r(Number(t[n+5]),Number(t[n+6]))),n+=7;break;default:e.push(t[n]),n+=1}}return e})(u.match(/([+-]?(\d+(\.\d+)?))|[MmZzLlHhVvCcSsQqTtAaBb]/g)),b=c.length,l="M"===c[0],m=c[0].toLowerCase()===c[0],d=new r(0,0);let p=(e=>{const r=document.createElementNS(t,"path");let n=e.getAttribute("d");return"M"!==n[0].toUpperCase()&&(n=`M 0,0 ${n}`),r.setAttribute("d",n),r.getPointAtLength(r.getTotalLength()).y-r.getPointAtLength(0).y})(n);if(void 0!==c[b-1].y&&p<c[b-1].y&&(p=c[b-1].y),p<=0)return void console.error("y offset is non-positive");A=h.height-h.height%p;const N=e(A,o,g,p);i.forEach(t=>{d.x=t,l||m||(a+=`M ${t} 0`),N.forEach(e=>{d.y=e,a+=m?`M ${t} ${e} ${u}`:c.map(t=>t.isPoint&&t.isPoint()?t.add(d):t).map(t=>t.isPoint&&t.isPoint()?t.toString():t).reduce((t,e)=>`${t} ${e}`,"")})}),s.style.fill="none"}s.setAttribute("d",a),N.appendChild(s)}),((t,e)=>{for(let r in e)t.setAttribute(r,e[r])})(N,{id:f,patternUnits:"userSpaceOnUse",patternContentUnits:c,width:w,height:A,x:h.x,y:h.y,patternTransform:`rotate(${b} 0 0) ${l}`}),u.parentElement.insertBefore(N,u),n.style.fill=`url(#${f})`,n.setAttribute("fill",`url(#${f})`)}}})}();