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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ docs/assets/node-queue/*.pdf binary
examples/matplotlib-report/** text eol=lf
docs/assets/matplotlib-report/** -text
docs/assets/matplotlib-report/*.pdf binary
examples/numbered-report/** text eol=lf
docs/assets/numbered-report/** -text
docs/assets/numbered-report/*.pdf binary
28 changes: 27 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,32 @@ concurrency:
cancel-in-progress: true

jobs:
numbered-report:
name: Numbered report (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install the public engine and independent PDF readers
run: |
python -m pip install --only-binary=:all: -r examples/numbered-report/requirements.txt pypdf==6.19.0 pypdfium2==5.14.0 Pillow==12.3.0
- name: Verify the exact download and edited section numbering
run: |
python tools/build_numbered_report.py
git diff --exit-code -- docs/assets/numbered-report/project.zip docs/assets/numbered-report/source.json
python tools/verify_numbered_report.py
- uses: actions/upload-artifact@v4
if: always()
with:
name: numbered-report-verification-${{ matrix.os }}
path: output/numbered-report-verification
retention-days: 14
matplotlib:
name: Matplotlib report (${{ matrix.os }})
strategy:
Expand Down Expand Up @@ -456,7 +482,7 @@ jobs:
path: site
deploy:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
needs: [build, aspnet, chinese, arabic, markdown, tagged-notice, rust-download, matplotlib]
needs: [build, aspnet, chinese, arabic, markdown, tagged-notice, rust-download, matplotlib, numbered-report]
runs-on: ubuntu-latest
permissions:
pages: write
Expand Down
Binary file added docs/assets/numbered-report/numbered-report.pdf
Binary file not shown.
Binary file added docs/assets/numbered-report/page-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/numbered-report/page-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/numbered-report/project.zip
Binary file not shown.
11 changes: 11 additions & 0 deletions docs/assets/numbered-report/render.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "2.5.18",
"pages": 2,
"pdf_sha256": "4eeb4c3282b8788453c0b1df45a186e04bdff6a7f18cb08f9b7b411913d9f860",
"html_sha256": "60eb5812ae7bc49b928d525aa9426e1ef4dd7fdd3cdca8aded84b1f73707bc4b",
"css_sha256": "7777c211a03725360577bbade9889fb843e75ea86c1fc42b39d8648986b6bc7c",
"font_sha256": "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031",
"missing_glyphs": [],
"deterministic_replay": true,
"fictional_content": true
}
37 changes: 37 additions & 0 deletions docs/assets/numbered-report/source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"source": "examples/numbered-report",
"files": [
{
"path": "LICENSE",
"bytes": 1079,
"sha256": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9"
},
{
"path": "README.md",
"bytes": 1467,
"sha256": "ffc953efb779d3b5d03a8e9b01eb7c9209317935b2a81dca38cbd40997b9f80e"
},
{
"path": "render.py",
"bytes": 2095,
"sha256": "d5434b5a8cb2efd17e58513d690765e5984d17283881a40d2ad8b3799da888ec"
},
{
"path": "report.css",
"bytes": 3557,
"sha256": "7777c211a03725360577bbade9889fb843e75ea86c1fc42b39d8648986b6bc7c"
},
{
"path": "report.html",
"bytes": 2918,
"sha256": "60eb5812ae7bc49b928d525aa9426e1ef4dd7fdd3cdca8aded84b1f73707bc4b"
},
{
"path": "requirements.txt",
"bytes": 18,
"sha256": "75d4b9591839d030cd0b987ea45133605bdf19743b768e7016ba2c7e5713de5a"
}
],
"zip_bytes": 5682,
"zip_sha256": "b0abc7244daa3858b834f265229b10d538116ff5578e364f4c99fb3e8573ba00"
}
38 changes: 38 additions & 0 deletions docs/assets/numbered-report/verification.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"ok": true,
"platform": "Windows",
"version": "2.5.18",
"zip_sha256": "b0abc7244daa3858b834f265229b10d538116ff5578e364f4c99fb3e8573ba00",
"baseline": {
"pages": 2,
"sha256": "4eeb4c3282b8788453c0b1df45a186e04bdff6a7f18cb08f9b7b411913d9f860",
"sections": [
"1.1",
"1.2",
"1.3",
"2.1",
"2.2",
"2.3"
],
"two_independent_readers": true,
"embedded_fonts": true,
"text_within_page_bounds": true
},
"edited": {
"pages": 3,
"sha256": "8c003003e2f539ec1f2141c96696c4612319c027b7a9f51e4d082bff63482cc9",
"sections": [
"1.1",
"1.2",
"1.3",
"1.4",
"2.1",
"2.2",
"2.3"
],
"two_independent_readers": true,
"embedded_fonts": true,
"text_within_page_bounds": true
},
"scope": "This downloaded report and one inserted section; no complete CSS counter or PDF standards conformance claim."
}
31 changes: 28 additions & 3 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,41 @@ Designed invoices, reports, notices, and statements. These examples use Fullblee
embedded typography, grids, tables, color, and vector graphics to turn structured
data into finished pages.

Every preview comes from the actual downloadable PDF. The first four examples
were generated with the public **Fullbleed 2.4.0** wheel; the pandas report below
Every preview comes from the actual downloadable PDF. The Northstar invoice,
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**. All sample data and organizations are fictional.
**2.5.10**. The numbered report uses **2.5.18**. 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 }

[Edit these designs in your browser](playground.md){ .md-button }

## From brief to handoff { #numbered-report }

<div class="showcase-entry" markdown>
<figure markdown>
[![A two-page project outline with a cream title panel, green display typography, numbered headings, and an automatically numbered table.](assets/numbered-report/page-1.png)](assets/numbered-report/numbered-report.pdf)
</figure>
<div markdown>

**Two pages · Editable HTML/CSS · Automatic numbering**

CSS assigns chapter, section, table-row, and page numbers. A warm cover panel,
large Inter headings, green decision cards, and orange accents give a short
project outline a clear visual structure.

[Open the PDF](assets/numbered-report/numbered-report.pdf){ .md-button }
[Download the project](assets/numbered-report/project.zip){ .md-button }

[Preview page 2](assets/numbered-report/page-2.png) ·
[Customize the report](guides/numbered-reports.md)

</div>
</div>

## Northstar Studio invoice { #styled-invoice }

<div class="showcase-entry" markdown>
Expand Down
119 changes: 119 additions & 0 deletions docs/guides/numbered-reports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: Numbered PDF reports with HTML and CSS in Python
description: Create a designed PDF report with automatic chapter, section, table-row, and page numbers. Download the editable HTML/CSS and Python project.
---

# Numbered reports with print CSS

Keep chapter and section numbers in the stylesheet. When you add a section or
reorder a chapter, CSS counters assign its number during PDF generation.
This two-page project outline pairs automatic numbering with large Inter
headings, a warm cover panel, a deliverables table, and restrained green and
orange accents.

[Open the PDF](../assets/numbered-report/numbered-report.pdf){ .md-button .md-button--primary }
[Get the editable project](../assets/numbered-report/project.zip){ .md-button }

[![Project outline with a cream title panel, oversized green heading, numbered subsections, and a deliverables table.](../assets/numbered-report/page-1.png)](../assets/numbered-report/numbered-report.pdf)

The fictional report uses **Fullbleed 2.5.18** and the Inter font bundled with
that wheel. These previews come from its saved PDF.
[View page 2](../assets/numbered-report/page-2.png).

## Run and edit

Extract the ZIP, open `fullbleed-numbered-report`, and use Python 3.10 or newer
in a virtual environment:

```sh
python -m pip install -r requirements.txt
python render.py --out output
```

Open `output/numbered-report.pdf`. The directory also contains finalized PDF
previews, a glyph report, and `render.json` with input/font hashes and a repeated
render check. The script requires a fresh output folder.

Edit `report.html` for the content and `report.css` for the design. Change the
palette, type sizes, page margins, or the table's columns. Run again with
`python render.py --out output-edited`. The font is registered explicitly from
the installed wheel; rendering needs no browser or font download.

## Chapters and sections

The headings are siblings inside `main`. A chapter increments one counter and
resets another; subsequent section headings inherit that reset:

```html
<main>
<h2 class="chapter">Define the work</h2>
<h3 class="section">Set an outcome</h3>
<h3 class="section">Agree on the boundaries</h3>
<h2 class="chapter">Deliver with confidence</h2>
<h3 class="section">Record the decisions</h3>
</main>
```

```css
main { counter-reset: chapter; }
.chapter { counter-increment: chapter; counter-reset: section; }
.section { counter-increment: section; }
.chapter::before { content: counter(chapter, decimal-leading-zero) " / "; }
.section::before { content: counter(chapter) "." counter(section) " "; }
```

The section labels become `1.1`, `1.2`, then `2.1`. Keep this sibling structure
when editing the sample: wrapping headings in additional containers changes
counter scope. The second chapter's `.next-page` class forces a page break;
the numbers continue from the document structure.

## Table rows and page footers

The first page's table resets a separate counter on `tbody`, increments it on
each row, and prints it in an empty first cell:

```css
.deliverables tbody { counter-reset: deliverable; }
.deliverables tbody tr { counter-increment: deliverable; }
.deliverables td.number::before {
content: counter(deliverable, decimal-leading-zero);
}
```

Page numbers use page-margin boxes. They reflect final pagination, including
additional pages caused by longer content:

```css
@page {
size: A4;
margin: 17mm 19mm 18mm;
@bottom-right {
content: counter(page) " / " counter(pages);
font-family: Inter;
font-size: 8pt;
}
}
```

Set the margin-box font with explicit `font-family` and `font-size`
declarations, as the starter does for both footers.

## Check the result

Review both PDF previews after changes. The starter stops before writing a PDF
if the registered font lacks a required glyph. Its repeat check compares bytes
within the same installed runtime; it does not promise identical PDF bytes
across engine versions.

The download is checked with independent PDF readers for section numbers,
table labels, embedded fonts, and page bounds. Its editing check inserts a
section and verifies that the next chapter still begins at `2.1`. These checks
cover this report and the supplied counter patterns, rather than complete CSS
counter or PDF standards conformance.

[Read the verification record](../assets/numbered-report/verification.json) for
the package version, PDF hashes, and scoped checks.

For data-driven documents, see [invoices](invoices.md) and
[variable-data documents](bank-statements.md). For a Markdown content workflow,
start with the [Markdown brief](markdown-pdf.md).
21 changes: 21 additions & 0 deletions examples/numbered-report/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.
33 changes: 33 additions & 0 deletions examples/numbered-report/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# The numbered report

A fictional, two-page project outline with editable HTML and print CSS. Chapters,
subsections, table rows, and page footers receive their numbers from the stylesheet.

Use Python 3.10 or newer in a virtual environment:

```sh
python -m pip install -r requirements.txt
python render.py --out output
```

Open `output/numbered-report.pdf` and both `page-*.png` previews. The command also
saves input/font hashes, a glyph report, and a deterministic replay result in
`render.json`. It requires a fresh output directory so old pages cannot remain.

Edit `report.html` to change the content. Keep chapter and section headings as
siblings inside `main`: `.chapter` resets the section counter, and `.section`
increments it. Insert another section without typing a number yourself.
Edit `report.css` for colors, typography, page size, margins, and page breaks.
The second chapter explicitly starts a new page; longer content can add pages.

The pinned wheel supplies the registered Inter font. Rendering does not fetch
remote fonts or require a browser. After editing, run with another output path:

```sh
python render.py --out output-edited
```

Inspect every page before sharing. The glyph check stops the script before it
writes a PDF when the registered font cannot cover the text. This example does
not claim PDF/UA, PDF/A, or complete CSS counter conformance. The sample content
and Northstar project are fictional. Source code is MIT licensed.
Loading
Loading