DEV Community

IronSoftware
IronSoftware

Posted on

ReportLab in Python: The Build-Versus-Buy Call Made Twice

from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet

styles = getSampleStyleSheet()
story = [
    Paragraph("Quarterly Statement", styles["Title"]),
    Spacer(1, 12),
    Paragraph("Account balance and transaction summary follow.", styles["Normal"]),
]

doc = SimpleDocTemplate("statement.pdf")
doc.build(story)
Enter fullscreen mode Exit fullscreen mode

Ten lines, and a quarterly statement comes out of them with a title, a twelve-point gap, and a body paragraph, each one a Python object taking its formatting from a shared stylesheet rather than from a coordinate. That is the build-versus-buy question being asked the first time, and hand-coding the layout is a defensible answer to it.

The question gets asked a second time later, and that time it costs money. When the source material turns out to be a web page, the options are re-authoring the layout as flowables, licensing the commercial tier that reads markup, or rendering the markup that already exists under a single licence. ReportLab prices that tier by monthly output volume rather than by seat, and teams tend to budget for the first question and meet the second one mid-project.

Full disclosure. Our team at Iron Software makes IronPDF. This read examines what ReportLab's markup gap and separate commercial product cost a team, and what IronPDF renders from the licence already in the build.

Where the Open-Source Line Falls

Markup support in the open-source package is scoped to formatting inside a single Paragraph, covering bold, italic, links, and inline images, and there is no document-level HTML or CSS parser anywhere in it. Turning a web page into a ReportLab document therefore means re-authoring its layout as Python flowables, one element at a time, or moving to the commercial tier for the markup path.

Reading a PDF the library did not generate sits on the far side of the same line. PageCatcher, the component that imports pages from documents produced elsewhere, ships only with the commercial tier, so an open-source-only pipeline can produce PDFs and cannot pull one back in to merge or restructure it.

Support stops at that line too. The package README asks users not to raise issues against it and points them at the reportlab-users mailing list instead, so a Platypus layout bug or a paragraph-markup edge case is a mailing-list-and-source-code problem rather than a support ticket. For a document pipeline expected to run for years, that is the maintenance model being chosen, not a detail of it.

What Does ReportLab PLUS Add, and How Is It Priced?

The commercial side is a separate package, rlextra, built around Report Markup Language. The open-source README states it plainly, describing the library as the foundation for the commercial RML product available in the ReportLab PLUS package, which offers a template-based style of document development and more features than the open-source line carries. The dependency runs deeper than a sales note, because reportlab.lib.pdfencrypt in the installed open-source package imports storeFormsInMemory and restoreFormsInMemory from rlextra.pageCatcher.pageCatcher, so the code path exists in the package a team already has and the module it needs sits on the other side of the purchase.

What teams underestimate is the shape of that purchase. The markup path is not an option inside the package already installed, it is a second product with its own package, its own document language, and its own commercial terms, quoted separately by the vendor. A pipeline that starts in the open-source line and later needs markup is therefore making a procurement decision rather than a version bump. IronPDF keeps that step to a licence key applied to the package already in the build.

How Far Does the Flowable Model Carry a Report?

Age is the case for ReportLab rather than a mark against it. The project has shipped 100 releases on PyPI since February 2009, 10 of them in the past twelve months, with 5.0.1 landing on 20 August 2026, which is a quarter century of edge cases newer libraries have not met yet. Platypus, the Page Layout and Typography Using Scripts model, is built from five layers, namely DocTemplates, PageTemplates, Frames, Flowables, and the underlying Canvas, so a document is assembled as flowable objects and reformatted globally through shared styles rather than page by page. The canvas API underneath gives point-level control over font, position, and stroke, and the open-source package ships a real vector graphics and charting subsystem rather than a stub, so bar and pie charts need no separate plotting library. The dependency footprint stays small with it, requiring only Pillow for images and charset-normalizer for text encoding, with rl_accel, rlPyCairo, and uharfbuzz text shaping as opt-in extras, on Python 3.9 and later per the packaging metadata. Where a report must match an exact printed layout and every glyph position is part of the specification, that model is the ceiling of what a code-first document toolkit needs to reach, and IronPDF answers the markup half of the same question from one package with the same page controls exposed through CSS.

The statement at the top of this piece is that model at its smallest, a SimpleDocTemplate choosing the page and a story deciding what lands on it. Scale the same code to a thousand-page run and the property worth having does not change, because the layout still lives in the stylesheet rather than in the loop emitting the rows, and restyling the run is one edit in one place.

Aspect ReportLab open source IronPDF for Python
Existing HTML and CSS as input Formatting inside a Paragraph only, no document parser RenderHtmlAsPdf, RenderUrlAsPdf, and RenderHtmlFileAsPdf
Documents built from code SimpleDocTemplate and flowables, or canvas drawing Data merged into a template, then rendered through Chromium
Typography control Point-level, code-driven through the canvas API CSS and standard browser layout rules
Charts and vector graphics Drawing and charting subsystem in the package Whatever the source page already draws, script included
Reading a PDF produced elsewhere rlextra.pageCatcher, commercial package only Load, merge, and stamp on the same PdfDocument
Vendor support Not provided for the open-source library Included with every licence
Licence BSD licence on the core, rlextra sold commercially Commercial, one licence for every deployment
Python versions accepted 3.9 and later 3.7 and later

Table 1. Document input and output paths only, ReportLab's open-source package against IronPDF for Python, with tier gating shown where ReportLab applies it.

Two rows here become invoices later, namely the markup row and the row about reading a PDF from elsewhere. The others are design taste.

The 5.x Line Changed a Security Default

The 5.x line changed a security default rather than a feature. The changelog for 5.0.0a1, dated 21 May 2026, records the decision to make a trustedHosts value of None mean no hosts are trusted in open_for_read, reversing the earlier no-restriction reading of the same setting, and that change shipped in 5.0.0 on 18 June 2026. Release 5.0.1, on 20 August 2026, went further and applied the trustedHosts and trustedSchemes checks to redirected hosts. No CVE was assigned to either, and any pipeline that pulls images from outside the build needs its allow-list written before that upgrade lands.

Four vulnerabilities sit against ReportLab's own code, all fixed years ago, namely CVE-2019-17626 and CVE-2019-19450, both critical at 9.8 for remote code execution in the XML-like paragraph markup parser and fixed in 3.5.28 and 3.5.31, CVE-2020-28463 for server-side request forgery through <img> tags, fixed in 3.5.55 and paired with the trustedHosts and trustedSchemes mitigation that 5.0 later hardened, and CVE-2023-33733 for code execution through a crafted input, fixed in 3.6.13. The most recent of those landed more than three years before the current 5.0.1, so this is a record rather than a live exposure, and it is what happens when one parser handles both trusted and less-trusted input, which belongs in the same review as the metadata and permissions on whatever produces the output.

One adjacent project decides the version plan. The tool most often reached for by teams wanting HTML with ReportLab underneath, xhtml2pdf, is maintained independently, and its current release pins reportlab<5,>=4.0.4, so it has not moved onto the 5.x line. Pairing the two therefore pins the ReportLab version as well.

Where the Markup-First Path Starts

The markup-first path starts from a page or a template that already exists rather than from a story of flowables.

from ironpdf import *

renderer = ChromePdfRenderer()
pdf = renderer.RenderUrlAsPdf("https://internal.example.com/reports/q3-statement")
pdf.SaveAs("q3-statement.pdf")
Enter fullscreen mode Exit fullscreen mode

That writes the statement exactly as the reporting app already draws it in a browser, stylesheet and script included, with no report definition in between. The same object takes a template file through RenderHtmlFileAsPdf and a string through RenderHtmlAsPdf, so pointing it at an internal URL and pointing it at a file are the same call with a different argument.

IronPDF renders the page a front-end team already maintains from the licence already in the build, and opens a PDF produced elsewhere on the same object, so neither capability arrives as a second product with its own commercial terms. The narrow case that stays with ReportLab is a document computed entirely from data where every glyph position belongs to the specification. IronPDF has a free trial if you want to run your existing template through it before pricing the markup tier.

Which category does your own pipeline fall into, computed pages or an HTML source of truth? Tell us in the comments, especially if you have quoted ReportLab PLUS for the markup path or found another route to it.

ReportLab and ReportLab PLUS are trademarks of ReportLab Inc, and we have no affiliation with the company. The release, pricing, and advisory details above are drawn from ReportLab's own documentation, FAQ, PyPI listing, and the published CVE records at the time of writing. If a detail has moved since, correct us below.

Top comments (0)