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
4 changes: 2 additions & 2 deletions .github/workflows/browser-starter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Browser starter
on:
push:
branches: [main]
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/browser-starter.yml']
pull_request:
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/browser-starter.yml']
workflow_dispatch:

permissions:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/react-starter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: React starter
on:
push:
branches: [main]
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/react-starter.yml']
pull_request:
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/react-starter.yml']
workflow_dispatch:

permissions:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/vue-starter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Vue starter
on:
push:
branches: [main]
paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/vue-starter.yml']
paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/vue-starter.yml']
pull_request:
paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/vue-starter.yml']
paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/vue-starter.yml']
workflow_dispatch:

permissions:
Expand Down
4 changes: 2 additions & 2 deletions examples/browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ explains CSP, custom assets, structured errors, and browser worker lifecycle lim

## Versions and verification

The lockfile pins Fullbleed npm 0.3.1 / engine 2.5.10 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.1).
The lockfile pins Fullbleed npm 0.3.2 / engine 2.5.11 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.2).
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
Expand Down
8 changes: 4 additions & 4 deletions examples/browser/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"build": "npm run assets && vite build",
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": { "fullbleed": "0.3.1" },
"dependencies": { "fullbleed": "0.3.2" },
"devDependencies": { "vite": "8.3.2" }
}
4 changes: 2 additions & 2 deletions examples/nextjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Open `http://127.0.0.1:3000` and select **Download sample PDF**. The route
IDs return 404. Use `npm run dev` while editing the app. `PORT` changes the port;
`FULLBLEED_HOST` changes the production server's loopback binding.

The lockfile pins Next.js 16.3.8, React 19.3.0 and Fullbleed Node 0.3.1
(engine 2.5.10) from npm. The registry package is byte-identical to the
The lockfile pins Next.js 16.3.8, React 19.3.0 and Fullbleed Node 0.3.2
(engine 2.5.11) from npm. The registry package is byte-identical to the
verified GitHub release tarball.
Installing this starter needs no Python, Rust, system fonts or browser renderer.

Expand Down
8 changes: 4 additions & 4 deletions examples/nextjs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"verify": "node tools/verify.mjs"
},
"dependencies": {
"fullbleed": "0.3.1",
"fullbleed": "0.3.2",
"next": "16.3.8",
"react": "19.3.0",
"react-dom": "19.3.0"
Expand Down
4 changes: 2 additions & 2 deletions examples/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ renders supplied static HTML/CSS rather than capturing the React page.
## Reuse the hook

Copy `src/usePdfPreview.ts` into your client application, install
`fullbleed@0.3.1`, and copy the runtime with
`fullbleed@0.3.2`, and copy the runtime with
`npx fullbleed-browser-assets public/fullbleed`. Keep the document input stable
with `useMemo` so ordinary component updates do not schedule another render:

Expand Down Expand Up @@ -94,7 +94,7 @@ WebAssembly, and Web Crypto are required.

## Versions and verification

The lockfile pins Fullbleed 0.3.1 / engine 2.5.10, React 19.3.0, TypeScript 7.0.2,
The lockfile pins Fullbleed 0.3.2 / engine 2.5.11, React 19.3.0, TypeScript 7.0.2,
and Vite 8.3.2. No Fullbleed engine or package release is needed for this example.

The repository's `React starter` workflow builds the locked project and checks
Expand Down
8 changes: 4 additions & 4 deletions examples/react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
"fullbleed": "0.3.1",
"fullbleed": "0.3.2",
"react": "19.3.0",
"react-dom": "19.3.0"
},
Expand Down
4 changes: 2 additions & 2 deletions examples/vue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Use [supported print CSS](https://docs.fullbleed.dev/css-coverage/).
## Reuse the composable

Copy `src/usePdfPreview.ts` into your client application, install
`fullbleed@0.3.1`, and copy the runtime with
`fullbleed@0.3.2`, and copy the runtime with
`npx fullbleed-browser-assets public/fullbleed`:

```vue
Expand Down Expand Up @@ -106,7 +106,7 @@ WebAssembly, and Web Crypto are required.

## Versions and verification

The lockfile pins Fullbleed 0.3.1 / engine 2.5.10, Vue 3.5.43, TypeScript 6.0.3,
The lockfile pins Fullbleed 0.3.2 / engine 2.5.11, Vue 3.5.43, TypeScript 6.0.3,
and Vite 8.3.2. TypeScript 6 is used because the pinned `vue-tsc` still needs
TypeScript's JavaScript compiler entry; TypeScript 7 does not expose it.

Expand Down
8 changes: 4 additions & 4 deletions examples/vue/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
"fullbleed": "0.3.1",
"fullbleed": "0.3.2",
"vue": "3.5.43"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion tools/check-browser-starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
from starter_standard_fonts import verify_edited_preview

parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist',type=Path,required=True)
Expand Down Expand Up @@ -88,11 +89,12 @@ def download(name,pages,text):
check('customized bytes differ from the default invoice',documents[-1]['sha256']!=documents[0]['sha256'])
page.locator('#template-editor summary').click()
page.locator('#html-source').fill('<h1>Edited template</h1><p>{{customer}} / {{reference}}</p><script>window.documentScriptExecuted = true;</script>')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
page.locator('#render-source').click();ready()
path=download('edited',1,['Edited template','Birch & <Briar>','TEST-2042'])
check('edited print CSS changes the PDF page size',abs(float(PdfReader(path).pages[0].mediabox.width)-419.52756)<1)
check('document script does not run on the page',page.evaluate('window.documentScriptExecuted !== true'))
verify_edited_preview(page, path, out, check)
page.screenshot(path=str(out/'edited-template.png'),full_page=True)
page.locator('#kind').select_option('report');page.locator('#generate').click();ready()
download('report',3,['COMMON','1,240','420,000.00'])
Expand Down
4 changes: 3 additions & 1 deletion tools/check-react-starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
from starter_standard_fonts import verify_edited_preview

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist', type=Path, required=True)
Expand Down Expand Up @@ -174,11 +175,12 @@ def download(name, count, text):

page.locator('#template-editor summary').click()
page.locator('#html-source').fill('<h1>React template</h1><p>{{customer}} / {{reference}}</p><script>window.documentScriptExecuted = true;</script>')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
ready()
edited = download('edited-template', 1, ['React template', 'Birch & <Briar>', 'REACT-2042'])
check('edited print CSS changes the page size', abs(float(PdfReader(edited).pages[0].mediabox.width) - 419.52756) < 1)
check('document scripts are not executed in the app', page.evaluate('window.documentScriptExecuted !== true'))
verify_edited_preview(page, edited, out, check)
page.screenshot(path=str(out / 'edited-template.png'), full_page=True)
page.locator('#kind').select_option('report')
ready()
Expand Down
4 changes: 3 additions & 1 deletion tools/check-vue-starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
from starter_standard_fonts import verify_edited_preview

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist', type=Path, required=True)
Expand Down Expand Up @@ -184,11 +185,12 @@ def previews_match(name, count):

page.locator('#template-editor summary').click()
page.locator('#html-source').fill('<h1>Vue template</h1><p>{{customer}} / {{reference}}</p><script>window.documentScriptExecuted = true;</script>')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
ready()
edited = download('edited-template', 1, ['Vue template', 'Birch & <Briar>', 'VUE-2042'])
check('edited print CSS changes the page size', abs(float(PdfReader(edited).pages[0].mediabox.width) - 419.52756) < 1)
check('document scripts are not executed in the app', page.evaluate('window.documentScriptExecuted !== true'))
verify_edited_preview(page, edited, out, check)
page.screenshot(path=str(out / 'edited-template.png'), full_page=True)
page.locator('#kind').select_option('report')
ready()
Expand Down
37 changes: 37 additions & 0 deletions tools/starter_standard_fonts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Check that a pasted Helvetica template has visible browser preview text."""
import base64
import hashlib
import json

from playwright.sync_api import expect
from pypdf import PdfReader


def verify_edited_preview(page, pdf_path, out, check):
pdf = PdfReader(pdf_path)
fonts = [item.get_object() for item in pdf.pages[0]['/Resources']['/Font'].values()]
names = sorted(str(font.get('/BaseFont')) for font in fonts)
check('edited template uses unembedded standard Helvetica faces',
bool(fonts) and all(name in ['/Helvetica', '/Helvetica-Bold'] for name in names)
and all('/FontDescriptor' not in font for font in fonts))
image = page.locator('#preview img')
expect(image).to_have_count(1)
result = image.evaluate('''async image => {
await image.decode();
const canvas = document.createElement('canvas');
canvas.width = image.naturalWidth; canvas.height = image.naturalHeight;
const context = canvas.getContext('2d'); context.drawImage(image, 0, 0);
const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data;
let ink = 0;
for (let i = 0; i < pixels.length; i += 4)
if (pixels[i + 3] > 0 && Math.min(pixels[i], pixels[i + 1], pixels[i + 2]) < 245) ink++;
return {width: canvas.width, height: canvas.height, inkPixels: ink,
png: canvas.toDataURL('image/png').split(',')[1]};
}''')
png = base64.b64decode(result.pop('png'))
(out / 'edited-preview-decoded.png').write_bytes(png)
result.update(fonts=names, pdfSha256=hashlib.sha256(pdf_path.read_bytes()).hexdigest(),
decodedPreviewSha256=hashlib.sha256(png).hexdigest())
(out / 'edited-preview-verification.json').write_text(
json.dumps(result, indent=2) + '\n', encoding='utf-8', newline='\n')
check('edited standard-font preview contains visible text', result['inkPixels'] > 500)
Loading
Loading