Repository navigation
Releases: p8nut/resvg-napi
Release list
v0.5.1
A patch release that only changes how the Linux x64 binary is built. No API change.
Fixed
resvg-napi-linux-x64-gnuloads on older Linux distributions again. It was compiled directly on the CI runner and therefore required glibc 2.35, so it failed to load on RHEL / Rocky / Alma 9, Amazon Linux 2023, Debian 11 and Ubuntu 20.04. It is now built against glibc 2.17 like the arm64 and armv7 binaries, and requires 2.14. Every release up to 0.5.0 had this problem. (#100)- CI now fails the build if any Linux glibc binary requires a version above 2.17, so the floor cannot creep up again unnoticed, for instance when
ubuntu-latestmoves to Ubuntu 26.
What's Changed
- ci: build x86_64 linux-gnu against glibc 2.17, and check the floor by @p8nut in #100
- chore(release): 0.5.1 by @p8nut in #101
Full Changelog: v0.5.0...v0.5.1
v0.5.0
More of resvg, usvg and fontdb exposed as they are. Every change adds to the API, nothing is removed or renamed.
New
Image.size: the encoded image's own size, which can differ from the element'swidth/height. (#96)toString({ indent, attributesIndent }):number | 'tabs' | 'none', the output formatting usvg's writer offers. An unknown value throws. (#96)ClipPath.clipPath()/Mask.mask(): a clip path or mask applied to another one. (#97)PositionedGlyph.font: the face that drew a glyph, read from its own document's database. (#97)FontDatabase.familyName(family): what a generic keyword such asserifresolves to in this database. (#98)FontDatabase.faceSource(face):{ kind: 'binary' | 'file' | 'sharedFile', path?, index }. (#98)
Fixed
faceOf(glyph)checks where the glyph comes from. A glyph laid out against another font database now returnsnull. Until now, two databases holding the same font issued the same keys, sofaceOfcould answer with an unrelated face. (#97)- Nodes reached through a definition (pattern, mask, clip path…) keep their document. Their
clipPath()/mask()now resolve instead of returningnull, and theirrenderPng()reports todoc.takeLogs(). (#97) SvgNode.clipPath()/mask()find the definition by identity. Matching byidmissed definitions without one and could pick a namesake. (#97)
What's Changed
- ci: drop the NPM_TOKEN fallback, trusted publishing proven by 0.4.1 by @p8nut in #95
- feat: Image.size, toString indent and attributesIndent by @p8nut in #96
- feat: nested clip paths and masks, glyph.font with provenance by @p8nut in #97
- feat: FontDatabase.familyName and faceSource by @p8nut in #98
- chore(release): 0.5.0 by @p8nut in #99
Full Changelog: v0.4.1...v0.5.0
v0.4.1
A patch release with no API change. index.d.ts is identical to 0.4.0's.
Fixed
- A panic inside usvg or resvg no longer kills the Node process. Parsing, rendering and
renderNodePngnow turn it into a JS error, in both the sync methods and their async variants. Until now the unwind reached napi'sextern "C"frame and aborted the process. (#91) node.renderPng()/renderPngAsync()report to their document. Their messages now show up indoc.takeLogs()and no longer only in the global buffer. Nodes reached through a definition (pattern, mask…) have no document and still log globally. (#91)
Faster
- The async variants share the document's image map instead of copying it on every call. With 500 embedded images, the cost per call drops from about 75 µs to about 30 µs. (#90)
Internal
ascasts are replaced byFrom/TryFrom. The only casts left are the two float narrowings Rust has no other way to write. Rendering is unchanged: all 1715 conformance references still match. (#92)clippy -D warningsnow also covers the generated bindings. (#93)- First release published through npm trusted publishing (OIDC).
What's Changed
- perf: async twins share the image map instead of copying it by @p8nut in #90
- fix: contain usvg/resvg panics, route node renders to the document log by @p8nut in #91
- refactor: replace
ascasts with From/TryFrom by @p8nut in #92 - refactor: lint the generated bindings with clippy by @p8nut in #93
- chore(release): 0.4.1 by @p8nut in #94
Full Changelog: v0.4.0...v0.4.1
v0.4.0
A minor: two behaviours change. Mostly security and correctness fixes from a
review of the binding.
Security
- An untrusted SVG could abort the process. A declared size or a render
scale large enough made the pixmap allocation fail, which aborts:
renderAsync('<svg width="200000" height="100000"/>')asked for 80 GB.
Renders now throw pastRenderParams.maxPixels(default 2^28, 1 GiB). #83 - An untrusted SVG could read local files. usvg's default resolver read any
absolutehref, and relative ones off the working directory, into the
pixels and thetoString()output;/dev/zeroexhausted memory. Disk reads
now needresourcesDir, and stay inside it (canonical path: no.., no
symlink out).RenderOptions.resourcesRootwidens the fence to an ancestor
for templates beside a shared assets folder. #83
Behaviour changes
- Without
resourcesDirthe disk is not read: anhrefthat resolved off the
working directory now lands inpendingImages(). SetresourcesDir, or pass
the bytes asimages. width/heightare rounded, and throw unless a positive pixel count
(napi'su32used to wrap:-1became 4294967295). A non-finitecrop
throws instead of rendering blank. #84
Fixes
FontDatabase.face()/removeFace()with a face from another database
answered with, or deleted, a face of this one. #84parseAsyncenumerated the system fonts on the event loop on first use. #84FontDatabase.queryclamps the weight to 1..=1000 instead of truncating. #84- The codegen binds only against the upstream version Cargo.lock picked. #85
New
Resvg.takeLogs(): a document's own messages, async renders included.
The module-leveltakeLogs()still receives everything. #87RenderParams.maxPixels,RenderOptions.resourcesRoot. #83
Performance
- A
FontDatabaseis shared with the parses that use it and copied on the first
write: 0.19 ms -> 0.013 ms per parse with 359 faces. #86
Tooling: @napi-rs/cli 3.10.6 (WASI shims report a crashed worker instead of
hanging, #82), a weekly stale-Cargo.lock probe (#81), CI on notices-only PRs (#80).
What's Changed
- build(deps-dev): bump @types/node from 26.5.0 to 26.5.1 in the dev-dependencies group across 1 directory by @dependabot[bot] in #58
- build(deps-dev): bump typescript from 6.0.3 to 7.0.2 by @dependabot[bot] in #59
- refactor(build): the crate a type comes from is a value, not a chain by @p8nut in #63
- fix(licenses): the notice ships with the binary it describes by @p8nut in #64
- refactor(build): a transform is recognised by its shape, not its name by @p8nut in #65
- feat(build): public means what the crate root exports, re-exports included by @p8nut in #66
- refactor(build): the crate a path is built from travels with the type by @p8nut in #67
- build: refresh dependencies, and regenerate the loaders on @napi-rs/cli 3.10.4 by @p8nut in #68
- build(deps): smallvec 1.16.2, siphasher 1.0.4 by @p8nut in #71
- fix(licenses): Apache prose is not a copyright holder by @p8nut in #74
- ci: say why npm ci can never work here by @p8nut in #72
- build: @napi-rs/cli 3.10.5, and regenerate the WASI shims by @p8nut in #73
- fix: refuse a signal that is not an AbortSignal by @p8nut in #76
- ci: fetch the whole crate graph before checking the notices by @p8nut in #79
- build(deps-dev): bump @types/node from 26.6.2 to 26.6.3 in the dev-dependencies group across 1 directory by @dependabot[bot] in #77
- ci: keep what a dependabot run regenerates by @p8nut in #75
- ci: run on pull requests that only touch the third-party notices by @p8nut in #80
- ci: flag a stale Cargo.lock on the Monday run by @p8nut in #81
- build: @napi-rs/cli 3.10.6 by @p8nut in #82
- fix: cap the canvas size and confine image reads to resourcesDir by @p8nut in #83
- fix: validate render sizes, check face provenance, parse off the loop by @p8nut in #84
- build: bind only against the sources Cargo.lock picked by @p8nut in #85
- perf: share FontDatabase with the parses that use it, copy on write by @p8nut in #86
- feat: per-document logs, Resvg.takeLogs() by @p8nut in #87
- ci: publish through npm trusted publishing by @p8nut in #89
- chore(release): 0.4.0 by @p8nut in #88
Full Changelog: v0.3.1...v0.4.0
v0.3.1
A patch, and the reason it could not wait.
0.3.0 could not be installed beside its own WASI package. The root asked for
resvg-napi-wasm32-wasi@0.2.0 — a pin written at the 0.2.0 release that nothing
moved since, because napi version does not touch dependency maps and the
publish-shape guard read optionalDependencies and nothing else:
npm i resvg-napi@0.3.0 resvg-napi-wasm32-wasi@0.3.0
npm error ERESOLVE — Conflicting peer dependency: resvg-napi-wasm32-wasi@0.2.0
Anyone taking the documented WASI path needed --force. An npm version is
immutable, so the fix had to travel in a new one. The guard now reads every map
that names a package of this family, and a second one compares the wasm
package's emnapi pins against the root's — the two ways this release found to
fail before it got out.
The binaries now carry the notices they owe. tiny-skia and tiny-skia-path —
the drawing surface this binding is named after — are BSD-3-Clause, copyright
Google Inc. and Yevhenii Reizner, statically linked into all thirteen .node
files and the .wasm. arrayref is BSD-2, libloading ISC, slotmap Zlib, and MIT
asks for its notice to travel with a copy. The package shipped LICENSE-APACHE,
LICENSE-MIT and nothing else; THIRD-PARTY-NOTICES.md is generated from the
crate graph and checked in CI, so a resvg bump that moves the graph fails there
rather than shipping a notice that no longer describes the binary.
Dependencies. The emnapi family moves to 1.11.3, which became possible only
when @napi-rs/cli 3.9.1 and @napi-rs/wasm-runtime 1.2.4 moved those packages
to peerDependencies — the exact pin a transitive wasm sub-package held over
@emnapi/core had blocked it twice before. Plus esbuild and @types/node.
No API change. faceOf, Font.families and everything else 0.3.0 added is
unchanged.
What's Changed
- fix(demo): fetch a font the CDN refuses to serve by @p8nut in #54
- feat(demo): the bench ships a font, so a first visit renders by @p8nut in #55
- fix: Cargo.toml was left at 0.2.0 by the 0.3.0 release by @p8nut in #57
- chore(deps): the emnapi family moves, and dependabot can carry it again by @p8nut in #56
- fix(pkg): the wasm peer pin, and the notices the binaries owe by @p8nut in #60
- chore(release): 0.3.1 by @p8nut in #61
- fix(pkg): the wasm package declares the emnapi the root uses by @p8nut in #62
Full Changelog: v0.3.0...v0.3.1
v0.3.0
One addition, and the reason it exists.
pendingFonts() has always named the families the database did not carry — the
loud failure. The quiet one is a family that is loaded and still did not draw
the text: a fallback renders, the page looks plausible, and nobody finds out
until it is printed.
const text = doc.node('title').text()
text.chunks[0].spans[0].font.families // ['Brand Sans', 'DejaVu Sans'] — asked for
doc.faceOf(glyph)?.postScriptName // 'DejaVuSans' — what actually drew itResvg.faceOf(glyph) returns the face that drew a glyph, looked up in the
database the document was parsed with; null means the glyph came from another
document. Font.families returns the families a span asked for, so the
answer has something to be compared against. The browser bench shows both: a
drawn with row, and a warning per substitution.
Nothing breaks. Two additions to the surface; no member changed shape or
left, which is why this is a minor.
Two things that landed earlier and reach npm only now, because a published
README changes only on publish: the README is rewritten around what the project
does rather than what it is, and toString() finally says out loud that it
converts text to paths. The bench is live at
p8nut.github.io/resvg-napi.
What's Changed
- feat(demo): deploy the bench to GitHub Pages by @p8nut in #36
- docs: titles that say what they contain, and the examples as images by @p8nut in #35
- chore(ci): a v* tag creates a GitHub release, and CHANGELOG.md goes by @p8nut in #34
- chore: dependabot for the actions and the dev dependencies by @p8nut in #37
- docs: link the deployed demo by @p8nut in #39
- chore(dependabot): the emnapi family moves in lockstep, majors on their own by @p8nut in #40
- chore(ci): drop the npm credentials probe by @p8nut in #41
- chore(dependabot): the CLI is the gate, so the emnapi pins wait for it by @p8nut in #44
- ci: the wasm job bundles the demo by @p8nut in #47
- build(deps-dev): bump @napi-rs/cli from 3.8.6 to 3.9.0 in the napi group across 1 directory by @dependabot[bot] in #46
- build(deps-dev): bump the dev-dependencies group across 1 directory with 2 updates by @dependabot[bot] in #43
- docs: npm says the same thing as the repo page by @p8nut in #48
- chore: no tool config in the repository by @p8nut in #49
- fix: npm's homepage is the README, not the demo by @p8nut in #50
- docs: the text-as-outlines behaviour has a title of its own by @p8nut in #51
- feat: every glyph names the face that drew it by @p8nut in #52
- chore(release): 0.3.0 by @p8nut in #53
New Contributors
- @dependabot[bot] made their first contribution in #46
Full Changelog: v0.2.0...v0.3.0
v0.2.0
Three things you could not read before, and nineteen defects an adversarial
review found. The minor bump is for the API additions; two of the fixes change
shapes, and they are listed under Breaking.
New
SvgNode.image() returns the content of an image node, and the four raster
variants of its kind carry the encoded bytes exactly as the document supplied
them. usvg says a raster payload should be decoded by the caller; this is the
caller.
const img = node.image()
img.kind.type // 'png' | 'jpeg' | 'gif' | 'webp' | 'svg'
img.kind.bytes // BufferTextSpan.decoration gives underline, overline and lineThrough, each with
the fill and stroke it is drawn with. FontFace.style says
'normal' | 'italic' | 'oblique'.
Breaking
The string enums are type-only unions rather than ambient const enums. An
ambient const enum cannot be used under isolatedModules -- every bundler --
so shapeRendering, textRendering and imageRendering had no way to be set at
all. Write shapeRendering: 'geometricPrecision' instead of
ShapeRendering.GeometricPrecision; the runtime is unchanged.
Span and PositionedGlyph are read-only classes rather than plain objects.
They expose more than before -- PositionedGlyph gained id and text -- and
the object form only existed because those members were invisible.
Fixed, and each of these shipped
A requested width came back one pixel too wide. Seventeen of the first four
hundred widths were wrong on a 100x50 document; renderPng({width: 120})
produced a PNG whose IHDR read 121x61. The scale was an f32 round trip, and every
width in the test suite was an exact multiple, so nothing could see it.
Three fields declared string while holding a Path. napi maps a type
named Path to string from the name alone. span.underline.fill was a type
error on working code.
npm ci was impossible -- the lock file predated the rename.
A conformance case that started throwing was reported as an improvement.
The Matrix doc claimed SVG's matrix() field order. It is sx ky kx sy tx ty, and reading the fields positionally mirrors the transform.
browser.js now declares the wasm package it imports, fit.mjs ships type
declarations, setLogLevel declares its six levels, and the publish loop can be
retried.
One thing about npm ci worth knowing rather than filing: it works against a
published version and cannot work against an unpublished one. The manifest names
its platform packages at the version being released, and until that version is on
the registry npm install cannot resolve them, so the lock file has nothing to
record and npm ci calls them missing. That is why CI uses npm install.
Guarded
One new upstream accessor used to delete every public field of a data type,
silently. The POW_VEC precision clamp was keyed on a field-name suffix with no
floor -- a rename upstream meant an index 242 past a 13-entry table, aborting the
process. Handle discovery truncated at 24 while the report listed the classes it
had dropped as generated. All three fail the build now, and each was made to fail
before being trusted.
Known
defaultSizeWidth and defaultSizeHeight reach usvg and usvg does not honour
them: its converter rewrites the size from default_size and then recomputes it
from the attribute, ignoring the rewrite. test/options.mjs records both the
behaviour and the reason.
v0.1.2
Same contents as 0.1.1, which never fully published. The number moved because
npm burned it on one of the fourteen packages.
What happened, because it is worth knowing before unpublishing anything on npm:
0.1.0 was unpublished by mistake, and npm blocks republishing a name for 24
hours after that -- but the block outlives the clock. More than a day later the
registry still refused to save a new packument for those names, answering
409 Conflict — Failed to save packument. A common cause is if you try to
publish a new package before the previous package has been fully processed.
The publish job died on the first of the fourteen. Except the registry then
accepted that first PUT anyway, forty minutes later, while having told the
client it had failed -- so resvg-napi-android-arm-eabi@0.1.1 exists and 0.1.1
is spent there. A version set where thirteen packages are 0.1.1 and one cannot
be is worse than a skipped number.
Nothing was installable at any point: the root package publishes last, so a
failure part-way leaves platform packages that nothing references rather than a
root package pointing at binaries that are not there. That ordering was put in
for exactly this and is the reason this entry describes an inconvenience instead
of a broken release.