@weasel-js/svg 1.9.3 → 1.10.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 +10 -8
- package/dist/index.d.ts +144 -146
- package/dist/index.js +1328 -854
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -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
1
|
import { Mat3 } from '@weasel-js/geom';
|
|
2
|
-
import { Path,
|
|
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 `Mat3`, so the
|
|
78
|
+
* two are interchangeable without a conversion.
|
|
96
79
|
*/
|
|
97
80
|
type Matrix = Mat3;
|
|
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;
|
|
@@ -235,22 +216,29 @@ interface SvgTextNode {
|
|
|
235
216
|
* AABB center `(x + width/2, y + height/2)`. Emitted as SVG
|
|
236
217
|
* `transform="rotate(angleDegrees cx cy)"`. */
|
|
237
218
|
rotation?: number;
|
|
219
|
+
/** The skew or per-axis scale the box, `rotation`, and flips cannot hold,
|
|
220
|
+
* applied about the box center inside the rotation; see `PoseLinear`.
|
|
221
|
+
* With one, the element is written with its whole frame as a
|
|
222
|
+
* `transform="matrix(...)"`. */
|
|
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;
|
|
238
228
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
239
229
|
meta?: NamespaceMeta;
|
|
240
230
|
}
|
|
241
231
|
/**
|
|
242
232
|
* Raster-image leaf — an SVG `<image>`. `href` is the element's `href` (or
|
|
243
233
|
* legacy `xlink:href`) verbatim: an external URL or a `data:` URI. The
|
|
244
|
-
* package neither fetches nor decodes it
|
|
245
|
-
* a reference and only resolves when something downstream loads it.
|
|
234
|
+
* package neither fetches nor decodes it.
|
|
246
235
|
*
|
|
247
236
|
* SVG's `preserveAspectRatio` is not modeled; the box is taken literally.
|
|
248
237
|
*
|
|
249
238
|
* With a `source` rect or a flip, the element is written as a `<g
|
|
250
239
|
* data-weasel-image>` holding a nested `<svg>` viewport at the box, whose
|
|
251
240
|
* `viewBox` is the source window over a unit-square `<image>` — plain SVG 1.1,
|
|
252
|
-
* which
|
|
253
|
-
* back as one image node.
|
|
241
|
+
* which `parseSvg` reads back as one image node.
|
|
254
242
|
*/
|
|
255
243
|
interface SvgImageNode {
|
|
256
244
|
kind: 'image';
|
|
@@ -279,19 +267,27 @@ interface SvgImageNode {
|
|
|
279
267
|
* AABB center `(x + width/2, y + height/2)`. Emitted as SVG
|
|
280
268
|
* `transform="rotate(angleDegrees cx cy)"`. */
|
|
281
269
|
rotation?: number;
|
|
270
|
+
/** The skew or per-axis scale the box, `rotation`, and flips cannot hold,
|
|
271
|
+
* applied about the box center inside the rotation; see `PoseLinear`.
|
|
272
|
+
* With one, the element is written with its whole frame as a
|
|
273
|
+
* `transform="matrix(...)"`. */
|
|
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;
|
|
282
279
|
/** Opaque per-element bag for declared namespaces. See `NamespaceMeta`. */
|
|
283
280
|
meta?: NamespaceMeta;
|
|
284
281
|
}
|
|
285
|
-
/**
|
|
282
|
+
/** Any node of a parsed SVG tree. */
|
|
286
283
|
type SvgNode = SvgPathNode | SvgGroupNode | SvgTextNode | SvgImageNode;
|
|
287
284
|
/** Options for {@link parseSvg}. */
|
|
288
285
|
interface ParseOptions {
|
|
289
286
|
/**
|
|
290
287
|
* Map of prefix → URI for XML namespaces the caller wants surfaced.
|
|
291
|
-
* Attributes
|
|
288
|
+
* Attributes and child elements in a declared namespace are collected
|
|
292
289
|
* into `SvgNode.meta` (per element) or `ParseResult.documentMeta` (root).
|
|
293
|
-
*
|
|
294
|
-
* the structured `meta` bag; they are silently dropped at serialize time.
|
|
290
|
+
* Content in an undeclared namespace is dropped.
|
|
295
291
|
*/
|
|
296
292
|
namespaces?: Record<string, string>;
|
|
297
293
|
/**
|
|
@@ -302,17 +298,19 @@ interface ParseOptions {
|
|
|
302
298
|
* those defaults.
|
|
303
299
|
*/
|
|
304
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;
|
|
305
307
|
}
|
|
306
308
|
/** Output of {@link parseSvg}. */
|
|
307
309
|
interface ParseResult {
|
|
308
310
|
nodes: SvgNode[];
|
|
309
311
|
/** Non-fatal notices (unsupported elements, unrecognized attributes). */
|
|
310
312
|
warnings: string[];
|
|
311
|
-
/**
|
|
312
|
-
* Opaque per-document bag for declared namespaces. Holds root-level
|
|
313
|
-
* namespaced attributes (`documentMeta.<prefix>.attrs`) and namespaced
|
|
314
|
-
* root children (`documentMeta.<prefix>.elements`).
|
|
315
|
-
*/
|
|
313
|
+
/** The root's namespaced attributes and children, for declared namespaces. */
|
|
316
314
|
documentMeta?: NamespaceMeta;
|
|
317
315
|
/** Root `viewBox` attribute, parsed from `"x y width height"`. */
|
|
318
316
|
viewBox?: {
|
|
@@ -321,9 +319,9 @@ interface ParseResult {
|
|
|
321
319
|
width: number;
|
|
322
320
|
height: number;
|
|
323
321
|
};
|
|
324
|
-
/** Root `width` attribute,
|
|
322
|
+
/** Root `width` attribute in user units when present, an absolute CSS unit converted at 96 per inch. */
|
|
325
323
|
width?: number;
|
|
326
|
-
/** Root `height` attribute
|
|
324
|
+
/** Root `height` attribute in user units when present, converted as `width` is. */
|
|
327
325
|
height?: number;
|
|
328
326
|
/** Text content of the first `<title>` child of `<svg>`, when present. */
|
|
329
327
|
title?: string;
|
|
@@ -337,10 +335,7 @@ interface ParseResult {
|
|
|
337
335
|
}
|
|
338
336
|
/** Options for {@link serializeSvg}. */
|
|
339
337
|
interface SerializeOptions {
|
|
340
|
-
/**
|
|
341
|
-
* Override the root `viewBox`. When omitted, the serializer computes
|
|
342
|
-
* a tight bounding box from the supplied nodes.
|
|
343
|
-
*/
|
|
338
|
+
/** The root `viewBox`. Default: a tight bounding box around the nodes. */
|
|
344
339
|
viewBox?: {
|
|
345
340
|
x: number;
|
|
346
341
|
y: number;
|
|
@@ -359,25 +354,23 @@ interface SerializeOptions {
|
|
|
359
354
|
width?: number;
|
|
360
355
|
/** Emit `height="..."` on the root `<svg>`. */
|
|
361
356
|
height?: number;
|
|
362
|
-
/**
|
|
363
|
-
* Emit `<title>...</title>` as the first child of the root `<svg>`. The
|
|
364
|
-
* value is XML-escaped on output. Empty strings are skipped.
|
|
365
|
-
*/
|
|
357
|
+
/** Emit `<title>` as the first child of the root `<svg>`. Empty strings are skipped. */
|
|
366
358
|
title?: string;
|
|
367
359
|
/**
|
|
368
|
-
* Map of prefix → URI for XML namespaces to declare on the root
|
|
369
|
-
*
|
|
370
|
-
* `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.
|
|
371
362
|
*/
|
|
372
363
|
namespaces?: Record<string, string>;
|
|
373
364
|
/**
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
* `<prefix>.elements[localName]` becomes `<prefix>:<localName>...>`
|
|
377
|
-
* 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.
|
|
378
367
|
*/
|
|
379
368
|
documentMeta?: NamespaceMeta;
|
|
380
|
-
/**
|
|
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
|
+
*/
|
|
381
374
|
pretty?: boolean;
|
|
382
375
|
}
|
|
383
376
|
|
|
@@ -400,7 +393,7 @@ declare function parseSvg(svg: string, opts?: ParseOptions): ParseResult;
|
|
|
400
393
|
|
|
401
394
|
/**
|
|
402
395
|
* Map between SVG `<linearGradient>` / `<radialGradient>` elements and
|
|
403
|
-
* weasel's `
|
|
396
|
+
* weasel's `Paint` gradient variants. Definitions are indexed by id
|
|
404
397
|
* wherever in the document they appear, and looked up when a `fill` /
|
|
405
398
|
* `stroke` attribute references one as `url(#id)`. On serialize, we
|
|
406
399
|
* emit a fresh `<defs>` block with stable generated ids.
|
|
@@ -418,9 +411,8 @@ declare function nativeSvgKind(kind: string): boolean;
|
|
|
418
411
|
declare function nativeSvgSpace(space: string | null | undefined): boolean;
|
|
419
412
|
|
|
420
413
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* as a tight bounding box around every leaf.
|
|
414
|
+
* Produce an SVG document string. The root `viewBox` is `opts.viewBox` if
|
|
415
|
+
* supplied, otherwise a tight bounding box around every leaf.
|
|
424
416
|
*/
|
|
425
417
|
declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
|
|
426
418
|
|
|
@@ -442,27 +434,17 @@ declare function svgNeeds(nodes: readonly SvgNode[]): RenderNeeds;
|
|
|
442
434
|
declare function warmSvg(nodes: readonly SvgNode[]): Promise<void>;
|
|
443
435
|
|
|
444
436
|
/**
|
|
445
|
-
* Map between SVG `<pattern>` elements and weasel's
|
|
446
|
-
* The counterpart to `gradients.ts` for the other paint-server kind.
|
|
447
|
-
*
|
|
448
|
-
* The tile's shapes come from `tileGeometry` in core, so the vector form a
|
|
449
|
-
* viewer sees is generated from the same description that rasterizes the GL
|
|
450
|
-
* texture. Alongside them we write the spec itself onto a `data-weasel-tile`
|
|
451
|
-
* attribute: any SVG consumer renders the shapes, and weasel reads the
|
|
452
|
-
* attribute back to recover the exact spec rather than reverse-engineering
|
|
453
|
-
* it from geometry.
|
|
437
|
+
* Map between SVG `<pattern>` elements and weasel's pattern paints.
|
|
454
438
|
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
439
|
+
* The tile's shapes are written for any SVG consumer to render, and the spec
|
|
440
|
+
* itself goes on a `data-weasel-tile` attribute, which weasel reads back
|
|
441
|
+
* rather than reverse-engineering it from geometry. A `texture` paint has no
|
|
442
|
+
* vector form and exports as nothing, with a warning.
|
|
459
443
|
*/
|
|
460
444
|
|
|
461
445
|
/**
|
|
462
|
-
* A single tile as a standalone `<svg>` document, sized to its own extent
|
|
463
|
-
*
|
|
464
|
-
* set as a repeating CSS `background-image` via a data URI. Shares the shape
|
|
465
|
-
* mapper with `patternXml`, so a preview can't drift from what paints.
|
|
446
|
+
* A single tile as a standalone `<svg>` document, sized to its own extent,
|
|
447
|
+
* for UI that shows a tile outside a document, such as a picker swatch.
|
|
466
448
|
*/
|
|
467
449
|
declare function tilePreviewSvg(spec: TilePatternSpec, background?: string): string;
|
|
468
450
|
/** `tilePreviewSvg` packed as a `url(...)` value for CSS `background-image`. */
|
|
@@ -473,6 +455,7 @@ declare function tilePreviewCssUrl(spec: TilePatternSpec, background?: string):
|
|
|
473
455
|
* honor the declaration ({@link isDeclarationHonored}), not whether a browser
|
|
474
456
|
* could parse it — the answer decides whether the guarded rules apply here.
|
|
475
457
|
*/
|
|
458
|
+
/** Options for evaluating a `@supports` condition. */
|
|
476
459
|
interface SupportsOptions {
|
|
477
460
|
/** Answers `selector(…)`; without it every selector test is false. */
|
|
478
461
|
readonly selector?: (selector: string) => boolean;
|
|
@@ -500,12 +483,15 @@ interface SvgKitPose {
|
|
|
500
483
|
width: number;
|
|
501
484
|
height: number;
|
|
502
485
|
rotation?: number;
|
|
486
|
+
/** Carried onto a text or image node. A path node has nowhere to hold it,
|
|
487
|
+
* so a path leaf's is baked into its geometry with its rotation. */
|
|
488
|
+
transform?: PoseLinear;
|
|
503
489
|
}
|
|
504
490
|
/** The leaf data the kit's path, text and image painters read — what
|
|
505
491
|
* `svgNodesToKitDrafts` writes. */
|
|
506
492
|
interface SvgKitLeafData {
|
|
507
493
|
path?: Path;
|
|
508
|
-
fill?:
|
|
494
|
+
fill?: Fill | null;
|
|
509
495
|
stroke?: Stroke | null;
|
|
510
496
|
text?: string;
|
|
511
497
|
style?: TextStyle;
|
|
@@ -516,7 +502,7 @@ interface SvgKitLeafData {
|
|
|
516
502
|
/** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
|
|
517
503
|
* gradient or pattern is resolved into `box`, since the geometry it paints
|
|
518
504
|
* is written already placed there. */
|
|
519
|
-
declare function svgPaintFromKit(fill:
|
|
505
|
+
declare function svgPaintFromKit(fill: Paint | null, box: SvgKitPose): SvgPaint;
|
|
520
506
|
/** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
|
|
521
507
|
* One with no paint or no width draws nothing and is written as no stroke. */
|
|
522
508
|
declare function svgStrokeFromKit(stroke: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
|
|
@@ -529,7 +515,9 @@ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose
|
|
|
529
515
|
* draws.
|
|
530
516
|
*
|
|
531
517
|
* A path with no `fill` takes `kit:path`'s fallback: the default shape fill
|
|
532
|
-
* when it has no stroke, and no fill when it has one.
|
|
518
|
+
* when it has no stroke, and no fill when it has one. Under a pose `transform`
|
|
519
|
+
* its stroke width takes the uniform part of the stretch, as `parseSvg` reads
|
|
520
|
+
* a stroke under a skewed element.
|
|
533
521
|
*/
|
|
534
522
|
declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | null;
|
|
535
523
|
/** One node as {@link svgNodesFromKit} reads it. */
|
|
@@ -572,25 +560,37 @@ interface SvgDraftBounds {
|
|
|
572
560
|
width: number;
|
|
573
561
|
height: number;
|
|
574
562
|
}
|
|
575
|
-
|
|
563
|
+
/** Where a draft node sits: its box, and the rotation or linear part its SVG transform left it with. */
|
|
564
|
+
type SvgDraftPose = SvgDraftBounds & {
|
|
576
565
|
rotation?: number;
|
|
566
|
+
transform?: PoseLinear;
|
|
577
567
|
};
|
|
568
|
+
|
|
578
569
|
/**
|
|
579
570
|
* One node the unpack wants the scene to materialize, in parent-before-child
|
|
580
571
|
* order (a draft's `parentId` always names an earlier draft, or `null` for
|
|
581
|
-
* roots).
|
|
572
|
+
* roots). `hidden` is the source node's, for the caller to map onto whatever
|
|
573
|
+
* it hides a node with.
|
|
574
|
+
*
|
|
575
|
+
* A container's `data` is empty unless its `<g>` is clipped. A scene container
|
|
576
|
+
* clips to what its painter draws, so a clipped one holds the clip as an
|
|
577
|
+
* unpainted path, `{ path, fill: null }`, and its `pose` is the clip's box
|
|
578
|
+
* rather than its children's.
|
|
582
579
|
*/
|
|
583
580
|
type SvgSceneDraft<TData = Record<string, unknown>> = {
|
|
584
581
|
kind: 'container';
|
|
585
582
|
id: string;
|
|
586
583
|
parentId: string | null;
|
|
587
|
-
pose:
|
|
584
|
+
pose: SvgDraftPose;
|
|
585
|
+
data: SvgKitLeafData;
|
|
586
|
+
hidden?: true;
|
|
588
587
|
} | {
|
|
589
588
|
kind: 'leaf';
|
|
590
589
|
id: string;
|
|
591
590
|
parentId: string | null;
|
|
592
|
-
pose:
|
|
591
|
+
pose: SvgDraftPose;
|
|
593
592
|
data: TData;
|
|
593
|
+
hidden?: true;
|
|
594
594
|
};
|
|
595
595
|
/** A leaf `SvgNode`: everything but a group. */
|
|
596
596
|
type SvgLeafNode = Exclude<SvgNode, SvgGroupNode>;
|
|
@@ -600,33 +600,27 @@ interface SvgNodesToKitDraftsOptions<TData> {
|
|
|
600
600
|
* the node it came from — for data shaped other than the kit painters', or
|
|
601
601
|
* metadata the document carries. `null` leaves it out. */
|
|
602
602
|
leaf?(id: string, draft: {
|
|
603
|
-
pose:
|
|
603
|
+
pose: SvgDraftPose;
|
|
604
604
|
data: SvgKitLeafData;
|
|
605
605
|
}, source: SvgLeafNode): TData | null;
|
|
606
606
|
}
|
|
607
|
-
/** Lower an `SvgPaint` onto the `kit:path` painter's
|
|
608
|
-
* `
|
|
609
|
-
* fill rather than falling back to its default).
|
|
610
|
-
* `fill-opacity`.
|
|
607
|
+
/** Lower an `SvgPaint` onto the paint of the `kit:path` painter's
|
|
608
|
+
* `data.fill` — a `Paint`, or `null` for SVG's `fill="none"` (the painter
|
|
609
|
+
* skips the fill rather than falling back to its default).
|
|
611
610
|
*
|
|
612
|
-
* A gradient
|
|
613
|
-
* the
|
|
614
|
-
*
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
*
|
|
619
|
-
* Everything the SVG carried — paint, width, cap, join, dash, miter limit,
|
|
620
|
-
* opacity — lands on the one `Stroke` `data.stroke` takes. The paint is
|
|
621
|
-
* normalized to the leaf's own box the same way a fill is, so a
|
|
622
|
-
* `userSpaceOnUse` gradient survives the fit-clamp and the drop-point
|
|
623
|
-
* placement. */
|
|
611
|
+
* A `userSpaceOnUse` gradient is rebased onto the leaf's own box, so it
|
|
612
|
+
* survives the fit-clamp and the drop-point placement that move the
|
|
613
|
+
* geometry out from under it. */
|
|
614
|
+
declare function fillDataFromSvg(paint: SvgPaint | undefined, box: SvgDraftBounds): Paint | null | undefined;
|
|
615
|
+
/** Lower an `SvgStroke` onto the leaf's `data.stroke`, or `undefined` for an
|
|
616
|
+
* unpainted one. The paint is rebased onto the leaf's own box the same way
|
|
617
|
+
* a fill is. */
|
|
624
618
|
declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftBounds): Stroke | undefined;
|
|
625
619
|
/**
|
|
626
620
|
* Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
|
|
627
621
|
* {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
|
|
628
|
-
* union AABB of its descendants
|
|
629
|
-
*
|
|
622
|
+
* 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.
|
|
630
624
|
*
|
|
631
625
|
* Handed a whole `ParseResult`, it also registers the document's markers
|
|
632
626
|
* (`parsed.markers`), without which the strokes naming them draw bare.
|
|
@@ -638,11 +632,15 @@ declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftB
|
|
|
638
632
|
*/
|
|
639
633
|
declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: ParseResult | readonly SvgNode[], nextId: (source: SvgNode) => string, options?: SvgNodesToKitDraftsOptions<TData>): SvgSceneDraft<TData>[];
|
|
640
634
|
/**
|
|
641
|
-
* Parse each file and insert its node tree
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
635
|
+
* Parse each file and insert its node tree, one undo step per file. Pass it
|
|
636
|
+
* as `<SceneCanvas ingestion={{ svg: { unpack: unpackSvgFiles } }}>`.
|
|
637
|
+
*
|
|
638
|
+
* Each file is fit-clamped to 90% of the visible viewport and centered on the
|
|
639
|
+
* drop point (or the viewport center), with later files cascading by a fixed
|
|
640
|
+
* offset. A multi-root file is wrapped in one container so it arrives as a
|
|
641
|
+
* single selectable unit. A file that fails to parse, or parses to nothing,
|
|
642
|
+
* is skipped with a `console.warn`.
|
|
645
643
|
*/
|
|
646
644
|
declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
|
|
647
645
|
|
|
648
|
-
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 };
|
|
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 };
|