DEV Community

hao jia
hao jia

Posted on

I ship documents as one offline HTML file. Here is what I verify.

A partner team once replied to my technical proposal with two words: can't open. Their office machine ran an older Office build, and the 36-row table in that document came apart on their screen. On my machine it looked fine.

That was not a one-off. A few months earlier, someone edited the numbers in a proposal we sent and forwarded it on, and the argument came back to us. So sending the original file carries two separate risks: they can't open it, and they can change it.

These days, for any document that is finished and will not be revised again, I ship a self-contained read-only HTML file alongside the original. One file. Double-click and it opens. No Office, no network, no login, and no editing.

The interesting part is not the idea. It is that the acceptance criteria suddenly became measurable. "They can open it" was never something I could check. "Their machine has nothing installed and the file still holds up" is.

What I test against

I did not want to experiment on a real project document, so I built a sample that looks like the proposals we normally write. Everything in it is fictional — company name, order numbers, amounts, people. I packed it with the things that usually break: A4 layout, six page breaks, a three-level numbered list, a 36-row six-column table, headers and footers, four footnotes, three images, one equation, and a WordArt cover title. 28,516 bytes.

I converted it with ImgIng (https://imging.ai/), which runs DOCX to HTML in the browser. I kept the network panel open across six runs: zero non-GET requests from import through conversion. The result is one 85,903-byte HTML file with no sibling asset folder.

The file growing by three times means nothing, by the way. A .docx is a zip archive and the output is an uncompressed single file. The comparison that matters is what the other side has to install.

The three things I actually check

One file, no folder. Plenty of "save as web page" exports hand you an HTML plus a directory full of images and stylesheets. Attach that to an email and drop the folder, and the whole thing collapses.

Zero external resources. No external script, link or img. Any one of them is a network request that will eventually fail — offline laptop, corporate proxy, a CDN path that moved two years after you shipped the file. In this output there are none. The three images are base64-inlined and take 31.0% of the bytes. Body and styles are 56.8%, script is 12.2%.

What is inside that single HTML file: body and styles 56.8%, inlined images 31.0%, script 12.2%, zero external resources and zero parser leftovers

The body survives with JavaScript off. This is the one I care about most, because the recipient's browser may be locked down by policy, may have a blocker, or may be an embedded webview.

There is also a sneakier failure mode worth naming: an export that stuffs the whole .docx plus a parser into the HTML and unzips it in the browser on open. That is technically one file and technically offline, but the text is not in the file's static bytes — and you just shipped the original anyway. I grepped for JSZip, fflate, pako, mammoth, inflateRaw, the OOXML namespace, <w: tags, the base64 zip header UEsDB, and .wasm. All zero.

The JavaScript-off comparison

Desktop viewport, 1440×1000. Same file opened twice, once with scripts on and once off.

My first measurement was wrong and it is worth admitting. I used document.body.innerText as the word count and got 5,997 with scripts on versus 3,224 with them off. I was ready to write that half the body was gone. Then I noticed the thumbnail rail on the left, which re-renders every page when scripts are on. Those 2,773 characters were duplicates.

Measuring only the content container gave the real answer: 9,822 bytes and 326 lines on both sides, and a line-by-line diff with zero lines unique to either. Select-all still copies 3,178 characters, the same number both ways. Seven pages, per-page headers, 37 table rows with 222 cells, three images and four footnotes all stay in place, with no console errors.

The same output HTML, JavaScript on at left and off at right, zero line-level differences in the body, still 3,178 selectable characters, 1440×1000 desktop viewport

I only verified this at desktop width. Narrow viewports behave differently and I am not extrapolating.

I also compared text completeness against the source: 286 visible paragraphs pulled from document.xml, 285 matched character for character in the output. The one that did not is a footnote superscript spliced into the middle of a sentence — nothing lost, just a marker added. The equation came through as MathML and stays selectable, which was better than I expected.

What it costs

Three places broke on my sample and I check all three before sending anything. The footer page-number field is not re-evaluated, so all seven pages read "page 1 of 10" — the cached value from the source file. The WordArt cover title converts to a blank block that still takes up its space. The embedded Excel object disappears entirely. The 36-row table also does not split across pages; instead page four stretches from 1123 px to 1733 px, which is invisible on screen and overflows on paper.

Embedded fonts do not travel either. I put a full font into a second sample using the proper embedding mechanism; the output has zero @font-face rules and keeps only the font name. That is worse than it sounds, because page heights are computed at conversion time and written into the markup. If the recipient happens to have a font with that name, text grows 45% to over 100% in height while the page shell does not move, and content pushes past the boundary. The practical rule: do not use non-system fonts in documents you send outside the company.

One thing went better than expected. I planted a VBA macro project, an OLE object and an ActiveX control in a sample, each with a unique marker string. Searching the output for those markers in plain text and again after base64-decoding every blob returns zero hits, with no CFB or ZIP file headers either. The delivered file carries nothing executable, which means one fewer security review on our side.

What I will not claim is that it looks identical to Word. I never compared against an actual Office render, pixel or page count, and pagination depends on font metrics and printer setup anyway. The three claims I will stand behind are narrower and each one is measurable: one file, zero external resources, body intact with scripts off.

Top comments (0)