{
  "version": "https://jsonfeed.org/version/1",
  "title": "Ooxml on LLBBL Blog",
  "icon": "https://avatars.micro.blog/avatars/2023/40/125738.jpg",
  "home_page_url": "https://llbbl.blog/",
  "feed_url": "https://llbbl.blog/feed.json",
  "items": [
      {
        "id": "http://llbbl.micro.blog/2026/08/18/word-documents-used-to-be.html",
        "title": "Word Documents Used to Be Filesystems",
        "content_html": "<p>Last post ended on a promise: a <code>.docx</code> is a ZIP file, and you already know how ZIP works.</p>\n<p>That&rsquo;s true, and it&rsquo;s the smaller half of the story. The interesting part is what <code>.docx</code> replaced, because the old <code>.doc</code> format was doing something strange. It wasn&rsquo;t a document. It was a filesystem with a document living inside it.</p>\n<hr>\n<h2 id=\"a-filesystem-in-a-file\">A Filesystem in a File</h2>\n<p>Here&rsquo;s a real <code>.doc</code> from 2015, 10,240 bytes. The first eight bytes:</p>\n<pre tabindex=\"0\"><code>d0 cf 11 e0 a1 b1 1a e1\n</code></pre><p>That&rsquo;s the Compound File Binary Format signature, also called OLE2. <code>file(1)</code> recognizes it and doesn&rsquo;t even mention Word:</p>\n<pre tabindex=\"0\"><code>$ file &#34;rich text.doc&#34;\nComposite Document File V2 Document, Little Endian, Os: Windows,\nVersion 1.0, Code page: -535, Revision Number: 0,\nCreate Time/Date: Thu Dec 10 13:38:22 2015\n</code></pre><p>&ldquo;Composite Document File&rdquo; is the honest description. CFBF is a container that implements directories and files, called <strong>storages</strong> and <strong>streams</strong>, inside a single flat file. It has a File Allocation Table. It has sectors. If that sounds like FAT16, that&rsquo;s because it&rsquo;s the same idea, scaled down to live inside one file on a real filesystem.</p>\n<p>Cracking this one open gives:</p>\n<pre tabindex=\"0\"><code>     106 bytes  CompObj\n      20 bytes  Ole\n     116 bytes  DocumentSummaryInformation\n     312 bytes  SummaryInformation\n    2411 bytes  1Table\n    3620 bytes  WordDocument\n\nsector size      : 2^9 = 512 bytes\nmini sector size : 2^6 = 64 bytes\n</code></pre><p>Two sector sizes, because a 512-byte sector is wasteful for a 20-byte stream. Streams under 4,096 bytes get allocated out of a separate <strong>mini-FAT</strong> in 64-byte units. There is a fragmentation strategy inside your Word document.</p>\n<p>The <code>WordDocument</code> stream is the main event, and it opens with a File Information Block whose magic number is <code>0xA5EC</code>:</p>\n<pre tabindex=\"0\"><code>WordDocument stream: 3620 bytes\n  FIB magic (wIdent) = 0xA5EC\n</code></pre><p>None of this is the text yet. This is all container.</p>\n<hr>\n<h2 id=\"the-text-is-not-in-order\">The Text Is Not in Order</h2>\n<p>You&rsquo;d expect the document&rsquo;s text to sit in the <code>WordDocument</code> stream in reading order. It doesn&rsquo;t. It sits there in <strong>edit order</strong>, and a separate structure called a <strong>piece table</strong> says how to reassemble it.</p>\n<p>The piece table is a list of descriptors, each saying &ldquo;characters at logical position X through Y live at physical offset Z.&rdquo; Reading a <code>.doc</code> means walking that table and gathering fragments scattered through the stream.</p>\n<p>Why build it that way? Because of a feature called <strong>Fast Save</strong>, and because in 1990 writing to disk was slow. When you edited a document, Word didn&rsquo;t rewrite the file. It appended your new text to the end of the stream and updated the piece table to point at it. Saving a one-word change to a 200-page document meant writing a few dozen bytes instead of a few hundred kilobytes.</p>\n<p>That&rsquo;s a good optimization. It has an obvious and terrible consequence.</p>\n<p><strong>The old text is still in the file.</strong> Deleting a paragraph removed it from the piece table, not from the stream. The bytes stayed exactly where they were, unreferenced, invisible in Word, and completely readable in a hex editor.</p>\n<p>Microsoft documented this themselves, in a knowledge base article about minimizing metadata in Word documents: <em>&ldquo;Because of the design of the FastSave feature, text that you delete from a document may remain in the document, even after you save the document.&rdquo;</em> The recommended fix was to go into Options and clear the &ldquo;Allow fast saves&rdquo; check box. From Word 97 SR-1 onward they turned it off by default.</p>\n<p>For years, &ldquo;open the document in a text editor and scroll&rdquo; was a functioning technique for reading text someone believed they had deleted. Every organization circulating Word files was potentially shipping its own edit history.</p>\n<p>The piece table itself has a respectable pedigree. Charles Simonyi brought the technique to Microsoft from Xerox PARC&rsquo;s Bravo editor, and it&rsquo;s an elegant way to represent an editable buffer. It&rsquo;s still how many text editors model documents in memory. The mistake wasn&rsquo;t the data structure. The mistake was persisting the whole scratch buffer to disk and shipping it to other people.</p>\n<hr>\n<h2 id=\"then-it-became-a-zip-of-xml\">Then It Became a ZIP of XML</h2>\n<p>Office 2007 replaced all of it with the Open Packaging Conventions: ECMA-376, later ISO/IEC 29500. A <code>.docx</code> is a ZIP archive containing XML.</p>\n<p>Every <code>.docx</code> opens with the same four bytes:</p>\n<pre tabindex=\"0\"><code>50 4b 03 04    &lt;- PK\\x03\\x04, a ZIP local file header\n</code></pre><p><code>PK</code>. Phil Katz&rsquo;s initials, from the last post, sitting at byte zero of every Word document written since 2007.</p>\n<p>Unzip one and the structure is legible:</p>\n<pre tabindex=\"0\"><code>[Content_Types].xml\n_rels/.rels\nword/document.xml\nword/_rels/document.xml.rels\nword/styles.xml\nword/settings.xml\nword/fontTable.xml\nword/theme/theme1.xml\ndocProps/core.xml\ndocProps/app.xml\n</code></pre><p><code>word/document.xml</code> holds the text. <code>[Content_Types].xml</code> maps each part to a MIME type. <code>_rels/.rels</code> is a relationship graph saying which part is the main document and how the parts connect. The whole thing is a tiny website, zipped.</p>\n<p>The text itself is WordprocessingML:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-xml\" data-lang=\"xml\"><span style=\"display:flex;\"><span><span style=\"color:#f92672\">&lt;w:p&gt;</span>\n</span></span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&lt;w:r&gt;</span>\n</span></span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">&lt;w:t&gt;</span>Hello, World!<span style=\"color:#f92672\">&lt;/w:t&gt;</span>\n</span></span><span style=\"display:flex;\"><span>  <span style=\"color:#f92672\">&lt;/w:r&gt;</span>\n</span></span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">&lt;/w:p&gt;</span>\n</span></span></code></pre></div><p>A paragraph containing a run containing text. Verbose, but you can read it, and more importantly a program you wrote in an afternoon can read it. That is important when building foundational file formats that outlive the creators.</p>\n<p>Extracting text from a <code>.doc</code> meant implementing a filesystem and a piece table. Extracting text from a <code>.docx</code> means unzipping and finding <code>&lt;w:t&gt;</code> elements.</p>\n<p>The XML contains the document, not the document&rsquo;s history. Deleted text is deleted.</p>\n<hr>\n<h2 id=\"xml-did-not-mean-simple\">XML Did Not Mean Simple</h2>\n<p>It would be tidy to end on &ldquo;and then it got clean.&rdquo; The specification runs to several thousand pages, and the ISO fast-track that pushed it through in 2008 was contentious enough to deserve its own post.</p>\n<p>What matters here is the shape it settled into. The standard shipped split in two: <strong>Strict</strong>, the clean format, and <strong>Transitional</strong>, which carries the legacy baggage forward so documents converted from the binary era still render correctly.</p>\n<p>Guess which one nearly everything emits.</p>\n<p>Open a Transitional document&rsquo;s settings and you find a <code>&lt;w:compat&gt;</code> block. Its children are a museum:</p>\n<pre tabindex=\"0\"><code>w:truncateFontHeightsLikeWP6    WordPerfect 6\nw:suppressTopSpacingWP          WordPerfect\nw:lineWrapLikeWord6             Word 6\nw:autoSpaceLikeWord95           Word 95\nw:footnoteLayoutLikeWW8         Word 97\nw:useWord97LineBreakRules       Word 97\nw:mwSmallCaps                   Mac Word\n</code></pre><p>Every one of those is a flag asking the renderer to reproduce how a specific piece of 1990s software behaved. Not what the format should do. What Word 6 <em>did</em> do, quirks included. Implementing this correctly means emulating applications whose behavior was never written down anywhere.</p>\n<p>The bugs were load-bearing, so they got standardized. The format stopped being a filesystem, but it did not stop being a thirty-year-old application&rsquo;s memory dumped to disk. It just picked a more legible way to write it down.</p>\n<p>Which is, in fairness, an enormous improvement. You can read the file now. You just can&rsquo;t read all of it quickly.</p>\n<h2 id=\"sources\">Sources</h2>\n<ul>\n<li><a href=\"https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b\">MS-CFB: Compound File Binary Format</a> — Microsoft&rsquo;s spec for the OLE2 container</li>\n<li><a href=\"https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/ccd7b486-7881-484c-a137-51170af7cc22\">MS-DOC: Word Binary File Format</a> — the FIB, the piece table, and the stream layout</li>\n<li><a href=\"https://ecma-international.org/publications-and-standards/standards/ecma-376/\">ECMA-376</a> — Office Open XML, the basis for <code>.docx</code>, and free to download. This is the same specification ISO published as ISO/IEC 29500, so read it here rather than paying ISO for the identical text</li>\n<li><a href=\"https://www.loc.gov/preservation/digital/formats/fdd/fdd000395.shtml\">Library of Congress format description for OOXML</a> — preservation notes and format history</li>\n<li><a href=\"https://jeffpar.github.io/kbarchive/kb/223/Q223790/\">KB Q223790: WD97: How to Minimize Metadata in Word Documents</a> — the fast-save warning, archived; Microsoft no longer hosts it</li>\n<li><a href=\"http://www.datypic.com/sc/ooxml/t-w_CT_Compat.html\"><code>w:compat</code> schema reference</a> — the full list of compatibility settings, browsable without downloading the spec</li>\n</ul>\n<blockquote>\n<p>I&rsquo;d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at <a href=\"https://micro.blog/llbbl?remote_follow=1\">@logan@llbbl.blog</a>.</p>\n</blockquote>\n",
        "date_published": "2026-08-18T10:00:00-05:00",
        "url": "https://llbbl.blog/2026/08/18/word-documents-used-to-be.html",
        "tags": ["Programming","File-formats","Word","Docx","Ooxml"]
      }
  ]
}
