How to Generate PDFs in Python
Create PDFs in Python with ReportLab, choose the right library, and handle page size, layout, fonts, errors, and deployment.
To generate a PDF in Python, install ReportLab, create a Canvas, draw content, finish the page with showPage(), and write the file with save(). This direct drawing approach works well for precise placement and repeatable reports. For document-oriented layouts with tables, automatic page breaks, or embedded Unicode fonts, consider fpdf2. If you need to change an existing PDF, use a PDF manipulation library instead of a generation workflow.
1. Create a PDF with ReportLab
Install ReportLab in your project environment. The project documents pip install reportlab; optional dependencies can vary by feature and version, so check its current installation guidance when you need bitmap or chart support.
python -m pip install reportlab
Save this as make_pdf.py and run python make_pdf.py. It writes hello.pdf in the current directory:
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
output_path = "hello.pdf"
pdf = canvas.Canvas(output_path, pagesize=letter)
page_width, page_height = letter
pdf.setTitle("Hello PDF")
pdf.setFont("Helvetica-Bold", 18)
pdf.drawString(72, page_height - 72, "Hello, PDF")
pdf.setFont("Helvetica", 11)
pdf.drawString(72, page_height - 96, "Generated with ReportLab in Python.")
pdf.showPage()
pdf.save()
print(f"Wrote {output_path}")
The coordinates above are points. The origin is at the lower-left corner: x increases to the right and y increases upward. The example places text from the top by subtracting its distance from the page height.
Use A4 or another explicit page size
ReportLab’s documented default is A4, but set the page size deliberately so output does not silently use a size that is wrong for printing or downstream processing. Common choices include A4 and US letter:
from reportlab.lib.pagesizes import A4, letter
pdf = canvas.Canvas("a4.pdf", pagesize=A4)
# Or:
pdf = canvas.Canvas("letter.pdf", pagesize=letter)
For a custom page, pass a width and height in points as a tuple, for example pagesize=(300, 500). One point is 1/72 inch. Keep margins inside the page bounds, especially when the PDF will be printed.
Draw more than one page
Call showPage() to end the current page; subsequent drawing commands apply to the next page. Canvas drawing state, including fonts, colors, and transformations, is reset at the page boundary, so set the state you need for each page:
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
pdf = canvas.Canvas("two-pages.pdf", pagesize=A4)
width, height = A4
for page_number in range(1, 3):
pdf.setFont("Helvetica-Bold", 16)
pdf.drawString(72, height - 72, f"Report page {page_number}")
pdf.setFont("Helvetica", 10)
pdf.drawString(72, height - 96, "Page content goes here.")
pdf.showPage()
pdf.save()
Call save() once after the final page. It finalizes the document; do not call it after every page.
Write to a binary stream
A Canvas can write to an open binary stream as well as a filename. This is useful when another part of your application will upload or return the PDF without first writing a permanent file:
from io import BytesIO
from reportlab.pdfgen import canvas
buffer = BytesIO()
pdf = canvas.Canvas(buffer)
pdf.drawString(72, 720, "PDF held in memory")
pdf.showPage()
pdf.save()
pdf_bytes = buffer.getvalue()
print(f"Generated {len(pdf_bytes)} bytes")
For large documents, account for the memory occupied by the output and any images or data used to create it. Prefer a file or managed temporary storage when keeping the whole result in memory is unsuitable.
2. Choose a PDF generation approach
| Need | Approach to evaluate | Why |
|---|---|---|
| Precise placement, repeated reports, or drawing charts and shapes | ReportLab | Its pdfgen Canvas is a low-level painting interface, and the toolkit also provides higher-level layout facilities. |
| Document-style content with tables, page breaks, images, links, or Unicode font embedding | fpdf2 |
These are documented capabilities; check the project docs for the API and current setup. |
| Content already authored as HTML and CSS | Evaluate an HTML-to-PDF renderer such as WeasyPrint | Confirm its current feature support, dependencies, and rendering behavior against your actual documents before choosing it. |
| Merge, split, crop, transform, extract information from, encrypt, or work with forms in an existing PDF | A PDF manipulation library | These are modification tasks. PyPDF2’s cited 3.0.0 documentation lists these capabilities; verify current project status and version-specific APIs. |
Compare tools using your input format, layout complexity, page-break needs, font and image requirements, and deployment environment. The cited documentation does not establish a performance winner or a comprehensive accessibility comparison, so measure and validate your own output where those properties matter.
3. Build structured layouts and handle text
Canvas drawing gives you control over coordinates, but you must decide where each item goes. For a fixed report, calculate positions from the page dimensions and margins. For variable-length text, tables, or content that may span pages, use a higher-level layout facility or evaluate a document-oriented library such as fpdf2 rather than assuming every string will fit at a fixed coordinate.
- Keep a consistent margin and define reusable drawing functions for headers, footers, and repeated sections.
- Check text width before placing labels in a fixed-width region; long values can run into other content or beyond the page edge.
- Use fonts that contain the glyphs required by your text. For Unicode requirements, review font embedding support and licensing for the selected library and font.
- For variable content, explicitly decide how to wrap, split, or move content to a new page.
ReportLab’s guide covers both low-level drawing and higher-level document layout. Its documented Canvas workflow is a good starting point, but complex flowing layouts need deliberate layout logic.
4. Generate PDFs from other input and modify existing files
When the source is HTML
If your content already exists as HTML and CSS, evaluate an HTML-to-PDF renderer. Do not assume that browser rendering and a PDF library have identical CSS support, font availability, or output behavior. Test representative pages, including long content and assets, and check the renderer’s current documentation for supported features and deployment requirements.
When a PDF already exists
Generation creates a new document. Merging, splitting, cropping, transforming pages, extracting text or metadata, encryption, and form work are modification tasks. Choose a tool whose current documentation covers the operations you need. The PyPDF2 source in this guide documents version 3.0.0, so treat its feature list as specific to that version rather than a claim about the latest release.
5. Deployment, reliability, and cost
For a small server-side report, writing a PDF to a file or byte stream is straightforward. Production behavior depends on the input and runtime environment, so plan for these cases:
- Fonts and assets: package or otherwise make required fonts and images available to the process. A file that exists on a developer’s machine may not exist in a container or server.
- Output paths: ensure the process can write to the chosen directory, or use an in-memory stream where appropriate.
- Variable document size: avoid unbounded in-memory accumulation for large output; consider temporary files or storage suited to your application.
- Repeatability: pin the library version in the application’s dependency management and verify upgrades against representative PDFs.
- Validation: check that the output opens, pages have the intended dimensions, and text and graphics stay within margins. The research sources do not provide benchmark figures or accessibility guarantees.
ReportLab is open-source software distributed as a Python package; the cited documentation does not establish a usage charge. Your operational costs depend on compute, storage, and any other services in your application. Check current package documentation for supported versions and optional dependencies before deployment.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
ModuleNotFoundError: No module named 'reportlab' |
The package was installed into a different Python environment. | Activate the intended virtual environment, then run python -m pip install reportlab using that same Python executable. |
| The PDF has the wrong paper dimensions | The page size was implicit or set to an unintended value. | Pass pagesize=A4, pagesize=letter, or an explicit custom tuple when creating the Canvas. |
| Text appears too low, too high, or off the page | Canvas uses a lower-left origin with y increasing upward. | Calculate y from the page height for top-down layouts, and keep x/y values inside the page bounds. |
| Formatting disappears on a later page | Canvas state is reset by showPage(). |
Reapply the font, color, and other required drawing state after each page break. |
| The PDF file is missing or incomplete | save() was omitted, or the process cannot write to the destination. |
Call save() after the final page and verify the output directory and permissions. |
| Some characters render incorrectly | The chosen font may lack the necessary glyphs, or font handling is not configured for the text. | Use a font and workflow that support the required characters; for document-oriented generation, compare fpdf2‘s documented Unicode font embedding support. |
| Long text overlaps or is clipped | Fixed-coordinate drawing does not automatically solve your wrapping or layout constraints. | Measure and wrap text, allocate more space, move content to another page, or use a higher-level layout approach. |
7. Or skip the browser setup
If your PDF workflow starts with a web page, ScreenshotNeo can return a screenshot or PDF through one API call. It is a website screenshot API and MCP server, not a Python PDF layout library. Use Python libraries above when you need to generate a document from application data; use a page capture when the source is a rendered website.
For API parameters and options, see the ScreenshotNeo API documentation. This Python example saves a PDF response for a web page:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"format": "pdf",
},
timeout=90,
)
r.raise_for_status()
with open("page.pdf", "wb") as output:
output.write(r.content)
Equivalent cURL and Node.js requests:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d format=pdf \
-o page.pdf
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
format: 'pdf',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
await require('node:fs/promises').writeFile('page.pdf', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
8. FAQ
Does ReportLab create a PDF when I call showPage()?
showPage() ends the current page. Call save() to finish and write the document.
Can I make a PDF without saving it to disk?
Yes. Pass an open binary stream such as BytesIO to the Canvas, then call save() and read the resulting bytes.
Should I use ReportLab or fpdf2?
Choose based on the document structure and features you need. ReportLab offers a drawing Canvas and higher-level layout facilities; fpdf2 documents features including tables, page breaks, images, links, and Unicode font embedding. Verify both against your actual requirements.
Can ReportLab edit an existing PDF?
This guide’s ReportLab workflow creates a new document. For existing-file operations such as merging or splitting, choose a PDF manipulation tool with documented support for the operation and version you will deploy.


