Convert HTML to PDF

Convert an .html or .htm file into a paginated PDF in your browser. Unfamiliar HTML renders inside a sandboxed iframe with scripting switched off.

🌐 Español

Drop your file here (.html, .htm)

πŸ”’ Private by design: your files are processed locally in your browser and never uploaded to any server.

No print engine in a browser tab, so the page gets photographed

Every desktop and server side HTML to PDF converter leans on the same thing, a full browser print pipeline, the machinery behind the print dialog that lays text out into paper sized boxes and emits real PDF text runs. That machinery is not reachable from ordinary JavaScript. A page can ask the browser to open its own print dialog, but it cannot drive that dialog, choose an output file or read the resulting bytes.

So this tool does what every honest client side converter does. It lays your markup out at the exact width of the paper you chose, rasterizes the result into one very tall bitmap, cuts that bitmap into page sized slices and places each slice onto a PDF page. What you get looks precisely like the page rendered, styling and images and tables intact, and the text in it is part of a picture rather than text you can select, search or copy.

That trade is fine for an invoice, a report snapshot or an archived page. It is the wrong trade when the words matter as words. If your source is plain text rather than markup, txt-to-pdf draws real font runs with pdf-lib and produces selectable output instead.

The sandbox attribute that makes an unfamiliar file worth opening

Converting markup somebody sent you is a different risk from converting your own. Markup can carry executable code in three shapes at least, script elements, inline event handler attributes and javascript URLs, and the naive way to render it for a screenshot, dropping it into this page’s own document, would run all three with full access to the site.

Instead the markup is handed to an iframe through its srcdoc property, and that frame carries a sandbox attribute containing exactly one token, the same origin one. The scripting token is absent, and per the sandboxing rules a browsing context without it has its scripting flag disabled outright. This is stronger than filtering tags, because there is no interpreter to reach. The single token that is present is needed for the opposite direction, since the capture step has to read the frame’s document and computed styles synchronously, which a document forced into an opaque origin would refuse. Once the bitmap is taken the frame is removed from the page.

If you would rather sanitize than sandbox, markdown-to-html runs its output through DOMPurify and gives you markup with the dangerous constructs already stripped.

From an .html file to a paginated PDF

  1. Drop your .html or .htm file onto the box above, or press Choose a file. Other extensions are turned away before anything runs, so an .xhtml or .mhtml file has to be renamed or re-saved first.
  2. Pick a Page size, A4 or US Letter, and an Orientation, Portrait or Landscape. The choice fixes the width your layout is measured at, not just the paper.
  3. Set Quality to Standard (faster, smaller file) or High (sharper, larger file).
  4. Press Convert HTML to PDF. The bar moves once the layout is measured, again once the capture completes, and then in steps as each page is written.
  5. Download the result, which keeps your file’s base name with a .pdf extension, so invoice.html arrives as invoice.pdf.

Assets that will not make it into the capture

The frame receives your markup as a string, with no folder behind it, so every relative reference in the file is resolved against the address of this page and fails. Absolute links to another site are a mixed bag: a stylesheet on a CDN loads and applies normally, but a picture is only drawn when that server permits a cross origin read of it, which many image hosts do not. Fonts are looser still, because the capture waits briefly for layout and lets the imaging step wait for the pictures it is about to draw, but nothing waits for a web font to finish downloading, so a face that arrives late is simply not the one that gets drawn.

The reliable shape for a file you intend to convert is self contained. Put your CSS in a style element in the head, and inline your pictures as data URIs, which image-to-base64 will produce for you along with a ready made img snippet. A table built in html-table-generator can be exported straight to a .html file from its download button and converted here without touching an editor.

Where the page breaks land, and why CSS cannot move them

Pagination happens after rendering, on the bitmap, which is why it is both dependable and blunt. The captured width divided by the paper width in points gives a pixels per point figure, the paper height multiplied by that figure gives the height of one page in captured pixels, and the tall image is cut into that many equal slices with a shorter remainder at the end. For A4 portrait at Standard that slice is a fraction under 1123 pixels tall.

The last slice is placed at its true proportional height rather than stretched to fill the sheet, so a short final page ends with white space instead of a distorted footer. Nothing inspects the content while cutting, so page break properties in your stylesheet are ignored and a table row or a heading can land across the join. Adding vertical padding above whatever keeps getting split is the only lever you have from the source.

For Markdown there is a shorter road. markdown-to-pdf wraps your text in one of three themes and hands the finished markup to this exact rendering path, so converting to markup first and then converting again is only worth it when you want to edit the intermediate file. The finished PDF can then be merged, compressed or recolored with the rest of the PDF tools.

See it in action

Screenshot of the Convert HTML to PDF tool with sysfenix-sample.html (262 B) loaded, Page size set to A4, Orientation set to Portrait
Convert HTML to PDF mid-process: sysfenix-sample.html (262 B) loaded, Page size set to A4, Orientation set to Portrait.
Screenshot of the Convert HTML to PDF result screen showing sysfenix-sample.pdf ready to download (15 KB, 5882% larger)
The finished result: sysfenix-sample.pdf ready to download (15 KB, 5882% larger). The download link is a local blob URL β€” the file never leaves your device.

Frequently asked questions

If the file contains a script tag, does it run during the conversion?

It does not. The markup is loaded into an iframe whose sandbox attribute grants same origin access and nothing else, and without the scripting token the browser gives that document no JavaScript engine at all. Script tags are inert, inline handlers such as onerror and onload are never wired to anything, and javascript URLs do not navigate. The restriction also cascades into any nested frame created inside it, because a sandboxed document cannot hand out permissions it does not hold.

How wide is my HTML laid out before it is captured?

The hidden frame is sized to match the paper you picked, using the standard ratio of 96 CSS pixels to an inch. A4 portrait gives 794 pixels of usable width, US Letter portrait gives 816, and the landscape versions of the two give 1123 and 1056. Media queries and responsive layouts react to that width rather than to your monitor, so a design whose mobile breakpoint sits at 800 pixels lays out in its mobile form on A4 portrait, whose 794 pixels fall under it, and in its desktop form on US Letter's 816.

Some images came out as blank gaps. What causes that?

Two separate things, and it is worth knowing which one you hit. A path such as images/logo.png is resolved against the address of this page rather than the folder your file sits in, because markup handed to a frame this way has no directory of its own to work from. Separately, a picture pulled from another website is only drawn when that server permits cross origin reads, and many do not. Embedding images as data URIs sidesteps both.

A heading was sliced in half across two pages. Can I control the break?

Not from the source file. This is not a print pipeline, so the CSS properties that request page breaks have nothing listening for them. The tall capture is cut into equal slices at the paper height and each slice becomes a page, which means a break falls wherever the arithmetic puts it. Inserting vertical space above the element that keeps getting cut is the practical fix.

My accented characters turned into strange symbols. How do I fix it?

The file is read as UTF-8, with no attempt to detect a legacy encoding, so a document saved as Windows-1252 or ISO-8859-1 will show mangled characters wherever it leaves plain ASCII. Re-save it as UTF-8 in your editor and add a matching charset meta tag while you are there. Anything already stored as an HTML entity is unaffected.

What changes when I pick High quality?

Only the density of the capture. Standard rasterizes one device pixel per CSS pixel, High rasterizes two, so the bitmap doubles in both directions and the file grows accordingly. The page count does not change, because the slicing works from the ratio between the captured width and the paper width rather than from a fixed pixel figure, so the same document splits into the same number of pages at either setting.

Related tools