@weasel-js/svg 1.11.0 → 1.12.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 CHANGED
@@ -183,6 +183,14 @@ writes a `kit:image` leaf back as an `SvgImageNode`.
183
183
  in (a non-`none` value warns) and written back as `none`, so a source file
184
184
  that relied on letterboxing imports stretched.
185
185
 
186
+ ## Writing XML by hand
187
+
188
+ `escapeAttr(s)` and `escapeText(s)` are the escaping this package writes XML
189
+ with, for a caller writing its own. Both drop the characters XML 1.0 cannot
190
+ hold, even as references, and turn a vertical tab or form feed into a line
191
+ break. `escapeAttr` also writes tabs and line breaks as references, since a
192
+ parser reads a literal one in an attribute as a space.
193
+
186
194
  ## License
187
195
 
188
196
  MIT
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
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';
2
+ export { pathToD as serializePathD } from '@weasel-js/geom';
3
+ import { Path, Paint, ScreenLength, StrokeAlign, MarkerRef, VertexWidth, StrokeNib, StyledRun, TextStyle, TextVerticalAlign, Fill, Stroke, PoseLinear, MarkerEntry, TextPoseLayout, RenderNeeds, TilePatternSpec, Inherit, ImageNodeData, IngestCtx } from '@weasel-js/core';
3
4
 
4
5
  /**
5
6
  * Media Queries 4 evaluation against a fixed environment.
@@ -133,6 +134,17 @@ interface SvgStroke {
133
134
  vertexWidths?: VertexWidth[];
134
135
  nib?: StrokeNib;
135
136
  }
137
+ /**
138
+ * Which of a node's paints its own element sets none of, so that it takes the
139
+ * paint of the enclosing `<g>` that does. Its `fill` and `stroke` still hold
140
+ * what that resolves to.
141
+ */
142
+ interface SvgInherits {
143
+ fill?: true;
144
+ /** Set only when the element sets no `stroke-*` or `marker-*` property
145
+ * either: the stroke is taken whole. */
146
+ stroke?: true;
147
+ }
136
148
  /**
137
149
  * Leaf node: a path geometry plus fill/stroke. Other shapes (`<rect>`,
138
150
  * `<ellipse>`, etc.) are lowered to this on parse.
@@ -142,6 +154,7 @@ interface SvgPathNode {
142
154
  path: Path;
143
155
  fill: SvgPaint;
144
156
  stroke?: SvgStroke;
157
+ inherits?: SvgInherits;
145
158
  /** Element-level opacity (`opacity="..."`), 0..1. */
146
159
  opacity?: number;
147
160
  /** Element-level rotation in **radians**, pivoting around the AABB
@@ -171,6 +184,12 @@ interface SvgGroupNode {
171
184
  * unions them and a `Path` holds one outline. */
172
185
  clip?: Path;
173
186
  opacity?: number;
187
+ /** The fill its children take when they set none — written as the group's
188
+ * own `fill`. Absent when neither it nor an enclosing group sets one. */
189
+ fill?: SvgPaint;
190
+ /** As `fill`, for the stroke; absent too when the one in effect is none. */
191
+ stroke?: SvgStroke;
192
+ inherits?: SvgInherits;
174
193
  /** Kept by `ParseOptions.keepHidden` from content SVG does not render:
175
194
  * `display="none"` here, or a leaf under `visibility="hidden"`. Written
176
195
  * back as `display="none"`. */
@@ -217,6 +236,7 @@ interface SvgTextNode {
217
236
  */
218
237
  fill?: Fill | null;
219
238
  stroke?: Stroke;
239
+ inherits?: SvgInherits;
220
240
  /** Element-level opacity (`opacity="..."`), 0..1. */
221
241
  opacity?: number;
222
242
  /** Element-level rotation in **radians**, pivoting around the unrotated
@@ -441,18 +461,19 @@ declare function nativeSvgSpace(space: string | null | undefined): boolean;
441
461
  declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
442
462
 
443
463
  /**
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.
464
+ * Escaping for the XML this package writes by hand.
448
465
  *
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`.
466
+ * XML 1.0 cannot hold most C0 controls, U+FFFE, U+FFFF, or half a surrogate
467
+ * pair, even as character references, and a parser rejects the whole
468
+ * document over one. A vertical tab or form feed is a line break where text
469
+ * comes from (a word processor's clipboard), so those become `\n`; the rest
470
+ * are dropped.
452
471
  */
453
-
454
- /** Serialize a weasel `Path` to an SVG `d=` attribute string. */
455
- declare function serializePathD(given: Path): string;
472
+ /** A parser turns a literal tab or line break in an attribute value into a
473
+ * space, so those are written as character references. */
474
+ declare function escapeAttr(s: string): string;
475
+ /** A literal carriage return in content is read back as a newline. */
476
+ declare function escapeText(s: string): string;
456
477
 
457
478
  /**
458
479
  * What `serializeSvg(nodes)` reads that loads on demand: the paint kind of
@@ -489,6 +510,21 @@ declare function tilePreviewSvg(spec: TilePatternSpec, background?: string): str
489
510
  /** `tilePreviewSvg` packed as a `url(...)` value for CSS `background-image`. */
490
511
  declare function tilePreviewCssUrl(spec: TilePatternSpec, background?: string): string;
491
512
 
513
+ /** CSS Selectors 4 specificity. */
514
+ /** A selector's specificity as (ids, classes, types). */
515
+ type Specificity = readonly [number, number, number];
516
+ /** Negative when `a` is less specific than `b`, positive when more, 0 when equal. */
517
+ declare function compareSpecificity(a: Specificity, b: Specificity): number;
518
+ /** CSS Selectors 4 specificity of one complex selector (no comma list). */
519
+ declare function specificity(selector: string): Specificity;
520
+
521
+ /**
522
+ * Split `s` on `sep` wherever it sits outside strings and (), [] nesting.
523
+ * With `dropBlocks`, a `{…}` block at depth zero is discarded along with the
524
+ * prelude before it, back to the previous separator.
525
+ */
526
+ declare function splitTopLevel(s: string, sep: string, dropBlocks?: boolean): string[];
527
+
492
528
  /**
493
529
  * `@supports` evaluation. A declaration test asks whether this parser would
494
530
  * honor the declaration ({@link isDeclarationHonored}), not whether a browser
@@ -530,8 +566,9 @@ interface SvgKitPose {
530
566
  * `svgNodesToKitDrafts` writes. */
531
567
  interface SvgKitLeafData {
532
568
  path?: Path;
533
- fill?: Fill | null;
534
- stroke?: Stroke | null;
569
+ /** `'inherit'` takes the enclosing container's. */
570
+ fill?: Fill | Inherit | null;
571
+ stroke?: Stroke | Inherit | null;
535
572
  text?: string;
536
573
  style?: TextStyle;
537
574
  runs?: readonly StyledRun[];
@@ -550,7 +587,7 @@ interface SvgKitLeafData {
550
587
  declare function svgPaintFromKit(fill: Paint | null, box: SvgKitPose): SvgPaint;
551
588
  /** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
552
589
  * One with no paint or no width draws nothing and is written as no stroke. */
553
- declare function svgStrokeFromKit(stroke: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
590
+ declare function svgStrokeFromKit(raw: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
554
591
  /** Write a `kit:image` leaf as an `SvgImageNode` — the inverse of the image
555
592
  * arm of `svgNodesToKitDrafts`. The pose is the image's box. */
556
593
  declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose): SvgImageNode;
@@ -560,10 +597,12 @@ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose
560
597
  * draws. A text node with a `background` is a group: the box, then the text.
561
598
  * One that clips is a group clipped to its box as it is drawn, turned with it.
562
599
  *
563
- * A path with no `fill` takes `kit:path`'s fallback: the default shape fill
564
- * when it has no stroke, and no fill when it has one. Under a pose `transform`
565
- * its stroke width takes the uniform part of the stretch, as `parseSvg` reads
566
- * a stroke under a skewed element.
600
+ * An `'inherit'` fill or stroke is none here, with no container to read;
601
+ * {@link svgNodesFromKit} resolves it first.
602
+ *
603
+ * A fill or stroke that paints nothing is written `none`. Under a pose
604
+ * `transform` a stroke's width takes the uniform part of the stretch, as
605
+ * `parseSvg` reads a stroke under a skewed element.
567
606
  */
568
607
  declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | SvgGroupNode | null;
569
608
  /** One node as {@link svgNodesFromKit} reads it. */
@@ -591,8 +630,11 @@ interface SvgNodesFromKitOptions<TId extends string = string> {
591
630
  roots?: readonly TId[];
592
631
  /** `false` leaves a node out, with everything under it — a hidden layer. */
593
632
  include?(id: TId): boolean;
594
- /** Lower a leaf yourself; `null` leaves it out. Defaults to {@link svgLeafFromKit}. */
595
- leaf?(id: TId, node: SvgKitTreeNode): SvgNode | null;
633
+ /** Lower a leaf yourself; `null` leaves it out. Defaults to {@link svgLeafFromKit}.
634
+ * `inherited` is what the leaf's `'inherit'` fields resolve to; write those
635
+ * as these, and the walk marks the result as inheriting where SVG's
636
+ * cascade can carry them. */
637
+ leaf?(id: TId, node: SvgKitTreeNode, inherited: SvgKitInherited): SvgNode | null;
596
638
  /** Decorate the group written for a container, e.g. with `meta`. */
597
639
  group?(id: TId, group: SvgGroupNode): SvgGroupNode;
598
640
  }
@@ -603,6 +645,12 @@ interface SvgNodesFromKitOptions<TId extends string = string> {
603
645
  * otherwise. A hidden node is written hidden, with its subtree as it is.
604
646
  */
605
647
  declare function svgNodesFromKit<TId extends string>(tree: SvgKitTree<TId>, options?: SvgNodesFromKitOptions<TId>): SvgNode[];
648
+ /** What a leaf's `'inherit'` fill and stroke resolve to — its nearest
649
+ * enclosing container's; `null` for none. */
650
+ interface SvgKitInherited {
651
+ fill: Fill | null;
652
+ stroke: Stroke | null;
653
+ }
606
654
 
607
655
  /** Axis-aligned box, in the coordinate space of the SVG being unpacked. Used
608
656
  * for the bounds a draft occupies before it becomes a scene node. */
@@ -701,4 +749,4 @@ declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: Par
701
749
  */
702
750
  declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
703
751
 
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 };
752
+ export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type Specificity, 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, compareSpecificity, escapeAttr, escapeText, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, specificity, splitTopLevel, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };