Table of Contents

Word snapshot format

A Word snapshot is UTF-8 text with LF line endings, saved as {name}.verified.docx.txt. It reads like the document: one line per paragraph with its runs merged, and only what differs from its style listed.

# docx-snapshot v1
document
  metadata title="Trail notes" created=yes modified=yes
  settings lang=en-US compat=15 track-changes=no protection=none even-odd-headers=no
  defaults font="Calibri" size=11
sections
  s1 letter margins=72,72,72,72 header=36 footer=36 columns=1 break=nextPage headers=[default]
styles
  ListParagraph paragraph "List Paragraph" font="Arial" size=12 indent-left=36
  TableGrid table "Table Grid" font="Calibri" size=11
numbering
  list 1 definition=1
    level 0 format=decimal text="%1." start=1 indent=36 hanging=18
body
  P style=ListParagraph list=1:0 label="1." "Bring a trail map"
    span "trail" bold
  Table style=TableGrid widths=[144,216]
    Row header
      Cell
        P "Trail"
      Cell
        P "Length"
  P "Visit example.com"
    link "example.com" target="https://example.com" at=6
  P
    image inline size=120x40 alt=(missing) type=png hash=3f9c0a1b2d4e5f60 at=0
headers
  s1 default
    P "Page 3"
      field "PAGE" result="3" at=5
footers
  (none)
notes
  (none)

Sections

Sections always appear in this order. Each can be left out with DocxSection.

Section Contents
document A metadata line with the file's title, subject, author, keywords, description, category, company and attached template, each only when set, and whether created and modified dates are present (created=yes); a property "Name" value="..." line for each custom property, sorted by name; a settings line with the language, the default East Asian and complex-script languages and fonts, compatibility mode, whether changes are tracked, protection and even and odd headers, with other= for the rest, such as hyphenation, the default tab stop, mirrored margins, footnote numbering, compatibility options and equation settings; a defaults line with what the document defaults (w:docDefaults) give text and paragraphs before any style, such as the font, size and spacing, with other= for the rest; parts that couldn't be read
sections One line per section (s1, s2, ...): page size, margins, header and footer distances, columns, section break, page numbering, title page, which headers and footers it has; a tracked change to its settings
styles The styles the document uses, with their properties resolved through the styles they're based on. A style's languages, and its East Asian and complex-script fonts, print only when they aren't the ones on the settings line. An inserted Word document's styles are compared with that line too, so one in another language prints lang= on its styles
numbering The lists the document uses, and each level's format, text, start and indents, with other= for what else it sets, such as the label's size, color or bold, its alignment, what follows it and a picture bullet's image. Lists, and the definitions they number from, are numbered from 1 in the order the snapshot first uses them, not by their ids in the file, which Word changes when it saves a file again
body Paragraphs, tables, content controls and inserted documents, in reading order
headers, footers Each section's headers and footers, by kind (default, first, even)
notes Footnotes, endnotes and comments, with their content. Each kind is numbered from 1 in the order the snapshot first refers to them, with the ones nothing refers to last
checks Only with Check(DocxCheckSet): what the checks find, one line per finding with its rule and path (ImageAltText body > P[3] > image), by rule in the order the rules are declared, then in snapshot order, or (none)

Body lines

Line Means
P ... "text" A paragraph: its style, list item and label, layout that differs from its style (such as align=, indent-left=, rtl-paragraph, borders= or shading=), then its text. When the text is in a complex script but the style isn't right to left, the style's complex-script bold, italic and size print here where they differ from the Latin ones (bold-complex=no), since the style line has no text to show whether they apply
span "text" ... Part of the paragraph whose formatting differs from the paragraph's, such as bold, font=, hidden (text Word doesn't show or print), double-strike, rtl (right to left) or lang=fr-FR. A span that covers all of the paragraph's text leaves the text out (span bold). East Asian and complex-script text has its own language and font (lang-east-asian=, lang-complex=, font-east-asian=, font-complex=); complex-script means the whole run takes the complex-script formatting. On right-to-left or complex-script text, complex-script bold, italic and size print where they differ from the Latin ones (bold-complex=no, size-complex=14)
Table ... A table: style, grid widths, width, cell margins, borders, and its alt text (caption=, description=)
Row / Row header A table row; a header row repeats on each page. Its height (height=20, height=20-exact or height=auto) and split=no when it's kept on one page
Cell ... A cell: span, merge, shading, width, margins, borders, vertical alignment (valign=), text direction (text-direction=)
Control <kind> ... A content control, with its tag (name=) and title. Under a cell, around marks one around the cell
Inserted <kind> An inserted document (w:altChunk); a DOCX is read in place, with its own defaults line (its languages, and its East Asian and complex-script fonts, only where they differ from the outer document's), styles, numbering, body and notes; other formats are kept by hash
bookmark "Name" A bookmark between blocks, rows or cells; columns=0-2 for a table-column bookmark
unsupported <element> hash=... Something PDiffy doesn't read yet, kept by hash so a change to it still shows
ends-section s1 The paragraph that ends a section

Lines inside a paragraph

Each ends with at=N, the position in the paragraph's text where it sits, counted in code points, so a character outside the Basic Multilingual Plane, such as an emoji, counts as one.

Line Means
link "text" target="..." A hyperlink and where it goes: a web address, or # and a bookmark's name
field "CODE" result="..." A field, such as PAGE, and the result Word last computed. A field nested in another's code shows there in braces, as in field "= {PAGE} - 2", and gets its own line after it
image, chart, shape, object, control, ... A picture, other graphic, embedded object or ActiveX control: inline or floating, size, alt text, type, and a hash of its data as the file stores it
math "text" hash=... An equation
symbol font=... code=... A character from a symbol font
break page / break column A page or column break
form <kind> ... A form control: name, title, value, choices (options=["Ohio","Texas"])
note footnote 1, comment 1 Where a note or comment is attached, numbered as in the notes section
bookmark "Name" A bookmark
insert, delete, move-to, move-from, format "text" A tracked change to text, with its author
paragraph-insert, paragraph-format, ... A tracked change to the paragraph itself
stray-text "text" Text in a place Word may drop or move it
unsupported <element> hash=... Something PDiffy doesn't read yet

Tables, rows and cells list their own tracked changes (table-format, row-insert, cell-delete and so on) under their line. See DocxRevisionKind for every kind.

Rules

  • When anything is ignored, the next lines list it: # ignored sections: ... and # ignored fields: .... Check adds # checks: ... with the rule set and how many rules it has, such as # checks: latest, 4 rules.
  • An empty section prints (none) and an ignored one prints (ignored).
  • Lengths are in points. Colors print as #rrggbb (color=#2f5496), and auto is Word's automatic color.
  • A list that can hold any number of items prints in brackets (widths=[144,216], tabs=[start:72], borders=[top:single:0.5]); a fixed set, such as a section's four margins, prints with commas alone.
  • Alignment and tab stops say start and end (align=end, tabs=[start:72]). Older files say left and right, and Word reads both as the side a line starts or ends on, so start is the right side in right-to-left text.
  • Text is quoted and normalized to Unicode NFC, so a letter typed with a separate accent prints as one letter. " and \ are escaped, a tab and a line break print as \t and \n, and characters a reader can't see or tell apart print as \uXXXX: control and format characters (such as the soft hyphen and zero-width and direction marks), private-use characters, line and paragraph separators, spaces other than U+0020 (such as a no-break space), U+FFFD to U+FFFF and variation selectors.
  • other= is a hash of the formatting PDiffy doesn't read, such as character spacing or a table's indent, on a style, span, paragraph, table, row, cell, section, list level, settings or defaults line, so a change to it still shows. Only the line's own properties count, not what it inherits. A style's hash covers its table, row and cell properties and a table style's formatting for header rows, banding and so on (w:tblStylePr); a row's covers its table exceptions (w:tblPrEx). For a property PDiffy reads part of, such as w:spacing, the hash still covers the attributes PDiffy doesn't print, such as automatic spacing or an underline's color, apart from defaults Word writes everywhere: a clear shading pattern, an auto shading color, a border space of 0, a cell margin in twentieths of a point (dxa), a page size's paper code, an orientation that matches the page's size, a gap between columns when there's only one, and a font for other Latin characters (w:hAnsi) that's the same as the ASCII one. Noise Word writes into nearly every document is left out: proofing and web-view marks, a document grid with no type or the default type, conditional-format markers, a paragraph mark's own formatting and later Word versions' extensions (w14: and so on). The paragraph mark's formatting does count in a list item, where it formats the label, and in an empty paragraph, where it sets the line's height. Settings Word rewrites on every save or takes from the computer's region are left out too, such as editing-session IDs, the zoom, the proofing state and the list separator.
  • A color set from the theme (w:themeColor, w:themeFill) is read as the theme's color after the settings' color mapping and its tint or shade are applied, whether it prints or counts in other=, so changing the theme's colors changes the snapshot. Word's own copy of the color, written next to it, is used only when the theme doesn't have the color.
  • Word's own theme leaves its East Asian and complex-script fonts empty and picks one by language, so a theme font the theme leaves empty prints as its theme name, such as font-east-asian="minorEastAsia".
  • Relationship IDs, editing-session IDs, the zip's layout and styles nothing uses are left out, so regenerating the same document gives the same snapshot.

Damaged files

A part that can't be read is treated as missing. It prints an unreadable line in the document section with a hash of its bytes, such as unreadable word/styles.xml hash=3f9c0a1b2d4e5f60, so a change to it still fails the test. A part is unreadable when it isn't well-formed XML, has a DTD, nests elements more than 256 deep (an inserted document's nesting counts from where it's inserted) or is larger than 256 MB. An inserted document that can't be read is marked where it's inserted instead: Inserted docx unreadable. A file whose body can't be read throws.