Turn Markdown into a PDF

Drop in a .md file or paste your Markdown, and get back a PDF that is set like a document: headings, lists, tables, code blocks and links that still click. It is built in this browser tab and your text is never uploaded.

How it works

Markdown is the format people write in when they want to think about the words rather than the formatting. A README, a set of meeting notes, a changelog, a specification, a piece of homework. It is plain text with a few marks in it, which is exactly what makes it good to write and awkward to hand to somebody else. The person who needs to read it wants a document, and what you have is a file full of hashes and asterisks.

This turns one into the other in your browser tab. The Markdown is parsed into headings, paragraphs, lists, quotes, code blocks and tables, and then each of those is set on the page: a size and a weight for every heading level, a rule under the top two so the sections of a long document are findable, a measure that stays comfortable to read, and a page break that never leaves a heading stranded at the foot of a page with its paragraph overleaf.

The one thing you choose is the paper, A4 or US Letter, because that depends on where you are and where the document is going. Everything else is decided, and deliberately so: nobody converting a README wants to pick a leading or a margin. Both sizes keep a half-inch margin, which is roughly the strip a home printer physically cannot print on, so the result prints as well as it reads.

Only the fonts that every PDF reader already has are used, which is why the file comes out small and needs nothing embedded. That has one honest cost: those built-in fonts cover Latin and accented European text but not Chinese, Japanese, Korean, Cyrillic, Greek, Hebrew or Arabic. If your document contains any of those, the tool tells you which characters it could not draw instead of quietly handing you a page of question marks.

How to use it

  1. 1 Choose a .md file, or paste your Markdown into the box. You can edit it there before converting.
  2. 2 Pick the paper: A4 or US Letter.
  3. 3 Press Make the PDF.
  4. 4 Download it, and open it once to check the headings and any tables came out as you expected.

Good to know

  • The first level-one heading becomes the document title and the name of the downloaded file, so start your file with a single # line.
  • Tables, fenced code blocks and nested lists are all supported. A long code line wraps rather than running off the page.
  • Links keep working: they are written into the PDF as real link annotations, not just blue text.
  • Images written as ![alt](path) are skipped. Fetching them would mean a network request, and this tool does not make any.
  • Accented European text is fine. CJK, Cyrillic, Greek, Hebrew and Arabic are not, because the built-in PDF fonts do not contain those letters. The tool lists what it had to replace.

Browser support

  • Chrome 94 and later
  • Firefox 93 and later
  • Safari 16.4 and later
  • Edge 94 and later

These are the oldest versions with everything the tool uses. They are worked out from the tool itself, so they change when it does.

Version numbers checked on 2026-09-04.

Why the asterisk in "2 * 3 * 4" is not italics, and other things a Markdown renderer has to get wrong first

The first version of the inline parser here turned "2 * 3 * 4 is 24" into an arithmetic expression with an emphasised 3 in the middle, and ate both asterisks doing it. That is the failure every naive Markdown implementation ships with, because the obvious algorithm is to find a marker and then scan forward for the next one. CommonMark solves it with delimiter runs and flanking rules, and the two that matter are short: an opening delimiter is never followed by whitespace, and a closing delimiter is never preceded by it. A lone asterisk with spaces on both sides can therefore open nothing and close nothing, which is exactly the behaviour a multiplication sign or a footnote marker needs. The rule also has to be applied to the run rather than to the character. When the parser advanced by one character after a failed "**", the trailing half of that run was still available to close as a single "*", and "**a ** b" came out as an emphasised "a *". Consuming the whole run on failure is what fixes it, and there is a test named for each of those two cases because both were real bugs rather than hypothetical ones.

The second thing that has to be got wrong first is the position of a list bullet. The obvious implementation records the cursor before rendering an item and puts the marker one line-height below it, which works until an item begins with a paragraph gap, or breaks across a page, or opens with a code block. All three shift the first line somewhere the cursor never predicted, and the bullet ends up floating above its own text. What works instead is to let the drawing code report where it actually put the first line, which is the only place that knows. That reporting has to be a stack rather than a single slot, because a list nested inside a list item opens its own capture while the outer one is still waiting: with one slot, the inner item overwrote the outer answer, so the outer bullet landed on the nested line and its own line got none. Rendering the sample document is what surfaced that, not the test suite, which is the argument for looking at the output rather than at a green tick.

Word spacing exposed a subtler problem, and the fix for it is the reason the files come out about forty per cent smaller. This renderer uses only the fourteen fonts every PDF reader already has, which means the document carries no width table: the reader lays out text using its own copy of Helvetica, and its glyph advances differ from the ones pdf-lib uses to measure by a fraction of a point per character. Placing every word at its own absolute x made that difference visible at every single space, because each gap was computed by one set of metrics and rendered against another. Drawing each run of same-styled words as one string instead lets the reader space the inside of the run, and leaves only the boundaries between styles positioned by us. Measured on a sample page, the gaps went from varying between 1.5 and 3.2 points to being exactly 3.058 everywhere.

The built-in fonts have a harder limit that cannot be engineered around, only reported. They are WinAnsi encoded, which covers printable ASCII, the Latin-1 range and about thirty typographic characters that live between 0x80 and 0x9F: curly quotes, the en and em dashes, the bullet, the ellipsis. Everything else, which includes all of Chinese, Japanese, Korean, Cyrillic, Greek, Hebrew and Arabic, would make pdf-lib throw when it tried to encode the string. Throwing at the user would be one honest answer and substituting silently would be a dishonest one, so the encoder does neither: characters with a real ASCII equivalent are folded (a non-breaking hyphen becomes a hyphen, a fraction slash becomes a slash), anything else is replaced with a question mark, and every distinct replaced character is collected and shown. The alternative is embedding a Unicode font, which costs several hundred kilobytes and a second library, and that is a different trade than the one this tool makes.

Pagination is one object rather than a rule scattered through the block renderers. Every block asks the writer to reserve the vertical space it needs before it draws, and the writer decides whether that means a new page, which is what keeps a heading from being set at the foot of a page with its paragraph overleaf: a heading reserves its own height plus the first line of whatever follows it. Two blocks need more than that. A fenced code block draws its tinted plate per page rather than as one tall rectangle, because a listing that spans a break would otherwise have its background hanging off the bottom of the first page and missing from the second. A blockquote does the same with its left bar, closing it on the old page and starting a fresh one at the top of the new. Table rows reserve per row, so a long table breaks between rows and never through the middle of one.

Table columns are sized from their contents, and when the natural widths overflow the measure the surplus is taken from the widest column repeatedly rather than shared equally. Sharing it equally is the intuitive approach and it is wrong: it squeezes a column containing "Yes" and "No" down to nothing in order to spare a column of prose that had room to give. Each column also has a floor of about four characters, and the loop stops when every column has reached it, which means a genuinely impossible table (twelve columns of paragraphs) degrades to something cramped rather than to something with negative widths. Links are written as real link annotations in the page object rather than as blue text, because a document whose links only look like links is a document whose links do not work, and there is a test that reads the URI back out of the annotation dictionary rather than searching the file for the string: pdf-lib writes compressed object streams, so the text is not in the clear and grepping for it passes for the wrong reason.

Frequently asked questions

How do I convert Markdown to PDF without uploading it?

Paste your Markdown here or choose the file. It is parsed and typeset into a PDF inside your browser tab by your own device, and handed back to you as a download. You can confirm it in your browser’s developer tools, on the Network tab: no request carries your text.

Does it keep my tables and code blocks?

Yes. GitHub-style tables are drawn with their column alignment honoured and their columns sized from the content, and fenced code blocks are set in a monospaced face on a tinted plate. A code line too long for the page wraps instead of being cut off at the margin.

Do the links still work in the PDF?

Yes. Every [text](url) link becomes a real link annotation in the document, so it is clickable in any PDF reader, as well as being coloured and underlined so you can see it is a link on paper.

Will my images be included?

No. An image in Markdown is a reference to a file somewhere else, and following it would mean a network request. This tool does not make any (that is the whole point of it), so image references are skipped. Use the image to PDF tool for pictures, and merge the two documents if you need both.

Why did some characters become question marks?

The PDF uses the fonts that every reader already has, which keeps the file small and means nothing has to be embedded. Those fonts cover Latin and accented European text but not Chinese, Japanese, Korean, Cyrillic, Greek, Hebrew or Arabic. When a character is outside what they can draw, it is replaced and the tool lists exactly which characters those were rather than letting you discover it later.

Can I control the font size or the margins?

No, and that is deliberate. The tool asks what the page should be and works the rest out, because a document that has been typeset consistently reads better than one assembled from a dozen sliders. If you need full control over the design, this is the wrong shape of tool for that job.

What Markdown does it understand?

Headings both with hashes and underlined, paragraphs, bold, italic, inline code, links, bullet and numbered lists including nested ones, blockquotes, fenced code blocks, horizontal rules and tables. It is not a full CommonMark implementation: it covers what people actually write in a README or a set of notes, and it parses only what it can also draw, so nothing is quietly lost.

Is there a size limit?

No fixed one. The whole document is held in the tab while the PDF is built, so a very long file will eventually run a modest phone out of memory, but ordinary documents (a README, a report, a hundred pages of notes) are not close to that.

Related tools

Your files are processed on your device and are not uploaded. You can verify this in your browser’s developer tools under the Network tab.