@emasoft/svg-matrix 1.0.28 → 1.0.29
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 +325 -0
- package/bin/svg-matrix.js +985 -378
- package/bin/svglinter.cjs +4172 -433
- package/bin/svgm.js +723 -180
- package/package.json +16 -4
- package/src/animation-references.js +71 -52
- package/src/arc-length.js +160 -96
- package/src/bezier-analysis.js +257 -117
- package/src/bezier-intersections.js +411 -148
- package/src/browser-verify.js +240 -100
- package/src/clip-path-resolver.js +350 -142
- package/src/convert-path-data.js +279 -134
- package/src/css-specificity.js +78 -70
- package/src/flatten-pipeline.js +751 -263
- package/src/geometry-to-path.js +511 -182
- package/src/index.js +191 -46
- package/src/inkscape-support.js +18 -7
- package/src/marker-resolver.js +278 -164
- package/src/mask-resolver.js +209 -98
- package/src/matrix.js +147 -67
- package/src/mesh-gradient.js +187 -96
- package/src/off-canvas-detection.js +201 -104
- package/src/path-analysis.js +187 -107
- package/src/path-data-plugins.js +628 -167
- package/src/path-simplification.js +0 -1
- package/src/pattern-resolver.js +125 -88
- package/src/polygon-clip.js +111 -66
- package/src/svg-boolean-ops.js +194 -118
- package/src/svg-collections.js +22 -18
- package/src/svg-flatten.js +282 -164
- package/src/svg-parser.js +427 -200
- package/src/svg-rendering-context.js +147 -104
- package/src/svg-toolbox.js +16381 -3370
- package/src/svg2-polyfills.js +93 -224
- package/src/transform-decomposition.js +46 -41
- package/src/transform-optimization.js +89 -68
- package/src/transforms2d.js +49 -16
- package/src/transforms3d.js +58 -22
- package/src/use-symbol-resolver.js +150 -110
- package/src/vector.js +67 -15
- package/src/vendor/README.md +110 -0
- package/src/vendor/inkscape-hatch-polyfill.js +401 -0
- package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
- package/src/vendor/inkscape-mesh-polyfill.js +843 -0
- package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
- package/src/verification.js +288 -124
package/README.md
CHANGED
|
@@ -255,6 +255,126 @@ svgm --show-plugins
|
|
|
255
255
|
|
|
256
256
|
Run `svgm --help` for all options.
|
|
257
257
|
|
|
258
|
+
### Embed External Dependencies
|
|
259
|
+
|
|
260
|
+
Make SVGs self-contained by embedding external resources as data URIs:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Embed all external dependencies
|
|
264
|
+
svgm --embed input.svg -o output.svg
|
|
265
|
+
|
|
266
|
+
# Embed specific resource types
|
|
267
|
+
svgm --embed-images --embed-css --embed-fonts input.svg -o output.svg
|
|
268
|
+
|
|
269
|
+
# Embed external SVG references with mode selection
|
|
270
|
+
svgm --embed-external-svgs --embed-svg-mode extract input.svg -o output.svg
|
|
271
|
+
|
|
272
|
+
# Embed audio files (for interactive SVGs)
|
|
273
|
+
svgm --embed-audio input.svg -o output.svg
|
|
274
|
+
|
|
275
|
+
# Subset fonts to only include used characters (smaller file size)
|
|
276
|
+
svgm --embed-fonts --embed-subset-fonts input.svg -o output.svg
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Embed options:**
|
|
280
|
+
|
|
281
|
+
| Option | What It Does |
|
|
282
|
+
|--------|--------------|
|
|
283
|
+
| `--embed` | Enable all embedding (images, CSS, fonts, scripts, audio) |
|
|
284
|
+
| `--embed-images` | Embed raster images as data URIs |
|
|
285
|
+
| `--embed-external-svgs` | Embed referenced SVG files |
|
|
286
|
+
| `--embed-svg-mode <mode>` | How to embed SVGs: `extract` (symbols only) or `full` |
|
|
287
|
+
| `--embed-css` | Embed external stylesheets |
|
|
288
|
+
| `--embed-fonts` | Embed web fonts as base64 |
|
|
289
|
+
| `--embed-scripts` | Embed external JavaScript |
|
|
290
|
+
| `--embed-audio` | Embed audio files as data URIs |
|
|
291
|
+
| `--embed-subset-fonts` | Subset fonts to used characters only |
|
|
292
|
+
| `--embed-recursive` | Recursively resolve nested dependencies |
|
|
293
|
+
| `--embed-max-depth <n>` | Max recursion depth (default: 10) |
|
|
294
|
+
| `--embed-timeout <ms>` | Fetch timeout in milliseconds (default: 30000) |
|
|
295
|
+
| `--embed-on-missing <mode>` | Action on missing resource: `warn`, `fail`, or `skip` |
|
|
296
|
+
|
|
297
|
+
### Export Embedded Resources
|
|
298
|
+
|
|
299
|
+
Extract embedded resources from self-contained SVGs back to external files:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Export all embedded resources to a folder
|
|
303
|
+
svgm --export input.svg -o output.svg --export-dir ./resources/
|
|
304
|
+
|
|
305
|
+
# Dry run - show what would be exported without writing files
|
|
306
|
+
svgm --export --export-dry-run input.svg
|
|
307
|
+
|
|
308
|
+
# Export only images
|
|
309
|
+
svgm --export --export-images input.svg -o output.svg --export-dir ./images/
|
|
310
|
+
|
|
311
|
+
# Extract resources without modifying the SVG
|
|
312
|
+
svgm --export --export-only input.svg --export-dir ./resources/
|
|
313
|
+
|
|
314
|
+
# Custom filename prefix for exported files
|
|
315
|
+
svgm --export --export-prefix myapp_ input.svg -o output.svg --export-dir ./assets/
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Export options:**
|
|
319
|
+
|
|
320
|
+
| Option | What It Does |
|
|
321
|
+
|--------|--------------|
|
|
322
|
+
| `--export` | Enable resource extraction mode |
|
|
323
|
+
| `--export-dir <path>` | Output directory for extracted files |
|
|
324
|
+
| `--export-prefix <str>` | Filename prefix for exported files |
|
|
325
|
+
| `--export-images` | Export embedded images only |
|
|
326
|
+
| `--export-audio` | Export embedded audio only |
|
|
327
|
+
| `--export-video` | Export embedded video only |
|
|
328
|
+
| `--export-scripts` | Export inline scripts to .js files |
|
|
329
|
+
| `--export-styles` | Export inline styles to .css files |
|
|
330
|
+
| `--export-fonts` | Export embedded fonts |
|
|
331
|
+
| `--export-only` | Extract files only, don't modify SVG |
|
|
332
|
+
| `--export-dry-run` | Preview extraction without writing files |
|
|
333
|
+
| `--export-ids <ids>` | Only export from specific element IDs |
|
|
334
|
+
|
|
335
|
+
### YAML Configuration
|
|
336
|
+
|
|
337
|
+
Instead of CLI flags, you can use a YAML configuration file:
|
|
338
|
+
|
|
339
|
+
```yaml
|
|
340
|
+
# svgm.yml
|
|
341
|
+
precision: 4
|
|
342
|
+
multipass: true
|
|
343
|
+
pretty: true
|
|
344
|
+
indent: 2
|
|
345
|
+
|
|
346
|
+
embed:
|
|
347
|
+
images: true
|
|
348
|
+
externalSVGs: true
|
|
349
|
+
externalSVGMode: extract
|
|
350
|
+
css: true
|
|
351
|
+
fonts: true
|
|
352
|
+
scripts: true
|
|
353
|
+
audio: true
|
|
354
|
+
subsetFonts: true
|
|
355
|
+
recursive: true
|
|
356
|
+
maxRecursionDepth: 10
|
|
357
|
+
timeout: 30000
|
|
358
|
+
onMissingResource: warn
|
|
359
|
+
|
|
360
|
+
export:
|
|
361
|
+
outputDir: ./resources/
|
|
362
|
+
filenamePrefix: resource_
|
|
363
|
+
images: true
|
|
364
|
+
audio: true
|
|
365
|
+
video: true
|
|
366
|
+
scripts: true
|
|
367
|
+
styles: true
|
|
368
|
+
fonts: true
|
|
369
|
+
extractOnly: false
|
|
370
|
+
dryRun: false
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# Use config file
|
|
375
|
+
svgm -c svgm.yml input.svg -o output.svg
|
|
376
|
+
```
|
|
377
|
+
|
|
258
378
|
### Namespace Preservation
|
|
259
379
|
|
|
260
380
|
Preserve vendor-specific namespaces during optimization:
|
|
@@ -417,6 +537,187 @@ const features = SVG2Polyfills.detectSVG2Features(doc);
|
|
|
417
537
|
SVG2Polyfills.injectPolyfills(doc);
|
|
418
538
|
```
|
|
419
539
|
|
|
540
|
+
### Embedding and Exporting Resources
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
import { embedExternalDependencies, exportEmbeddedResources } from '@emasoft/svg-matrix';
|
|
544
|
+
|
|
545
|
+
// Embed external resources into SVG
|
|
546
|
+
const embedded = await embedExternalDependencies(svgString, {
|
|
547
|
+
basePath: '/path/to/file.svg',
|
|
548
|
+
embedImages: true,
|
|
549
|
+
embedFonts: true,
|
|
550
|
+
embedCSS: true,
|
|
551
|
+
embedScripts: true,
|
|
552
|
+
embedAudio: true,
|
|
553
|
+
subsetFonts: true,
|
|
554
|
+
onMissingResource: 'warn',
|
|
555
|
+
timeout: 30000,
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// Export embedded resources back to external files
|
|
559
|
+
const result = await exportEmbeddedResources(embeddedSvg, {
|
|
560
|
+
outputDir: './extracted/',
|
|
561
|
+
filenamePrefix: 'resource_',
|
|
562
|
+
extractImages: true,
|
|
563
|
+
extractAudio: true,
|
|
564
|
+
extractVideo: true,
|
|
565
|
+
extractScripts: true,
|
|
566
|
+
extractStyles: true,
|
|
567
|
+
extractFonts: true,
|
|
568
|
+
extractOnly: false, // true = extract without modifying SVG
|
|
569
|
+
dryRun: false, // true = preview only, don't write files
|
|
570
|
+
elementIds: null, // filter by element IDs
|
|
571
|
+
onProgress: (phase, current, total) => console.log(`${phase}: ${current}/${total}`),
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
console.log(result.extractedFiles); // Array of extracted file info
|
|
575
|
+
console.log(result.summary); // { images, audio, scripts, stylesheets, fonts, totalSize }
|
|
576
|
+
console.log(result.doc); // Modified SVG string (null if extractOnly)
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## SVG Embedding Options
|
|
582
|
+
|
|
583
|
+
When using SVG files in web pages, the embedding method affects what features work:
|
|
584
|
+
|
|
585
|
+
| Embedding Method | Animation (SMIL) | JavaScript | Audio | Use Case |
|
|
586
|
+
|------------------|------------------|------------|-------|----------|
|
|
587
|
+
| `<img src="file.svg">` | Yes | No | No | Static display, icons |
|
|
588
|
+
| `<object data="file.svg">` | Yes | Yes | No* | Interactive SVGs |
|
|
589
|
+
| `<embed src="file.svg">` | Yes | Yes | No* | Legacy support |
|
|
590
|
+
| `<iframe src="file.svg">` | Yes | Yes | No* | Isolated context |
|
|
591
|
+
| Inline `<svg>...</svg>` | Yes | Yes | No* | Full DOM access |
|
|
592
|
+
| Standalone (file://) | Yes | Yes | No | Direct file viewing |
|
|
593
|
+
| Standalone (http://) | Yes | Yes | No* | Web server |
|
|
594
|
+
|
|
595
|
+
*Audio requires user interaction due to browser autoplay policies.
|
|
596
|
+
|
|
597
|
+
### SVG Audio Playback Limitations
|
|
598
|
+
|
|
599
|
+
Modern browsers (Chrome, Firefox, Safari) enforce strict autoplay policies that significantly limit audio playback in SVG files:
|
|
600
|
+
|
|
601
|
+
| Scenario | Protocol | Example | Audio |
|
|
602
|
+
|----------|----------|---------|-------|
|
|
603
|
+
| Standalone SVG | `file://` | `file:///path/to/sample.svg` | ❌ **Blocked** |
|
|
604
|
+
| Standalone SVG | `http://` | `http://localhost:8080/sample.svg` | ❌ **Blocked** |
|
|
605
|
+
| HTML with hardcoded audio data URIs | `file://` | `file:///path/to/sample.html` | ❌ **Blocked** (even after click) |
|
|
606
|
+
| HTML with hardcoded audio data URIs | `http://` | `http://localhost:8080/sample.html` | ❌ **Blocked** (even after click) |
|
|
607
|
+
| HTML extracts audio from SVG dynamically | `file://` | `file:///path/to/sample.html` | ✅ **Works** (after click) |
|
|
608
|
+
| HTML extracts audio from SVG dynamically | `http://` | `http://localhost:8080/sample.html` | ✅ **Works** (after click) |
|
|
609
|
+
|
|
610
|
+
**Key findings:**
|
|
611
|
+
- Audio elements inside SVG `<foreignObject>` are blocked by browsers regardless of protocol or embedding method
|
|
612
|
+
- Audio sources must be set **dynamically** (not hardcoded in HTML) for playback to work
|
|
613
|
+
- Empty `<audio>` elements + dynamic `src` assignment on load + `play()` on click = success
|
|
614
|
+
- Hardcoded data URIs in HTML `<source>` tags fail even with user click
|
|
615
|
+
- There is no workaround for truly autonomous SVG audio playback
|
|
616
|
+
|
|
617
|
+
**Technical reasons:**
|
|
618
|
+
1. SVG has no native `<audio>` element - audio requires HTML `<foreignObject>`
|
|
619
|
+
2. Browser autoplay policies block audio without direct user gesture
|
|
620
|
+
3. Click events inside SVG don't propagate as "trusted" user gestures for audio
|
|
621
|
+
4. This is a browser security feature, not an SVG limitation
|
|
622
|
+
|
|
623
|
+
**Recommended approach for SVG with audio:**
|
|
624
|
+
|
|
625
|
+
Extract audio sources from the SVG at runtime and play them via HTML `<audio>` elements. This keeps the SVG file unchanged while enabling audio playback. See `samples/SVG_WITH_EMBEDDED_AUDIO/test-embed-with-audio.html` for a complete working example.
|
|
626
|
+
|
|
627
|
+
```html
|
|
628
|
+
<!-- HTML wrapper that extracts audio from SVG -->
|
|
629
|
+
<div class="player-container">
|
|
630
|
+
<object id="svgObject" data="animation.svg" type="image/svg+xml"></object>
|
|
631
|
+
<div class="click-overlay" id="overlay">Click to Play</div>
|
|
632
|
+
</div>
|
|
633
|
+
<audio id="audio_external" preload="auto"></audio>
|
|
634
|
+
|
|
635
|
+
<script>
|
|
636
|
+
const svgObject = document.getElementById('svgObject');
|
|
637
|
+
const audio = document.getElementById('audio_external');
|
|
638
|
+
let svgRoot = null;
|
|
639
|
+
|
|
640
|
+
// On SVG load: pause animation and extract audio source
|
|
641
|
+
svgObject.addEventListener('load', function() {
|
|
642
|
+
const svgDoc = svgObject.contentDocument;
|
|
643
|
+
svgRoot = svgDoc.documentElement;
|
|
644
|
+
svgRoot.pauseAnimations(); // Pause SMIL animation
|
|
645
|
+
|
|
646
|
+
// Extract audio source from SVG (audio data stays in SVG file)
|
|
647
|
+
const svgAudio = svgDoc.getElementById('audio1');
|
|
648
|
+
if (svgAudio) {
|
|
649
|
+
const source = svgAudio.querySelector('source');
|
|
650
|
+
if (source) audio.src = source.src; // Copy data URI to HTML audio
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// On user click: start animation and audio together
|
|
655
|
+
document.getElementById('overlay').addEventListener('click', function() {
|
|
656
|
+
this.style.display = 'none';
|
|
657
|
+
svgRoot.unpauseAnimations(); // Resume SMIL animation
|
|
658
|
+
audio.play(); // Play audio from HTML context
|
|
659
|
+
});
|
|
660
|
+
</script>
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
This approach:
|
|
664
|
+
1. Keeps SVG file unchanged (read-only) - audio data URIs remain in SVG
|
|
665
|
+
2. Extracts audio sources at runtime to HTML `<audio>` elements
|
|
666
|
+
3. Pauses SMIL animation until user clicks
|
|
667
|
+
4. Starts animation and audio simultaneously for perfect sync
|
|
668
|
+
5. Audio plays from HTML context where browser allows it
|
|
669
|
+
|
|
670
|
+
### Audio Alternatives Research (2024-2025)
|
|
671
|
+
|
|
672
|
+
Extensive testing confirms there is **no way to bypass Chrome's autoplay policy** without user interaction. This is by design for user experience.
|
|
673
|
+
|
|
674
|
+
#### What Doesn't Work
|
|
675
|
+
|
|
676
|
+
| Method | Status | Why It Fails |
|
|
677
|
+
|--------|--------|--------------|
|
|
678
|
+
| SVG 2.0 native `<audio>` | ❌ | Same autoplay restrictions apply |
|
|
679
|
+
| SMIL `<audio>` element | ❌ | Poor browser support + autoplay blocked |
|
|
680
|
+
| `<foreignObject>` bypass | ❌ | No special privileges for embedded HTML |
|
|
681
|
+
| Web Audio API (AudioContext) | ❌ | Starts in "suspended" state, same restrictions |
|
|
682
|
+
| CSS audio / `background-sound` | ❌ | Does not exist in any specification |
|
|
683
|
+
| Data URIs / Blob URLs | ❌ | Encoding method doesn't affect autoplay policy |
|
|
684
|
+
| SVG `onload` event | ❌ | Not considered a user gesture |
|
|
685
|
+
| SMIL animation `beginEvent` | ❌ | Animation events are not user gestures |
|
|
686
|
+
| `xlink:href` to audio file | ❌ | Creates link, doesn't trigger playback |
|
|
687
|
+
|
|
688
|
+
#### What Works
|
|
689
|
+
|
|
690
|
+
| Method | Status | Notes |
|
|
691
|
+
|--------|--------|-------|
|
|
692
|
+
| User click/touch/keydown | ✅ | **Mandatory** - no exceptions |
|
|
693
|
+
| HTML wrapper with dynamic audio extraction | ✅ | **Best practice** (see example above) |
|
|
694
|
+
| PWA (Progressive Web App) | ✅ | Desktop only, requires user to install app |
|
|
695
|
+
| Media Engagement Index (MEI) | ⚠️ | Chrome-only, long-term strategy |
|
|
696
|
+
|
|
697
|
+
#### Media Engagement Index (MEI)
|
|
698
|
+
|
|
699
|
+
Chrome tracks media consumption per domain. After meeting these criteria, autoplay may be allowed on future visits:
|
|
700
|
+
- User plays media for >7 seconds
|
|
701
|
+
- Audio is unmuted
|
|
702
|
+
- Tab is active and visible
|
|
703
|
+
- Video element is >200x140 pixels
|
|
704
|
+
|
|
705
|
+
This is a **long-term strategy** for apps with repeat users, not an immediate solution.
|
|
706
|
+
|
|
707
|
+
#### Browser Compatibility
|
|
708
|
+
|
|
709
|
+
| Feature | Chrome | Firefox | Safari | Edge |
|
|
710
|
+
|---------|--------|---------|--------|------|
|
|
711
|
+
| User gesture required | ✅ | ✅ | ✅ | ✅ |
|
|
712
|
+
| Web Audio API | ✅ | ✅ | ⚠️ iOS quirks | ✅ |
|
|
713
|
+
| Data URI audio | ✅ | ⚠️ >1MB issues | ✅ | ✅ |
|
|
714
|
+
| MEI autoplay | ✅ | ❌ | ❌ | ✅ |
|
|
715
|
+
| PWA autoplay | ✅ | ⚠️ | ⚠️ | ✅ |
|
|
716
|
+
|
|
717
|
+
**Conclusion:** User interaction is mandatory. The HTML wrapper approach with dynamic audio extraction (`test-embed-with-audio.html`) is the industry best practice recommended by browser vendors.
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
420
721
|
### Exclusive Features (Not in SVGO)
|
|
421
722
|
|
|
422
723
|
| Function | Description |
|
|
@@ -426,6 +727,8 @@ SVG2Polyfills.injectPolyfills(doc);
|
|
|
426
727
|
| `flattenGradients()` | Bake gradients into fills |
|
|
427
728
|
| `flattenPatterns()` | Expand pattern tiles |
|
|
428
729
|
| `flattenUseElements()` | Inline use/symbol references |
|
|
730
|
+
| `embedExternalDependencies()` | Embed external resources as data URIs |
|
|
731
|
+
| `exportEmbeddedResources()` | Extract embedded resources to files |
|
|
429
732
|
| `detectCollisions()` | GJK collision detection |
|
|
430
733
|
| `validateSVG()` | W3C schema validation |
|
|
431
734
|
| `decomposeTransform()` | Matrix decomposition |
|
|
@@ -500,6 +803,28 @@ import { Matrix, Vector, Transforms2D } from '@emasoft/svg-matrix';
|
|
|
500
803
|
|
|
501
804
|
---
|
|
502
805
|
|
|
806
|
+
## Third-Party Licenses
|
|
807
|
+
|
|
808
|
+
This project is licensed under the MIT License (see [LICENSE](LICENSE)).
|
|
809
|
+
|
|
810
|
+
### SVG 2.0 Polyfill Dependencies
|
|
811
|
+
|
|
812
|
+
When using the `--svg2-polyfills` option with `svgm` or `svg-matrix`, the following third-party code is embedded in the output SVG:
|
|
813
|
+
|
|
814
|
+
**Inkscape mesh.js Polyfill** by Tavmjong Bah
|
|
815
|
+
|
|
816
|
+
- **Purpose:** Provides browser compatibility for SVG 2.0 mesh gradients via canvas fallback
|
|
817
|
+
- **License:** GNU General Public License version 3 or later (GPLv3)
|
|
818
|
+
- **Source:** [https://gitlab.com/Tavmjong/mesh.js/](https://gitlab.com/Tavmjong/mesh.js/)
|
|
819
|
+
- **Location:** `src/vendor/inkscape-mesh-polyfill.js`
|
|
820
|
+
|
|
821
|
+
**Important:** When you use `--svg2-polyfills`, the generated SVG file will contain GPLv3-licensed JavaScript code. This means:
|
|
822
|
+
- The output SVG file is subject to GPLv3 terms
|
|
823
|
+
- If you distribute the SVG, you must provide source code access as required by GPLv3
|
|
824
|
+
- Without `--svg2-polyfills`, all generated files remain under MIT license
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
503
828
|
<p align="center">
|
|
504
829
|
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='30' viewBox='0 0 200 30'%3E%3Cg stroke='%23ddd' stroke-width='0.5' fill='none'%3E%3Cline x1='0' y1='15' x2='60' y2='15'/%3E%3Crect x='70' y='10' width='10' height='10' transform='rotate(45 75 15)'/%3E%3Ccircle cx='100' cy='15' r='5'/%3E%3Crect x='120' y='10' width='10' height='10' transform='rotate(45 125 15)'/%3E%3Cline x1='140' y1='15' x2='200' y2='15'/%3E%3C/g%3E%3C/svg%3E" alt=""/>
|
|
505
830
|
</p>
|