跳到正文
DEV Community· Keenan Finkelstein·· 2 小時前AI 評分51

Fullbleed 教學:如何透過 FastAPI 提供樣式化 PDF 發票

Serve a styled PDF invoice from FastAPI

AI 導讀

Fullbleed 的教學提供可執行的 FastAPI 範例,使用 Fullbleed 2.5.37 從可編輯的 HTML/CSS 樣板產生 PDF 發票並提供下載。範例以一張虛構發票示範,三項明細合共 USD 1,870.00;作者提醒,處理真實發票前須將示例資料載入函式換成應用程式內經授權的查詢,較長任務則可交由工作佇列處理。

正文

An invoice endpoint needs a document renderer, your application's record lookup, and an HTTP response containing PDF bytes. Here is a runnable FastAPI example with an editable HTML/CSS invoice, bundled fonts, and a small download page.

Fullbleed is the MIT-licensed PDF engine used here; this walkthrough is published by the project. The sample is a fictional Northstar Studio invoice for Maple & Finch, with three line items totaling USD 1,870.00.

Northstar Studio invoice with large green typography, a three-item table, and an orange-accented total panel.

Open the sample PDF

Download the starter and open its fullbleed-python-invoice directory. Use Python 3.10–3.14 and create a virtual environment:

python -m venv .venv

Activate it with source .venv/bin/activate on macOS/Linux, or .venv\Scripts\Activate.ps1 in Windows PowerShell. Then install and start the app:

python -m pip install fullbleed==2.5.37 -r requirements-fastapi.txt
python -m uvicorn fastapi_app:app --host 127.0.0.1 --port 8000

Open http://127.0.0.1:8000/ and select Download invoice. You can also request http://127.0.0.1:8000/invoices/INV-1042.pdf directly. The download is generated from the current template; the browser page shows a saved sample preview.

The PDF portion of fastapi_app.py is small:

from fastapi import FastAPI, HTTPException, Response
from invoice import PDF_HEADERS, load_invoice, render_invoice

class PDFResponse(Response):
    media_type = "application/pdf"

app = FastAPI(title="Fullbleed invoice example")

@app.get("/invoices/{invoice_id}.pdf", response_class=PDFResponse)
def invoice_pdf(invoice_id: str) -> PDFResponse:
    invoice = load_invoice(invoice_id)
    if invoice is None:
        raise HTTPException(status_code=404, detail="Invoice not found")
    return PDFResponse(render_invoice(invoice), headers=PDF_HEADERS)

The invoice import is the shared renderer included in the ZIP. load_invoice() currently returns the fictional record. Replace it with your application's authorized lookup before serving real invoices, keeping access checks before rendering.

PDFResponse declares the PDF media type in the generated OpenAPI schema. Its content is already binary PDF data, which FastAPI's Response accepts directly.

The route uses a normal def: Fullbleed's render call is synchronous, and the template is read from disk. FastAPI runs these path operations in its thread pool. Calling the same renderer directly inside async def would still occupy that event-loop thread during the call.

The shared response headers make the result a private attachment:

PDF_HEADERS = {
    "Content-Disposition": 'attachment; filename="invoice.pdf"',
    "Cache-Control": "private, no-store",
}

A new engine renders each request into memory. There is no shared output filename, and rendering this document does not launch a browser or fetch fonts from the network.

To change the document, edit templates/invoice.html for its structure and templates/invoice.css for typography, columns, spacing, and color. For example, .invoice .total styles the green total panel and its orange border. The local server reads these files for each download, so template edits do not require a restart.

The renderer uses Decimal for amounts and escapes text before a single string.Template substitution. Placeholders include $customer, $number, $rows, and $total; write $$ for a literal dollar sign in the template. Fonts and their licenses travel with the project and Fullbleed wheel.

The included layout is a fixed one-page A4 sample for three items. Longer invoices need pagination work and output review. Tax, discounts, invoice numbering, authentication, and payment collection belong to your application.

The ZIP also includes Flask and Django adapters using the same renderer. To check all three, stop the local server and run:

python -m pip install -r requirements-check.txt
python check_examples.py --out output/check

Use a new output directory. The check exercises framework clients and real HTTP servers, including missing records, response headers, simultaneous downloads, embedded fonts, expected text and totals, and the saved preview. Inspect the resulting PDFs and output/check/verification.json after making changes.

For a container, follow the Docker guide. For longer jobs, move rendering into your application's job queue and store the completed PDF for download.

The full integration guide includes the framework variants. The source record identifies the download by source commit and hashes.

Drafted and checked by an AI coding agent for the Fullbleed project, using Fullbleed 2.5.37 and FastAPI 0.142.2. The exact code excerpt was executed against the runnable starter.

來源:DEV Community · dev.to