Recursive AI

Fields at layout time

All onedocument docs

Fields that depend on pages, and the table of contents, index and table of authorities, are resolved while a document is laid out (LayoutEngine.Layout, so also DocumentRenderer, Office.Convert and document.SaveAs("x.pdf")). The document is only read: results go to the pages, never back into the file. References are to ECMA-376 Part 1; items marked to check against Word are rules the specification leaves open and are listed in the table at the end.

Policy

WhatDefaultWith LayoutOptions.UpdateFields (or w:updateFields in the file)
PAGE, NUMPAGES, SECTIONPAGES, SECTION, PAGEREF, NOTEREF, REF, STYLEREF, SEQcomputed from the layoutthe same
TOC, INDEX, TOA with no stored result (or only an empty one)builtrebuilt
TOC with a stored resultentries kept as stored; the PAGEREF fields in them are recomputed like any PAGEREFrebuilt from the document
DATE, TIMEstored resultcomputed from LayoutOptions.Clock; stored result when Clock is null
CREATEDATE, SAVEDATE, PRINTDATE, AUTHOR, TITLE, SUBJECT, KEYWORDS, COMMENTS, LASTSAVEDBY, REVNUM, DOCPROPERTY, FILENAME, NUMWORDS, NUMCHARS, IF, =, SYMBOL, QUOTEstored resultcomputed
Everything else (HYPERLINK, INCLUDEPICTURE, INCLUDETEXT, MERGEFIELD, …)stored resultstored result
  • A result that equals the stored one is left alone, so the field keeps the formatting of its stored runs. A computed result goes into the first result run (its character formatting) and the other result runs of that field show nothing.
  • A field with w:fldLock is never rebuilt; a table of contents, index or authorities whose field is not closed, or whose code and result are not in paragraphs of one container, keeps its stored result and adds layout.field-skipped.
  • w:updateFields in the settings part (§17.15.1.90, Word updates every field when it opens the file) turns UpdateFields on, because that is what Word does with the file.
  • Nothing is fetched: INCLUDEPICTURE, INCLUDETEXT and HYPERLINK targets are never followed.
  • The system clock is never read. File dates come from the document properties, shown in the clock's time zone (UTC without a clock).

Passes

A field that depends on pages cannot know its value until pages exist, and its value can change the pages (a longer number, a longer contents list). Layout therefore runs again with the facts of the pass before — the printed number and number format of each page, the page each paragraph starts on (and, for a paragraph that splits, where each piece starts), the page counts of the document and its sections — until a pass produces the facts it was given:

  1. Pass 1 has no facts: fields that need them keep their stored result (NUMPAGES shows 1).
  2. Pass k reads the facts of pass k − 1 and produces its own.
  3. When both are equal the pass stands. When LayoutOptions.MaxFieldPasses (default 6) is reached first, the last pass stands and LayoutDocument.Notes gets layout.fields-unsettled. LayoutDocument.FieldPasses tells how many passes ran.
  4. A document in which no field read the facts is laid out once.

A table of contents is built before pass 1 (its length does not depend on pages); only its page numbers wait for the facts. That is how a contents list that makes the headings move down a page ends with the numbers of the pages the headings are on.

The working copy

A table of contents, index or table of authorities that has to be built is built in a copy of the document (the document is saved to memory and opened again; LayoutDocument.WorkingCopy returns it, and LayoutParagraph.Source of that layout belongs to the copy). Entries are real paragraphs, as Word writes them:

FieldEntry paragraphs
TOCstyle TOC 1–TOC 9 (a built-in-like direct format when the document has none: indent 220 twips per level, 5 pt after); a right tab with dot leader at the text width; the heading's list number, a tab, its text; a tab and a PAGEREF field; with \h all of it in a w:hyperlink to the heading's _Toc bookmark. The heading gets a _Toc bookmark in the copy unless it has one.
INDEXstyles Index 1–Index 9; the entry, the \e separator (default ,), then a page-list field naming bookmarks that the builder puts at each XE field; \t cross references follow \k (default .).
TOAstyle Table of Authorities; category headings with \h; the long citation, a right tab with dot leader (or \e), the page list.

The page-list field exists only in the copy (ODXEPAGES, with the bookmark names as arguments and \l and \g separators), so the lists follow the pages like PAGEREF does.

Fields

FieldWhat is computed
PAGEthe printed number of the page (§17.16.5.44) in the section's pgNumType format; in the body, the page of the paragraph the field is in
NUMPAGES, SECTIONPAGES, SECTIONpage count, pages of the section, section number (§17.16.5.42, .54, .55)
PAGEREFthe printed page of the bookmark in the format of that page; \p gives above/below on the same page and on page N on another (§17.16.5.45); \h has no effect on the text
NOTEREFthe mark the note prints (the numbering the notes are laid out with: format, start, restart per section or page, custom marks), for the note referenced inside the bookmark; \p as REF
REFthe text of the bookmark (a SEQ result inside it is the computed number); \n the paragraph number without trailing periods, \r the levels the field's paragraph does not share, \w all levels, the levels joined as written (1. a. i. read 1.a.i); \p gives above (the bookmark is before the field) or below, and after a number switch it is appended (i above) (§17.16.5.51)
STYLEREFin a header or footer: the first (\l: last) text of the style on the page, else the nearest before the page, else after; in the body: the nearest before the field, else after (\l: after, else before); paragraph styles give the paragraph text, character styles the first or last stretch of runs; \n, \r, \w, \p as REF (§17.16.5.59)
SEQa counter per identifier in document order; \c repeats, \r n resets, \h hides unless a \* switch is present (the number still counts), \s n restarts at each heading of level n; a SEQ inside the stored result of a TOC, index, REF, STYLEREF or PAGEREF is a copy and does not count (§17.16.5.56)
TOC\o (heading styles by name, heading N), \u (outline levels), \t (style name, level pairs; , or ;), \b, \c (paragraphs holding a SEQ of that identifier), \f/\l (TC fields), \n (no page numbers, all levels or a range), \p (separator text instead of the tab), \h, \w (keep tabs), \x (keep line breaks); \a, \s, \d, \z are not applied (§17.16.5.68)
INDEX with XEmain entries and subentries to any depth (:), sorted without regard to case; \b bold and \i italic page numbers, \t cross references, \r page ranges (XE \r), \f entry types, \b range, \e, \k, \l, \g, \h (heading per letter, A replaced by the letter, a space for a blank line), \r (run-in subentries: main: sub, 3; sub, 4), \c 1–4 columns (the index sits in a continuous section of its own); \p, \s, \y, \z are not applied (§17.16.5.29, .72)
TOA with TAcategories 1–7 named as Word names them, other numbers Category N; \c 0 all, \h headings, \e, \l, \g, \p (passim from five pages); \s is not applied (§17.16.5.67, .62)
DATE, TIME, CREATEDATE, SAVEDATE, PRINTDATEwith \@ pictures (the mail-merge WordPicture); defaults M/d/yyyy, h:mm AM/PM, M/d/yyyy h:mm AM/PM; a never-printed document gives 0/0/0000 0:00:00 AM (§17.16.5.47)
AUTHOR, TITLE, SUBJECT, KEYWORDS, COMMENTS, LASTSAVEDBY, REVNUM, DOCPROPERTYthe core, extended or custom property; an argument replaces the value (nothing is written back)
FILENAMEthe file name (\p the path) of a document opened from a path; the stored result otherwise
NUMWORDS, NUMCHARSthe value recorded in the extended properties (§17.16.5.43), else a count of the text of the body
IFthe comparison of the mail merge (numbers, else text; ? and * wildcards), nested fields in the instruction contribute their results
=the calc engine on the expression: ABOVE/BELOW/LEFT/RIGHT as the contiguous numeric cells of the table in that direction (a blank first cell counts as 0), cell names and ranges (A1, B1:B3, B:B), bookmark names with a number, MOD and INT as §17.16.3.4 defines them; without a \# picture at most two decimals are kept (1/3 is 0.33) (§17.16.3)
SYMBOL, QUOTEthe character (decimal or 0x code; symbol fonts map as list bullets do), the quoted text

All of them take \* (Upper, Lower, FirstCap, Caps, Roman, roman, ALPHABETIC, alphabetic, Arabic, ArabicDash, CardText, OrdText, Ordinal, Hex), \# and \@ as in mail-merge.md (§17.16.4). MERGEFORMAT and CHARFORMAT are accepted and ignored. A reference to a bookmark that does not exist keeps the stored result and adds layout.field-source-missing; with UpdateFields it shows Error! Reference source not found..

Notes

layout.fields-unsettled, layout.field-source-missing, layout.field-skipped.

Open points (to check against Word)

PointWhat is done
Page of a bookmark inside a paragraph that splits across pagesthe page of the line at the same fraction of the paragraph's text; the start of the paragraph when it does not split
TOC heading numbers and tabsnumber, tab, text; a left tab at 440 twips per level for numbered entries (Word's built-in styles)
Closing paragraph of a TOC field written as one paragraphthe end character moves to the last entry paragraph, so there is no extra empty paragraph (Word writes a separate one for its own tables)
INDEX \can empty trailing section is left after the index when nothing follows it
Cross-page NOTEREF \p, REF \pabove/below only (the specification names on page N for PAGEREF alone)
= fields in a table that read other formula cellsread the stored result of that cell, not its new value
Cell numbers such as $20.50, 1,234, 15%read as numbers; Word's rule is not documented
Sort order of index entriesinvariant culture, case-insensitive
STYLEREF in footnotes and commentsnot supported (body and headers/footers only)

Tests and differential inputs

tests/OneDocument.Rendering.Tests/FieldTests.cs lays out generated documents (FieldDocs): a three-level contents list whose entries move after relayout, numbered headings, TOC switches and TC fields, PAGEREF/REF/NOTEREF/STYLEREF/SEQ with their switches, the picture switches, index and authorities (multi-level, ranges, run-in, columns), the stored-versus-updated policy, IF/=/dates, a bounded non-converging relayout and a 800-heading contents list.

The same documents are cases 100–112 of LayoutCases.All(), so tools/layout-differential.sh sends them to Word with the rest of the layout set. They are unanswered until the differential runs; the cases that need a built table of contents carry w:updateFields, so Word has to update fields before its export (or the oracle has to call its field update).

For a later document operation

FieldUpdater (builds the entries in a copy), FieldModel (bookmarks, fields, SEQ and note numbers) and FieldEvaluator (one value per field type) are internal to RecursiveAI.OneDocument.Rendering; a hub-level UpdateFields() writes the computed results with Field.SetResult after one layout, using the facts of its last pass. LayoutDocument.WorkingCopy already holds a document with the tables built, which that operation can save.