@emasoft/svg-matrix 1.0.25 → 1.0.27
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.
- package/README.md +57 -1
- package/bin/svg-matrix.js +19 -3
- package/bin/svgm.js +28 -2
- package/package.json +1 -1
- package/src/index.js +8 -6
- package/src/inkscape-support.js +248 -0
- package/src/svg-toolbox.js +15 -19
- package/src/svg2-polyfills.js +444 -0
- package/src/text-to-path.js +0 -820
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG 2.0 Polyfill Generator
|
|
3
|
+
*
|
|
4
|
+
* Detects SVG 2.0 features (mesh gradients, hatches) and generates inline
|
|
5
|
+
* JavaScript polyfills for browser compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Uses the existing mesh-gradient.js math for Coons patch evaluation.
|
|
8
|
+
* All polyfills are embedded inline in the SVG for self-contained output.
|
|
9
|
+
*
|
|
10
|
+
* @module svg2-polyfills
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* SVG 2.0 features that can be polyfilled
|
|
15
|
+
*/
|
|
16
|
+
export const SVG2_FEATURES = {
|
|
17
|
+
MESH_GRADIENT: 'meshGradient',
|
|
18
|
+
HATCH: 'hatch',
|
|
19
|
+
CONTEXT_PAINT: 'context-paint',
|
|
20
|
+
AUTO_START_REVERSE: 'auto-start-reverse'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Detect SVG 2.0 features that need polyfills in a document.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} doc - Parsed SVG document
|
|
27
|
+
* @returns {{meshGradients: string[], hatches: string[], contextPaint: boolean, autoStartReverse: boolean}} Detected features
|
|
28
|
+
*/
|
|
29
|
+
export function detectSVG2Features(doc) {
|
|
30
|
+
const features = {
|
|
31
|
+
meshGradients: [],
|
|
32
|
+
hatches: [],
|
|
33
|
+
contextPaint: false,
|
|
34
|
+
autoStartReverse: false
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const walk = (el) => {
|
|
38
|
+
if (!el) return;
|
|
39
|
+
|
|
40
|
+
// Check tag name for mesh gradient (case-insensitive)
|
|
41
|
+
const tagName = el.tagName?.toLowerCase();
|
|
42
|
+
if (tagName === 'meshgradient') {
|
|
43
|
+
const id = el.getAttribute('id');
|
|
44
|
+
if (id) features.meshGradients.push(id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for hatch element
|
|
48
|
+
if (tagName === 'hatch') {
|
|
49
|
+
const id = el.getAttribute('id');
|
|
50
|
+
if (id) features.hatches.push(id);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for context-paint in fill/stroke
|
|
54
|
+
const fill = el.getAttribute('fill');
|
|
55
|
+
const stroke = el.getAttribute('stroke');
|
|
56
|
+
if (fill === 'context-fill' || fill === 'context-stroke' ||
|
|
57
|
+
stroke === 'context-fill' || stroke === 'context-stroke') {
|
|
58
|
+
features.contextPaint = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check for auto-start-reverse in markers
|
|
62
|
+
const orient = el.getAttribute('orient');
|
|
63
|
+
if (orient === 'auto-start-reverse') {
|
|
64
|
+
features.autoStartReverse = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Recurse into children
|
|
68
|
+
if (el.children) {
|
|
69
|
+
for (const child of el.children) {
|
|
70
|
+
walk(child);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
walk(doc);
|
|
76
|
+
return features;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if document needs any SVG 2 polyfills.
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} doc - Parsed SVG document
|
|
83
|
+
* @returns {boolean} True if polyfills are needed
|
|
84
|
+
*/
|
|
85
|
+
export function needsPolyfills(doc) {
|
|
86
|
+
const features = detectSVG2Features(doc);
|
|
87
|
+
return features.meshGradients.length > 0 ||
|
|
88
|
+
features.hatches.length > 0 ||
|
|
89
|
+
features.contextPaint ||
|
|
90
|
+
features.autoStartReverse;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate the mesh gradient polyfill code.
|
|
95
|
+
* This polyfill renders mesh gradients to canvas and uses them as image fills.
|
|
96
|
+
*
|
|
97
|
+
* @returns {string} JavaScript polyfill code
|
|
98
|
+
*/
|
|
99
|
+
function generateMeshPolyfillCode() {
|
|
100
|
+
return `
|
|
101
|
+
// Mesh Gradient Polyfill - SVG 2.0 to Canvas fallback
|
|
102
|
+
// Generated by svg-matrix
|
|
103
|
+
(function() {
|
|
104
|
+
'use strict';
|
|
105
|
+
|
|
106
|
+
// Skip if browser supports mesh gradients natively
|
|
107
|
+
if (typeof document.createElementNS('http://www.w3.org/2000/svg', 'meshGradient').x !== 'undefined') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Find all mesh gradients
|
|
112
|
+
var meshes = document.querySelectorAll('meshGradient, meshgradient');
|
|
113
|
+
if (!meshes.length) return;
|
|
114
|
+
|
|
115
|
+
// Parse color string to RGBA
|
|
116
|
+
function parseColor(str) {
|
|
117
|
+
if (!str) return {r: 0, g: 0, b: 0, a: 255};
|
|
118
|
+
var m = str.match(/rgba?\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/);
|
|
119
|
+
if (m) return {r: +m[1], g: +m[2], b: +m[3], a: m[4] ? Math.round(+m[4] * 255) : 255};
|
|
120
|
+
if (str[0] === '#') {
|
|
121
|
+
var hex = str.slice(1);
|
|
122
|
+
if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
|
|
123
|
+
return {r: parseInt(hex.slice(0,2), 16), g: parseInt(hex.slice(2,4), 16), b: parseInt(hex.slice(4,6), 16), a: 255};
|
|
124
|
+
}
|
|
125
|
+
return {r: 0, g: 0, b: 0, a: 255};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Bilinear color interpolation
|
|
129
|
+
function bilinearColor(c00, c10, c01, c11, u, v) {
|
|
130
|
+
var mu = 1 - u, mv = 1 - v;
|
|
131
|
+
return {
|
|
132
|
+
r: Math.round(mu*mv*c00.r + u*mv*c10.r + mu*v*c01.r + u*v*c11.r),
|
|
133
|
+
g: Math.round(mu*mv*c00.g + u*mv*c10.g + mu*v*c01.g + u*v*c11.g),
|
|
134
|
+
b: Math.round(mu*mv*c00.b + u*mv*c10.b + mu*v*c01.b + u*v*c11.b),
|
|
135
|
+
a: Math.round(mu*mv*c00.a + u*mv*c10.a + mu*v*c01.a + u*v*c11.a)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Evaluate cubic Bezier at t
|
|
140
|
+
function evalBezier(p0, p1, p2, p3, t) {
|
|
141
|
+
var mt = 1 - t, mt2 = mt * mt, mt3 = mt2 * mt;
|
|
142
|
+
var t2 = t * t, t3 = t2 * t;
|
|
143
|
+
return {
|
|
144
|
+
x: mt3*p0.x + 3*mt2*t*p1.x + 3*mt*t2*p2.x + t3*p3.x,
|
|
145
|
+
y: mt3*p0.y + 3*mt2*t*p1.y + 3*mt*t2*p2.y + t3*p3.y
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Process each mesh gradient
|
|
150
|
+
meshes.forEach(function(mesh) {
|
|
151
|
+
var id = mesh.getAttribute('id');
|
|
152
|
+
if (!id) return;
|
|
153
|
+
|
|
154
|
+
// Create canvas for rasterization
|
|
155
|
+
var canvas = document.createElement('canvas');
|
|
156
|
+
var ctx = canvas.getContext('2d');
|
|
157
|
+
|
|
158
|
+
// Get bounding box from referencing elements
|
|
159
|
+
var refs = document.querySelectorAll('[fill="url(#' + id + ')"], [stroke="url(#' + id + ')"]');
|
|
160
|
+
if (!refs.length) return;
|
|
161
|
+
|
|
162
|
+
var bbox = refs[0].getBBox();
|
|
163
|
+
var size = Math.max(bbox.width, bbox.height, 256);
|
|
164
|
+
canvas.width = canvas.height = size;
|
|
165
|
+
|
|
166
|
+
// Parse mesh patches
|
|
167
|
+
var patches = [];
|
|
168
|
+
var rows = mesh.querySelectorAll('meshrow');
|
|
169
|
+
rows.forEach(function(row) {
|
|
170
|
+
var rowPatches = row.querySelectorAll('meshpatch');
|
|
171
|
+
rowPatches.forEach(function(patch) {
|
|
172
|
+
var stops = patch.querySelectorAll('stop');
|
|
173
|
+
if (stops.length >= 4) {
|
|
174
|
+
patches.push({
|
|
175
|
+
colors: [
|
|
176
|
+
parseColor(stops[0].getAttribute('stop-color') || stops[0].style.stopColor),
|
|
177
|
+
parseColor(stops[1].getAttribute('stop-color') || stops[1].style.stopColor),
|
|
178
|
+
parseColor(stops[2].getAttribute('stop-color') || stops[2].style.stopColor),
|
|
179
|
+
parseColor(stops[3].getAttribute('stop-color') || stops[3].style.stopColor)
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Render patches with bilinear interpolation
|
|
187
|
+
if (patches.length > 0) {
|
|
188
|
+
var imgData = ctx.createImageData(size, size);
|
|
189
|
+
var data = imgData.data;
|
|
190
|
+
var patch = patches[0];
|
|
191
|
+
for (var y = 0; y < size; y++) {
|
|
192
|
+
for (var x = 0; x < size; x++) {
|
|
193
|
+
var u = x / (size - 1);
|
|
194
|
+
var v = y / (size - 1);
|
|
195
|
+
var c = bilinearColor(patch.colors[0], patch.colors[1], patch.colors[2], patch.colors[3], u, v);
|
|
196
|
+
var i = (y * size + x) * 4;
|
|
197
|
+
data[i] = c.r;
|
|
198
|
+
data[i+1] = c.g;
|
|
199
|
+
data[i+2] = c.b;
|
|
200
|
+
data[i+3] = c.a;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
ctx.putImageData(imgData, 0, 0);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Create pattern from canvas
|
|
207
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
208
|
+
var pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
|
|
209
|
+
pattern.setAttribute('id', id + '_polyfill');
|
|
210
|
+
pattern.setAttribute('patternUnits', 'objectBoundingBox');
|
|
211
|
+
pattern.setAttribute('width', '1');
|
|
212
|
+
pattern.setAttribute('height', '1');
|
|
213
|
+
|
|
214
|
+
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
215
|
+
img.setAttribute('href', dataUrl);
|
|
216
|
+
img.setAttribute('width', '1');
|
|
217
|
+
img.setAttribute('height', '1');
|
|
218
|
+
img.setAttribute('preserveAspectRatio', 'none');
|
|
219
|
+
pattern.appendChild(img);
|
|
220
|
+
|
|
221
|
+
// Add pattern to defs
|
|
222
|
+
var defs = mesh.closest('svg').querySelector('defs') || (function() {
|
|
223
|
+
var d = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
224
|
+
mesh.closest('svg').insertBefore(d, mesh.closest('svg').firstChild);
|
|
225
|
+
return d;
|
|
226
|
+
})();
|
|
227
|
+
defs.appendChild(pattern);
|
|
228
|
+
|
|
229
|
+
// Update references to use polyfill pattern
|
|
230
|
+
refs.forEach(function(ref) {
|
|
231
|
+
if (ref.getAttribute('fill') === 'url(#' + id + ')') {
|
|
232
|
+
ref.setAttribute('fill', 'url(#' + id + '_polyfill)');
|
|
233
|
+
}
|
|
234
|
+
if (ref.getAttribute('stroke') === 'url(#' + id + ')') {
|
|
235
|
+
ref.setAttribute('stroke', 'url(#' + id + '_polyfill)');
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
})();
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Generate the hatch pattern polyfill code.
|
|
245
|
+
* Converts SVG 2 hatch elements to SVG 1.1 pattern elements.
|
|
246
|
+
*
|
|
247
|
+
* @returns {string} JavaScript polyfill code
|
|
248
|
+
*/
|
|
249
|
+
function generateHatchPolyfillCode() {
|
|
250
|
+
return `
|
|
251
|
+
// Hatch Pattern Polyfill - SVG 2.0 to SVG 1.1 pattern conversion
|
|
252
|
+
// Generated by svg-matrix
|
|
253
|
+
(function() {
|
|
254
|
+
'use strict';
|
|
255
|
+
|
|
256
|
+
// Find all hatch elements
|
|
257
|
+
var hatches = document.querySelectorAll('hatch');
|
|
258
|
+
if (!hatches.length) return;
|
|
259
|
+
|
|
260
|
+
hatches.forEach(function(hatch) {
|
|
261
|
+
var id = hatch.getAttribute('id');
|
|
262
|
+
if (!id) return;
|
|
263
|
+
|
|
264
|
+
// Get hatch properties
|
|
265
|
+
var href = hatch.getAttribute('href') || hatch.getAttribute('xlink:href');
|
|
266
|
+
var hatchUnits = hatch.getAttribute('hatchUnits') || 'objectBoundingBox';
|
|
267
|
+
var hatchContentUnits = hatch.getAttribute('hatchContentUnits') || 'userSpaceOnUse';
|
|
268
|
+
var pitch = parseFloat(hatch.getAttribute('pitch')) || 8;
|
|
269
|
+
var rotate = parseFloat(hatch.getAttribute('rotate')) || 0;
|
|
270
|
+
var transform = hatch.getAttribute('transform') || '';
|
|
271
|
+
|
|
272
|
+
// Create equivalent pattern
|
|
273
|
+
var pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
|
|
274
|
+
pattern.setAttribute('id', id + '_polyfill');
|
|
275
|
+
pattern.setAttribute('patternUnits', hatchUnits);
|
|
276
|
+
pattern.setAttribute('width', pitch);
|
|
277
|
+
pattern.setAttribute('height', pitch);
|
|
278
|
+
|
|
279
|
+
if (transform || rotate) {
|
|
280
|
+
var fullTransform = transform;
|
|
281
|
+
if (rotate) fullTransform += ' rotate(' + rotate + ')';
|
|
282
|
+
pattern.setAttribute('patternTransform', fullTransform.trim());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Copy hatchpath children as lines
|
|
286
|
+
var hatchpaths = hatch.querySelectorAll('hatchpath, hatchPath');
|
|
287
|
+
hatchpaths.forEach(function(hp) {
|
|
288
|
+
var d = hp.getAttribute('d');
|
|
289
|
+
var strokeColor = hp.getAttribute('stroke') || 'black';
|
|
290
|
+
var strokeWidth = hp.getAttribute('stroke-width') || '1';
|
|
291
|
+
|
|
292
|
+
var line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
293
|
+
line.setAttribute('d', d || 'M0,0 L' + pitch + ',0');
|
|
294
|
+
line.setAttribute('stroke', strokeColor);
|
|
295
|
+
line.setAttribute('stroke-width', strokeWidth);
|
|
296
|
+
line.setAttribute('fill', 'none');
|
|
297
|
+
pattern.appendChild(line);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// If no hatchpaths, create default diagonal line
|
|
301
|
+
if (!hatchpaths.length) {
|
|
302
|
+
var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
303
|
+
line.setAttribute('x1', '0');
|
|
304
|
+
line.setAttribute('y1', '0');
|
|
305
|
+
line.setAttribute('x2', pitch);
|
|
306
|
+
line.setAttribute('y2', pitch);
|
|
307
|
+
line.setAttribute('stroke', 'black');
|
|
308
|
+
line.setAttribute('stroke-width', '1');
|
|
309
|
+
pattern.appendChild(line);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Add pattern to defs
|
|
313
|
+
var defs = hatch.closest('svg').querySelector('defs') || (function() {
|
|
314
|
+
var d = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
315
|
+
hatch.closest('svg').insertBefore(d, hatch.closest('svg').firstChild);
|
|
316
|
+
return d;
|
|
317
|
+
})();
|
|
318
|
+
defs.appendChild(pattern);
|
|
319
|
+
|
|
320
|
+
// Update references
|
|
321
|
+
var refs = document.querySelectorAll('[fill="url(#' + id + ')"], [stroke="url(#' + id + ')"]');
|
|
322
|
+
refs.forEach(function(ref) {
|
|
323
|
+
if (ref.getAttribute('fill') === 'url(#' + id + ')') {
|
|
324
|
+
ref.setAttribute('fill', 'url(#' + id + '_polyfill)');
|
|
325
|
+
}
|
|
326
|
+
if (ref.getAttribute('stroke') === 'url(#' + id + ')') {
|
|
327
|
+
ref.setAttribute('stroke', 'url(#' + id + '_polyfill)');
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
})();
|
|
332
|
+
`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Generate complete polyfill script based on detected features.
|
|
337
|
+
*
|
|
338
|
+
* @param {{meshGradients: string[], hatches: string[], contextPaint: boolean, autoStartReverse: boolean}} features - Detected features
|
|
339
|
+
* @returns {string|null} Complete polyfill script or null if none needed
|
|
340
|
+
*/
|
|
341
|
+
export function generatePolyfillScript(features) {
|
|
342
|
+
const parts = [];
|
|
343
|
+
|
|
344
|
+
parts.push('/* SVG 2.0 Polyfills - Generated by svg-matrix */');
|
|
345
|
+
|
|
346
|
+
if (features.meshGradients.length > 0) {
|
|
347
|
+
parts.push(generateMeshPolyfillCode());
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (features.hatches.length > 0) {
|
|
351
|
+
parts.push(generateHatchPolyfillCode());
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (parts.length === 1) {
|
|
355
|
+
return null; // Only header, no actual polyfills
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return parts.join('\n');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Inject polyfill script into SVG document.
|
|
363
|
+
*
|
|
364
|
+
* @param {Object} doc - Parsed SVG document
|
|
365
|
+
* @param {Object} [options] - Options
|
|
366
|
+
* @param {boolean} [options.force=false] - Force injection even if no features detected
|
|
367
|
+
* @returns {Object} The document (modified in place)
|
|
368
|
+
*/
|
|
369
|
+
export function injectPolyfills(doc, options = {}) {
|
|
370
|
+
const features = detectSVG2Features(doc);
|
|
371
|
+
|
|
372
|
+
// Check if polyfills are needed
|
|
373
|
+
if (!options.force &&
|
|
374
|
+
features.meshGradients.length === 0 &&
|
|
375
|
+
features.hatches.length === 0) {
|
|
376
|
+
return doc;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const script = generatePolyfillScript(features);
|
|
380
|
+
if (!script) return doc;
|
|
381
|
+
|
|
382
|
+
// Find or create the SVG root
|
|
383
|
+
const svg = doc.documentElement || doc;
|
|
384
|
+
|
|
385
|
+
// Create script element
|
|
386
|
+
// Note: We need to handle this based on the parser being used
|
|
387
|
+
const scriptEl = {
|
|
388
|
+
tagName: 'script',
|
|
389
|
+
attributes: new Map([['type', 'text/javascript']]),
|
|
390
|
+
children: [],
|
|
391
|
+
textContent: script,
|
|
392
|
+
getAttribute(name) { return this.attributes.get(name); },
|
|
393
|
+
setAttribute(name, value) { this.attributes.set(name, value); },
|
|
394
|
+
hasAttribute(name) { return this.attributes.has(name); },
|
|
395
|
+
removeAttribute(name) { this.attributes.delete(name); },
|
|
396
|
+
getAttributeNames() { return [...this.attributes.keys()]; },
|
|
397
|
+
appendChild(child) { this.children.push(child); },
|
|
398
|
+
removeChild(child) {
|
|
399
|
+
const idx = this.children.indexOf(child);
|
|
400
|
+
if (idx >= 0) this.children.splice(idx, 1);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// Insert script at beginning of SVG (after any xml declaration)
|
|
405
|
+
if (svg.children && svg.children.length > 0) {
|
|
406
|
+
svg.children.unshift(scriptEl);
|
|
407
|
+
} else if (svg.children) {
|
|
408
|
+
svg.children.push(scriptEl);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return doc;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Remove polyfill scripts from SVG document.
|
|
416
|
+
*
|
|
417
|
+
* @param {Object} doc - Parsed SVG document
|
|
418
|
+
* @returns {Object} The document (modified in place)
|
|
419
|
+
*/
|
|
420
|
+
export function removePolyfills(doc) {
|
|
421
|
+
const walk = (el) => {
|
|
422
|
+
if (!el || !el.children) return;
|
|
423
|
+
|
|
424
|
+
// Remove script elements that are svg-matrix polyfills
|
|
425
|
+
el.children = el.children.filter(child => {
|
|
426
|
+
if (child.tagName === 'script') {
|
|
427
|
+
const content = child.textContent || '';
|
|
428
|
+
if (content.includes('SVG 2.0 Polyfill') ||
|
|
429
|
+
content.includes('Generated by svg-matrix')) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Recurse
|
|
437
|
+
for (const child of el.children) {
|
|
438
|
+
walk(child);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
walk(doc);
|
|
443
|
+
return doc;
|
|
444
|
+
}
|