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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Retain the pinned upstream metrics and license bytes exactly.
tools/data/base14/* -text -whitespace
src/base14_metrics_data.rs text eol=lf
# Preserve PDF stream bytes and xref offsets; never normalize them as text.
*.pdf binary
15 changes: 14 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: CI

env:
FULLBLEED_CI_VERSION: "2.5.5"
FULLBLEED_CI_VERSION: "2.5.6"
RUST_MIN_STACK: "4194304"

on:
Expand Down Expand Up @@ -144,6 +144,19 @@ jobs:
- name: Verify finalized PDF gradient previews
run: python -I tools/smoke_gradient_preview.py --out target/gradient-preview

- name: Verify single-copy styled PDF text with an independent reader
run: |
python -m pip install pypdf==6.19.0
python -I tools/smoke_text_extraction.py --out target/text-extraction

- name: Retain styled PDF text and preview evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: text-extraction-${{ runner.os }}
path: target/text-extraction
if-no-files-found: warn

- name: Retain gradient PDFs and preview evidence
if: always()
uses: actions/upload-artifact@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-crates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
description: "Annotated release tag whose exact source commit will be published"
required: true
type: string
default: "v2.5.5"
default: "v2.5.6"
publish:
description: "Publish after all validation gates pass"
required: true
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ jobs:
- name: Verify finalized PDF gradient previews
run: python -I tools/smoke_gradient_preview.py --out target/gradient-preview

- name: Verify single-copy styled PDF text with an independent reader
run: |
python -m pip install pypdf==6.19.0
python -I tools/smoke_text_extraction.py --out target/text-extraction

- name: Retain styled PDF text and preview evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: release-text-extraction
path: target/text-extraction
if-no-files-found: warn

- name: Retain gradient PDFs and preview evidence
if: always()
uses: actions/upload-artifact@v7
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fullbleed"
version = "2.5.5"
version = "2.5.6"
edition = "2024"
rust-version = "1.85"
license = "MIT"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Path("invoice.pdf").write_bytes(pdf)

Open `invoice.pdf`. The [quickstart](https://docs.fullbleed.dev/getting-started/quickstart/) adds fonts, previews, and a complete project. The runnable source is in [`examples/quickstart/hello.py`](examples/quickstart/hello.py).

**Using Rust?** Start with the [native Rust quickstart](https://docs.fullbleed.dev/getting-started/rust/). The [`fullbleed` crate](https://crates.io/crates/fullbleed) exposes `FullBleed::builder()` and `render_to_buffer(...)`, with no Python requirement. The guide includes runnable invoice and report examples with explicit fonts and PNG previews, plus the [Rust API reference](https://docs.rs/fullbleed/2.5.5/fullbleed/).
**Using Rust?** Start with the [native Rust quickstart](https://docs.fullbleed.dev/getting-started/rust/). The [`fullbleed` crate](https://crates.io/crates/fullbleed) exposes `FullBleed::builder()` and `render_to_buffer(...)`, with no Python requirement. The guide includes runnable invoice and report examples with explicit fonts and PNG previews, plus the [Rust API reference](https://docs.rs/fullbleed/2.5.6/fullbleed/).

**Using Node.js?** The [Node.js quickstart](https://docs.fullbleed.dev/getting-started/node/) includes a downloadable designed invoice project. The separate [Node package](https://github.com/fullbleed-engine/fullbleed-node) bundles the engine as WebAssembly and includes fonts, ESM/CommonJS support, TypeScript declarations, and PNG previews. It requires Node 22+ and covers ordinary PDF generation.

Expand Down Expand Up @@ -230,7 +230,7 @@ python -m pip install fullbleed
From a local wheel:

```bash
python -m pip install C:\path\to\fullbleed-2.5.5-cp310-abi3-win_amd64.whl
python -m pip install C:\path\to\fullbleed-2.5.6-cp310-abi3-win_amd64.whl
```

From a source checkout with Rust installed, no Python build package is needed:
Expand Down
27 changes: 27 additions & 0 deletions ReleaseNotes.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# Fullbleed Release Notes - 2.5.6

Release date: October 3, 2026

## Keep synthesized bold text searchable once

PDFs using upright synthesized bold previously contained both visible Type 3
glyph text and an invisible logical text copy. Independent readers could extract
headings twice or interleave their letters. Visible glyphs now use shared vector
paint forms, leaving one logical text copy for search and extraction.

The finalized preview reader preserves the affine transform of those path forms
so outlines and strokes scale together. Selecting an unused fallback font after
a transformed element no longer causes an embedded-font profile to reject the
document. Fonts used by actual text still require embedding for those profiles.

Installed-wheel checks cover 36 styled fixtures across ordinary, tagged, and
PDF/UA-1-profile output, with retained PDF/PNG evidence and an independent text
reader in CI. Local comparisons use four text readers. These fixture checks do
not establish general renderer parity, screen-reader acceptance, or standards
conformance. The core gains no runtime dependency.

Affected PDF and finalized-preview hashes change. Review saved baselines before
updating them. See the [2.5.6 release runbook](docs/release/2.5.6-runbook.md).

---

# Fullbleed Release Notes - 2.5.5

Release date: October 2, 2026
Expand Down
2 changes: 1 addition & 1 deletion cli_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- GENERATED FILE: DO NOT EDIT. Run `python tools/generate_agent_contract.py`. -->
# CLI and Agent JSON Contract

Generated from the installed Fullbleed **2.5.5** runtime. The canonical machine artifact is `fullbleed-agent-contract.json`.
Generated from the installed Fullbleed **2.5.6** runtime. The canonical machine artifact is `fullbleed-agent-contract.json`.

## Authority and invocation

Expand Down
56 changes: 56 additions & 0 deletions docs/release/2.5.6-runbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Fullbleed 2.5.6 release runbook

This patch removes duplicate searchable text from upright synthesized bold and
keeps its glyph paint reusable. Publish from a clean annotated `v2.5.6` tag after
the checks below pass.

## Build and verify

Keep Cargo, Python, CI smoke metadata, release defaults, and generated agent
artifacts synchronized at 2.5.6. The optional MCP adapter remains 0.1.1 and the
audit crate remains 0.1.3. Historical example pins retain their verified versions.

Build and install a wheel in an isolated environment. Generate agent artifacts
using `tools/generate_agent_contract.py --python PATH --json`, rebuild and install
the final wheel, then run the generator with `--check`. Build the sdist from that
same source. Do not hand-edit generated agent artifacts.

- Run the synthetic-bold PDF writer and finalized-preview Rust tests, then the
full Rust suite with default and Python/SVG features.
- Run focused installed-native Python extraction, preview, compiled-table, and
profile tests; then run the full Python suite with
`FULLBLEED_TEST_INSTALLED_NATIVE=1`.
Include repeat rendering of fixed, reflow, and compact bindings so glyph
resources survive cached, multiple-record output.
- Install the test-only reader `pypdf==6.19.0` and run
`python -I tools/smoke_text_extraction.py --out target/text-extraction`.
Retain all 36 PDFs, source HTML/CSS, direct/finalized PNGs, text, coverage
measurements, and hashes. The public 2.5.5 quickstart must fail the single-copy
check. A candidate that painted oversized strokes was rejected by the visual
coverage control; do not weaken that check to accept boxes or blank glyphs.
- Extract the same PDFs with PyMuPDF, pdfminer.six, and PDFium. Compare normalized
text with the authored expected values; retain reader versions and results.
- Compare actual PDFs with public 2.5.5 fixtures using an independent renderer.
Compare only identical HTML/CSS inputs. Inspect native previews, including
Unicode, italic, opacity, shadows, rotated, scaled, and sheared bold.
- Verify that previews of retained earlier-version PDFs remain unchanged.
- Review golden changes. The Acme invoice loses its duplicate Type 3 text and
needs a new PDF hash; its direct PNG baseline stays unchanged. Compare the
before/after PDFs with an independent renderer before accepting the hash.
- Run installed-package smoke, agent-contract checks, metadata/license checks,
deterministic goldens, CSS status, and the bundled Skill publishing dry run.
- Require Linux and Windows CI, the full platform-wheel/source-distribution
release workflow, and the existing print-profile checks.

## Publish and verify

Use the configured PyPI trusted publisher and crates.io workflow. Retain the
source tag, successful CI, public registry metadata, and artifact hashes. Install
the public wheel in a fresh environment and repeat the styled extraction check.
Update the optional Node package to the published engine, verify the packed npm
tarball, and repeat the original Node invoice reproduction before promoting it.

These checks establish behavior for the retained fixtures. They do not establish
general PDF renderer parity, screen-reader acceptance, or profile conformance.
The dedicated PDF/VT validator remains unconfigured; its skipped check does not
establish independent conformance. PDF/PNG hashes can change for affected output.
Loading
Loading