← Back to blog

Forme 0.8.0: Forms, Accessibility, and Digital Signatures

AcroForms, PDF/UA-1 compliance, PDF/A archival, and PKCS#7 digital signatures.

Forme 0.8.0 adds four feature areas that close the gap between "fast PDF generation" and "production-ready for regulated industries": fillable AcroForms, PDF/UA-1 accessibility, PDF/A archival, and PKCS#7 digital signatures. Every feature is opt-in. Existing templates render identically.

AcroForms

AcroForms are the PDF standard for interactive form fields. They work in every major PDF viewer without JavaScript, without custom rendering. Four new components:

import {
  Document, Page, View, Text,
  TextField, Checkbox, Dropdown, RadioButton,
} from '@formepdf/react';

const PatientIntake = () => (
  <Document>
    <Page size="Letter" margin={54}>
      <Text style={{ fontSize: 24, fontWeight: 700, marginBottom: 20 }}>
        Patient Intake Form
      </Text>

      <View style={{ marginBottom: 16 }}>
        <Text style={{ fontSize: 10, color: '#666', marginBottom: 4 }}>Full Name</Text>
        <TextField name="fullName" width={300} height={28} fontSize={10} />
      </View>

      <View style={{ marginBottom: 16 }}>
        <Text style={{ fontSize: 10, color: '#666', marginBottom: 4 }}>Date of Birth</Text>
        <TextField name="dob" width={160} height={28} fontSize={10} />
      </View>

      <View style={{ marginBottom: 16 }}>
        <Text style={{ fontSize: 10, color: '#666', marginBottom: 4 }}>Insurance Provider</Text>
        <Dropdown
          name="insurance"
          options={['Aetna', 'Blue Cross', 'Cigna', 'Kaiser', 'United', 'Other']}
          width={240}
          height={28}
        />
      </View>

      <View style={{ flexDirection: 'row', gap: 24, marginBottom: 16 }}>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
          <RadioButton name="contactMethod" value="email" />
          <Text style={{ fontSize: 11 }}>Email</Text>
        </View>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
          <RadioButton name="contactMethod" value="phone" />
          <Text style={{ fontSize: 11 }}>Phone</Text>
        </View>
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
          <RadioButton name="contactMethod" value="mail" />
          <Text style={{ fontSize: 11 }}>Mail</Text>
        </View>
      </View>

      <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
        <Checkbox name="consent" />
        <Text style={{ fontSize: 11 }}>
          I consent to treatment and authorize billing to my insurance.
        </Text>
      </View>
    </Page>
  </Document>
);

Recipients open the PDF, fill in the fields, and save. The data lives inside the PDF file itself.

Form flattening

Once a form is filled, you often want to lock it down -- turn the interactive fields into static content so nothing can be changed. Pass flattenForms: true at render time:

import { renderDocument } from '@formepdf/core';

const pdf = await renderDocument(<PatientIntake />, {
  flattenForms: true,
});

The output is a regular PDF with no interactive elements. The field values are baked into the page content. This is the standard pattern for filled-and-signed documents: fill programmatically, flatten, then distribute.

PDF/UA accessibility

PDF/UA (Universal Accessibility) is the ISO 14289 standard for accessible PDF documents. It requires a tagged structure tree, correct reading order, alternative text for images, and XMP metadata. Government agencies in the US (Section 508), EU (EN 301 549), and Canada (AODA) mandate it.

Forme makes it a single prop:

<Document pdfUa title="Q4 2025 Earnings Report" author="Acme Corp" lang="en">
  <Page size="A4" margin={54}>
    <Text style={{ fontSize: 24, fontWeight: 700 }}>Q4 2025 Earnings Report</Text>

    <Image
      src="/charts/revenue.png"
      alt="Revenue chart showing 23% year-over-year growth from $4.2M to $5.2M"
      width={400}
      style={{ marginTop: 16, marginBottom: 16 }}
    />

    <Text style={{ fontSize: 18, fontWeight: 700, marginTop: 20 }}>Summary</Text>
    <Text style={{ fontSize: 11, lineHeight: 1.6 }}>
      Total revenue increased 23% year-over-year, driven by enterprise contract
      expansion and new customer acquisition in the healthcare vertical.
    </Text>
  </Page>
</Document>

When pdfUa is set, Forme automatically generates a complete structure tree with tagged content, sets the document language, assigns tab order based on document structure, creates a role map for custom element types, tags decorative elements as artifacts so screen readers skip them, and writes XMP metadata with pdfuaid:part=1.

Images without an alt prop are marked as decorative artifacts. Always provide alt text for meaningful images.

PDF/A archival

PDF/A is the ISO 19005 standard for long-term document preservation. It guarantees that a PDF will render identically decades from now, regardless of software or operating system. Legal archives, government records, financial audits, and healthcare documentation all require it.

<Document pdfa="2b" title="Service Agreement" author="Legal Dept">
  <Page size="A4" margin={54}>
    <Text style={{ fontSize: 20, fontWeight: 700 }}>Service Agreement</Text>
    <Text style={{ fontSize: 11, marginTop: 12, lineHeight: 1.6 }}>
      This agreement is entered into as of March 29, 2026...
    </Text>
  </Page>
</Document>

Forme supports two conformance levels:

  • PDF/A-2b ("2b") -- Visual preservation. Guarantees the document renders identically. Most common for archival.
  • PDF/A-2a ("2a") -- Visual preservation plus full tagged structure. Forces the same tagging requirements as PDF/UA. Use when both archival and accessibility are required.

PDF/A-3b (embedded file attachments) is on the roadmap but not yet available.

When pdfa is set, Forme automatically embeds all fonts, includes an sRGB output intent ICC profile, writes XMP metadata, adds a document ID, and strips any features that violate the spec. The output passes veraPDF out of the box.

For documents that need both accessibility and archival, combine both props:

<Document pdfUa pdfa="2b" title="Accessible Archival Report" lang="en">
  {/* ... */}
</Document>

Digital signatures

Digital signatures prove that a document has not been changed since signing and verify the signer's identity. Forme uses PKCS#7 (CMS) detached signatures with X.509 certificates, implemented in pure Rust:

import { readFileSync } from 'fs';
import { Document, Page, Text } from '@formepdf/react';
import { renderDocument } from '@formepdf/core';

const certificatePem = readFileSync('./cert.pem', 'utf-8');
const privateKeyPem = readFileSync('./key.pem', 'utf-8');

const pdfBytes = await renderDocument(
  <Document
    title="Signed Agreement"
    signature={{
      certificatePem,
      privateKeyPem,
      reason: 'Contract execution',
      location: 'San Francisco, CA',
      contact: 'legal@acme.com',
    }}
  >
    <Page size="Letter" margin={54}>
      <Text style={{ fontSize: 20, fontWeight: 700 }}>Signed Service Agreement</Text>
      <Text style={{ fontSize: 11, marginTop: 12, lineHeight: 1.6 }}>
        This document has been digitally signed and any modification will
        invalidate the signature.
      </Text>
    </Page>
  </Document>
);

The signature is applied at render time. The output PDF shows a signature panel in Adobe Reader and other viewers that support PKCS#7 validation.

For workflows where the PDF is rendered first and signed later, the hosted API exposes a /v1/sign endpoint:

curl -X POST https://api.formepdf.com/v1/sign \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d "{
    \"pdf\": \"$(base64 -w0 unsigned.pdf)\",
    \"certificatePem\": \"$(cat cert.pem)\",
    \"privateKeyPem\": \"$(cat key.pem)\",
    \"reason\": \"Approved\"
  }" \
  --output signed.pdf

No breaking changes

Every feature in 0.8.0 is opt-in. If you do not use pdfUa, pdfa, signature, or the form components, your existing templates produce byte-identical output. The render pipeline, layout engine, and component API are unchanged.

SDK parity

All 0.8.0 features are available across SDKs: TypeScript (@formepdf/react + @formepdf/core), the hosted API, and the Rust engine crate (forme-pdf). Python and Go SDKs support form fields and flattening via the hosted API.

Install

npm install @formepdf/react@0.8.0 @formepdf/core@0.8.0

If you are using the CLI for local development:

npm install @formepdf/cli@0.8.0

The hosted API at api.formepdf.com already supports all 0.8.0 features. Pass the new props in your template and they work immediately.

Docs