@weasel-js/svg 1.9.4 → 1.10.1
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 -9
- package/dist/index.d.ts +139 -147
- package/dist/index.js +738 -424
- 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
|
|
|
@@ -20,16 +20,13 @@ import { /* … */ } from '@weasel-js/svg';
|
|
|
20
20
|
|
|
21
21
|
## Text fidelity
|
|
22
22
|
|
|
23
|
-
Two things worth knowing before you rely on a text round-trip.
|
|
24
|
-
|
|
25
23
|
**The format is exactly as expressive as the runs model.** A run can turn
|
|
26
24
|
`underline` on but not off, so `<g text-decoration="underline"><text
|
|
27
25
|
text-decoration="none">` has no representation on the way in — and neither
|
|
28
26
|
does "this word is not underlined inside an underlined node" on the way out.
|
|
29
|
-
That is not loss in the serializer; it is the model
|
|
30
|
-
|
|
31
|
-
serializer change.
|
|
32
|
-
expressive than the serializer — that was a real data-loss bug, since fixed.)
|
|
27
|
+
That is not loss in the serializer; it is the model. An
|
|
28
|
+
"un-underline this word" feature needs a model change, not a
|
|
29
|
+
serializer change.
|
|
33
30
|
|
|
34
31
|
**Decoration is treated as inherited.** Real CSS says `text-decoration` is
|
|
35
32
|
not inherited and cannot be cancelled by a descendant; a browser shown
|
|
@@ -151,8 +148,13 @@ query against the same environment.
|
|
|
151
148
|
`@supports` asks whether this parser honors a declaration, not whether a browser
|
|
152
149
|
parses it: `(fill: red)` holds, `(display: grid)` and `(clip-path: url(#c))` do
|
|
153
150
|
not, because nothing reads them from a stylesheet. `@import` is not fetched, and
|
|
154
|
-
each one is reported in `warnings`. Rules inside
|
|
155
|
-
|
|
151
|
+
each one is reported in `warnings`. Rules inside other at-rules are skipped.
|
|
152
|
+
|
|
153
|
+
`@container` size queries are answered against the root `<svg>`, the one box a
|
|
154
|
+
parse knows the size of: the same viewport `@media` sees. They apply to its
|
|
155
|
+
descendants when the root declares `container-type: size` or `inline-size`
|
|
156
|
+
(or the `container` shorthand), by name when the query gives one. A style
|
|
157
|
+
query never holds.
|
|
156
158
|
|
|
157
159
|
## Raster images
|
|
158
160
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Path,
|
|
1
|
+
import { Affine } from '@weasel-js/geom';
|
|
2
|
+
import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, StyledRun, TextStyle, TextVerticalAlign, Fill, Stroke, PoseLinear, MarkerEntry, RenderNeeds, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Media Queries 4 evaluation against a fixed environment.
|
|
@@ -41,39 +41,25 @@ declare const DEFAULT_MEDIA_ENVIRONMENT: SvgMediaEnvironment;
|
|
|
41
41
|
declare function evaluateMediaQuery(list: string, env: SvgMediaEnvironment): boolean;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* Public types for `@weasel-js/svg
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* paint.
|
|
48
|
-
*
|
|
49
|
-
* Parsing collapses every `transform="..."` onto its descendants' geometry,
|
|
50
|
-
* so the `SvgNode` tree returned from `parseSvg` never has a non-identity
|
|
51
|
-
* group transform. Consumers may still construct groups with explicit
|
|
52
|
-
* transforms before serializing, in which case the serializer emits a
|
|
53
|
-
* single `matrix(a b c d e f)` on the `<g>`.
|
|
44
|
+
* Public types for `@weasel-js/svg`: a node model (`SvgNode`) that mirrors
|
|
45
|
+
* the SVG element tree but uses weasel-native leaf data (`Path`, `Paint`)
|
|
46
|
+
* for geometry and paint.
|
|
54
47
|
*/
|
|
55
48
|
|
|
56
49
|
/**
|
|
57
|
-
* Opaque pass-through bag for namespaced XML content.
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* attributes or child elements in a *declared* XML namespace round-trip
|
|
61
|
-
* losslessly through parse → serialize. Consumers (e.g. an app-specific
|
|
62
|
-
* bridge layer) hang their domain semantics off this structure.
|
|
50
|
+
* Opaque pass-through bag for namespaced XML content. The package does not
|
|
51
|
+
* interpret it; attributes and child elements in a *declared* XML namespace
|
|
52
|
+
* round-trip losslessly through parse → serialize.
|
|
63
53
|
*
|
|
64
|
-
* Keyed by namespace prefix
|
|
65
|
-
*
|
|
66
|
-
* / `SerializeOptions.namespaces`).
|
|
54
|
+
* Keyed by namespace prefix. The URI is the canonical identifier and is
|
|
55
|
+
* supplied via `ParseOptions.namespaces` / `SerializeOptions.namespaces`.
|
|
67
56
|
*/
|
|
68
57
|
interface NamespaceMeta {
|
|
69
58
|
[prefix: string]: {
|
|
70
59
|
/** Local-name → string-value map for attributes in this namespace. */
|
|
71
60
|
attrs?: Record<string, string>;
|
|
72
|
-
/**
|
|
73
|
-
*
|
|
74
|
-
* is an array because a namespace can host multiple sibling elements
|
|
75
|
-
* with the same tag (e.g. `<wd:layer/><wd:layer/>`).
|
|
76
|
-
*/
|
|
61
|
+
/** Child elements in this namespace, keyed by local name. An array
|
|
62
|
+
* because siblings can share a tag. */
|
|
77
63
|
elements?: Record<string, NamespacedElement[]>;
|
|
78
64
|
};
|
|
79
65
|
}
|
|
@@ -88,21 +74,17 @@ interface NamespacedElement {
|
|
|
88
74
|
}
|
|
89
75
|
/**
|
|
90
76
|
* 2x3 affine matrix in SVG's `matrix(a b c d e f)` order, mapping
|
|
91
|
-
* `[x', y'] = [a*x + c*y + e, b*x + d*y + f]`.
|
|
92
|
-
*
|
|
93
|
-
* The same order the kernel's affine tier uses, so this is that type under
|
|
94
|
-
* the name the SVG spec gives it — not a parallel one. An `SvgGroupNode`'s
|
|
95
|
-
* transform and a geom `Mat3` are interchangeable without a conversion.
|
|
77
|
+
* `[x', y'] = [a*x + c*y + e, b*x + d*y + f]`. It is geom's `Affine`, so the
|
|
78
|
+
* two are interchangeable without a conversion.
|
|
96
79
|
*/
|
|
97
|
-
type Matrix =
|
|
80
|
+
type Matrix = Affine;
|
|
98
81
|
|
|
99
|
-
/**
|
|
82
|
+
/** The identity matrix. */
|
|
100
83
|
declare const IDENTITY_MATRIX: Matrix;
|
|
101
84
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* `fill-opacity` and `stroke-opacity` round-trip cleanly.
|
|
85
|
+
* A paint in the SVG sense: explicit `none`, a solid color, or a weasel-native
|
|
86
|
+
* gradient `Paint`. Solid colors are normalized to `#rrggbb` strings, with
|
|
87
|
+
* opacity carried separately.
|
|
106
88
|
*/
|
|
107
89
|
type SvgPaint = {
|
|
108
90
|
kind: 'none';
|
|
@@ -112,14 +94,13 @@ type SvgPaint = {
|
|
|
112
94
|
opacity?: number;
|
|
113
95
|
} | {
|
|
114
96
|
kind: 'gradient';
|
|
115
|
-
paint:
|
|
97
|
+
paint: Paint;
|
|
116
98
|
};
|
|
117
99
|
/** Stroke description: a paint plus structural line parameters. */
|
|
118
100
|
interface SvgStroke {
|
|
119
101
|
paint: SvgPaint;
|
|
120
102
|
/** World units, or `{ px }` for a width that holds its rendered thickness
|
|
121
|
-
* however the document is scaled — SVG's `vector-effect="non-scaling-stroke"`.
|
|
122
|
-
* Literally the kit's own unit, not a second spelling of it. */
|
|
103
|
+
* however the document is scaled — SVG's `vector-effect="non-scaling-stroke"`. */
|
|
123
104
|
width: ScreenLength;
|
|
124
105
|
opacity?: number;
|
|
125
106
|
/** `stroke-linecap`. Default per SVG spec is `'butt'`. */
|
|
@@ -146,10 +127,8 @@ interface SvgStroke {
|
|
|
146
127
|
markerEnd?: MarkerRef;
|
|
147
128
|
}
|
|
148
129
|
/**
|
|
149
|
-
* Leaf node: a path geometry plus fill/stroke.
|
|
150
|
-
*
|
|
151
|
-
* parse — `<rect>` uses weasel's `RectPath` fast-path subtype when
|
|
152
|
-
* possible, everything else becomes a `PolygonPath`.
|
|
130
|
+
* Leaf node: a path geometry plus fill/stroke. Other shapes (`<rect>`,
|
|
131
|
+
* `<ellipse>`, etc.) are lowered to this on parse.
|
|
153
132
|
*/
|
|
154
133
|
interface SvgPathNode {
|
|
155
134
|
kind: 'path';
|
|
@@ -162,14 +141,17 @@ interface SvgPathNode {
|
|
|
162
141
|
* center of the underlying `path` geometry. Emitted as SVG
|
|
163
142
|
* `transform="rotate(angleDegrees cx cy)"`. */
|
|
164
143
|
rotation?: number;
|
|
144
|
+
/** Kept by `ParseOptions.keepHidden` from content SVG does not render:
|
|
145
|
+
* `display="none"` here, or a leaf under `visibility="hidden"`. Written
|
|
146
|
+
* back as `display="none"`. */
|
|
147
|
+
hidden?: boolean;
|
|
165
148
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
166
149
|
meta?: NamespaceMeta;
|
|
167
150
|
}
|
|
168
151
|
/**
|
|
169
|
-
* Group node: an SVG `<g>`.
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* `matrix(a b c d e f)`.
|
|
152
|
+
* Group node: an SVG `<g>`. Parsing collapses any `transform` attribute onto
|
|
153
|
+
* the descendants' geometry, so a parsed group never has one. On serialize, a
|
|
154
|
+
* non-identity `transform` is emitted as `matrix(a b c d e f)`.
|
|
173
155
|
*/
|
|
174
156
|
interface SvgGroupNode {
|
|
175
157
|
kind: 'group';
|
|
@@ -182,21 +164,21 @@ interface SvgGroupNode {
|
|
|
182
164
|
* unions them and a `Path` holds one outline. */
|
|
183
165
|
clip?: Path;
|
|
184
166
|
opacity?: number;
|
|
167
|
+
/** Kept by `ParseOptions.keepHidden` from content SVG does not render:
|
|
168
|
+
* `display="none"` here, or a leaf under `visibility="hidden"`. Written
|
|
169
|
+
* back as `display="none"`. */
|
|
170
|
+
hidden?: boolean;
|
|
185
171
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
186
172
|
meta?: NamespaceMeta;
|
|
187
173
|
}
|
|
188
174
|
/**
|
|
189
|
-
* Text leaf node
|
|
190
|
-
*
|
|
191
|
-
* optional `TextStyle` for the node-wide defaults.
|
|
175
|
+
* Text leaf node: a bounding box plus text content, optional per-range
|
|
176
|
+
* `runs`, and an optional `TextStyle` for the node-wide defaults.
|
|
192
177
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* explicit box dimensions round-trip losslessly. External SVG text
|
|
198
|
-
* (lacking the data-* attrs) imports with the width the kit's text layout
|
|
199
|
-
* measures — an em-based estimate when no registered font can — and
|
|
178
|
+
* Native `<text>` has no width or height, so the serializer writes the box
|
|
179
|
+
* as `data-weasel-width` / `data-weasel-height`. External SVG text, lacking
|
|
180
|
+
* them, imports with the width the kit's text layout measures — an em-based
|
|
181
|
+
* estimate when no registered font can — and
|
|
200
182
|
* `height = fontSize * lineHeight * (lineCount || 1)`.
|
|
201
183
|
*
|
|
202
184
|
* `x` here is always the box's left edge. In the SVG it is the
|
|
@@ -210,24 +192,23 @@ interface SvgTextNode {
|
|
|
210
192
|
height: number;
|
|
211
193
|
/** Plain text. When `runs` is set, `runsToPlainText(runs)` must equal `text`. */
|
|
212
194
|
text: string;
|
|
213
|
-
/**
|
|
195
|
+
/** Per-range styling. */
|
|
214
196
|
runs?: StyledRun[];
|
|
215
|
-
/** Node-wide typography.
|
|
197
|
+
/** Node-wide typography. */
|
|
216
198
|
style?: TextStyle;
|
|
217
199
|
/** Where the laid-out text sits inside the box — `kit:text`'s
|
|
218
200
|
* `data.verticalAlign`. SVG text has no box to align in, so this rides in
|
|
219
201
|
* `data-weasel-vertical-align` and other readers draw the text top-aligned. */
|
|
220
202
|
verticalAlign?: TextVerticalAlign;
|
|
221
203
|
/**
|
|
222
|
-
* Node-wide glyph paint, the `
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* `StyledRun.fill` / `.stroke` override these per range.
|
|
204
|
+
* Node-wide glyph paint, as the kit's `Fill` / `Stroke` rather than
|
|
205
|
+
* the path nodes' `SvgPaint`. `StyledRun.fill` / `.stroke` override these
|
|
206
|
+
* per range.
|
|
226
207
|
*
|
|
227
208
|
* `null` is SVG's `fill="none"`: outline-only text, painted by its stroke
|
|
228
209
|
* alone. Absent takes the default black.
|
|
229
210
|
*/
|
|
230
|
-
fill?:
|
|
211
|
+
fill?: Fill | null;
|
|
231
212
|
stroke?: Stroke;
|
|
232
213
|
/** Element-level opacity (`opacity="..."`), 0..1. */
|
|
233
214
|
opacity?: number;
|
|
@@ -240,22 +221,24 @@ interface SvgTextNode {
|
|
|
240
221
|
* With one, the element is written with its whole frame as a
|
|
241
222
|
* `transform="matrix(...)"`. */
|
|
242
223
|
transform?: PoseLinear;
|
|
224
|
+
/** Kept by `ParseOptions.keepHidden` from content SVG does not render:
|
|
225
|
+
* `display="none"` here, or a leaf under `visibility="hidden"`. Written
|
|
226
|
+
* back as `display="none"`. */
|
|
227
|
+
hidden?: boolean;
|
|
243
228
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
244
229
|
meta?: NamespaceMeta;
|
|
245
230
|
}
|
|
246
231
|
/**
|
|
247
232
|
* Raster-image leaf — an SVG `<image>`. `href` is the element's `href` (or
|
|
248
233
|
* legacy `xlink:href`) verbatim: an external URL or a `data:` URI. The
|
|
249
|
-
* package neither fetches nor decodes it
|
|
250
|
-
* a reference and only resolves when something downstream loads it.
|
|
234
|
+
* package neither fetches nor decodes it.
|
|
251
235
|
*
|
|
252
236
|
* SVG's `preserveAspectRatio` is not modeled; the box is taken literally.
|
|
253
237
|
*
|
|
254
238
|
* With a `source` rect or a flip, the element is written as a `<g
|
|
255
239
|
* data-weasel-image>` holding a nested `<svg>` viewport at the box, whose
|
|
256
240
|
* `viewBox` is the source window over a unit-square `<image>` — plain SVG 1.1,
|
|
257
|
-
* which
|
|
258
|
-
* back as one image node.
|
|
241
|
+
* which `parseSvg` reads back as one image node.
|
|
259
242
|
*/
|
|
260
243
|
interface SvgImageNode {
|
|
261
244
|
kind: 'image';
|
|
@@ -289,19 +272,22 @@ interface SvgImageNode {
|
|
|
289
272
|
* With one, the element is written with its whole frame as a
|
|
290
273
|
* `transform="matrix(...)"`. */
|
|
291
274
|
transform?: PoseLinear;
|
|
275
|
+
/** Kept by `ParseOptions.keepHidden` from content SVG does not render:
|
|
276
|
+
* `display="none"` here, or a leaf under `visibility="hidden"`. Written
|
|
277
|
+
* back as `display="none"`. */
|
|
278
|
+
hidden?: boolean;
|
|
292
279
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
293
280
|
meta?: NamespaceMeta;
|
|
294
281
|
}
|
|
295
|
-
/**
|
|
282
|
+
/** Any node of a parsed SVG tree. */
|
|
296
283
|
type SvgNode = SvgPathNode | SvgGroupNode | SvgTextNode | SvgImageNode;
|
|
297
284
|
/** Options for {@link parseSvg}. */
|
|
298
285
|
interface ParseOptions {
|
|
299
286
|
/**
|
|
300
287
|
* Map of prefix → URI for XML namespaces the caller wants surfaced.
|
|
301
|
-
* Attributes
|
|
288
|
+
* Attributes and child elements in a declared namespace are collected
|
|
302
289
|
* into `SvgNode.meta` (per element) or `ParseResult.documentMeta` (root).
|
|
303
|
-
*
|
|
304
|
-
* the structured `meta` bag; they are silently dropped at serialize time.
|
|
290
|
+
* Content in an undeclared namespace is dropped.
|
|
305
291
|
*/
|
|
306
292
|
namespaces?: Record<string, string>;
|
|
307
293
|
/**
|
|
@@ -312,17 +298,19 @@ interface ParseOptions {
|
|
|
312
298
|
* those defaults.
|
|
313
299
|
*/
|
|
314
300
|
media?: Partial<SvgMediaEnvironment>;
|
|
301
|
+
/**
|
|
302
|
+
* Keep `display="none"` and `visibility="hidden"` content, each node marked
|
|
303
|
+
* `hidden`, so a file's hidden layers survive the import. By default it is
|
|
304
|
+
* left out, as a renderer leaves it out.
|
|
305
|
+
*/
|
|
306
|
+
keepHidden?: boolean;
|
|
315
307
|
}
|
|
316
308
|
/** Output of {@link parseSvg}. */
|
|
317
309
|
interface ParseResult {
|
|
318
310
|
nodes: SvgNode[];
|
|
319
311
|
/** Non-fatal notices (unsupported elements, unrecognized attributes). */
|
|
320
312
|
warnings: string[];
|
|
321
|
-
/**
|
|
322
|
-
* Opaque per-document bag for declared namespaces. Holds root-level
|
|
323
|
-
* namespaced attributes (`documentMeta.<prefix>.attrs`) and namespaced
|
|
324
|
-
* root children (`documentMeta.<prefix>.elements`).
|
|
325
|
-
*/
|
|
313
|
+
/** The root's namespaced attributes and children, for declared namespaces. */
|
|
326
314
|
documentMeta?: NamespaceMeta;
|
|
327
315
|
/** Root `viewBox` attribute, parsed from `"x y width height"`. */
|
|
328
316
|
viewBox?: {
|
|
@@ -347,10 +335,7 @@ interface ParseResult {
|
|
|
347
335
|
}
|
|
348
336
|
/** Options for {@link serializeSvg}. */
|
|
349
337
|
interface SerializeOptions {
|
|
350
|
-
/**
|
|
351
|
-
* Override the root `viewBox`. When omitted, the serializer computes
|
|
352
|
-
* a tight bounding box from the supplied nodes.
|
|
353
|
-
*/
|
|
338
|
+
/** The root `viewBox`. Default: a tight bounding box around the nodes. */
|
|
354
339
|
viewBox?: {
|
|
355
340
|
x: number;
|
|
356
341
|
y: number;
|
|
@@ -369,25 +354,23 @@ interface SerializeOptions {
|
|
|
369
354
|
width?: number;
|
|
370
355
|
/** Emit `height="..."` on the root `<svg>`. */
|
|
371
356
|
height?: number;
|
|
372
|
-
/**
|
|
373
|
-
* Emit `<title>...</title>` as the first child of the root `<svg>`. The
|
|
374
|
-
* value is XML-escaped on output. Empty strings are skipped.
|
|
375
|
-
*/
|
|
357
|
+
/** Emit `<title>` as the first child of the root `<svg>`. Empty strings are skipped. */
|
|
376
358
|
title?: string;
|
|
377
359
|
/**
|
|
378
|
-
* Map of prefix → URI for XML namespaces to declare on the root
|
|
379
|
-
*
|
|
380
|
-
* `node.meta[prefix]` to write the actual attributes and elements.
|
|
360
|
+
* Map of prefix → URI for XML namespaces to declare on the root `<svg>`.
|
|
361
|
+
* Only `documentMeta` and `node.meta` entries under a declared prefix are written.
|
|
381
362
|
*/
|
|
382
363
|
namespaces?: Record<string, string>;
|
|
383
364
|
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* `<prefix>.elements[localName]` becomes `<prefix>:<localName>...>`
|
|
387
|
-
* children placed immediately before any geometry.
|
|
365
|
+
* Per-document namespaced extras: `attrs` go on the root `<svg>`, and
|
|
366
|
+
* `elements` become children placed immediately before any geometry.
|
|
388
367
|
*/
|
|
389
368
|
documentMeta?: NamespaceMeta;
|
|
390
|
-
/**
|
|
369
|
+
/**
|
|
370
|
+
* Put each element on its own line, indented two spaces per level. A
|
|
371
|
+
* `<text>` element, and any element holding character data, stays on one
|
|
372
|
+
* line, since whitespace inside it is content.
|
|
373
|
+
*/
|
|
391
374
|
pretty?: boolean;
|
|
392
375
|
}
|
|
393
376
|
|
|
@@ -410,7 +393,7 @@ declare function parseSvg(svg: string, opts?: ParseOptions): ParseResult;
|
|
|
410
393
|
|
|
411
394
|
/**
|
|
412
395
|
* Map between SVG `<linearGradient>` / `<radialGradient>` elements and
|
|
413
|
-
* weasel's `
|
|
396
|
+
* weasel's `Paint` gradient variants. Definitions are indexed by id
|
|
414
397
|
* wherever in the document they appear, and looked up when a `fill` /
|
|
415
398
|
* `stroke` attribute references one as `url(#id)`. On serialize, we
|
|
416
399
|
* emit a fresh `<defs>` block with stable generated ids.
|
|
@@ -428,9 +411,13 @@ declare function nativeSvgKind(kind: string): boolean;
|
|
|
428
411
|
declare function nativeSvgSpace(space: string | null | undefined): boolean;
|
|
429
412
|
|
|
430
413
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
|
|
414
|
+
* Serialize a `SvgNode[]` tree to an SVG document string. Every path leaf is
|
|
415
|
+
* emitted as a `<path>`, even shapes that started as `<rect>` etc.
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Produce an SVG document string. The root `viewBox` is `opts.viewBox` if
|
|
420
|
+
* supplied, otherwise a tight bounding box around every leaf.
|
|
434
421
|
*/
|
|
435
422
|
declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
|
|
436
423
|
|
|
@@ -452,27 +439,17 @@ declare function svgNeeds(nodes: readonly SvgNode[]): RenderNeeds;
|
|
|
452
439
|
declare function warmSvg(nodes: readonly SvgNode[]): Promise<void>;
|
|
453
440
|
|
|
454
441
|
/**
|
|
455
|
-
* Map between SVG `<pattern>` elements and weasel's
|
|
456
|
-
* The counterpart to `gradients.ts` for the other paint-server kind.
|
|
442
|
+
* Map between SVG `<pattern>` elements and weasel's pattern paints.
|
|
457
443
|
*
|
|
458
|
-
* The tile's shapes
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
* attribute back to recover the exact spec rather than reverse-engineering
|
|
463
|
-
* it from geometry.
|
|
464
|
-
*
|
|
465
|
-
* A pattern whose payload is a `TextureHandle` has no vector form — the
|
|
466
|
-
* handle is a session-scoped registry key with no tile description behind
|
|
467
|
-
* it — and exports as nothing, with a warning. Same treatment conic
|
|
468
|
-
* gradients get.
|
|
444
|
+
* The tile's shapes are written for any SVG consumer to render, and the spec
|
|
445
|
+
* itself goes on a `data-weasel-tile` attribute, which weasel reads back
|
|
446
|
+
* rather than reverse-engineering it from geometry. A `texture` paint has no
|
|
447
|
+
* vector form and exports as nothing, with a warning.
|
|
469
448
|
*/
|
|
470
449
|
|
|
471
450
|
/**
|
|
472
|
-
* A single tile as a standalone `<svg>` document, sized to its own extent
|
|
473
|
-
*
|
|
474
|
-
* set as a repeating CSS `background-image` via a data URI. Shares the shape
|
|
475
|
-
* mapper with `patternXml`, so a preview can't drift from what paints.
|
|
451
|
+
* A single tile as a standalone `<svg>` document, sized to its own extent,
|
|
452
|
+
* for UI that shows a tile outside a document, such as a picker swatch.
|
|
476
453
|
*/
|
|
477
454
|
declare function tilePreviewSvg(spec: TilePatternSpec, background?: string): string;
|
|
478
455
|
/** `tilePreviewSvg` packed as a `url(...)` value for CSS `background-image`. */
|
|
@@ -511,14 +488,15 @@ interface SvgKitPose {
|
|
|
511
488
|
width: number;
|
|
512
489
|
height: number;
|
|
513
490
|
rotation?: number;
|
|
514
|
-
/**
|
|
491
|
+
/** Carried onto a text or image node. A path node has nowhere to hold it,
|
|
492
|
+
* so a path leaf's is baked into its geometry with its rotation. */
|
|
515
493
|
transform?: PoseLinear;
|
|
516
494
|
}
|
|
517
495
|
/** The leaf data the kit's path, text and image painters read — what
|
|
518
496
|
* `svgNodesToKitDrafts` writes. */
|
|
519
497
|
interface SvgKitLeafData {
|
|
520
498
|
path?: Path;
|
|
521
|
-
fill?:
|
|
499
|
+
fill?: Fill | null;
|
|
522
500
|
stroke?: Stroke | null;
|
|
523
501
|
text?: string;
|
|
524
502
|
style?: TextStyle;
|
|
@@ -529,7 +507,7 @@ interface SvgKitLeafData {
|
|
|
529
507
|
/** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
|
|
530
508
|
* gradient or pattern is resolved into `box`, since the geometry it paints
|
|
531
509
|
* is written already placed there. */
|
|
532
|
-
declare function svgPaintFromKit(fill:
|
|
510
|
+
declare function svgPaintFromKit(fill: Paint | null, box: SvgKitPose): SvgPaint;
|
|
533
511
|
/** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
|
|
534
512
|
* One with no paint or no width draws nothing and is written as no stroke. */
|
|
535
513
|
declare function svgStrokeFromKit(stroke: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
|
|
@@ -542,7 +520,9 @@ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose
|
|
|
542
520
|
* draws.
|
|
543
521
|
*
|
|
544
522
|
* A path with no `fill` takes `kit:path`'s fallback: the default shape fill
|
|
545
|
-
* when it has no stroke, and no fill when it has one.
|
|
523
|
+
* when it has no stroke, and no fill when it has one. Under a pose `transform`
|
|
524
|
+
* its stroke width takes the uniform part of the stretch, as `parseSvg` reads
|
|
525
|
+
* a stroke under a skewed element.
|
|
546
526
|
*/
|
|
547
527
|
declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | null;
|
|
548
528
|
/** One node as {@link svgNodesFromKit} reads it. */
|
|
@@ -550,6 +530,9 @@ interface SvgKitTreeNode {
|
|
|
550
530
|
kind: string;
|
|
551
531
|
data: unknown;
|
|
552
532
|
pose: SvgKitPose;
|
|
533
|
+
/** A container's copies, as the kit's `ContainerNode.copiesFromPose`
|
|
534
|
+
* draws them: each is written as a transformed group of its children. */
|
|
535
|
+
copiesFromPose?(pose: SvgKitPose, node: never): readonly Matrix[] | null;
|
|
553
536
|
}
|
|
554
537
|
/** The read side of a scene that {@link svgNodesFromKit} walks. A kit
|
|
555
538
|
* `Scene` is one as it stands. */
|
|
@@ -585,26 +568,37 @@ interface SvgDraftBounds {
|
|
|
585
568
|
width: number;
|
|
586
569
|
height: number;
|
|
587
570
|
}
|
|
588
|
-
|
|
571
|
+
/** Where a draft node sits: its box, and the rotation or linear part its SVG transform left it with. */
|
|
572
|
+
type SvgDraftPose = SvgDraftBounds & {
|
|
589
573
|
rotation?: number;
|
|
590
574
|
transform?: PoseLinear;
|
|
591
575
|
};
|
|
576
|
+
|
|
592
577
|
/**
|
|
593
578
|
* One node the unpack wants the scene to materialize, in parent-before-child
|
|
594
579
|
* order (a draft's `parentId` always names an earlier draft, or `null` for
|
|
595
|
-
* roots).
|
|
580
|
+
* roots). `hidden` is the source node's, for the caller to map onto whatever
|
|
581
|
+
* it hides a node with.
|
|
582
|
+
*
|
|
583
|
+
* A container's `data` is empty unless its `<g>` is clipped. A scene container
|
|
584
|
+
* clips to what its painter draws, so a clipped one holds the clip as an
|
|
585
|
+
* unpainted path, `{ path, fill: null }`, and its `pose` is the clip's box
|
|
586
|
+
* rather than its children's.
|
|
596
587
|
*/
|
|
597
588
|
type SvgSceneDraft<TData = Record<string, unknown>> = {
|
|
598
589
|
kind: 'container';
|
|
599
590
|
id: string;
|
|
600
591
|
parentId: string | null;
|
|
601
|
-
pose:
|
|
592
|
+
pose: SvgDraftPose;
|
|
593
|
+
data: SvgKitLeafData;
|
|
594
|
+
hidden?: true;
|
|
602
595
|
} | {
|
|
603
596
|
kind: 'leaf';
|
|
604
597
|
id: string;
|
|
605
598
|
parentId: string | null;
|
|
606
|
-
pose:
|
|
599
|
+
pose: SvgDraftPose;
|
|
607
600
|
data: TData;
|
|
601
|
+
hidden?: true;
|
|
608
602
|
};
|
|
609
603
|
/** A leaf `SvgNode`: everything but a group. */
|
|
610
604
|
type SvgLeafNode = Exclude<SvgNode, SvgGroupNode>;
|
|
@@ -614,33 +608,27 @@ interface SvgNodesToKitDraftsOptions<TData> {
|
|
|
614
608
|
* the node it came from — for data shaped other than the kit painters', or
|
|
615
609
|
* metadata the document carries. `null` leaves it out. */
|
|
616
610
|
leaf?(id: string, draft: {
|
|
617
|
-
pose:
|
|
611
|
+
pose: SvgDraftPose;
|
|
618
612
|
data: SvgKitLeafData;
|
|
619
613
|
}, source: SvgLeafNode): TData | null;
|
|
620
614
|
}
|
|
621
|
-
/** Lower an `SvgPaint` onto the `kit:path` painter's
|
|
622
|
-
* `
|
|
623
|
-
* fill rather than falling back to its default).
|
|
624
|
-
* `fill-opacity`.
|
|
615
|
+
/** Lower an `SvgPaint` onto the paint of the `kit:path` painter's
|
|
616
|
+
* `data.fill` — a `Paint`, or `null` for SVG's `fill="none"` (the painter
|
|
617
|
+
* skips the fill rather than falling back to its default).
|
|
625
618
|
*
|
|
626
|
-
* A gradient
|
|
627
|
-
* the
|
|
628
|
-
*
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
*
|
|
633
|
-
* Everything the SVG carried — paint, width, cap, join, dash, miter limit,
|
|
634
|
-
* opacity — lands on the one `Stroke` `data.stroke` takes. The paint is
|
|
635
|
-
* normalized to the leaf's own box the same way a fill is, so a
|
|
636
|
-
* `userSpaceOnUse` gradient survives the fit-clamp and the drop-point
|
|
637
|
-
* placement. */
|
|
619
|
+
* A `userSpaceOnUse` gradient is rebased onto the leaf's own box, so it
|
|
620
|
+
* survives the fit-clamp and the drop-point placement that move the
|
|
621
|
+
* geometry out from under it. */
|
|
622
|
+
declare function fillDataFromSvg(paint: SvgPaint | undefined, box: SvgDraftBounds): Paint | null | undefined;
|
|
623
|
+
/** Lower an `SvgStroke` onto the leaf's `data.stroke`, or `undefined` for an
|
|
624
|
+
* unpainted one. The paint is rebased onto the leaf's own box the same way
|
|
625
|
+
* a fill is. */
|
|
638
626
|
declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftBounds): Stroke | undefined;
|
|
639
627
|
/**
|
|
640
628
|
* Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
|
|
641
629
|
* {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
|
|
642
|
-
* union AABB of its descendants
|
|
643
|
-
*
|
|
630
|
+
* union AABB of its descendants, or its clip's box when it has one; leaves
|
|
631
|
+
* carry kit-painter-native data. Empty groups are dropped.
|
|
644
632
|
*
|
|
645
633
|
* Handed a whole `ParseResult`, it also registers the document's markers
|
|
646
634
|
* (`parsed.markers`), without which the strokes naming them draw bare.
|
|
@@ -652,11 +640,15 @@ declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftB
|
|
|
652
640
|
*/
|
|
653
641
|
declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: ParseResult | readonly SvgNode[], nextId: (source: SvgNode) => string, options?: SvgNodesToKitDraftsOptions<TData>): SvgSceneDraft<TData>[];
|
|
654
642
|
/**
|
|
655
|
-
* Parse each file and insert its node tree
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
643
|
+
* Parse each file and insert its node tree, one undo step per file. Pass it
|
|
644
|
+
* as `<SceneCanvas ingestion={{ svg: { unpack: unpackSvgFiles } }}>`.
|
|
645
|
+
*
|
|
646
|
+
* Each file is fit-clamped to 90% of the visible viewport and centered on the
|
|
647
|
+
* drop point (or the viewport center), with later files cascading by a fixed
|
|
648
|
+
* offset. A multi-root file is wrapped in one container so it arrives as a
|
|
649
|
+
* single selectable unit. A file that fails to parse, or parses to nothing,
|
|
650
|
+
* is skipped with a `console.warn`.
|
|
659
651
|
*/
|
|
660
652
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
661
653
|
|
|
662
|
-
export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, 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 };
|
|
654
|
+
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 };
|