diff --git a/.gitattributes b/.gitattributes index 4f75624..8c6f6be 100644 --- a/.gitattributes +++ b/.gitattributes @@ -182,3 +182,9 @@ docs/assets/cloudflare-starter/** -text docs/assets/cloudflare-starter/*.pdf binary docs/assets/cloudflare-starter/*.png binary docs/assets/cloudflare-starter/*.zip binary + +# Keep the linked-report downloads and verification bytes immutable. +docs/assets/pdf-links-alpha14/** -text +docs/assets/pdf-links-alpha14/*.pdf binary +docs/assets/pdf-links-alpha14/*.png binary +docs/assets/pdf-links-alpha14/*.zip binary diff --git a/docs/assets/pdf-links-alpha14/linked-report.pdf b/docs/assets/pdf-links-alpha14/linked-report.pdf new file mode 100644 index 0000000..444394b Binary files /dev/null and b/docs/assets/pdf-links-alpha14/linked-report.pdf differ diff --git a/docs/assets/pdf-links-alpha14/manifest.json b/docs/assets/pdf-links-alpha14/manifest.json new file mode 100644 index 0000000..320557d --- /dev/null +++ b/docs/assets/pdf-links-alpha14/manifest.json @@ -0,0 +1,34 @@ +{ + "ok": true, + "at": "2026-10-23T14:23:20.103968+00:00", + "version": "2.6.0a14", + "source_commit": "0e418e174a0d1a4cfe44bc29828c48e635829d90", + "guide_sha256": "39282c56abfac46d68b76c9031ff56aecb62ed3b50a97e9bd4886b1d88409d37", + "public_pdf_identical": true, + "public_vs_local_native_previews_identical": true, + "public_vs_local_pdfium_previews_identical": true, + "website_preview_source": "PDFium raster of the downloadable PDF", + "files": { + "linked-report.pdf": { + "sha256": "1ad53b6d97dc3ad6f605e33c82325b1b0e75a805f5b06069cf0553fe1b3b1274", + "bytes": 160746 + }, + "page-1.png": { + "sha256": "8d6a014be2a686cc37fcea4a866dcebd5a6671dfee0520ce4b0531bbfee3ea15", + "bytes": 80497 + }, + "page-2.png": { + "sha256": "bf1b28c24516e2e765116558fcc6b36d9282f018fee6fe074f2185563e9467a1", + "bytes": 126640 + }, + "project.zip": { + "sha256": "a3ae4e0ea1a49c32d7c34788010b1757d70b99820afeaf6291f514dce2d4577a", + "bytes": 213884 + }, + "verification.json": { + "sha256": "62797ba48caf667be3d414f67203c3ad20ae7478f325f1880658d686d0f08d52", + "bytes": 4068 + } + }, + "snippets_receipt_sha256": "34a79d65ce8d82b61f970de75e94f136cd0b814f27ebf443bd9fbeda5764bdf3" +} diff --git a/docs/assets/pdf-links-alpha14/page-1.png b/docs/assets/pdf-links-alpha14/page-1.png new file mode 100644 index 0000000..07a0840 Binary files /dev/null and b/docs/assets/pdf-links-alpha14/page-1.png differ diff --git a/docs/assets/pdf-links-alpha14/page-2.png b/docs/assets/pdf-links-alpha14/page-2.png new file mode 100644 index 0000000..bcb688e Binary files /dev/null and b/docs/assets/pdf-links-alpha14/page-2.png differ diff --git a/docs/assets/pdf-links-alpha14/project.zip b/docs/assets/pdf-links-alpha14/project.zip new file mode 100644 index 0000000..38398dd Binary files /dev/null and b/docs/assets/pdf-links-alpha14/project.zip differ diff --git a/docs/assets/pdf-links-alpha14/verification.json b/docs/assets/pdf-links-alpha14/verification.json new file mode 100644 index 0000000..8ad2d21 --- /dev/null +++ b/docs/assets/pdf-links-alpha14/verification.json @@ -0,0 +1,121 @@ +{ + "ok": true, + "at": "2026-10-23T14:23:19.622531+00:00", + "version": "2.6.0a14", + "pdf_sha256": "1ad53b6d97dc3ad6f605e33c82325b1b0e75a805f5b06069cf0553fe1b3b1274", + "pages": 2, + "authored_links": 5, + "annotation_fragments": 6, + "links": [ + { + "page": 1, + "rect": [ + 98.352996, + 197.929016, + 160.666994, + 210.029015 + ], + "target_page": 2, + "target_y": 495.591004 + }, + { + "page": 1, + "rect": [ + 98.352996, + 161.929017, + 198.196992, + 174.029017 + ], + "target_page": 2, + "target_y": 226.591015 + }, + { + "page": 2, + "rect": [ + 55.684998, + 183.364016, + 227.956991, + 195.464016 + ], + "uri": "https://docs.fullbleed.dev/" + }, + { + "page": 2, + "rect": [ + 55.684998, + 162.364017, + 160.796994, + 174.464017 + ], + "uri": "mailto:hello@example.com" + }, + { + "page": 2, + "rect": [ + 39.659998, + 105.60502, + 60.682998, + 116.495019 + ], + "target_page": 1, + "target_y": 555.591002 + }, + { + "page": 2, + "rect": [ + 60.707998, + 105.60502, + 112.939995, + 116.495019 + ], + "target_page": 1, + "target_y": 555.591002 + } + ], + "diagnostics_ok": true, + "component_snippet_verified": true, + "component_annotation_fragments": 3, + "visual_review_pending": false, + "source_sha256": { + "report.html": "532fe583e0ca173443e0bfc55d7f20712c83d04cea63072d38c088cd61760354", + "report.css": "c3c2d6f4d1ff17fb0604cf049ba096c7deb19bd4b0d1f9201799797e3cdb9183", + "render.py": "26bc3dbc4a0107cbaccda75469a57be831ffe5fc69908ad1c7ffcac6b6ce023c" + }, + "files": { + "component-links.pdf": "d8eb3d846ca2c71a72a69bee5b806dffb74f69f643dda223b16185054b149a89", + "component-styled.html": "12f8304efbde609e039d08fb3aa480cde3fb85643e7b096d4050c5958497bb60", + "component-unstyled.html": "ace387d8d59fd5e56e09e1d1f1ceda50fbae79fdb3fa7f9aee281b415acc62d5", + "diagnostics.json": "63e1c38dc9bfd16e1307c69cf823387235240398d2017c6a882e5cf0395542a2", + "diagnostics.stderr.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "pdfium-page-1.png": "8d6a014be2a686cc37fcea4a866dcebd5a6671dfee0520ce4b0531bbfee3ea15", + "pdfium-page-2.png": "bf1b28c24516e2e765116558fcc6b36d9282f018fee6fe074f2185563e9467a1" + }, + "scope": "Actual two-page report, all five authored links in six native annotation fragments, page content and bounds, independent PDFium renders, runtime layout/font diagnostics, and the component snippet exports. The back link has two adjacent fragments with the same destination. Visual review is separate. No universal viewer or accessibility claim.", + "visual_review": { + "at": "2026-10-23T13:23:50.683692+00:00", + "reviewed_pages": [ + 1, + 2 + ], + "native_png_pages": [ + 1, + 2 + ], + "independent_reader": "PDFium", + "observations": [ + "Two complete A5 pages; headings, metrics, index and footer fit without overlap or clipping.", + "All content and both page headers are present; the initial preview concern was not reproduced.", + "Link text is readable and underlined; both section destinations and the return link resolve to the intended pages.", + "Native width is 839 pixels; PDFium width is 840 at 144dpi because of page-size rounding. No pixel-parity assertion is made." + ], + "reproduced_from_public_wheel": true, + "matching_pdf_and_native_pngs": true, + "matching_independent_pdfium_pngs": true, + "local_receipt_sha256": "9e0eac8907af7d286afc9b4f348738ad6c1fd95e7190c038b77e28c10602d4e8" + }, + "public_package": { + "version": "2.6.0a14", + "source_commit": "0e418e174a0d1a4cfe44bc29828c48e635829d90", + "release_verification_sha256": "e373f26fa3636e7e117d129161e31a0cb03d4e0688db1bdf6507495232b8db68" + } +} diff --git a/docs/examples.md b/docs/examples.md index 2e93d88..09b6c8e 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -14,8 +14,8 @@ Common Ground report, Riverton notice, and Hillside statements were generated with the public **Fullbleed 2.4.0** wheel; the pandas report below uses **2.5.2**, the bilingual invoice uses **2.5.8**, and the Markdown brief uses **2.5.10**. The numbered report uses **2.5.18**, and the Streamlit report uses -**2.5.23**. All sample data and organizations -are fictional. +**2.5.23**. The linked project handoff uses the opt-in **2.6.0a14** +prerelease. All sample data and organizations are fictional. [Download showcase sources](assets/showcase/source.zip){ .md-button .md-button--primary } [Browse the source](https://github.com/fullbleed-engine/fullbleed-official/tree/b57e8ea5bb8315f04f7daac41a3489d21a2be158/examples/design_showcase){ .md-button } @@ -107,6 +107,28 @@ project outline a clear visual structure. +## A connected project handoff { #linked-report } + +
+
+[![Cedar Field Studio's green and cream project report with a snapshot and linked contents.](assets/pdf-links-alpha14/page-1.png)](assets/pdf-links-alpha14/linked-report.pdf) +
+
+ +**Two pages · Section navigation · Web and email links** + +Jump from the contents to either section, open a source, or return to the +overview. The project includes the HTML, CSS, bundled-font setup and Python +render script. Generated with the opt-in **2.6.0a14** prerelease; stable 2.5.34 +predates the native-navigation feature. + +[Open the linked PDF](assets/pdf-links-alpha14/linked-report.pdf){ .md-button } +[Download the project](assets/pdf-links-alpha14/project.zip){ .md-button } +[Add links to your PDFs](guides/pdf-links.md) + +
+
+ ## Northstar Studio invoice { #styled-invoice }
diff --git a/docs/guides/component-preview-12.md b/docs/guides/component-preview-12.md index ac53996..d62b84a 100644 --- a/docs/guides/component-preview-12.md +++ b/docs/guides/component-preview-12.md @@ -13,11 +13,12 @@ tree as a PDF, styled HTML or unstyled HTML without writing HTML strings. It preserves both earlier import paths and adds a complete document migration example: two implementations produce the same A5 and Letter landscape reports. -The current opt-in release is **2.6.0a13**, which fixes rich-inline initial -styling. This guide and its downloadable kit stay pinned to alpha 12; see the -[release status](component-status.md) for the latest install command and -[alpha 13 release notes](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a13) -for the rendering fix. +The current opt-in release is **2.6.0a14**, which adds native PDF navigation +and fixes inline opacity and table-cell alignment. This guide and its downloadable +kit stay pinned to alpha 12. See the [release status](component-status.md) for +the latest install command, the [PDF links guide](pdf-links.md) for a working +report, and the [alpha 14 release notes](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a14) +for the rendering changes. [Download the migration source kit](https://github.com/fullbleed-engine/fullbleed-official/releases/download/v2.6.0a12/fullbleed-component-migration-2.6.0a12.zip){ .md-button .md-button--primary } [Read the migration reference](https://github.com/fullbleed-engine/fullbleed-official/blob/v2.6.0a12/docs/component-namespace.md){ .md-button } diff --git a/docs/guides/component-status.md b/docs/guides/component-status.md index d865842..5a333bd 100644 --- a/docs/guides/component-status.md +++ b/docs/guides/component-status.md @@ -5,13 +5,13 @@ description: Choose the stable Fullbleed Python component API or its opt-in 2.6 # Python component release status -Reviewed **21 October 2026**. Fullbleed's released Python component API is +Reviewed **23 October 2026**. Fullbleed's released Python component API is available today. The newer API remains an opt-in alpha. | Channel | Install | Authoring surface | | --- | --- | --- | | Stable **2.5.34** | `python -m pip install fullbleed` | HTML/CSS rendering and the existing `fullbleed.ui` components | -| Alpha **2.6.0a13** | `python -m pip install --pre "fullbleed==2.6.0a13"` | New authoring values under `fullbleed.components`; earlier imports remain supported | +| Alpha **2.6.0a14** | `python -m pip install --pre "fullbleed==2.6.0a14"` | New authoring values under `fullbleed.components`; earlier imports remain supported | Start with the [Python quickstart](../getting-started/quickstart.md) for either HTML/CSS or stable components. Both examples produce the same invoice. @@ -24,8 +24,9 @@ without styles. The [namespace and migration guide](component-preview-12.md) includes explicit styling examples and a complete kit pinned to alpha 12. Those authoring paths remain supported in the current alpha. -Alpha 13 fixes `::first-letter` styling in paragraphs containing styled spans -or links, including floated initials and paragraphs that continue across pages. +Alpha 14 adds [native PDF links](pdf-links.md), including section jumps, web +URLs and email links. It also corrects inline opacity and mixed-content table +alignment. The earlier rich-inline initial fixes remain included. For interactive walkthroughs, [Explore the versioned component notebook](../getting-started/component-notebook.md) or [Try the current styling notebook](../getting-started/component-styling.md). @@ -33,15 +34,15 @@ Each notebook states its own package pin and includes downloadable output. ## Decision: retain the alpha -The alpha is **merged into `master` and published on PyPI**. It is not an -unmerged prototype. Merging it makes the implementation available for development; -it does not make the new API a stable compatibility promise. Ordinary -`pip install fullbleed` continues to select the stable channel. +The component authoring implementation is **merged into `master`**, and +prerelease packages are published on PyPI. The alpha label describes the API +compatibility promise; merging the implementation does not make that promise +stable. Ordinary `pip install fullbleed` continues to select the stable channel. The current release has substantial rendered-output evidence, including paired HTML/CSS and component showcases, saved PDFs, styled/unstyled HTML, page-band checks, typing checks and browser layout checks. The -[alpha 13 release](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a13) +[alpha 14 release](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a14) records the exact package and fixture evidence. Passing those checks establishes the tested behavior; it does not settle how the API should evolve for its callers. diff --git a/docs/guides/css-corpus.md b/docs/guides/css-corpus.md index 0a255c1..11be005 100644 --- a/docs/guides/css-corpus.md +++ b/docs/guides/css-corpus.md @@ -38,6 +38,27 @@ comparison policy. They do not establish complete CSS support, arbitrary website rendering, or PDF standards conformance. The historical runs below keep their original versions and results. +## Navigation alpha source comparison: 2.6.0a14 + +The [final alpha 14 source run](https://github.com/fullbleed-engine/fullbleed-official/actions/runs/39953154688) +tests the tagged source tree at `0e418e174a0d1a4cfe44bc29828c48e635829d90`. +It records **1,625 PASS, 19 FAIL and 18 REFERENCE-DISPUTED** across the same +1,662 fixtures. The all-pass gate remains failed. Two fixtures improve against +published alpha 13, with no new failing fixtures. + +All 7,467 manifest entries were verified. The 7,435 saved PDF/PNG files and +every fixture verdict and raster metric match the qualified candidate; the +29 remaining-failure PNGs also match published alpha 13. Inputs, references, +comparator and thresholds are unchanged. + +[Complete source-run evidence](https://github.com/fullbleed-engine/fullbleed-official/releases/download/v2.6.0a14/fullbleed-css-corpus-2.6.0a14.zip) +· [Scoped release checks](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a14) + +This is the final **source-built Linux wheel** comparison. The separate public +package checks verify distribution hashes, a fresh installation and targeted +fixtures; they are not a full-corpus run of every published wheel. The online +explorer continues to show the stable published-wheel result above. + ## Component alpha source comparison: 2.6.0a11 The [alpha 11 source run](https://github.com/fullbleed-engine/fullbleed-official/actions/runs/39403828268) diff --git a/docs/guides/pdf-links.md b/docs/guides/pdf-links.md new file mode 100644 index 0000000..32915d1 --- /dev/null +++ b/docs/guides/pdf-links.md @@ -0,0 +1,129 @@ +--- +title: Clickable links in PDFs with Python +description: Create PDF web links, email links and table-of-contents jumps in Python. Download a styled Fullbleed report with its HTML, CSS and source. +--- + +# Add clickable links to a PDF + +Use an HTML anchor to send a PDF reader to a web page, an email address, or a +section in the same document. Fullbleed turns the visible link into a native +PDF link when it renders your HTML and CSS. + +The sample below is a two-page project handoff. Its contents links jump to +different positions on page 2, and **Back to overview** returns to page 1. +It uses fictional project data and a font bundled with Fullbleed. + +[Open the linked report](../assets/pdf-links-alpha14/linked-report.pdf){ .md-button .md-button--primary } +[Download the Python project](../assets/pdf-links-alpha14/project.zip){ .md-button } + +[![A green and cream Cedar Field Studio report with a project snapshot and linked contents.](../assets/pdf-links-alpha14/page-1.png)](../assets/pdf-links-alpha14/linked-report.pdf) + +!!! info "Requires the 2.6.0a14 prerelease" + Use Python 3.10 or later in a virtual environment and install + `python -m pip install fullbleed==2.6.0a14`. + Stable 2.5.34 predates this native-navigation feature. Ordinary + `pip install fullbleed` continues to select the stable channel. + +## Start with an anchor + +An external link needs a complete URL. An internal link needs a fragment +such as `#findings` and an element with the matching `id="findings"`: + +```python +from importlib.resources import files +from pathlib import Path +import fullbleed + +html = """ +

Project report

+

Go to project notes

+

Read the documentation

+ +

Project notes

+

Keep the handoff clear, connected, and easy to use.

+

Back to overview

+""" +css = """ +@page { size: A5; margin: 18mm; } +body { font-family: Inter; font-size: 11pt; line-height: 16pt; } +a { color: #12665c; text-decoration: underline; } +""" + +font = files("fullbleed_assets").joinpath("fonts/Inter-Variable.ttf") +engine = fullbleed.PdfEngine(font_files=[str(font)]) +Path("report.pdf").write_bytes(engine.render_pdf(html, css)) +``` + +The renderer does not fetch the linked page. It stores the destination in +the PDF; the reader handles activation. PDF-reader security settings can +affect opening external URLs. + +| Destination | HTML | +| --- | --- | +| Web page | `Open the source report` | +| Email address | `Email the project team` | +| Document section | `Project notes` with an element whose `id` is `findings` | + +Email links use the reader's configured mail application. The example address +is illustrative. Use descriptive labels so the destination is clear before +someone selects a link. + +## Adapt the complete report + +Unzip the project, then run: + +```sh +python -m pip install -r requirements.txt +python render.py +``` + +Edit `report.html` for the content and destinations. Edit `report.css` for +the color palette, spacing, typography, and page layout. The script writes +`output/linked-report.pdf` and two PNG previews. Open the PDF to try the links; +PNG previews show the layout and do not contain interactive links. + +Use unique IDs. Missing destinations produce no active internal link, and +duplicate IDs resolve to the first occurrence. Link text may span multiple +activation regions when it wraps or contains separate shaped fragments. + +## Use the same links in components + +The component authoring path emits HTML anchors with the same behavior. +Pass native HTML attributes through `attrs`, including a heading's `id`: + +```python +from pathlib import Path +from fullbleed.components import Document, Heading, Renderer +from fullbleed.components.bootstrap import Link + +document = Document( + Link("Project notes", href="#findings"), + Heading("Project notes", level=2, attrs={"id": "findings"}), + Link("Documentation", href="https://docs.fullbleed.dev/"), + title="Component link example", +) +Path("component-links.pdf").write_bytes(document.render_pdf(Renderer())) +styled_html = document.to_html(include_styles=True) +unstyled_html = document.to_html(include_styles=False) +``` + +Both HTML exports keep the `href` values. The new component API remains alpha; +see its [release status](component-status.md) and +[styling and export guide](component-preview-12.md). + +## What was checked + +The report was checked for two complete pages and working section, web, and +email destinations. Its layout was reviewed in native previews and an +independent PDF reader; layout/font checks reported no overflow or missing +glyphs. Both Python examples were executed, and the component example's +styled and unstyled HTML exports were inspected. + +[Verification record](../assets/pdf-links-alpha14/verification.json) · +[Release evidence](https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.6.0a14) + +Keep links rectangular when precise activation boundaries matter. Some readers +expand the clickable area of rotated or diagonally clipped links. This example +does not establish keyboard, screen-reader, or PDF/UA conformance. The +[navigation reference and reader observations](https://github.com/fullbleed-engine/fullbleed-official/blob/v2.6.0a14/docs/pdf-links.md) +describe the tested geometry and remaining limits. diff --git a/mkdocs.yml b/mkdocs.yml index a418d0b..8151d4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -91,6 +91,7 @@ nav: - JavaScript components and JSX: guides/javascript-components.md - C# components and styles: guides/csharp-components.md - Complete HTML documents: guides/complete-html.md + - Clickable PDF links: guides/pdf-links.md - Invoices from data: guides/invoices.md - Numbered reports: guides/numbered-reports.md - Markdown to PDF: guides/markdown-pdf.md