| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-09-03 | 6.2 kB | |
| v0.2.18 source code.tar.gz | 2026-09-03 | 7.1 MB | |
| v0.2.18 source code.zip | 2026-09-03 | 7.2 MB | |
| Totals: 3 Items | 14.3 MB | 2 | |
- Support ReportLab 5 and Python 3.10-3.14, fix 14 bugs, harden the test suite
Widen the ReportLab constraint to >=4.0.4,<6 and raise the Python floor to 3.10, after validating empirically that ReportLab 5 does not change rendering: references built with one ReportLab major and compared against a render from the other produce zero pixel differences across all 23 testrender fixtures, in both directions and down to the declared 4.0.4 floor. The CI matrix now covers Python 3.10 to 3.14 against both ReportLab majors.
The only behavioural regression in ReportLab 5 is rl_config.trustedHosts=None changing from "every host is trusted" to "no host is trusted", which makes open_for_read refuse every URL and data: URI. That default is deliberate SSRF hardening and is left untouched; PmlImageReader falls back to xhtml2pdf's own fetcher instead.
The ReportLab 5 assessment also surfaced that the rendering comparison could
never fail. CI regenerated the reference from the same commit and the same
ReportLab immediately before comparing, and tox passed --nofail. Removing
--nofail alone would not have helped: ImageMagick compare exits 1 when images
differ and exec_cmd treated that as a fatal error, aborting on the first
differing page before the HTML report was written. A cross-version job now
builds the reference with one ReportLab major and renders with the other, in
both directions.
Bug fixes:
- @page name:left / @page name:right now work. A stray sys.exit() in PmlBaseDoc.handle_nextPageTemplate terminated the calling process, and the vendored PTCycle predated ReportLab's next_value protocol, so the alternating left/right page feature had never worked. ReportLab's own PTCycle is used now.
- Temporary files are no longer shared between threads. TmpFiles subclasses
threading.local but declared
filesas a class attribute, so one request's cleanFiles() closed files another request was still reading. - The WSGI middleware produces PDFs. PisaMiddleware.filter wrote PDF bytes into a StringIO, and the response buffer rejected the bytes chunks that PEP 3333 requires applications to yield.
- pisaPDF.addFromString() works; it passed an unsupported
capacitykeyword to getFile() and appended raw bytes where PdfReader needs a file-like object. - pisaFileObject accepts bytes and pathlib.Path, as its type hints claimed. Both raised AttributeError, swallowed into a silent None.
- data: URIs are parsed per RFC 2397. The percent-encoded form, which is the usual way inline SVG is written, was rejected outright.
- HTTP responses follow redirects (bounded to 5 hops), accept any 2xx status rather than the literal "200 OK" reason phrase, close their connection, and log a warning instead of a debug message on failure.
- Plain HTTP requests honour the configured http_timeout; it was only applied to HTTPS connections, so plain HTTP could hang indefinitely.
- pisaTempFile(capacity=-1) keeps its buffer in memory, as documented.
len(buffer) > capacityis true for any buffer when capacity is negative, so it spilled to disk immediately -- the default for pisaPDF and pisaContext. - Temporary files are registered with the cleanup registry even when the
resource is empty; registration sat inside an
if data:, so those files were never closed by cleanFiles(). Surfaced by the new ResourceWarning in 3.14. - pisaTempFile.getFileName() returns the file name instead of None.
- Paragraph.getPlainText() works; a misplaced bracket made it join a sequence of one-element lists and raise TypeError on every call.
- Filter.should_filter is abstract instead of printing headers to stdout and returning None.
- The memoization cache in xhtml2pdf.util is bounded and cleared after each render; its keys come from CSS in the rendered document.
Test suite:
- The suite no longer reaches the public internet; fixtures are served from tests/httpserver.py on localhost. A CI job runs it with no outbound network.
- make test-render fails with a clear message when the reference set has not been generated, instead of reporting every page as a difference. New make test-render-all creates the reference and compares in one go.
- A missing reference counts as a difference rather than being silently skipped, and exec_cmd distinguishes "images differ" from a real error.
- The tox envlist matches the CI matrix again; TOXENV=py3.13 previously matched no environment, so that leg ran no tests. CI now asserts the match.
- New coverage for pdf.py, wsgi.py, the pisa CLI, files.py and reportlab_paragraph.py, plus a contract test pinning every private ReportLab symbol the package imports. Removed the dead tests/runtests.py harness.
- The asian font canary asserts a subset, so an upstream addition no longer fails the suite.
Deprecations: Python 3.8 and 3.9 are no longer supported. The renderpm extra is now an alias for pycairo; ReportLab 5 removed the C renderPM backend along with its renderpm extra, so reportlab[renderpm] silently installed nothing there.
- Add a browser conformance oracle to testrender, and use it to fix CSS support
testrender compared xhtml2pdf against itself: the reference was produced by the same library, so it caught changes in the output but never told anyone whether that output was right. There was no external reference anywhere in the project, and a CSS bug could therefore live for years without a test noticing.
New Contributors
- @dimbleby made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/796
- @kianmeng made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/801
- @hsmett made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/800
- @dependabot[bot] made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/798
- @alionLevkin made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/812
- @jonbb made their first contribution in https://github.com/xhtml2pdf/xhtml2pdf/pull/803
Full Changelog: https://github.com/xhtml2pdf/xhtml2pdf/compare/v0.2.17...v0.2.18