The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →To generate a single, full-height PDF from HTML in an aiohttp application, render the page in an asynchronous Playwright browser, measure its rendered height, pass that height to page.pdf(), then return the resulting bytes with the application/pdf content type. This avoids ordinary Letter- or A4-sized page breaks, but it produces one unusually tall PDF page—not a conventionally paginated document.
How the full-height PDF flow works
aiohttp handles the HTTP request and response; it does not itself lay out HTML as a PDF. For browser-rendered HTML and CSS, use Playwright: load the content, wait for the resources that affect layout, measure the document, and ask Chromium to print a PDF with explicit dimensions. Playwright’s page.pdf() returns a PDF buffer, uses print CSS by default, and accepts width, height, and margin values with units.
The essential choice is the PDF page size. If you set a standard format such as A4 or Letter, a long document flows across multiple pages. If you set the page height to the rendered content height and use zero margins, the PDF instead has one tall page. That suits some receipts, reports, or archival captures, but it is not automatically the best format for reading or printing long documents.
Install the dependencies
Use Python 3.9 or later as a practical baseline for this example, then install aiohttp and Playwright and install Playwright’s Chromium browser. The commands below are for a virtual environment on macOS, Linux, or Windows PowerShell:
Recommended Free Tools
#1 Best Overall
python -m venv .venv
# macOS or Linux:
source .venv/bin/activate
# Windows PowerShell:
# .venvScriptsActivate.ps1
python -m pip install aiohttp playwright
python -m playwright install chromium
On a Linux server, Chromium may also need operating-system libraries. If the browser launch reports missing shared libraries, install the dependencies required by Playwright for that distribution or use Playwright’s supported dependency-install command where appropriate. Keep the browser package and its installed browser version aligned; a browser missing from the runtime environment cannot be launched by the application.
Complete aiohttp example: render one tall page and return it
Save this as app.py. It returns a PDF for a fixed HTML document, making the example straightforward to run. Replace the example content with your own trusted template or a carefully sanitized input; do not insert arbitrary user HTML without security controls.
from aiohttp import web
from playwright.async_api import async_playwright
HTML = """<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
@page { margin: 0; }
html, body { margin: 0; padding: 0; }
body {
width: 800px;
box-sizing: border-box;
padding: 24px;
font: 16px/1.5 sans-serif;
overflow-wrap: anywhere;
}
h1 { margin: 0 0 16px; }
</style>
</head>
<body>
<main>
<h1>Example report</h1>
<p>This HTML is rendered in Chromium and returned as a PDF.</p>
</main>
</body>
</html>"""
async def pdf_handler(request: web.Request) -> web.Response:
async with async_playwright() as p:
browser = await p.chromium.launch()
try:
page = await browser.new_page(viewport={"width": 800, "height": 1000})
await page.set_content(HTML, wait_until="networkidle", timeout=30_000)
await page.evaluate("document.fonts.ready")
await page.evaluate("""async () => {
await Promise.all(Array.from(document.images, image => {
if (image.complete) return Promise.resolve();
return new Promise(resolve => {
image.addEventListener('load', resolve, { once: true });
image.addEventListener('error', resolve, { once: true });
});
}));
}""")
height_px = await page.evaluate("""() => Math.max(
document.documentElement.scrollHeight,
document.body ? document.body.scrollHeight : 0
)""")
if height_px < 1:
raise web.HTTPInternalServerError(text="Rendered document has no height")
# Set a deployment-appropriate upper bound to limit runaway renders.
max_height_px = 20_000
if height_px > max_height_px:
raise web.HTTPRequestEntityTooLarge(
max_size=max_height_px,
actual_size=height_px,
text="Rendered document exceeds the configured PDF height limit",
)
pdf_bytes = await page.pdf(
width="800px",
height=f"{height_px}px",
margin={"top": "0px", "right": "0px", "bottom": "0px", "left": "0px"},
print_background=True,
prefer_css_page_size=False,
)
finally:
await browser.close()
return web.Response(
body=pdf_bytes,
content_type="application/pdf",
headers={"Content-Disposition": 'inline; filename="report.pdf"'},
)
app = web.Application()
app.router.add_get("/document.pdf", pdf_handler)
web.run_app(app, host="127.0.0.1", port=8080)
Run it with python app.py, then request http://127.0.0.1:8080/document.pdf. The response body is the PDF bytes. Content-Disposition: inline asks browsers to display the document when possible; use attachment instead if the endpoint should prompt a download.
This runnable pattern launches a browser for each request, which is simple but relatively expensive. For a service with repeated traffic, keep a browser process or pool alive and manage page/context creation per job. In either design, close pages, contexts, and the browser during shutdown or in finally blocks, and add request-level and render-level timeouts.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
Set the page dimensions and avoid clipping
Measure after layout is ready
The measured height is only as reliable as the page state at measurement time. set_content() supports readiness milestones including commit, domcontentloaded, load, and networkidle. Choose the earliest one that guarantees the required content is present. A network-idle milestone can be unsuitable for pages with ongoing network activity, while an early milestone can capture before images, fonts, or data have settled.
The example also awaits document.fonts.ready and waits for images to load or fail before measuring. If your page fetches data through JavaScript, wait for a specific application selector or state as well; a completed initial navigation does not prove that asynchronous application content is ready. Failed images should be handled deliberately if a missing asset makes the document unusable.
Use the same layout width for measuring and printing
The CSS width in the example is 800 pixels, and the PDF width is also 800 CSS pixels. Keep those values consistent: text wrapping changes with width, which changes the height. Remove default margins and decide whether padding belongs inside the document’s content. If an element extends outside the normal document flow or uses absolute positioning, check whether the root’s scroll height includes the intended content; measuring a specific report container may be more appropriate for a known template.
Understand CSS pixels and PDF units
Playwright accepts dimensions with units such as pixels, inches, centimeters, and millimeters. CSS print pixels use 96 pixels per inch, so a measured CSS height can also be converted to inches with height_px / 96. Passing the measured value directly as "{height_px}px" avoids doing that conversion explicitly. Fractional layout can cause a tiny cutoff at the bottom; if that appears in your output, add a small, deliberate safety allowance to the measured height and verify it against your browser version and fonts.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →The example imposes a 20,000-pixel cap as an adjustable safety limit, not as a universal maximum or recommended production value. Select a limit based on your content, memory budget, renderer behavior, and PDF consumers. A single enormous page may be awkward to view, print, or process even when Chromium successfully generates it.
Choose print or screen styling
PDF generation uses print media by default. That means @media print rules can affect the result even when the browser viewport is set to 800 by 1,000 pixels. This is usually desirable for documents. If the intended output is specifically the screen design, call await page.emulate_media(media="screen") before measuring and generating the PDF.
Set print_background=True when colored backgrounds or background images need to appear. Without it, the PDF can omit print backgrounds. CSS page-size rules and explicit PDF dimensions can conflict: this example sets prefer_css_page_size=False so the explicit width and height control the output. If the document’s own @page size should take precedence, use the CSS page-size preference and design the stylesheet accordingly.
When to use a tall page versus normal pagination
| Output goal | Approach | Trade-off |
|---|---|---|
| One continuous receipt, chart, or compact report | Measure the rendered height and pass an explicit PDF height. | A very tall page may be inconvenient in standard PDF viewers and printers. |
| Readable pages for printing or ordinary document navigation | Use a standard size such as A4 or Letter; do not set a content-sized page height. | The document breaks across pages, so manage page breaks and repeated headers as needed. |
| Long, JavaScript-driven web content | Use Playwright so a browser executes the page and applies browser CSS. | Chromium adds deployment and resource-management requirements. |
| Mostly static HTML/CSS without browser JavaScript | Consider WeasyPrint’s Python API, HTML(...).write_pdf(...), with CSS @page rules. |
It is a different rendering engine; validate its CSS and asset behavior with your templates. |
| Programmatically placed text, tables, charts, and drawing primitives | Consider ReportLab rather than rendering HTML. | You build the layout with PDF-oriented primitives instead of relying on browser HTML/CSS. |
There are no universal performance figures that settle the renderer choice. Benchmark your own templates and deployment if throughput or startup time matters. Playwright is a natural choice when browser fidelity or JavaScript is required; simpler static or directly drawn documents may be better served by another renderer.
Return PDFs reliably and safely
- Limit input and output work. Enforce a maximum HTML size, resource count, render duration, and resulting page height. A small request can still trigger expensive external resources or produce a very tall page.
- Constrain network access. Treat HTML, CSS, URLs, and user-provided resources as untrusted. A renderer that can navigate freely may be abused to access internal network services. Restrict destinations and network egress when rendering supplied content.
- Control asset access. Prefer known local or approved assets, and make font and image availability deterministic. If fonts load after the height measurement, line wrapping can change and the PDF may clip.
- Manage browser lifecycle. Reuse the browser process where safe, isolate concurrent work with contexts or pages, and close resources even when navigation or PDF generation raises an exception.
- Return the right response headers. Set
Content-Type: application/pdf. Add an inline or attachment disposition only according to the product’s viewing or download behavior. - Log useful failures. Record navigation timeouts, browser launch errors, rejected dimensions, and rendering exceptions without logging secrets or untrusted content unnecessarily.
Troubleshooting common failures
The PDF stops before the bottom of the page
The height was probably measured before all content affected layout, or it was measured from the wrong element. Await fonts, images, and application data before measuring; compare the root’s scroll height with the report container’s height; and confirm that the same print/screen media mode is used for measurement and PDF generation. If the cutoff is only a few pixels, add a small height allowance.
The PDF has unexpected page breaks
Check whether you are passing the measured height as an explicit PDF option and whether CSS page-size rules are overriding it. If you actually want normal pagination, remove the custom content-sized height and use a standard format. Review print styles such as page-break rules that may create intentional breaks.
Background colors or images are missing
Set print_background=True. Also inspect print-specific CSS: a page can be correctly rendered in print mode while a print stylesheet hides or changes elements that were visible on screen.
Fonts or images are missing or alter the layout
Check that the Chromium process can reach each asset and that remote resources do not require browser state or authentication. Await document.fonts.ready and image completion before measuring. For predictable output, bundle approved fonts and assets or serve them from a controlled location.
Best Value
The request hangs, times out, or exhausts resources
Set a navigation timeout and an overall job deadline, cap input and measured height, and investigate resources that keep network activity open. A per-request Chromium launch can add substantial work; reuse a managed browser process for repeated captures. Ensure a timed-out task still closes its page/context and that application shutdown closes the browser.
Chromium fails to launch in deployment
Install the Playwright browser for the same environment where the aiohttp app runs and provide required system libraries. A successful local installation does not guarantee the server container has the browser binary or operating-system dependencies.
Or skip the browser setup
If what you need is a capture of a URL rather than a custom HTML document rendered by your own application, ScreenshotNeo offers a website screenshot API that can return a screenshot or PDF. Its API can remove cookie/consent banners, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status in headers. It also has an MCP server for AI agents, with take_screenshot, get_page_info, and capture_pdf tools. The call below requests a WebP capture; consult the ScreenshotNeo API documentation for its PDF options. This is not a replacement for rendering arbitrary in-memory HTML with your own Playwright page.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo has a free allowance of 1,000 screenshots per month with no card required; paid plans start at $5 for 3,000 screenshots. All features are on every plan. Learn more at ScreenshotNeo, or sign up for the free 1,000 screenshots a month with no card.
Frequently Asked Questions
Does a full-height PDF contain one page or many?
With an explicit content-sized height, the intended result is one tall page. Check the PDF page count with the viewer or processing library you use, especially if CSS page rules or renderer behavior may affect the output.
Can I make the PDF selectable and searchable?
A PDF generated from HTML through Playwright is browser-rendered PDF output, rather than a screenshot image embedded as a page. Verify text selection with your actual fonts and content if it is a delivery requirement.
Does aiohttp need to be replaced to use Playwright?
No. aiohttp can continue handling asynchronous HTTP requests and responses; Playwright handles the browser rendering step inside the request handler.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

