Measured9 min read

Converting Word to PDF: what survives, and what leaks

The received wisdom about converting Word documents outside Word is that the layout drifts and the fonts go wrong. I built twelve minimal documents, each isolating one feature, and pushed them through the same headless LibreOffice this site uses. The font problem turned out to be largely solved, and by a mechanism I did not know about. The thing that did go wrong is not a layout problem at all. It is a disclosure problem, and if you send client documents as PDFs you should read the tracked changes section.

How this was measured

The photo benchmark in the last article had a weakness: the corpus was my own files, so nobody else can reproduce the exact table. This one has no such problem. Every document is generated by the script, in code, as minimal OOXML: [Content_Types].xml, a relationships part, and a document.xml carrying one feature and nothing else. No template, no Word, no hand-made fixtures to go stale.

node scripts/measure-docx.mjs

LibreOffice via src/lib/soffice.ts, the same path /convert/docx-to-pdf uses.
Inspection with poppler: pdftotext, pdfinfo, pdffonts.
A4, 1 inch margins, so page geometry is comparable across documents.

One honest limitation up front: Microsoft Word is not installed on this machine, so nothing below is a Word-versus-LibreOffice comparison. What it compares is what the document declares against what actually came out of the converter. That is the comparison that matters if you are handing a file to a server, because the server does not have Word either.

Font substitution is a solved problem, and I had it backwards

The fear is reasonable. A DOCX does not usually carry its fonts; it names them and assumes the machine opening it has them. Word on Windows has Calibri. A Linux server does not. So what does the server do?

One paragraph per font, each declaring a typeface a Word user picks without thinking.
Document asks forPDF actually containsRendered width
CalibriCarlito215.7 pt
CambriaCaladea228.9 pt
ArialArialMT233.8 pt
Times New RomanTimesNewRomanPSMT216.6 pt
GeorgiaGeorgia235.3 pt
NotARealFont123Liberation Serif216.6 pt

Width is the rendered extent of one identical 12pt string, 'Hamburgefonstiv 12345 the quick brown fox', measured from the PDF text bounding boxes with pdftotext -bbox. All six faces are subset-embedded into the output, so the PDF renders the same anywhere even though the source document guaranteed nothing.

Look at the two highlighted rows. A font that does not exist and Times New Roman render at exactly the same width, to a tenth of a point. That is not luck.

Which reframes the actual risk. It is not that fonts get substituted, it is which font you started from. Calibri, Cambria, Arial and Times New Roman all have a metric-compatible stand-in, so a document built from Word's defaults comes through with its line breaks intact. A brand typeface, a purchased display face, or anything the render machine has never seen falls through to Liberation Serif at Times metrics. It will not crash, it will not warn you, and it will not look like your document.

The fix, if your document depends on a specific face, is to embed the fonts in the DOCX before converting rather than to hope. Word can do that, and it is the difference between a guarantee and a coincidence.

Tracked changes come through, and they bring the deletions

This is the finding worth the whole exercise. A document with one unaccepted edit: the phrase “4 percent” deleted, the word “healthy” inserted in its place. With changes accepted, the sentence reads Our margin is healthy this year.

Here is the text layer of the PDF that came out:

document, changes accepted:  "Our margin is healthy this year."
PDF text layer contains:     "Our margin is 4 percenthealthy this year."

deleted text present in the PDF: YES

And here is how that page renders:

A rendered PDF line reading 'Our margin is 4 percent healthy this year', where '4 percent' is struck through in orange and 'healthy' is underlined in orange, with a vertical change bar in the left margin.
The actual first line of the converted PDF, rendered at 110 dpi and cropped. The strikethrough, the colour and the change bar in the margin are all in the output file.

The converter exported the document with markup showing. The number you removed is struck through rather than gone, it is a real string in the PDF text layer, and it survives copy and paste, text extraction and search. Anyone who opens that PDF can read the figure you deleted and see who deleted it.

This is not a bug. Showing markup is a legitimate default, and it is what you want when the PDF is for a reviewer. It is only dangerous because the person converting usually assumes the opposite, and because nothing in the process asks.

Comments, by contrast, do not come through

I expected the same problem from comments and got the opposite result. A document with a comment reading INTERNAL: do not send to the client anchored to a public sentence produced a PDF whose entire text layer is "Public sentence." The comment body is not in the output.

So the two features most people mentally file together behave differently: comments are dropped, tracked deletions are printed. Worth knowing in that direction specifically, because guessing the safe one wrong is the expensive mistake.

The parts that quietly do nothing

A header and footer part that exists in the package but is not referenced from the section's sectPr renders nothing at all. The PDF text layer came back as "Body text." and no more.

That is correct behaviour, and it is also the shape of a whole class of confusing bugs. A DOCX is a zip of parts wired together by relationship files, and a part that is present but unreferenced is invisible. Files assembled by export tools, templating libraries or scripts get this wrong regularly, and the symptom is not an error. It is a header that silently is not there. If something you can see in Word does not appear in the PDF, the first thing to check is whether it is actually wired into the document rather than merely present in it.

What it costs

A one page document through the pipeline this site runs.
MeasureValue
Output PDF11,907 bytes
Warm conversion1,181 ms
Fonts embedded6 of 6, subset
Cold startmuch slower, builds the LO profile

Warm means LibreOffice's user profile already exists. The cold number is deliberately not quoted here because it depends entirely on the host: on our server that first run once blew a 30 second timeout, which is why the profile is now built once and reused. scripts/check-doc.mjs asserts that property.

Twelve kilobytes for a page of text, with six typefaces subset-embedded into it, is a good trade. It also explains something people find surprising when they convert a DOCX to PDF: the PDF is often larger than the DOCX, because the DOCX named its fonts and the PDF has to carry them.

What I would actually tell someone

  • Accept or reject tracked changes first. This is the only item on the list that can embarrass you. Everything else is cosmetic.
  • Word's default fonts are safe. Calibri, Cambria, Arial and Times New Roman have metric-compatible substitutes and your line breaks will hold.
  • A brand font is not safe. Embed it in the DOCX before converting, or accept that the output lands on Liberation Serif.
  • Check the first and last page, not the whole file. If the fonts resolved, the middle is fine. If they did not, you will see it immediately.
  • Comments are dropped, so do not rely on them surviving if you wanted them to.

Our own DOC and DOCX conversions run on the server rather than in your browser, because a headless LibreOffice is doing the work and there is no version of that which fits in a tab. That means the file reaches us, is converted in memory, and is discarded when your download starts. Given what the tracked changes result shows about what a document can be carrying, that is worth stating plainly rather than leaving in the privacy policy.

The reproduction is one command, and if your LibreOffice has a different font set than mine the first table will be different, which is the point of publishing the method along with the numbers. The other article in this series measured what every image format costs on real photographs.

Try it on your own files

Most image conversions run in your browser and never leave your device.

Open the converter

Read next

The attachment limit is not the number you were told