Skip to content

Releases: danmolitor/forme

v0.10.4

05 Jun 15:11

Choose a tag to compare

Forme 0.10.4

Four layout bug fixes, all user-reported. The
table header one is silent — upgrade if you use
<Row header>.


Fixed

Tables with <Row header> no longer inflate
page count 3–5×

When a table started low enough on a page that
the header didn't fit before a page break, the
engine emitted multiple near-duplicate pages —
the same body rows repeated, with the header
visibly "doubling and sliding one column to the
right" on each successive page. A 24-row,
5-column table produced 8 pages where the same
content without a header row produced 3.

// Before: 8 pages, garbled doubled headers
// After:  3 pages, correct

  
  
    {headerCells}
    {rows.map(r => {...})}
  

<View> wrapping a <Table> no longer
auto-grows to roughly the page height

measure_node_height had no handler for
Table or TableRow, so they fell into a
generic path that summed cell heights instead
of taking the max — a 3-column row of 16pt
cells measured to 48pt, and any wrapping
<View> inherited that inflation. Now
delegates to the same helpers layout_table
already uses, so measurement matches what
renders.

<Svg viewBox="…"> content scales to fit
the display box

SVG paths previously rendered at raw viewBox
coordinates and overflowed — the viewBox
parameters were parsed but unused, and the
PDF scale was always 1.0. Now implements the
SVG viewport algorithm with xMidYMid meet
as the default preserveAspectRatio (uniform
min(sx, sy) scale + centering).

// Before: paths spilled outside the 200×80 box
// After:  scaled to fit

  …paths…

marginTop: 'auto' works in column layouts

Previously a no-op in flexDirection: 'column'
parents — only the horizontal version worked.
Now distributes slack the same way: top-only
pushes to bottom, both autos center, bottom-only
carries forward. Auto margins consume slack
before justifyContent, per the CSS spec.

// "Sign here" now sits at the bottom, not the top

  
    Sign here
  

Upgrade

npm install @formepdf/core@0.10.4 @formepdf/react@0.10.4
# plus any of: cli renderer hono next mcp resend sdk tailwind templates

Other consumers:

  • Rust: cargo add forme-pdf@0.10.4
  • Python: pip install formepdf==0.10.4
  • Go: go get github.com/formepdf/forme-go@v0.10.4
  • Docker: docker pull formepdf/forme:0.10.4
    docker pull formepdf/rasterizer:0.10.4
  • VS Code: update "Forme PDF Preview" in the
    Extensions panel

v0.10.3

28 May 18:38

Choose a tag to compare

Forme 0.10.3

Bug-fix release. If you're on 0.10.2, upgrade - it shipped a silent text-layout regression that caused right-aligned text to render off-page.


Fixed

<Text style={{ width }}> inside a flex row now renders at the correct width

A 0.10.2 regression caused text elements with an explicit width inside a flex row to be sized to the full row width instead. With textAlign: 'right', glyphs were aligned to the right edge of the oversized box and pushed off the page — clipped by PDF viewers and effectively invisible.

The bug was easy to miss: PDF bytes were deterministic, so byte-hash snapshot tests still passed. The corruption only appeared when opening the file.

// Broken on 0.10.2 — "$10.00" rendered off-page
// Correct again on 0.10.3
<View style={{ flexDirection: 'row', justifyContent: 'flex-end' }}>
  <Text style={{ width: 120, textAlign: 'right' }}>Tax:</Text>
  <Text style={{ width: 80,  textAlign: 'right' }}>$10.00</Text>
</View>

layout_text and the text branch of measure_node_height now honor a resolved fixed style.width, matching how <View> and <Image> already behaved. The <View style={{ width: N }}> wrapper workaround is no longer needed.

The 0.10.2 flex-percentage and grid-page-break fixes are unaffected.


Upgrade

npm install @formepdf/core@0.10.3 @formepdf/react@0.10.3
# plus any of: cli renderer hono next mcp resend sdk tailwind templates

Other consumers:

  • Rust: cargo add forme-pdf@0.10.3
  • Python: pip install formepdf==0.10.3
  • Go: go get github.com/formepdf/forme-go@v0.10.3
  • Docker: docker pull formepdf/forme:0.10.3
    docker pull formepdf/rasterizer:0.10.3
  • VS Code: update "Forme PDF Preview" in the
    Extensions panel

Full changelog: engine/CHANGELOG.md

v0.10.2

21 May 21:10

Choose a tag to compare

Forme 0.10.2

Two engine layout bug fixes. No API changes —
upgrade is drop-in.


Fixed

Flex row percentage widths resolve against
the parent

Children of a flexDirection: 'row' container
with explicit percentage widths were being
double-resolved — width: '30%' was computed
as 30% of the child's already-distributed width
(~9% of the row) instead of 30% of the row itself.

<View style={{ flexDirection: 'row' }}>
  <View style={{ width: '30%' }}><Text>30%</Text></View>
  <View style={{ width: '70%' }}><Text>70%</Text></View>
</View>
// Before: 30% child was ~44pt wide on a 487pt row
// After:  30% child is 146pt, 70% child is 341pt

Grid page-break keeps columns aligned

Grid containers that didn't fit on the current
page were scattering each column onto its own
page. An off-by-one guard in the first row's
page-break check suppressed the break, causing
each cell's content to individually overflow
and trigger its own new page. The entire row
now moves to the next page together, keeping
columns at the same y position.


Upgrade

npm install @formepdf/core@0.10.2 @formepdf/react@0.10.2
# plus any of: cli renderer hono next mcp resend sdk tailwind templates

Other consumers:

  • Rust: cargo add forme-pdf@0.10.2
  • Python: pip install formepdf==0.10.2
  • Go: go get github.com/formepdf/forme-go@v0.10.2
  • Docker: docker pull formepdf/forme:0.10.2

Full changelog: engine/CHANGELOG.md

v0.10.1

20 May 12:55

Choose a tag to compare

Forme 0.10.1

Patch release fixing two regressions introduced in 0.10.0. No engine or rendering changes — existing PDFs render identically.


Fixed

Cloudflare Workers crash on import

@formepdf/core@0.10.0 called wasm.__wbindgen_start() at the top level of the bundler-target build. Wrangler passes import wasm from '*.wasm' back as { default: WebAssembly.Module } rather than an instantiated namespace, so this threw immediately:

TypeError: wasm.__wbindgen_start is not a function

Fixed by shipping a third build (--target webpkg-web/) and a new dist/worker.js entry that takes an explicit init(wasmModule) call. The worker / edge-light / deno conditional exports now route here. Your 0.9.x pattern works again:

import { init, renderDocument } from '@formepdf/core'
import wasm from '@formepdf/core/pkg-web/forme_bg.wasm'  // recommended
// or:
import wasm from '@formepdf/core/pkg/forme_bg.wasm'      // legacy, also works
await init(wasm)

Missing pkg-node/ in the published tarball

wasm-pack's --target nodejs output ships with a .gitignore containing *. npm publish honored it and silently dropped the entire directory. The 0.10.0 Node entry tried to import from ../pkg-node/forme.js, which wasn't there:

Cannot find module '../pkg-node/forme.js' from
'node_modules/@formepdf/core/dist/index.js'

Fixed by stripping .gitignore from all three pkg dirs during the WASM build, and adding a prepublishOnly assertion that re-packs the tarball and fails if anything required is missing.

VS Code extension build path mismatch

The bundled extension read ${__dirname}/forme_bg.wasm but the esbuild config still copied the WASM to the old pkg/ location. Now copies to dist/forme_bg.wasm. Only affected fresh 0.10.x rebuilds of the extension.


Added

  • @formepdf/core/worker subpath export
  • @formepdf/core/pkg-web/forme.js and @formepdf/core/pkg-web/forme_bg.wasm subpath exports
  • packages/core/scripts/assert-tarball.sh — runs as prepublishOnly and on every CI PR. Asserts every entry-point file is present in the tarball and that no pkg dir ships a stray .gitignore. Would have caught both regressions above at PR time.
  • Workerd smoke test (@cloudflare/vitest-pool-workers) under npm run test:workers. Runs inside real workerd, calls init(wasm) + renderPdf(), and verifies the result has a %PDF header. Catches the exact regression class that Node-based unit tests can't see.

Migration

You don't need to change anything. Existing 0.10.0 code works on 0.10.1.

If you're on Cloudflare Workers and were hitting the __wbindgen_start crash, upgrade to 0.10.1 — the import + init() pattern from 0.9.x is restored. pkg-web/forme_bg.wasm is recommended over pkg/forme_bg.wasm for new Workers code, but the legacy path still works.


Packages updated

Package Reason
@formepdf/core The fix
@formepdf/renderer, @formepdf/cli, @formepdf/hono, @formepdf/next, @formepdf/mcp, @formepdf/resend Updated to pull the core fix transitively
@formepdf/react, @formepdf/sdk, @formepdf/tailwind, @formepdf/templates Version parity bump only — no code changes
VS Code extension Build config fix for new core layout

Not changed: Rust engine (same WASM bytecode across all three targets), Rust crate, Python SDK, Go SDK, Docker images, rasterizer — all still on 0.10.0.

v0.10.0

19 May 19:04

Choose a tag to compare

Forme 0.10.0

New visual properties

Six new style features in the engine, available in React JSX and JSON document inputs.

opacity cascades to children
Previously, opacity: 0.5 on a <View> faded the background but left text at full alpha. Opacity now wraps the full element subtree — children inherit the fade, and nested opacities multiply correctly.

<View style={{ opacity: 0.5 }}>
  <Text>Fades with the background</Text>
</View>

wordSpacing
Maps to the PDF Tw operator. Stacks additively with text-align: 'justify'.

<Text style={{ wordSpacing: 4 }}>extra space between words</Text>

boxShadow
Offset drop shadow behind any element. Honors borderRadius. Accepts an object or CSS shorthand string.

<View style={{
  borderRadius: 12,
  boxShadow: { offsetX: 2, offsetY: 4, blur: 0, color: '#00000033' },
}}>...</View>

// or:
<View style={{ boxShadow: '4 6 0 #00000040' }}>...</View>

borderRadius + rounded clipping
borderRadius now also rounds the overflow clip path when overflow: 'hidden' is set. Children that exceed the parent's bounds clip to the rounded corners, not a sharp rectangle.

<View style={{ overflow: 'hidden', borderRadius: 16 }}>
  <Image src="..." />  {/* clipped to rounded corners */}
</View>

Page backgroundImage
Watermark-style background images on every page. Supports backgroundSize, backgroundPosition, and backgroundOpacity. The same URL across multiple pages shares a single embedded XObject.

<Page
  backgroundImage="https://cdn.example.com/logo.png"
  backgroundSize="cover"
  backgroundPosition="center"
  backgroundOpacity={0.08}
>...</Page>

CSS gradients via background
Linear and radial gradients with CSS-compatible syntax. Multi-stop gradients use PDF Type 3 stitching functions. Supports deg, turn, rad, grad units and to <side> keywords.

<View style={{ background: 'linear-gradient(135deg, #667eea, #764ba2)' }} />
<View style={{ background: 'radial-gradient(circle, #10b981, #059669)' }} />
<View style={{ background: 'linear-gradient(180deg, #ff0000 0%, #00ff00 50%, #0000ff 100%)' }} />

Build fix: Next.js / Webpack / Turbopack

@formepdf/core@0.9.x shipped a --target web WASM build that referenced ./forme_bg.js — a file wasm-pack doesn't emit for that target. Static-analysis bundlers (Next.js Webpack and Turbopack) failed on the missing file.

@formepdf/core@0.10.0 now ships two WASM builds:

  • pkg/ (--target bundler) — for Vite, Webpack, Turbopack, Wrangler
  • pkg-node/ (--target nodejs) — for Node SSR; self-initializes via fs

@formepdf/next and @formepdf/hono drop their previous init(wasm) workaround. The browser entry now instantiates WASM implicitly at module load.

Vite users: action required

npm install -D vite-plugin-wasm vite-plugin-top-level-await
// vite.config.ts
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';

export default defineConfig({
  plugins: [wasm(), topLevelAwait()],
  worker: {
    format: 'es',
    plugins: () => [wasm(), topLevelAwait()],
  },
});

Without these plugins, Vite throws "ESM integration proposal for Wasm" is not supported currently.


Security: @formepdf/mcp sandbox hardening

The render_custom_pdf sandbox has been rebuilt from the ground up.

What was wrong: The previous new Function(...) evaluator was bypassable in one line via new Function('return process')(). The 30-second timeout only covered the WASM render step — a while(true){} template hung the MCP server indefinitely. validateOutputPath was effectively a no-op.

What changed:

  • Worker-thread isolation with 128 MB memory cap and crash containment
  • vm.Context with codeGeneration: false — blocks eval and string-based Function
  • vm.runInContext with a 5-second sync timeout that actually interrupts infinite loops
  • 10-second wall-clock timeout backed by worker.terminate()
  • AST denylist (acorn) — clear error messages for blocked patterns before the worker starts
  • Post-eval asset sanitizer — font/image src must be data: URIs; closes the file-path exfiltration vector
  • Output path allowlist — writes restricted to CWD by default; opt-in via FORME_MCP_OUTPUT_DIRS

Trust model: This sandbox is hardened for accidental misuse on a trusted local machine. It is not a service-grade boundary for arbitrary attacker code — for that, use containers or isolated-vm. The README now says this explicitly.

Known limitation: The 5-second sync timeout does not interrupt async hangs. A template that awaits an unresolved Promise is caught by the outer 10-second wall-clock timeout instead.


Install

# JavaScript / TypeScript
npm install @formepdf/react@0.10.0 @formepdf/core@0.10.0

# Python
pip install formepdf==0.10.0

# Rust
cargo add forme-pdf@0.10.0

# Go
go get github.com/formepdf/forme-go@v0.10.0

# Docker
docker pull formepdf/forme:0.10.0
docker pull formepdf/rasterizer:0.10.0

Per-package changelogs: [engine](engine/CHANGELOG.md) · [core](packages/core/CHANGELOG.md) · [react](packages/react/CHANGELOG.md) · [mcp](packages/mcp/CHANGELOG.md) · [next](packages/next/CHANGELOG.md) · [hono](packages/hono/CHANGELOG.md)

v0.9.2

29 Apr 02:30

Choose a tag to compare

[0.9.2] - 2026-04-28

Fixed

  • Redaction precision: Text-stripping now uses real per-CID glyph advances when locating regions, so partial-line redactions match the visible overlay precisely. Previously, redacting Molitor in Dear Daniel Molitor would also strip Dear Daniel
  • CID font handling: Decode CID/Type0 fonts in the redaction text extractor, with parsing that survives binary font streams
  • Multi-style text grouping: text_decoration is now part of the glyph style key, so a line-through span inside an otherwise plain text node is no longer merged with its neighbors during PDF emission

Changed

  • Rasterizer body limit: Default Axum 2 MB request body limit removed — large PDFs now flow through the rasterizer sidecar without 413 errors
  • MCP tool surfaces: Synced with the current @formepdf/react component set so generated prompts reflect shipping components

v0.9.1

07 Apr 03:54

Choose a tag to compare

0.9.1 — React Compiler compatibility

Patch release. No breaking changes.


What's fixed

If you enabled React Compiler in your Next.js app (or any
React app) and passed a component reference directly to a
Forme entry point like pdfResponse(MyInvoice), you'd get
a cryptic Invalid hook call error with no indication of
what caused it or how to fix it.

Root cause: React Compiler injects a useMemoCache hook
into compiled functions. Forme's serializer calls user
components as plain functions outside of React's render
cycle — which is illegal for any function containing hooks.

Fix: 0.9.1 catches that error at every call site and
rethrows with a clear, actionable message:
Component "MyInvoice" appears to be compiled by React
Compiler, which injects hooks that cannot run outside of
React's render cycle.
Fix: Add 'use no memo' at the top of the function to opt it out:
function MyInvoice() {
'use no memo';
return ...;
}
Alternatively, wrap it in an inline arrow:
pdfResponse(() => )

The diagnostic is wired into:

  • @formepdf/reactserialize() (covers all consumers)
  • @formepdf/nextrenderPdf(), pdfResponse()
  • @formepdf/honopdfResponse()
  • @formepdf/resendsendPdf(), renderAndAttach()

Who needs this

Anyone using React Compiler (reactCompiler: true in
Next.js 16, or the experimental Vite plugin) who passes
component references directly to Forme entry points.

The common inline pattern was already fine:

// ✅ This was never affected
pdfResponse(() => <MyInvoice data={data} />)

Only the direct reference form tripped the bug:

// ❌ This threw a cryptic error in 0.9.0
pdfResponse(MyInvoice)

Other changes

  • Docker image formepdf/forme now bases on
    formepdf/rasterizer:0.9.1
  • RELEASE.md updated with --provenance=true --sbom=true
    flags for supply-chain attestations on Docker builds

Upgrade

npm install @formepdf/react@0.9.1 @formepdf/core@0.9.1

# Plus any framework integrations you use:
npm install @formepdf/next@0.9.1
npm install @formepdf/hono@0.9.1
npm install @formepdf/resend@0.9.1

Docker: formepdf/forme:0.9.1, formepdf/rasterizer:0.9.1
Crates: forme-pdf = "0.9.1"

v0.9.0

04 Apr 22:08

Choose a tag to compare

Forme 0.9.0

Released April 4, 2026

The biggest release since launch. 0.9.0 adds PDF operations
(redact, merge, certify, rasterize), a Documents archive,
audit trail, certificate storage, and text-search redaction
— transforming Forme from a PDF generation library into a
full PDF operations platform.


⚠️ Breaking Changes

sign → certify rename

All signing API surfaces renamed to better reflect the
cryptographic nature of the operation:

Old New
signPdf() certifyPdf()
SignatureConfig CertificationConfig
signature prop certification prop
POST /v1/sign POST /v1/certify
certificatePem field certificate field
privateKeyPem field privateKey field
Sign() (Go) Certify() (Go)
sign_pdf() (Python) certify_pdf() (Python)

Old names continue to work via deprecation shims and serde
aliases. /v1/sign is removed from the self-hosted server.

See the migration guide.


New Features

True PDF Redaction

Content-stream text removal — not just a visual overlay.
Text operators are removed from the PDF byte stream,
metadata is scrubbed automatically on every redaction
(author, creator, edit history), and a black rectangle
is drawn over the redacted area.

65% of "redacted" PDFs produced by other tools still
expose the underlying text. Forme removes it entirely.

Available via POST /v1/redact or engine functions
redact_pdf(), redact_text(), find_text_regions().

curl -X POST https://api.formepdf.com/v1/redact \
  -H "Authorization: Bearer $FORME_API_KEY" \
  -d '{ "pdf": "...", "presets": ["ssn", "email"] }'

Docs →

Text-Search Redaction

Redact by literal string, regex pattern, or built-in preset
— no coordinate boxes required. Patterns can be scoped to
specific pages.

Built-in presets: ssn, email, phone, date-of-birth,
credit-card

{
  "pdf": "...",
  "patterns": [
    { "pattern": "John Smith", "pattern_type": "Literal" },
    { "pattern": "\\d{3}-\\d{2}-\\d{4}", "pattern_type": "Regex" }
  ],
  "presets": ["email"]
}

Redaction Templates

Save named pattern sets on the hosted API and reference
them by slug:

POST /v1/redact
{ "pdf": "...", "template": "hipaa-patient-record" }

Create and manage templates in the dashboard under Redaction.
Docs →

PDF Merging

Combine up to 20 PDFs into one via POST /v1/merge or
the merge_pdfs() engine function. PDFs merged in array order.

curl -X POST https://api.formepdf.com/v1/merge \
  -H "Authorization: Bearer $FORME_API_KEY" \
  -d '{ "pdfs": ["", ""] }'

Docs →

PDF Rasterization

Convert PDF pages to high-quality PNG images. Powered by
PDFium — the same engine Chrome uses. Configurable DPI (72–300).

curl -X POST https://api.formepdf.com/v1/rasterize \
  -H "Authorization: Bearer $FORME_API_KEY" \
  -d '{ "pdf": "...", "dpi": 150 }'

Returns { "pages": ["<base64 PNG>", ...] } — one per page.
Docs →

Documents Archive

Every hosted API render is saved automatically to your
Documents archive. Upload existing PDFs, redact them,
merge them, certify them — all from the dashboard or API.

  • Source tracking: generated, uploaded, redacted,
    merged, certified
  • Retention: Free 30d · Pro 90d · Team 1yr · Business unlimited
  • Storage: Cloudflare R2
  • Opt out with save: false on any render

Tag documents with developer metadata for filtering:

{ "metadata": { "customerId": "cust_123", "department": "legal" } }

GET /v1/documents?metadata.customerId=cust_123

Docs →

Certificate Storage

Save X.509 certificates in the dashboard, reference by ID
at certify time. Private keys encrypted at rest (AES-256-GCM).
The plaintext key is never returned after saving.

POST /v1/certify
{ "pdf": "...", "certificateId": "cert_abc123" }

Plan limits: Free 1 · Pro 5 · Team/Business unlimited
Docs →

Audit Trail

Full operation history on every document — who did what
and when, whether via dashboard or API key. Events logged:
uploaded, generated, redacted, merged, certified,
downloaded, deleted. Events survive document deletion.

Visible in the History panel in DocumentEditor and
queryable via API.
Docs →

Resource Listing Endpoints

List and retrieve your resources programmatically via API key:
GET /v1/templates
GET /v1/templates/:slug
GET /v1/documents
GET /v1/documents/:id
GET /v1/redaction-templates
GET /v1/redaction-templates/:slug
GET /v1/certificates

Async AI Template Generation

AI template generation no longer blocks the UI. The modal
closes immediately, the template appears in the list with
a shimmer while generating, and the thumbnail populates
automatically when complete. Powered by BullMQ + Redis.

Dashboard — DocumentEditor Redesign

Tools moved from a crowded top bar to a left sidebar panel:

  • Redact — Draw mode (click and drag) + Search mode
    (text patterns, presets, load template)
  • Merge — add PDFs, merge in order
  • Certify — select saved certificate or paste PEM
  • Organize — coming soon

History panel slides out from the right — full audit
trail per document without leaving the editor.


Self-Hosted Server Parity

The formepdf/forme Docker image is updated to 0.9.0
with improved parity on all operation endpoints:

  • POST /v1/certify (renamed from /v1/sign)
  • POST /v1/redact — coordinate regions + text patterns + presets
  • POST /v1/merge
  • POST /v1/rasterize — PDFium sidecar included
  • flattenForms=true query parameter on render endpoints
  • Content-Disposition header on slug renders
  • Consistent { "error": "...", "code": "NOT_IMPLEMENTED" }
    shape on hosted-only feature stubs
  • Preset name validation, max 20 presets, regex compilation
    validation on /v1/redact

Self-hosted intentionally excludes: Documents archive,
certificateId, redaction template slugs, save/saveName.
Pass credentials and patterns directly.

docker pull formepdf/forme:0.9.0

Python SDK (0.9.1)

  • New API client methods: certify(), redact(),
    merge(), rasterize()
  • Local WASM rendering via wasmtime with full component DSL
  • sign() removed, certify_pdf() replaces sign_pdf()
  • Full docs at docs.formepdf.com/python-sdk

Go SDK (0.9.1)

  • New API methods: Redact(), Rasterize()
  • Sign() deprecated shim removed — use Certify()
  • Full docs at docs.formepdf.com/go-sdk

Docs Restructure

  • Per-endpoint API reference pages (render, certify,
    redact, merge, rasterize)
  • New concepts section (documents, credentials, redaction,
    redaction templates, digital certification, audit trail)
  • Migration guide for 0.8 → 0.9
  • Bring Your Own UI guide

Bug Fixes

  • WASM time paniccertify and redact previously
    panicked in browser WASM with "time not implemented on
    this platform". Fixed with #[cfg] conditional using
    js_sys::Date::now() for WASM targets,
    SystemTime::now() for WASI targets (Python/Go SDKs)
  • PKCS#1 auto-conversioncertify_pdf() now accepts
    both PKCS#8 (BEGIN PRIVATE KEY) and PKCS#1
    (BEGIN RSA PRIVATE KEY) formats automatically
  • __formeType version guard — prevents misleading
    "Top-level element must be <Document>" errors when
    @formepdf/core and @formepdf/react are on different versions
  • SVG children — JSX children inside <Svg> now
    correctly serialized
  • SVG opacityopacity, fill-opacity,
    stroke-opacity now work correctly via ExtGState
  • Page style inheritance<Page style={{ fontFamily }}>
    now correctly resolves to child nodes

Packages

Package Version
@formepdf/core 0.9.0
@formepdf/react 0.9.0
@formepdf/cli 0.9.0
@formepdf/renderer 0.9.0
@formepdf/hono 0.9.0
@formepdf/next 0.9.0
@formepdf/resend 0.9.0
@formepdf/mcp 0.9.0
@formepdf/sdk 0.9.0
@formepdf/tailwind 0.9.0
@formepdf/templates 0.9.0
forme-pdf (VS Code) 0.9.0
formepdf (PyPI) 0.9.1
forme-go (Go SDK) v0.9.1
forme-pdf (crates.io) 0.9.0
formepdf/forme (Docker) 0.9.0
formepdf/rasterizer (Docker) 0.9.0

v0.8.3

02 Apr 04:48

Choose a tag to compare

Forme 0.8.3

New Features

SVG Children API

<Svg> now accepts JSX children as an alternative to the raw content string prop. This restores intellisense, syntax highlighting, and proper JSX formatting for SVG content.

Before:

<Svg width={794} height={200} viewBox="0 0 794 200"
  content={`<path fill="#e7f2fe" d="M0 120C80 80 160 60 260 75..."/>`}
/>

After:

<Svg width={794} height={200} viewBox="0 0 794 200">
  <path fill="#e7f2fe" d="M0 120C80 80 160 60 260 75..." />
  <path fill="#d7e8fe" d="M0 150C100 120 180 100 280 120..." />
</Svg>

Both forms continue to work. camelCase props are automatically converted to kebab-case SVG attributes (strokeWidthstroke-width, fillOpacityfill-opacity, etc.). Nested children via <g> are supported.

SVG Opacity Support

opacity, fill-opacity, and stroke-opacity attributes on SVG elements are now applied correctly via PDF ExtGState. Previously these attributes were silently ignored.

<Svg width={400} height={200} viewBox="0 0 400 200">
  <path fill="#60a5fa" opacity="0.3" d="..." />
  <rect fill="#ef4444" fill-opacity="0.5" x="0" y="0" width="100" height="100" />
</Svg>

Opacity inherits through <g> groups correctly.

VS Code Extension — Python Preview

The VS Code extension now supports previewing Python SDK templates. Open a .py file that imports formepdf and the "Preview PDF" button appears in the editor toolbar. The extension runs the script using your Python interpreter and displays the output PDF in the preview panel.

The script should write PDF bytes to stdout:

import sys
import formepdf
 
doc = formepdf.Document(...)
pdf = doc.render()
sys.stdout.buffer.write(pdf)

Uses the Python interpreter from the VS Code Python extension if installed, falls back to python3 / python.


Bug Fixes

Page style prop now applies correctly

<Page style={{ fontFamily: 'Outfit' }}> was silently dropped — the style was not inherited by child elements. Fixed in both the React serializer and the layout engine. Page-level styles now propagate to children as expected.


Upgrading

npm install @formepdf/react@0.8.3 @formepdf/core@0.8.3
 
# Docker
docker pull formepdf/forme:0.8.3
 
# Go SDK
go get github.com/formepdf/forme-go@v0.8.3
 
# Rust crate
cargo add forme-pdf@0.8.3

No breaking changes. Drop-in upgrade from 0.8.2.


All Packages

@formepdf/react · @formepdf/core · @formepdf/cli · @formepdf/renderer · @formepdf/hono · @formepdf/next · @formepdf/resend · @formepdf/mcp · @formepdf/sdk · @formepdf/tailwind · @formepdf/templates · forme-pdf (VS Code) · forme-pdf (crates.io) · formepdf (PyPI) · formepdf/forme (Docker) · github.com/formepdf/forme-go

v0.8.2

31 Mar 14:52

Choose a tag to compare

Forme 0.8.2

Fixed

Custom font weight resolution

When multiple weights are registered for the same font family, the PDF now correctly embeds and renders each weight as a distinct font. Previously, all weights were silently collapsed to 400 or 700 regardless of what was registered.

Font.register({ family: 'Geist', fontWeight: 200, src: '...Geist-UltraLight.ttf' });
Font.register({ family: 'Geist', fontWeight: 300, src: '...Geist-Light.ttf' });
Font.register({ family: 'Geist', fontWeight: 400, src: '...Geist-Regular.ttf' });
 
// All three now render with their correct font files
<Text style={{ fontFamily: 'Geist', fontWeight: 200 }}>Ultra Light</Text>
<Text style={{ fontFamily: 'Geist', fontWeight: 300 }}>Light</Text>
<Text style={{ fontFamily: 'Geist', fontWeight: 400 }}>Regular</Text>

The bug was in the PDF serializer — four sites were snapping font_weight to 400 or 700 before building the font lookup key, discarding the actual registered weight. All four sites now use the raw weight value, with a fallback chain (exact → snapped → Helvetica) for cases where the requested weight has no registered variant.


Improved

  • Docker build — Added .dockerignore to exclude target/ and node_modules/ from the build context, fixing out-of-space errors during multi-platform builds.
  • Release process — Documented SDK WASM rebuild steps (Python + Go), PyPI dist cleanup, and README update checklist.

Upgrading

npm install @formepdf/react@0.8.2 @formepdf/core@0.8.2
 
# Docker
docker pull formepdf/forme:0.8.2
 
# Go SDK
go get github.com/formepdf/forme-go@v0.8.2
 
# Rust crate
cargo add forme-pdf@0.8.2

No breaking changes. Drop-in upgrade from 0.8.1.


All Packages

@formepdf/react · @formepdf/core · @formepdf/cli · @formepdf/renderer · @formepdf/hono · @formepdf/next · @formepdf/resend · @formepdf/mcp · @formepdf/sdk · @formepdf/tailwind · @formepdf/templates · forme-pdf (VS Code) · forme-pdf (crates.io) · formepdf (PyPI) · formepdf/forme (Docker) · github.com/formepdf/forme-go