<rss version="2.0">
  <channel>
    <title>Word on LLBBL Blog</title>
    <link>https://llbbl.blog/categories/word/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Tue, 18 Aug 2026 10:00:00 -0500</lastBuildDate>
    
    <item>
      <title>Word Documents Used to Be Filesystems</title>
      <link>https://llbbl.blog/2026/08/18/word-documents-used-to-be.html</link>
      <pubDate>Tue, 18 Aug 2026 10:00:00 -0500</pubDate>
      
      <guid>http://llbbl.micro.blog/2026/08/18/word-documents-used-to-be.html</guid>
      <description>&lt;p&gt;Last post ended on a promise: a &lt;code&gt;.docx&lt;/code&gt; is a ZIP file, and you already know how ZIP works.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s true, and it&amp;rsquo;s the smaller half of the story. The interesting part is what &lt;code&gt;.docx&lt;/code&gt; replaced, because the old &lt;code&gt;.doc&lt;/code&gt; format was doing something strange. It wasn&amp;rsquo;t a document. It was a filesystem with a document living inside it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;a-filesystem-in-a-file&#34;&gt;A Filesystem in a File&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a real &lt;code&gt;.doc&lt;/code&gt; from 2015, 10,240 bytes. The first eight bytes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;d0 cf 11 e0 a1 b1 1a e1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s the Compound File Binary Format signature, also called OLE2. &lt;code&gt;file(1)&lt;/code&gt; recognizes it and doesn&amp;rsquo;t even mention Word:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ file &amp;#34;rich text.doc&amp;#34;
Composite Document File V2 Document, Little Endian, Os: Windows,
Version 1.0, Code page: -535, Revision Number: 0,
Create Time/Date: Thu Dec 10 13:38:22 2015
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;ldquo;Composite Document File&amp;rdquo; is the honest description. CFBF is a container that implements directories and files, called &lt;strong&gt;storages&lt;/strong&gt; and &lt;strong&gt;streams&lt;/strong&gt;, inside a single flat file. It has a File Allocation Table. It has sectors. If that sounds like FAT16, that&amp;rsquo;s because it&amp;rsquo;s the same idea, scaled down to live inside one file on a real filesystem.&lt;/p&gt;
&lt;p&gt;Cracking this one open gives:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     106 bytes  CompObj
      20 bytes  Ole
     116 bytes  DocumentSummaryInformation
     312 bytes  SummaryInformation
    2411 bytes  1Table
    3620 bytes  WordDocument

sector size      : 2^9 = 512 bytes
mini sector size : 2^6 = 64 bytes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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 &lt;strong&gt;mini-FAT&lt;/strong&gt; in 64-byte units. There is a fragmentation strategy inside your Word document.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;WordDocument&lt;/code&gt; stream is the main event, and it opens with a File Information Block whose magic number is &lt;code&gt;0xA5EC&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;WordDocument stream: 3620 bytes
  FIB magic (wIdent) = 0xA5EC
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;None of this is the text yet. This is all container.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-text-is-not-in-order&#34;&gt;The Text Is Not in Order&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;d expect the document&amp;rsquo;s text to sit in the &lt;code&gt;WordDocument&lt;/code&gt; stream in reading order. It doesn&amp;rsquo;t. It sits there in &lt;strong&gt;edit order&lt;/strong&gt;, and a separate structure called a &lt;strong&gt;piece table&lt;/strong&gt; says how to reassemble it.&lt;/p&gt;
&lt;p&gt;The piece table is a list of descriptors, each saying &amp;ldquo;characters at logical position X through Y live at physical offset Z.&amp;rdquo; Reading a &lt;code&gt;.doc&lt;/code&gt; means walking that table and gathering fragments scattered through the stream.&lt;/p&gt;
&lt;p&gt;Why build it that way? Because of a feature called &lt;strong&gt;Fast Save&lt;/strong&gt;, and because in 1990 writing to disk was slow. When you edited a document, Word didn&amp;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.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a good optimization. It has an obvious and terrible consequence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The old text is still in the file.&lt;/strong&gt; 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.&lt;/p&gt;
&lt;p&gt;Microsoft documented this themselves, in a knowledge base article about minimizing metadata in Word documents: &lt;em&gt;&amp;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.&amp;rdquo;&lt;/em&gt; The recommended fix was to go into Options and clear the &amp;ldquo;Allow fast saves&amp;rdquo; check box. From Word 97 SR-1 onward they turned it off by default.&lt;/p&gt;
&lt;p&gt;For years, &amp;ldquo;open the document in a text editor and scroll&amp;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.&lt;/p&gt;
&lt;p&gt;The piece table itself has a respectable pedigree. Charles Simonyi brought the technique to Microsoft from Xerox PARC&amp;rsquo;s Bravo editor, and it&amp;rsquo;s an elegant way to represent an editable buffer. It&amp;rsquo;s still how many text editors model documents in memory. The mistake wasn&amp;rsquo;t the data structure. The mistake was persisting the whole scratch buffer to disk and shipping it to other people.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;then-it-became-a-zip-of-xml&#34;&gt;Then It Became a ZIP of XML&lt;/h2&gt;
&lt;p&gt;Office 2007 replaced all of it with the Open Packaging Conventions: ECMA-376, later ISO/IEC 29500. A &lt;code&gt;.docx&lt;/code&gt; is a ZIP archive containing XML.&lt;/p&gt;
&lt;p&gt;Every &lt;code&gt;.docx&lt;/code&gt; opens with the same four bytes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;50 4b 03 04    &amp;lt;- PK\x03\x04, a ZIP local file header
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;PK&lt;/code&gt;. Phil Katz&amp;rsquo;s initials, from the last post, sitting at byte zero of every Word document written since 2007.&lt;/p&gt;
&lt;p&gt;Unzip one and the structure is legible:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Content_Types].xml
_rels/.rels
word/document.xml
word/_rels/document.xml.rels
word/styles.xml
word/settings.xml
word/fontTable.xml
word/theme/theme1.xml
docProps/core.xml
docProps/app.xml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;word/document.xml&lt;/code&gt; holds the text. &lt;code&gt;[Content_Types].xml&lt;/code&gt; maps each part to a MIME type. &lt;code&gt;_rels/.rels&lt;/code&gt; is a relationship graph saying which part is the main document and how the parts connect. The whole thing is a tiny website, zipped.&lt;/p&gt;
&lt;p&gt;The text itself is WordprocessingML:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;w:p&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;w:r&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;w:t&amp;gt;&lt;/span&gt;Hello, World!&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/w:t&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/w:r&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/w:p&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Extracting text from a &lt;code&gt;.doc&lt;/code&gt; meant implementing a filesystem and a piece table. Extracting text from a &lt;code&gt;.docx&lt;/code&gt; means unzipping and finding &lt;code&gt;&amp;lt;w:t&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;
&lt;p&gt;The XML contains the document, not the document&amp;rsquo;s history. Deleted text is deleted.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;xml-did-not-mean-simple&#34;&gt;XML Did Not Mean Simple&lt;/h2&gt;
&lt;p&gt;It would be tidy to end on &amp;ldquo;and then it got clean.&amp;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.&lt;/p&gt;
&lt;p&gt;What matters here is the shape it settled into. The standard shipped split in two: &lt;strong&gt;Strict&lt;/strong&gt;, the clean format, and &lt;strong&gt;Transitional&lt;/strong&gt;, which carries the legacy baggage forward so documents converted from the binary era still render correctly.&lt;/p&gt;
&lt;p&gt;Guess which one nearly everything emits.&lt;/p&gt;
&lt;p&gt;Open a Transitional document&amp;rsquo;s settings and you find a &lt;code&gt;&amp;lt;w:compat&amp;gt;&lt;/code&gt; block. Its children are a museum:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;w:truncateFontHeightsLikeWP6    WordPerfect 6
w:suppressTopSpacingWP          WordPerfect
w:lineWrapLikeWord6             Word 6
w:autoSpaceLikeWord95           Word 95
w:footnoteLayoutLikeWW8         Word 97
w:useWord97LineBreakRules       Word 97
w:mwSmallCaps                   Mac Word
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;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 &lt;em&gt;did&lt;/em&gt; do, quirks included. Implementing this correctly means emulating applications whose behavior was never written down anywhere.&lt;/p&gt;
&lt;p&gt;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&amp;rsquo;s memory dumped to disk. It just picked a more legible way to write it down.&lt;/p&gt;
&lt;p&gt;Which is, in fairness, an enormous improvement. You can read the file now. You just can&amp;rsquo;t read all of it quickly.&lt;/p&gt;
&lt;h2 id=&#34;sources&#34;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b&#34;&gt;MS-CFB: Compound File Binary Format&lt;/a&gt; — Microsoft&amp;rsquo;s spec for the OLE2 container&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/ccd7b486-7881-484c-a137-51170af7cc22&#34;&gt;MS-DOC: Word Binary File Format&lt;/a&gt; — the FIB, the piece table, and the stream layout&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ecma-international.org/publications-and-standards/standards/ecma-376/&#34;&gt;ECMA-376&lt;/a&gt; — Office Open XML, the basis for &lt;code&gt;.docx&lt;/code&gt;, 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&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.loc.gov/preservation/digital/formats/fdd/fdd000395.shtml&#34;&gt;Library of Congress format description for OOXML&lt;/a&gt; — preservation notes and format history&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://jeffpar.github.io/kbarchive/kb/223/Q223790/&#34;&gt;KB Q223790: WD97: How to Minimize Metadata in Word Documents&lt;/a&gt; — the fast-save warning, archived; Microsoft no longer hosts it&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.datypic.com/sc/ooxml/t-w_CT_Compat.html&#34;&gt;&lt;code&gt;w:compat&lt;/code&gt; schema reference&lt;/a&gt; — the full list of compatibility settings, browsable without downloading the spec&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;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 &lt;a href=&#34;https://micro.blog/llbbl?remote_follow=1&#34;&gt;@logan@llbbl.blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>