Changelog¶
All notable changes to this project are documented here.
The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
1.0.0 - 2026-08-09¶
A full rewrite. The import name, the API and the rendering engine all change, so 0.x code does not run against this release. See Migrating from 0.x.
Changed¶
- The package is now
website2pdf, lower case, in asrc/layout.import WebSite2PDFno longer resolves. - Rendering moved from Selenium to Playwright. Playwright manages a pinned
Chromium build, so there is no chromedriver to match against a system browser,
and
page.pdf()replaces the private CDP call the old driver reached for. pdf()is nowconvert()andconvert_many(). The old method was four near-identical branches returningbytes | Iterable[bytes] | str | Iterable[str], which callers could not use withoutisinstance.convert()returnsbyteswithout a destination andPathwith one, through typed overloads.- Options are typed objects, not dicts.
PdfOptions,BrowserOptionsandRenderOptionsare frozen dataclasses that validate on construction, so a bad scale or a header template withoutdisplay_header_footerfails immediately instead of at render time. - Waiting is explicit.
wait_until,wait_for_selectorandextra_waitreplace thedelayargument, which was implemented by abusing Selenium'sstaleness_ofcondition. - Licence changed to MIT. The
LICENSEfile held plain GPL-3.0 text while the metadata declared LGPL-3.0. Releases before 1.0.0 keep the licence they were published under. - The exception hierarchy is flat under
WebSite2PDFError. The aggregateErrorsandDriversclasses are gone, as areClientAlreadyStartedandClientAlreadyStopped,start()andclose()are idempotent now. - Naming follows PEP 8:
pdfOptionsispdf_options,seleniumOptionsisbrowser_options.
Added¶
AsyncClient, an asyncio-native client that renders several pages at once in independent browser contexts sharing one browser process, bounded byconcurrency.- A
website2pdfcommand line, behind thecliextra. Reads targets from arguments or stdin, writes to a file, a directory or stdout. - Context-manager support on both clients, plus
start(),close()andis_running. py.typed: type hints are visible to downstream type checkers.- Cookies, extra headers, HTTP basic auth, viewport, locale and timezone through
BrowserOptions. emulate_media, so a page can be rendered as it appears on screen rather than in print media.- Filename de-duplication: pages sharing a title no longer overwrite each other.
- 197 tests, including browser-backed integration tests served over loopback, and a CI matrix across Python 3.10-3.14 on Linux, macOS and Windows.
Fixed¶
- A client could only be used once.
stop_client()calledquit()but left the handle in place, so the next conversion raisedClientAlreadyStarted. - Filename sanitisation did not work. The pattern
"(\/|\\|\?|%|\*|:|\||\"|<|>)"was written in a non-raw string, where\\collapses to a single backslash that escapes the following|. Windows reserved device names were not handled at all. - Mutable default arguments on the client constructor shared one dict and one list across every instance.
delay=0was silently ignored, because the argument was selected with a falsy check rather than aNonecheck.- Cookies never arrived. They were added to the driver before navigation, which requires already being on the target domain. They now go on the browser context, where pre-navigation is the correct place.
- The Firefox path could hang forever on an unbounded
while not os.path.isfile(...)loop, and depended on a"Microsoft Print to PDF"printer that does not exist outside Windows. - URL validation no longer issues a separate
requests.get(), which doubled traffic and failed against sites that reject non-browser user agents.
Removed¶
- Firefox support. Only Chromium exposes a print-to-PDF command; the Firefox branch had never worked.
- The
delayargument,Errors,Drivers,ClientAlreadyStarted,ClientAlreadyStopped,setup.py,requirements.txtand theMakefile. - Python 3.7 to 3.9 support. The floor is 3.10.