resvg-napi 0.1.2 → 0.2.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
@@ -1,5 +1,9 @@
1
1
  # resvg-napi
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/resvg-napi)](https://www.npmjs.com/package/resvg-napi)
4
+ [![conformance](https://img.shields.io/badge/resvg%20conformance-1715%2F1715-brightgreen)](#conformance)
5
+ [![licence](https://img.shields.io/npm/l/resvg-napi)](#licence)
6
+
3
7
  Node.js bindings for [resvg](https://github.com/linebender/resvg) 0.48: render
4
8
  SVG to PNG, and read back what usvg made of the document — the resolved tree,
5
9
  text metrics, paint, geometry.
@@ -45,10 +49,17 @@ one knows what it is:
45
49
  const n = doc.node('surname')
46
50
  n.kind // 'group' | 'path' | 'image' | 'text'
47
51
  n.path() // geometry, fill, stroke — null unless it is a shape
48
- n.text() // chunks, layouted spans, positioned glyphs
52
+ n.text() // chunks, layouted spans, positioned glyphs, decoration
49
53
  n.renderPng() // that element alone, cropped to its own extent
50
54
  ```
51
55
 
56
+ A span carries what it is drawn with, down to the lines through it:
57
+
58
+ ```js
59
+ const span = n.text().chunks[0].spans[0]
60
+ span.decoration.underline?.fill?.paint // …and .overline, .lineThrough
61
+ ```
62
+
52
63
  Paint is a discriminated union, so TypeScript narrows it:
53
64
 
54
65
  ```ts
@@ -64,7 +75,8 @@ that distinction bites.
64
75
 
65
76
  **Fonts.** `FontDatabase` is `fontdb` itself: `loadSystemFonts()`,
66
77
  `loadFontData(buffer)`, `loadFontFile(path)`, `faces()`, `query()`, and the
67
- generic-family setters. `pendingFonts()` on a parsed document names the
78
+ generic-family setters. A face reports its `families`, `weight`, `style`
79
+ (`'normal' | 'italic' | 'oblique'`) and whether it is `monospaced`. `pendingFonts()` on a parsed document names the
68
80
  families it wanted and did not get; `pendingImages()` does the same for hrefs.
69
81
 
70
82
  **Definitions.** `linearGradients()`, `radialGradients()`, `patterns()`,
@@ -75,6 +87,21 @@ Something missing? The generator keeps a report of every upstream member it
75
87
  left alone, with the reason. `RESVG_NAPI_CODEGEN_LOG=1 cargo build` prints it,
76
88
  and [CONTRIBUTING.md](CONTRIBUTING.md) explains what the reasons mean.
77
89
 
90
+ ## Conformance
91
+
92
+ resvg's own test corpus, rendered through these bindings: **1715 of 1715 match**
93
+ the reference PNGs upstream asserts on, within 1/255 per channel — a tolerance
94
+ measured rather than chosen, with the reason recorded in
95
+ [`scripts/conformance.mjs`](scripts/conformance.mjs).
96
+
97
+ ```bash
98
+ npm run conformance:fetch # the corpus, at the tag Cargo.toml pins
99
+ npm run conformance # 15 seconds including the fetch
100
+ ```
101
+
102
+ CI runs it on every pull request, so a resvg bump that changes a render shows up
103
+ as a diff rather than passing unnoticed.
104
+
78
105
  ## Platforms
79
106
 
80
107
  Thirteen targets.
package/fit.d.mts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Horizontal text fitting, driven by `data-maxwidth`.
3
+ *
4
+ * Declarations for `fit.mjs`, which is JavaScript with JSDoc: written by hand,
5
+ * unlike `index.d.ts`, so they can drift. `npm run typecheck` reads them.
6
+ */
7
+
8
+ /** A `Resvg` instance, however the caller chose to build one. */
9
+ type Renderer = {
10
+ node(id: string): { extent(): { width: number } | null } | null
11
+ }
12
+
13
+ /** One element carrying a width constraint, and what is needed to rewrite it. */
14
+ export interface WidthConstraint {
15
+ /** The element's opening tag, verbatim. */
16
+ tag: string
17
+ /** The tag name: `text`, `tspan`, ... */
18
+ name: string
19
+ /** The attribute text inside that tag, verbatim. */
20
+ attrs: string
21
+ /** The element's `id`, or null when it had none and one must be generated. */
22
+ id: string | null
23
+ /** The limit, in the document's own units -- not canvas pixels. */
24
+ max: number
25
+ /** Byte offset of the tag in the source string. */
26
+ index: number
27
+ }
28
+
29
+ /** One element that was compressed, and by how much. */
30
+ export interface Adjustment {
31
+ id: string
32
+ /** Measured width before, in the document's units. */
33
+ from: number
34
+ /** The limit it was fitted to. */
35
+ to: number
36
+ /** The horizontal scale applied: `to / from`. */
37
+ factor: number
38
+ /** Measured width after, as a check on the one-pass assumption. */
39
+ measured: number
40
+ }
41
+
42
+ export interface FitResult {
43
+ /** The source, with a `scale(k 1)` composed onto each fitted element. */
44
+ svg: string
45
+ adjustments: Adjustment[]
46
+ /** Constraints that could not be honoured, each saying why. */
47
+ problems: string[]
48
+ }
49
+
50
+ /** Elements carrying a width constraint, with the tag text needed to rewrite it. */
51
+ export function findWidthConstraints(svg: string): WidthConstraint[]
52
+
53
+ /**
54
+ * Fits every `data-maxwidth` element by compressing it horizontally.
55
+ *
56
+ * @param svg source, after any templating
57
+ * @param render `(svg) => Resvg`; supply fonts and options here
58
+ */
59
+ export function fitTextWidths(
60
+ svg: string,
61
+ render: (svg: string) => Renderer,
62
+ ): FitResult