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: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ jobs:
- name: Verify the introductory Python examples
run: |
python -m venv output/reference-env
output/reference-env/bin/python -m pip install --only-binary=:all: fullbleed==2.5.22
output/reference-env/bin/python -m pip install --only-binary=:all: fullbleed==2.5.34
output/reference-env/bin/python -I tools/verify_quickstarts.py
- name: Verify the downloadable Node invoice and guide
run: python tools/verify_node_examples.py
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,14 @@ python tools/sync_reference.py --source /path/to/fullbleed-official --ref v2.5.2

`docs/reference-source.json` records the exact source commit. Use a built, installed release wheel to rerun examples and review the final PDFs and previews before replacing downloadable artifacts. `docs/assets/examples/verification.json` records the checks performed on published examples.

Run `python -I tools/verify_quickstarts.py` in an environment with that reference
release installed. It executes the Python snippets directly from five introductory
pages, checks their PDF text and page counts, and exercises the documented project
commands. CI retains the PDFs, previews, and structured report. Older showcase,
playground, and tutorial downloads keep their own verified release pins.
Run `python -I tools/verify_quickstarts.py` in an environment with
`fullbleed==2.5.34` installed. The first-run examples are qualified against this
stable package independently of the frozen API reference. The verifier executes
the Python snippets directly from five introductory pages, checks their PDF
text, embedded fonts and page counts, and exercises the documented project
commands. It runs both Python quickstart tabs and requires their PDFs and previews
to match byte for byte. CI retains the outputs and structured report. Older
showcase, playground, and tutorial downloads keep their own verified release pins.

The tagged C# notice lives in `examples/dotnet-accessibility`. When updating it,
restore its lockfile from public NuGet, render both `ua1` and `ua2`, and review
Expand Down
93 changes: 70 additions & 23 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,79 @@
---
title: Generate PDFs in Python from HTML and CSS
description: Copy a small Python script to create your first PDF, then try a complete Fullbleed project with bundled fonts and previews.
title: Generate PDFs in Python with components or HTML and CSS
description: Create your first PDF with reusable Python components or HTML/CSS. Copy either complete example, use bundled fonts, and render the same invoice.
---
# Create your first PDF in Python

Prefer to try it before setting up Python? [Open the browser playground](../playground.md)
to edit HTML/CSS and download a PDF or runnable project. Use the
[invoice notebook](notebook.md) to work with Python data in Colab.

Install Fullbleed into your Python environment:
Choose **Python components** to compose a document with functions, or
**HTML/CSS** to bring a template. Both examples below use the stable API, embed
the bundled Inter font, and write the same one-page invoice.

Install Fullbleed in your Python environment. If you need an isolated environment,
follow the [virtual environment setup](installation.md#use-a-virtual-environment)
first.

```bash
python -m pip install fullbleed
```

Save this as `hello.py`:

```python
from importlib.resources import files
from pathlib import Path
import fullbleed

font = files("fullbleed_assets").joinpath("fonts/Inter-Variable.ttf")
engine = fullbleed.PdfEngine(font_files=[str(font)])
html = "<h1>Invoice INV-1042</h1><p>Consulting: USD 1,200.00</p>"
css = """
@page { size: A4; margin: 20mm; }
body { font-family: Inter; color: #203a36; }
h1 { color: #175c52; }
"""
pdf = engine.render_pdf(html, css)
Path("invoice.pdf").write_bytes(pdf)
```

The example embeds the Inter font included with Fullbleed.
Choose one tab and save its complete example as `hello.py`. These examples are
checked with Fullbleed **2.5.34**.

=== "Python components"

```python
from importlib.resources import files
from pathlib import Path
import fullbleed
from fullbleed.ui import Document, Heading, Paragraph

@Document(
title="Invoice INV-1042",
styles={
"@page": {"size": "A4", "margin": "20mm"},
"body": {"font_family": "Inter", "color": "#203a36"},
".invoice-title": {"color": "#175c52"},
},
)
def Invoice(number, description, amount):
return (
Heading("Invoice ", number, level=1, class_name="invoice-title"),
Paragraph(description, ": ", amount),
)

font = files("fullbleed_assets").joinpath("fonts/Inter-Variable.ttf")
engine = fullbleed.PdfEngine(font_files=[str(font)])
invoice = Invoice("INV-1042", "Consulting", "USD 1,200.00")
Path("invoice.pdf").write_bytes(invoice.render_pdf(engine))
```

=== "HTML/CSS"

```python
from importlib.resources import files
from pathlib import Path
import fullbleed

font = files("fullbleed_assets").joinpath("fonts/Inter-Variable.ttf")
engine = fullbleed.PdfEngine(font_files=[str(font)])
html = '<h1 class="invoice-title">Invoice INV-1042</h1><p>Consulting: USD 1,200.00</p>'
css = """
@page { size: A4; margin: 20mm; }
body { font-family: Inter; color: #203a36; }
.invoice-title { color: #175c52; }
"""
pdf = engine.render_pdf(html, css)
Path("invoice.pdf").write_bytes(pdf)
```

`Invoice(...)` receives ordinary Python arguments. `@Document(styles=...)`
attaches the stylesheet to its result. The heading's
`class_name="invoice-title"` matches the `.invoice-title` rule; the HTML/CSS
example uses the same class and values.

Run it and open the resulting file:

Expand All @@ -44,6 +84,13 @@ python -m fullbleed inspect pdf invoice.pdf --json

For a PNG preview of the finalized PDF, continue to the [font and preview walkthrough](first-pdf.md).

Continue with [reusable components, lists, and styles](../guides/python-components.md)
or the [designed HTML/CSS invoice](../guides/invoices.md). The separate
[component alpha](../guides/component-preview-11.md) adds portable component
styles, themes, named parts, and styled/unstyled HTML export. It has its own
explicit installation instructions; the examples above use the stable
`fullbleed.ui` API.

## Start with a complete project

For a component-based report with vendored assets and verification output, run these commands in a new directory:
Expand Down
41 changes: 36 additions & 5 deletions tools/verify_quickstarts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import fullbleed

ROOT = Path(__file__).resolve().parents[1]
QUICKSTART_RELEASE = "2.5.34"
FIXTURES = [
("index.md", "invoice.pdf", [("Invoice INV-1042", "USD 1,200.00")], True),
("getting-started/quickstart.md", "invoice.pdf", [("Invoice INV-1042", "USD 1,200.00")], True),
Expand All @@ -34,7 +35,9 @@ def main():
out = args.out.resolve()
out.mkdir(parents=True, exist_ok=True)
reference = json.loads((ROOT / "docs/reference-source.json").read_text(encoding="utf-8"))
expected = reference["release"].removeprefix("v")
# First-run examples track a qualified stable package independently of the
# frozen API reference and the older downloadable showcase projects.
expected = QUICKSTART_RELEASE
installed = version("fullbleed")
if installed != expected:
raise SystemExit(f"Install fullbleed=={expected} in this environment; found {installed}.")
Expand Down Expand Up @@ -83,8 +86,37 @@ def inspect(path, page_texts=None, embedded_font=False):
blocks = re.findall(r"```python\n(.*?)```", source, flags=re.S)
# The first home-page example uses the newer component API. Its own
# Windows/Linux gate runs it with the component starter's pinned release.
expected_blocks = 2 if page in ("index.md", "engine/assets.md") else 1
expected_blocks = 2 if page in (
"index.md", "engine/assets.md", "getting-started/quickstart.md"
) else 1
assert len(blocks) == expected_blocks, (page, len(blocks))
if page == "getting-started/quickstart.md":
assert f"Fullbleed **{QUICKSTART_RELEASE}**" in source
paired = []
for authoring, block in zip(("components", "html-css"), blocks):
folder = workspace / ("getting-started_quickstart_" + authoring)
folder.mkdir()
code = dedent(block)
(folder / "hello.py").write_text(code, encoding="utf-8", newline="\n")
run("hello", ["-I", "hello.py"], folder)
result = run("inspect-cli", [
"-I", "-m", "fullbleed", "inspect", "pdf", "invoice.pdf", "--json"
], folder)
assert json.loads(result.stdout)["ok"]
evidence = {
"page": page, "authoring": authoring,
"source_sha256": hashlib.sha256(code.encode()).hexdigest(),
**inspect(folder / output, required, embedded_font),
}
snippets.append(evidence)
paired.append(evidence)
assert paired[0]["sha256"] == paired[1]["sha256"], (
"Quickstart authoring paths must produce the same PDF", paired
)
assert [p["sha256"] for p in paired[0]["previews"]] == [
p["sha256"] for p in paired[1]["previews"]
], "Quickstart previews differ"
continue
folder = workspace / page.removesuffix(".md").replace("/", "_")
folder.mkdir()
code = dedent(blocks[1] if page == "index.md" else blocks[0])
Expand All @@ -108,9 +140,6 @@ def inspect(path, page_texts=None, embedded_font=False):
"feature": "bundled_standard_font_previews", "available": available})
if page == "getting-started/first-pdf.md":
assert len(list((folder / "output/preview").glob("*.png"))) == 1
if page == "getting-started/quickstart.md":
result = run("inspect-cli", ["-I", "-m", "fullbleed", "inspect", "pdf", "invoice.pdf", "--json"], folder)
assert json.loads(result.stdout)["ok"]

project = workspace / "my-report"
project.mkdir()
Expand All @@ -123,6 +152,8 @@ def inspect(path, page_texts=None, embedded_font=False):
assert (folder / "report.py").is_file(), folder

result = {"ok": True, "engine": installed, "reference_commit": reference["commit"],
"quickstart_authoring_paths": ["components", "html-css"],
"quickstart_pdf_and_previews_identical": True,
"platform": sys.platform, "python": sys.version, "snippets": snippets,
"capability_snippets": capability_snippets,
"scaffold": scaffold, "commands": checks,
Expand Down
Loading