@weasel-js/svg 1.10.0 → 1.11.0
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 +11 -1
- package/dist/index.d.ts +74 -16
- package/dist/index.js +651 -306
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ SVG import/export for @weasel-js/core: parse SVG strings into weasel-native shap
|
|
|
4
4
|
|
|
5
5
|
Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
|
|
6
6
|
scene-graph canvas kit for React. See the
|
|
7
|
-
[API reference](https://orochi235.github.io/weasel/
|
|
7
|
+
[API reference](https://orochi235.github.io/weasel/#pkg/svg).
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -37,6 +37,16 @@ answer, and the alternative is unrepresentable anyway (see above). It only
|
|
|
37
37
|
shows up on foreign SVG that sets decoration on a group and cancels it on a
|
|
38
38
|
child.
|
|
39
39
|
|
|
40
|
+
**SVG text does not wrap.** By default a `<text>` is written one line per
|
|
41
|
+
hard break, pitched at the node's font size times its line height, and a
|
|
42
|
+
wrapping or justified node keeps that only in `data-weasel-*` attributes for
|
|
43
|
+
weasel's own reader. Pass `serializeSvg(nodes, { textLayout: layoutTextPose })`
|
|
44
|
+
to write the lines the renderer draws instead: each line is a `<tspan>` at the
|
|
45
|
+
layout's `x` and baseline `y`, each word of a line the layout justified gets
|
|
46
|
+
its own `x`, and a line holding a larger run sits where the renderer puts it.
|
|
47
|
+
The layout measures with the registered fonts, so `await warmSvg(nodes, {
|
|
48
|
+
textLayout })` first; a node no face could measure is written the default way.
|
|
49
|
+
|
|
40
50
|
## Paints SVG cannot express
|
|
41
51
|
|
|
42
52
|
SVG has `<linearGradient>`, `<radialGradient>` and `<pattern>` and nothing
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, StyledRun, TextStyle, TextVerticalAlign, Fill, Stroke, PoseLinear, MarkerEntry, RenderNeeds, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
|
|
1
|
+
import { Affine } from '@weasel-js/geom';
|
|
2
|
+
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, VertexWidth, StrokeNib, StyledRun, TextStyle, TextVerticalAlign, Fill, Stroke, PoseLinear, MarkerEntry, TextPoseLayout, RenderNeeds, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Media Queries 4 evaluation against a fixed environment.
|
|
@@ -74,10 +74,10 @@ interface NamespacedElement {
|
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
76
|
* 2x3 affine matrix in SVG's `matrix(a b c d e f)` order, mapping
|
|
77
|
-
* `[x', y'] = [a*x + c*y + e, b*x + d*y + f]`. It is geom's `
|
|
77
|
+
* `[x', y'] = [a*x + c*y + e, b*x + d*y + f]`. It is geom's `Affine`, so the
|
|
78
78
|
* two are interchangeable without a conversion.
|
|
79
79
|
*/
|
|
80
|
-
type Matrix =
|
|
80
|
+
type Matrix = Affine;
|
|
81
81
|
|
|
82
82
|
/** The identity matrix. */
|
|
83
83
|
declare const IDENTITY_MATRIX: Matrix;
|
|
@@ -125,6 +125,13 @@ interface SvgStroke {
|
|
|
125
125
|
markerStart?: MarkerRef;
|
|
126
126
|
markerMid?: MarkerRef;
|
|
127
127
|
markerEnd?: MarkerRef;
|
|
128
|
+
/** The kit stroke's `vertexWidths` and `nib`. SVG has no variable width, so
|
|
129
|
+
* the serializer writes such a stroke's outline as a filled shape after the
|
|
130
|
+
* path, and keeps these in the private namespace for the parser. An entry
|
|
131
|
+
* of `vertexWidths`, or one side of a pair, that is not a number of zero or
|
|
132
|
+
* more takes `width`. */
|
|
133
|
+
vertexWidths?: VertexWidth[];
|
|
134
|
+
nib?: StrokeNib;
|
|
128
135
|
}
|
|
129
136
|
/**
|
|
130
137
|
* Leaf node: a path geometry plus fill/stroke. Other shapes (`<rect>`,
|
|
@@ -372,6 +379,18 @@ interface SerializeOptions {
|
|
|
372
379
|
* line, since whitespace inside it is content.
|
|
373
380
|
*/
|
|
374
381
|
pretty?: boolean;
|
|
382
|
+
/**
|
|
383
|
+
* The lines a text node lays out into — pass core's `layoutTextPose`, the
|
|
384
|
+
* layout the renderer draws with. With it, each line is written as a
|
|
385
|
+
* `<tspan>` at the layout's own `x` and baseline `y`, a justified line
|
|
386
|
+
* gives each word its own `x`, and a line holding a larger run is pitched
|
|
387
|
+
* as the renderer pitches it, so a reader that cannot wrap draws the lines
|
|
388
|
+
* where weasel does. A node it returns nothing for, or lays out to zero
|
|
389
|
+
* width because no face could measure it, is written without it: one line
|
|
390
|
+
* per hard break, at the node's own pitch. Fonts it reads must have loaded;
|
|
391
|
+
* `warmSvg(nodes, { textLayout })` loads them.
|
|
392
|
+
*/
|
|
393
|
+
textLayout?: (node: SvgTextNode) => TextPoseLayout | null | undefined;
|
|
375
394
|
}
|
|
376
395
|
|
|
377
396
|
/**
|
|
@@ -410,28 +429,48 @@ declare function nativeSvgKind(kind: string): boolean;
|
|
|
410
429
|
* but renderers other than weasel blend it in sRGB. */
|
|
411
430
|
declare function nativeSvgSpace(space: string | null | undefined): boolean;
|
|
412
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Serialize a `SvgNode[]` tree to an SVG document string. Every path leaf is
|
|
434
|
+
* emitted as a `<path>`, even shapes that started as `<rect>` etc.
|
|
435
|
+
*/
|
|
436
|
+
|
|
413
437
|
/**
|
|
414
438
|
* Produce an SVG document string. The root `viewBox` is `opts.viewBox` if
|
|
415
439
|
* supplied, otherwise a tight bounding box around every leaf.
|
|
416
440
|
*/
|
|
417
441
|
declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
|
|
418
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Serialize a weasel `Path` to an SVG `d=` attribute string. `RectPath`
|
|
445
|
+
* lowers to `M h v h Z`; `PolygonPath` walks the (commands, coords)
|
|
446
|
+
* arrays, emitting one SVG command per kit command, with corner shapes
|
|
447
|
+
* expanded into the geometry they draw.
|
|
448
|
+
*
|
|
449
|
+
* Output uses absolute commands only and a small amount of whitespace
|
|
450
|
+
* compression (no leading zeros stripped beyond what `Number.toString`
|
|
451
|
+
* already does). Numbers are rounded to ~6 decimals via `trimNumber`.
|
|
452
|
+
*/
|
|
453
|
+
|
|
454
|
+
/** Serialize a weasel `Path` to an SVG `d=` attribute string. */
|
|
455
|
+
declare function serializePathD(given: Path): string;
|
|
456
|
+
|
|
419
457
|
/**
|
|
420
458
|
* What `serializeSvg(nodes)` reads that loads on demand: the paint kind of
|
|
421
459
|
* every fill and stroke it writes as a paint server, and the face of every
|
|
422
460
|
* run whose script size it takes from font metrics. The kinds come from the
|
|
423
461
|
* serializer's own paint pre-pass, so the two cannot disagree. Registered
|
|
424
|
-
* kinds are included.
|
|
462
|
+
* kinds are included. Given the `textLayout` the export will run, every face
|
|
463
|
+
* a text node is set in, which that layout measures with.
|
|
425
464
|
*/
|
|
426
|
-
declare function svgNeeds(nodes: readonly SvgNode[]): RenderNeeds;
|
|
465
|
+
declare function svgNeeds(nodes: readonly SvgNode[], opts?: Pick<SerializeOptions, 'textLayout'>): RenderNeeds;
|
|
427
466
|
/**
|
|
428
|
-
* Load what {@link svgNeeds} lists, so a `serializeSvg(nodes)` issued
|
|
429
|
-
* resolves writes every paint server's def and every script size
|
|
430
|
-
* face it renders in. Only those load: a lazily registered kind or font the
|
|
467
|
+
* Load what {@link svgNeeds} lists, so a `serializeSvg(nodes, opts)` issued
|
|
468
|
+
* after it resolves writes every paint server's def, and every script size and
|
|
469
|
+
* laid-out line from the face it renders in. Only those load: a lazily registered kind or font the
|
|
431
470
|
* nodes do not use is never fetched, and cannot fail the export. A kind
|
|
432
471
|
* nothing registered is passed over; the export warns about it instead.
|
|
433
472
|
*/
|
|
434
|
-
declare function warmSvg(nodes: readonly SvgNode[]): Promise<void>;
|
|
473
|
+
declare function warmSvg(nodes: readonly SvgNode[], opts?: Pick<SerializeOptions, 'textLayout'>): Promise<void>;
|
|
435
474
|
|
|
436
475
|
/**
|
|
437
476
|
* Map between SVG `<pattern>` elements and weasel's pattern paints.
|
|
@@ -497,6 +536,12 @@ interface SvgKitLeafData {
|
|
|
497
536
|
style?: TextStyle;
|
|
498
537
|
runs?: readonly StyledRun[];
|
|
499
538
|
verticalAlign?: TextVerticalAlign;
|
|
539
|
+
/** A text node's paint over its whole box, under the glyphs. */
|
|
540
|
+
background?: Fill | null;
|
|
541
|
+
/** A text node's inset from its box to its text, on every side. */
|
|
542
|
+
padding?: number;
|
|
543
|
+
/** `'clip'` cuts a text node's text at its box. */
|
|
544
|
+
overflow?: 'visible' | 'clip';
|
|
500
545
|
image?: ImageNodeData['image'];
|
|
501
546
|
}
|
|
502
547
|
/** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
|
|
@@ -512,19 +557,26 @@ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose
|
|
|
512
557
|
/**
|
|
513
558
|
* Write one leaf as the `SvgNode` its built-in painter draws — image, text or
|
|
514
559
|
* path, in the order the painters claim a node. `null` for data none of them
|
|
515
|
-
* draws.
|
|
560
|
+
* draws. A text node with a `background` is a group: the box, then the text.
|
|
561
|
+
* One that clips is a group clipped to its box as it is drawn, turned with it.
|
|
516
562
|
*
|
|
517
563
|
* A path with no `fill` takes `kit:path`'s fallback: the default shape fill
|
|
518
564
|
* when it has no stroke, and no fill when it has one. Under a pose `transform`
|
|
519
565
|
* its stroke width takes the uniform part of the stretch, as `parseSvg` reads
|
|
520
566
|
* a stroke under a skewed element.
|
|
521
567
|
*/
|
|
522
|
-
declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | null;
|
|
568
|
+
declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | SvgGroupNode | null;
|
|
523
569
|
/** One node as {@link svgNodesFromKit} reads it. */
|
|
524
570
|
interface SvgKitTreeNode {
|
|
525
571
|
kind: string;
|
|
526
572
|
data: unknown;
|
|
527
573
|
pose: SvgKitPose;
|
|
574
|
+
/** The kit's `SceneNode.hidden`: written with `display="none"`, which
|
|
575
|
+
* `parseSvg(…, { keepHidden: true })` reads back as `hidden`. */
|
|
576
|
+
hidden?: boolean;
|
|
577
|
+
/** A container's copies, as the kit's `ContainerNode.copiesFromPose`
|
|
578
|
+
* draws them: each is written as a transformed group of its children. */
|
|
579
|
+
copiesFromPose?(pose: SvgKitPose, node: never): readonly Matrix[] | null;
|
|
528
580
|
}
|
|
529
581
|
/** The read side of a scene that {@link svgNodesFromKit} walks. A kit
|
|
530
582
|
* `Scene` is one as it stands. */
|
|
@@ -548,7 +600,7 @@ interface SvgNodesFromKitOptions<TId extends string = string> {
|
|
|
548
600
|
* Walk a scene's container tree into `SvgNode`s ready for `serializeSvg`:
|
|
549
601
|
* every `container` becomes a group of its children, in z-order, and every
|
|
550
602
|
* leaf goes through {@link svgLeafFromKit} unless `options.leaf` says
|
|
551
|
-
* otherwise.
|
|
603
|
+
* otherwise. A hidden node is written hidden, with its subtree as it is.
|
|
552
604
|
*/
|
|
553
605
|
declare function svgNodesFromKit<TId extends string>(tree: SvgKitTree<TId>, options?: SvgNodesFromKitOptions<TId>): SvgNode[];
|
|
554
606
|
|
|
@@ -603,6 +655,10 @@ interface SvgNodesToKitDraftsOptions<TData> {
|
|
|
603
655
|
pose: SvgDraftPose;
|
|
604
656
|
data: SvgKitLeafData;
|
|
605
657
|
}, source: SvgLeafNode): TData | null;
|
|
658
|
+
/** Keep a group that draws nothing, which is otherwise dropped — for one
|
|
659
|
+
* whose metadata means something with no children under it. It is drafted
|
|
660
|
+
* with its clip's box, or a zero box at the origin. */
|
|
661
|
+
keepEmpty?(source: SvgGroupNode): boolean;
|
|
606
662
|
}
|
|
607
663
|
/** Lower an `SvgPaint` onto the paint of the `kit:path` painter's
|
|
608
664
|
* `data.fill` — a `Paint`, or `null` for SVG's `fill="none"` (the painter
|
|
@@ -620,7 +676,8 @@ declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftB
|
|
|
620
676
|
* Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
|
|
621
677
|
* {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
|
|
622
678
|
* union AABB of its descendants, or its clip's box when it has one; leaves
|
|
623
|
-
* carry kit-painter-native data. Empty groups are dropped
|
|
679
|
+
* carry kit-painter-native data. Empty groups are dropped unless
|
|
680
|
+
* `options.keepEmpty` keeps them, and so is a group holding only those.
|
|
624
681
|
*
|
|
625
682
|
* Handed a whole `ParseResult`, it also registers the document's markers
|
|
626
683
|
* (`parsed.markers`), without which the strokes naming them draw bare.
|
|
@@ -638,9 +695,10 @@ declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: Par
|
|
|
638
695
|
* Each file is fit-clamped to 90% of the visible viewport and centered on the
|
|
639
696
|
* drop point (or the viewport center), with later files cascading by a fixed
|
|
640
697
|
* offset. A multi-root file is wrapped in one container so it arrives as a
|
|
641
|
-
* single selectable unit.
|
|
698
|
+
* single selectable unit. Content the file hides arrives hidden
|
|
699
|
+
* (`SceneNode.hidden`) rather than being left out. A file that fails to parse, or parses to nothing,
|
|
642
700
|
* is skipped with a `console.warn`.
|
|
643
701
|
*/
|
|
644
702
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
645
703
|
|
|
646
|
-
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, type SvgDraftPose, type SvgGroupNode, type SvgImageNode, type SvgKitLeafData, type SvgKitPose, type SvgKitTree, type SvgKitTreeNode, type SvgLeafNode, type SvgMediaEnvironment, type SvgNode, type SvgNodesFromKitOptions, type SvgNodesToKitDraftsOptions, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
|
|
704
|
+
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, type SvgDraftPose, type SvgGroupNode, type SvgImageNode, type SvgKitLeafData, type SvgKitPose, type SvgKitTree, type SvgKitTreeNode, type SvgLeafNode, type SvgMediaEnvironment, type SvgNode, type SvgNodesFromKitOptions, type SvgNodesToKitDraftsOptions, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializePathD, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };
|