@weasel-js/svg 1.10.1 → 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 CHANGED
@@ -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
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';
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.
@@ -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
  /**
@@ -421,22 +440,37 @@ declare function nativeSvgSpace(space: string | null | undefined): boolean;
421
440
  */
422
441
  declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
423
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
+
424
457
  /**
425
458
  * What `serializeSvg(nodes)` reads that loads on demand: the paint kind of
426
459
  * every fill and stroke it writes as a paint server, and the face of every
427
460
  * run whose script size it takes from font metrics. The kinds come from the
428
461
  * serializer's own paint pre-pass, so the two cannot disagree. Registered
429
- * 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.
430
464
  */
431
- declare function svgNeeds(nodes: readonly SvgNode[]): RenderNeeds;
465
+ declare function svgNeeds(nodes: readonly SvgNode[], opts?: Pick<SerializeOptions, 'textLayout'>): RenderNeeds;
432
466
  /**
433
- * Load what {@link svgNeeds} lists, so a `serializeSvg(nodes)` issued after it
434
- * resolves writes every paint server's def and every script size from the
435
- * 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
436
470
  * nodes do not use is never fetched, and cannot fail the export. A kind
437
471
  * nothing registered is passed over; the export warns about it instead.
438
472
  */
439
- declare function warmSvg(nodes: readonly SvgNode[]): Promise<void>;
473
+ declare function warmSvg(nodes: readonly SvgNode[], opts?: Pick<SerializeOptions, 'textLayout'>): Promise<void>;
440
474
 
441
475
  /**
442
476
  * Map between SVG `<pattern>` elements and weasel's pattern paints.
@@ -502,6 +536,12 @@ interface SvgKitLeafData {
502
536
  style?: TextStyle;
503
537
  runs?: readonly StyledRun[];
504
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';
505
545
  image?: ImageNodeData['image'];
506
546
  }
507
547
  /** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
@@ -517,19 +557,23 @@ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose
517
557
  /**
518
558
  * Write one leaf as the `SvgNode` its built-in painter draws — image, text or
519
559
  * path, in the order the painters claim a node. `null` for data none of them
520
- * 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.
521
562
  *
522
563
  * A path with no `fill` takes `kit:path`'s fallback: the default shape fill
523
564
  * when it has no stroke, and no fill when it has one. Under a pose `transform`
524
565
  * its stroke width takes the uniform part of the stretch, as `parseSvg` reads
525
566
  * a stroke under a skewed element.
526
567
  */
527
- declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | null;
568
+ declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | SvgGroupNode | null;
528
569
  /** One node as {@link svgNodesFromKit} reads it. */
529
570
  interface SvgKitTreeNode {
530
571
  kind: string;
531
572
  data: unknown;
532
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;
533
577
  /** A container's copies, as the kit's `ContainerNode.copiesFromPose`
534
578
  * draws them: each is written as a transformed group of its children. */
535
579
  copiesFromPose?(pose: SvgKitPose, node: never): readonly Matrix[] | null;
@@ -556,7 +600,7 @@ interface SvgNodesFromKitOptions<TId extends string = string> {
556
600
  * Walk a scene's container tree into `SvgNode`s ready for `serializeSvg`:
557
601
  * every `container` becomes a group of its children, in z-order, and every
558
602
  * leaf goes through {@link svgLeafFromKit} unless `options.leaf` says
559
- * otherwise.
603
+ * otherwise. A hidden node is written hidden, with its subtree as it is.
560
604
  */
561
605
  declare function svgNodesFromKit<TId extends string>(tree: SvgKitTree<TId>, options?: SvgNodesFromKitOptions<TId>): SvgNode[];
562
606
 
@@ -611,6 +655,10 @@ interface SvgNodesToKitDraftsOptions<TData> {
611
655
  pose: SvgDraftPose;
612
656
  data: SvgKitLeafData;
613
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;
614
662
  }
615
663
  /** Lower an `SvgPaint` onto the paint of the `kit:path` painter's
616
664
  * `data.fill` — a `Paint`, or `null` for SVG's `fill="none"` (the painter
@@ -628,7 +676,8 @@ declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftB
628
676
  * Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
629
677
  * {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
630
678
  * 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.
679
+ * carry kit-painter-native data. Empty groups are dropped unless
680
+ * `options.keepEmpty` keeps them, and so is a group holding only those.
632
681
  *
633
682
  * Handed a whole `ParseResult`, it also registers the document's markers
634
683
  * (`parsed.markers`), without which the strokes naming them draw bare.
@@ -646,9 +695,10 @@ declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: Par
646
695
  * Each file is fit-clamped to 90% of the visible viewport and centered on the
647
696
  * drop point (or the viewport center), with later files cascading by a fixed
648
697
  * 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,
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,
650
700
  * is skipped with a `console.warn`.
651
701
  */
652
702
  declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
653
703
 
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 };
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 };