Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/browser-starter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Browser starter

on:
push:
branches: [main]
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
pull_request:
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
workflow_dispatch:

permissions:
contents: read

jobs:
browser:
name: Browser starter (${{ matrix.browser }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
browser: [chrome, firefox, webkit]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: npm
cache-dependency-path: examples/browser/package-lock.json
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: npm ci --ignore-scripts --no-audit --no-fund
working-directory: examples/browser
- run: npm run build
working-directory: examples/browser
- run: python -m pip install -r tools/browser-requirements.txt
- run: python -m playwright install --with-deps ${{ matrix.browser }}
- run: python tools/check-browser-starter.py --dist examples/browser/dist --out output/starter-${{ matrix.browser }} --browser ${{ matrix.browser }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: starter-${{ matrix.browser }}
path: output/starter-${{ matrix.browser }}/
retention-days: 14
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ data; connect your own authenticated record lookup when adapting it.

## Use it in a browser

Start with the [complete browser application](examples/browser): editable HTML/CSS,
invoice and report designs, previews, cancellation, and PDF downloads.


Copy the browser runtime into your application's static directory:

```sh
Expand Down
3 changes: 3 additions & 0 deletions docs/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ dedicated Web Worker. It uses the same pinned 2.5.8 engine and bundled fonts as
the Node entry. The result is a `Uint8Array`, suitable for a PDF `Blob`, plus
optional PNG previews. No PDF server or account is required.

The [runnable browser starter](../examples/browser) includes editable templates,
previews, downloads, and production build settings.

## Copy the runtime into your site

Install the package in your web project, then copy its browser files into the
Expand Down
4 changes: 4 additions & 0 deletions examples/browser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
public/fullbleed/
output/
21 changes: 21 additions & 0 deletions examples/browser/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Fullbleed Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
69 changes: 69 additions & 0 deletions examples/browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Fullbleed browser starter

A complete static web application: editable HTML/CSS templates, a designed invoice
and three-page report, local PDF/PNG rendering, cancellation, and downloads.
The sample records and amounts are fictional. MIT licensed.

## Run

Use Node.js 22.12 or newer for installation and builds:

```sh
npm ci
npm run dev
```

Open the localhost URL printed by Vite. The first preview loads the engine and
fonts from that server. Each render runs in a fresh Web Worker; the starter does
not upload document inputs to a PDF service. Nothing persists after a reload.

## Make your template

Change the invoice fields, or open **Edit the HTML & CSS** below the preview.
Edits in that editor last until reload. To keep them in the project, edit:

- `src/invoice.html` and `src/invoice.css`
- `src/report.html` and `src/report.css`

Invoice HTML uses `{{customer}}` and `{{reference}}`; print CSS uses `{{ink}}`.
`src/main.js` replaces these values, escapes text, bounds rendering, handles
errors, and releases old PDF/preview Blob URLs. The sample line items, totals,
addresses, and dates are fixed. Add your own structured data and calculations
when adapting it for real documents.

Use [supported Fullbleed print CSS](https://docs.fullbleed.dev/css-coverage/).
Document scripts do not run. Supply custom image/font bytes through the SDK's
`assets` and `fonts` options; document URLs do not trigger browser fetches.

## Build and deploy

```sh
npm run build
npm run preview
```

Deploy the whole `dist/` directory to a static host over HTTPS. It needs no Node
server. The build uses relative URLs and supports a nested deployment path.
The asset-copy step verifies and copies the installed client, worker, engine,
fonts, build manifest, and notices. Keep those files together when deploying;
repeat the build after a package upgrade.

Browsers need Web Workers, WebAssembly, and Web Crypto. Local development works
on localhost; production needs HTTPS. The starter renders one job at a time,
with a 30-second deadline and 20-page limit. It exposes ordinary PDF generation
and previews, not PDF/A, PDF/UA, PDF/X, or VDP options.

The [browser API and hosting guide](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/browser.md)
explains CSP, custom assets, structured errors, and browser worker lifecycle limits.

## Versions and verification

The lockfile pins Fullbleed npm 0.3.0 / engine 2.5.8 and Vite 8.3.2.
The browser SDK is covered by the [versioned release evidence](https://github.com/fullbleed-engine/fullbleed-node/releases/tag/v0.3.0).
The repository's starter check builds this app, serves it below `/nested/demo/`,
downloads actual PDFs, checks their text and page size, exercises editing and
cancellation, and captures desktop/mobile screenshots. Playwright WebKit is not
branded Safari certification.

See the [public walkthrough](https://docs.fullbleed.dev/guides/browser-pdf/) or
the [source repository](https://github.com/fullbleed-engine/fullbleed-node/tree/main/examples/browser).
64 changes: 64 additions & 0 deletions examples/browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Generate a designed PDF in your browser with Fullbleed. A runnable JavaScript example with local rendering, previews and downloads.">
<title>Browser PDF starter · Fullbleed</title>
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
</head>
<body>
<header class="site-header">
<a class="wordmark" href="https://docs.fullbleed.dev/" aria-label="Fullbleed documentation">fullbleed<span>.</span></a>
<span class="header-note">BROWSER STARTER / MIT</span>
<a class="docs-link" href="https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/browser.md">API guide <span aria-hidden="true">↗</span></a>
</header>
<main>
<section class="intro">
<p class="eyebrow"><span aria-hidden="true"></span> HTML + CSS → PDF</p>
<h1>A proper document.<br>Made in your browser.</h1>
<p class="lede">Change the details. Choose a color. Download the real PDF.<br class="wide-only"> Fullbleed handles the print layout locally, in a worker.</p>
</section>
<div class="workspace">
<section class="controls" aria-labelledby="design-heading">
<div class="panel-heading"><span class="step">01</span><h2 id="design-heading">Make it yours</h2></div>
<form id="design-form">
<fieldset id="fields">
<legend class="sr-only">Document details</legend>
<label for="kind">Start with a design</label>
<select id="kind" name="kind"><option value="invoice">Studio invoice</option><option value="report">Community report</option></select>
<div id="invoice-fields">
<label for="customer">Customer name</label>
<input id="customer" name="customer" value="Maple &amp; Finch" maxlength="80" required autocomplete="off">
<label for="reference">Invoice reference</label>
<input id="reference" name="reference" value="NS-1042" maxlength="24" required autocomplete="off">
<label for="accent">Ink color</label>
<div class="color-row"><input id="accent" name="accent" type="color" value="#17382e"><span id="color-value">#17382E</span><span class="color-hint">Your print palette</span></div>
</div>
</fieldset>
<p class="sample-note" id="sample-note">Fictional invoice. Items and amounts are fixed for this example.</p>
<div class="actions"><button id="generate" type="submit">Generate PDF <span aria-hidden="true">→</span></button><button id="cancel" class="secondary" type="button" hidden>Cancel</button></div>
</form>
<p id="status" role="status" aria-live="polite">Preparing the first preview…</p>
<div class="local-note"><span class="local-dot" aria-hidden="true"></span><p><strong>Rendered here.</strong> The app loads its engine and fonts from this site. Document inputs are not uploaded by this starter.</p></div>
<div class="source-note"><h3>Your HTML. Your print styles.</h3><p>Open the template editor below. Paste your own HTML and CSS, then generate a new PDF.</p><a href="https://docs.fullbleed.dev/css-coverage/">Explore supported CSS <span aria-hidden="true">↗</span></a></div>
</section>
<section class="preview-panel" aria-labelledby="preview-heading">
<div class="preview-toolbar"><div><span class="step">02</span><h2 id="preview-heading">The actual PDF</h2><span id="page-count"></span></div><button id="download" type="button" disabled>Download PDF <span aria-hidden="true">↓</span></button></div>
<div id="preview" class="preview-area" aria-busy="true"><div class="placeholder"><span class="paper-icon" aria-hidden="true"></span><p>Your document will appear here.</p></div></div>
</section>
</div>
<details class="template-editor" id="template-editor">
<summary>Edit the HTML &amp; CSS</summary>
<p class="editor-note">Make the template your own. Invoice fields replace <code>{{customer}}</code> and <code>{{reference}}</code>; <code>{{ink}}</code> applies the color in CSS. Edits stay in this tab until you reload. Fullbleed uses static print HTML and CSS; document scripts do not run.</p>
<fieldset id="editor-fields"><legend class="sr-only">Template source</legend><div class="editor-grid">
<div><label for="html-source">HTML</label><textarea id="html-source" spellcheck="false" aria-label="Document HTML"></textarea></div>
<div><label for="css-source">Print CSS</label><textarea id="css-source" spellcheck="false" aria-label="Document print CSS"></textarea></div>
</div></fieldset>
<div class="editor-actions"><button type="button" id="render-source">Generate edited PDF</button><button type="button" class="secondary" id="reset-source">Reset this template</button></div>
</details>
<footer><p>Static HTML. Deterministic print layout. Open source.</p><a href="https://docs.fullbleed.dev/">Built with Fullbleed <span aria-hidden="true">↗</span></a></footer>
</main>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Loading
Loading