Skip to content

feat(builder): add dedicated PDF design mode#110

Open
lattln wants to merge 7 commits into
mainfrom
70-pdf-viewer-add-dedicated-pdf-view-mode
Open

feat(builder): add dedicated PDF design mode#110
lattln wants to merge 7 commits into
mainfrom
70-pdf-viewer-add-dedicated-pdf-view-mode

Conversation

@lattln

@lattln lattln commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

This branch adds a dedicated PDF workflow to eSheet. It introduces a new @esheet/pdf package for generating fillable AcroForm PDFs from eSheet form definitions, visually editing their layouts in the builder, importing existing PDF forms, completing them through EsheetRenderer, and exporting completed or enhanced PDFs.

The original work adds a standalone builder PDF mode for generating and designing PDFs from eSheet definitions. The current uncommitted work extends that foundation into a broader PDF representation: Build supports authoring and editing PDF-backed forms, while Preview and EsheetRenderer support filling imported or generated PDFs using normal eSheet response state.

At a high level, this provides a PDF-oriented output and round-trip path alongside the existing build, code, and preview experiences:

  1. Generate a fillable PDF from an eSheet definition.
  2. Visually inspect and adjust mapped AcroForm fields.
  3. Add mapped fields directly from the PDF designer.
  4. Download the enhanced PDF with its eSheet mapping manifest.
  5. Import an existing AcroForm PDF into eSheet.
  6. Render, fill, and export the PDF through EsheetRenderer.

What Changed

Added @esheet/pdf

A new pdf library provides generation, import, mapping, and layout-editing utilities.

The package provides:

  • generatePdf(definition, options) for creating fillable PDFs from FormDefinition objects.
  • importPdf(source) for importing an existing Uint8Array, ArrayBuffer, or Blob PDF into an eSheet form session.
  • applyPdfFieldLayout(source, mappings, options) for writing edited AcroForm field rectangles, metadata, and responses back into a PDF.
  • Public types for generated output, imported sessions, field mappings, warnings, page sizes, response maps, import sources, and layout options.
  • A README documenting initial field support, deterministic mappings, limitations, and layout editing.

PDF Generation

generatePdf converts eSheet definitions into fillable AcroForm PDFs and returns deterministic PdfFieldMapping records. A mapping connects an eSheet field ID to its PDF field name, AcroForm kind, page number, rectangle, and, where relevant, radio option ID.

The generator supports:

  • text and longtext as AcroForm text fields.
  • boolean as a checkbox.
  • radio, rating, and slider as radio groups.
  • check and multiselectdropdown as checkbox groups.
  • dropdown as an AcroForm dropdown.
  • multitext as one text field per option.
  • display, section, and pages as static PDF content.
  • Unsupported field types as static fallback content with warnings.

It also handles:

  • Pagination and page footers.
  • Deterministic PDF field naming.
  • Custom field names stored in _sourceData.esheet.pdf.fieldName.
  • Prefilled eSheet responses.
  • Unsupported-character normalization.
  • Warning collection for fallback or unsupported behavior.

PDF Import and Round-Tripping

importPdf imports supported AcroForm controls into an eSheet FormDefinition and preserves existing PDF values as eSheet response state.

Importing a PDF:

  • Produces PdfFieldMapping records for each supported widget.
  • Retains original PDF field names, page positions, dimensions, control types, and radio option associations.
  • Maps radio widgets to individual eSheet option IDs, enabling grouped radio controls to round-trip correctly.
  • Restores an embedded eSheet manifest when opening a PDF previously enhanced by eSheet, rather than inferring fields again.
  • Emits warnings for encrypted PDFs, malformed or unsupported controls, missing labels, and PDFs with no supported fields.

This enables eSheet to begin with an existing PDF template, not just PDFs generated from an eSheet definition.

Dedicated PDF Builder Workflow

The committed branch adds a fourth builder mode, pdf, alongside build, code, and preview.

The builder header adds a PDF tab and PdfIcon. Selecting it renders the PDF workflow instead of the normal builder canvas, code editor, or preview layout.

The current local work refines this approach: rather than retaining PDF as a separate top-level activity, PDF becomes a representation within the existing Build and Preview workflows:

  • Build provides PDF authoring and layout editing.
  • Preview provides a fillable PDF experience without authoring tools.

This preserves the existing builder activity model while making authoring and answer entry distinct.

PDF Designer UI

The PDF designer generates or imports a PDF and renders pages through pdfjs-dist.

It includes:

  • Canvas-rendered PDF pages.
  • Loading and error states for generation, import, and canvas rendering.
  • Page thumbnails for multi-page PDFs.
  • Active-page tracking while scrolling.
  • Smooth thumbnail and page navigation.
  • Zoom controls.
  • An overlay layer for mapped AcroForm controls.
  • Selection state for mapped fields.
  • A right-side inspector with field type, page, position, width, and height.
  • Reset-layout functionality.
  • Download/export functionality that persists edited mappings into the final PDF.

Interactive Field Layout Editing

PdfCanvasPage.tsx overlays generated or imported mappings above the rendered PDF canvas.

Users can:

  • Select mapped fields.
  • Drag controls to reposition them.
  • Resize controls through resize handles.
  • Keep edited rectangles constrained within the PDF page.
  • Preview mapped text, dropdown, checkbox, and radio response state.
  • Inspect grouped radio controls correctly.
  • Reset moved or resized fields to generated mappings.

Edits are stored as updated PdfFieldMapping rectangles. Export then applies those mappings to the PDF.

Custom PDF Fields

The PDF designer supports editor-added fields on the active page.

Users can add and manage:

  • Text fields.
  • Checkboxes.
  • Radio groups and options.
  • Dropdowns and options.

The builder supports:

  • Adding fields from the PDF surface.
  • Duplicating and deleting editor-added fields.
  • Editing question labels.
  • Editing required state.
  • Editing custom PDF field names.
  • Adding, editing, and removing dropdown options.
  • Adding, editing, and removing radio-group options.
  • Moving and resizing newly created controls.

New mappings are tracked separately from generated/imported fields and passed to applyPdfFieldLayout so the exported PDF creates the corresponding AcroForm widgets.

Applying Layout, Metadata, and Responses

applyPdfFieldLayout now updates both mapping geometry and PDF form content.

It can:

  • Move and resize existing mapped AcroForm widgets.
  • Create AcroForm widgets for editor-added fields.
  • Create all widgets required for newly authored radio groups.
  • Add dropdown choice values.
  • Write current eSheet responses into text, checkbox, radio, and dropdown controls.
  • Write eSheet question text into PDF alternate field names.
  • Synchronize required state to AcroForm fields.
  • Embed the eSheet form definition and mappings as a manifest for future import and round-trip restoration.

PDF Representation in EsheetRenderer

EsheetRenderer now supports PDF representation in addition to standard eSheet rendering.

<EsheetRenderer representation="pdf" pdfSource={file} />

The renderer props are now a discriminated union:

  • Standard eSheet rendering uses formDataInput, with representation omitted or set to 'esheet'.
  • PDF rendering uses representation: 'pdf' and requires pdfSource.

In PDF representation, the renderer:

  • Imports the source PDF asynchronously.
  • Initializes the form store from imported PDF definitions and responses.
  • Applies initialResponses.
  • Renders PDF-specific loading and import-error states.
  • Renders source pages through PDF.js.
  • Overlays fillable eSheet controls at mapped AcroForm positions.
  • Persists user input through FormStore.setResponse.

The initial overlay supports:

  • Text fields.
  • Checkboxes.
  • Radio groups.
  • Single-select dropdowns.

PDF Export From EsheetRenderer

EsheetRendererHandle now exposes:

exportPdf(): Promise<Uint8Array>

For imported PDFs, this writes current responses and mappings into the original source PDF. For ordinary eSheet rendering, it generates a PDF from the eSheet definition and applies the resulting mappings before returning PDF bytes.

This creates an end-to-end response flow:

  1. Import or generate a PDF.
  2. Fill controls through eSheet state.
  3. Export a completed AcroForm PDF.

PDF Thumbnails

PdfPageThumbnail renders PDF.js canvas thumbnails for multi-page documents.

The thumbnail rail:

  • Appears only when the PDF has multiple pages.
  • Highlights the active page.
  • Scrolls the main viewer to the selected page.
  • Keeps navigation synchronized with the visible PDF page.

HTML Field Iframe Sizing and Sandboxing

HtmlField was updated so embedded HTML can report its height from inside the sandboxed iframe through postMessage.

Changes include:

  • The iframe sandbox changes from allow-same-origin to allow-scripts.
  • The injected script can report height without giving embedded content access to parent DOM, cookies, or local storage.
  • The parent listens for esheet-iframe-height messages and adjusts preview height.
  • The edit-mode height slider now has a distinct ID and correctly associated label.

Demo Application

The demo application now includes a renderer PDF page.

Users can:

  1. Upload an AcroForm PDF.
  2. Render and complete supported controls through EsheetRenderer representation="pdf".
  3. Download the completed PDF using exportPdf().

This demonstrates the import, fill, and export flow independently from the builder.

Tests Added

PDF tests cover:

  • Creating real AcroForm fields.
  • Prefilling generated PDF fields from eSheet responses.
  • Multiline text behavior.
  • Checkbox, radio, checkbox-group, and dropdown selection behavior.
  • Deterministic field mappings.
  • Pagination for long forms.
  • Unsupported-field warnings.
  • Persisting moved/resized widgets.
  • Creating newly added AcroForm fields through layout application.
  • Preserving custom PDF field names.
  • Writing current responses into mapped AcroForm fields.
  • Creating all radio widgets for added radio groups.
  • Importing AcroForm PDFs into eSheet definitions and response state.
  • Preserving imported radio-widget option mappings.
  • Restoring enhanced PDFs through the embedded eSheet manifest.
  • Adding dropdown choices to enhanced source PDFs.
  • Writing labels and required state to existing mapped PDF fields.

Builder tests cover:

  • Switching to the committed PDF builder mode.
  • Separation between PDF authoring and PDF preview behavior.
  • Creating fields from the PDF designer.
  • Creating, editing, and removing radio-group options.
  • Creating and editing dropdown options.
  • Editing mappings and field metadata.

Renderer tests cover:

  • Initializing PDF representation.
  • Importing source PDFs into renderer state.
  • Applying PDF-specific initialization without competing with normal schema initialization.

Package and Build Configuration

The branch wires PDF support into the Nx workspace:

  • Adds pdf as an Nx project.
  • Adds exports for @esheet/pdf.
  • Adds TypeScript references from root and builder configurations.
  • Updates module boundaries to permit builder/private projects to depend on scope:pdf.
  • Adds scope:pdf dependency constraints.
  • Adds @esheet/pdf, pdf-lib, and pdfjs-dist dependencies where required.
  • Updates builder Vite configuration for PDF worker URL imports and @esheet/pdf externalization.
  • Adds Vite and test configuration for the PDF package.
  • Updates renderer configuration to support the PDF.js worker asset and PDF representation types.

@lattln lattln linked an issue Jul 17, 2026 that may be closed by this pull request
7 tasks
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying esheet with  Cloudflare Pages  Cloudflare Pages

Latest commit: b18bfe3
Status: ✅  Deploy successful!
Preview URL: https://6b72f9e8.esheet.pages.dev
Branch Preview URL: https://70-pdf-viewer-add-dedicated.esheet.pages.dev

View logs

lattln added 4 commits July 22, 2026 17:06
- Add @esheet/pdf for generating, importing, enhancing, and round-tripping
  AcroForm PDFs with eSheet definitions, mappings, metadata, and responses
- Add builder PDF canvas with source import, page navigation, field placement,
  resizing, duplication, deletion, required state, field names, and options
- Support adding text, checkbox, radio, and dropdown fields directly on PDFs
- Separate PDF authoring in Build from response entry in Preview
- Add EsheetRenderer PDF representation with async PDF import, PDF.js canvas
  overlays, response synchronization, and exportPdf() handle API
- Export eSheet responses, labels, required state, dropdown options, and
  complete radio widget groups back to mapped AcroForm fields
- Preserve custom PDF field names and radio option mappings during round-trips
- Add renderer PDF demo route with upload and completed-PDF download flow
- Expand focused PDF import/export, builder, and renderer test coverage
- Wire renderer PDF dependencies and worker-compatible TypeScript configuration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Import fillable PDFs as eSheet questionnaires and sync with PDF viewer Context PDF Viewer: Add dedicated PDF view mode

1 participant