dxpdf 0.2.6

A fast DOCX-to-PDF converter powered by Skia
# Fields — §17.16.18

## Simple Fields

`Inline::Field` contains a parsed `FieldInstruction` and cached result `content`:

```xml
<w:fldSimple w:instr="PAGE \* Arabic">
  <w:r><w:t>3</w:t></w:r>
</w:fldSimple>
```

The `content` inlines contain the cached result from when the document was last saved.

## Complex Fields

Complex fields use a state machine of `FieldChar` markers:

```
Begin → InstrText... → Separate → result TextRuns → End
```

```xml
<w:r><w:rPr>...formatting...</w:rPr><w:fldChar w:fldCharType="begin"/></w:r>
<w:r><w:rPr>...formatting...</w:rPr><w:instrText>PAGE \* Arabic</w:instrText></w:r>
<w:r><w:rPr>...formatting...</w:rPr><w:fldChar w:fldCharType="separate"/></w:r>
<w:r><w:rPr>...formatting...</w:rPr><w:t>3</w:t></w:r>
<w:r><w:rPr>...formatting...</w:rPr><w:fldChar w:fldCharType="end"/></w:r>
```

### State Machine in `collect_fragments`

```
field_depth: i32    — tracks nesting (Begin increments, Separate decrements)
field_instr: String — accumulated InstrText between Begin and Separate
field_sub_pending: Option<String> — substitution text awaiting first result TextRun
field_sub_emitted: bool — substitution was applied, skip remaining result TextRuns
```

- **Begin**: `field_depth += 1`, clear state
- **InstrText**: append to `field_instr` (only when `field_depth > 0`)
- **Separate**: parse `field_instr` via `dxpdf_field::parse()`, evaluate for PAGE/NUMPAGES. Set `field_sub_pending` if substitution available. `field_depth -= 1`
- **TextRun** (between Separate and End):
  - If `field_sub_pending` is set: use substituted text with this TextRun's resolved font properties (§17.16.19 MERGEFORMAT), then set `field_sub_emitted = true`
  - If `field_sub_emitted`: skip
  - Otherwise: collect normally (cached result)
- **End**: if `field_sub_pending` still set (no result TextRun was present), emit with paragraph default font. Clear state.

## Dynamic Field Evaluation

`FieldContext` provides runtime values:

```rust
pub struct FieldContext {
    pub page_number: Option<usize>,  // 1-based
    pub num_pages: Option<usize>,
}
```

Currently evaluated fields:
- `PAGE` (§17.16.4.1) — current page number
- `NUMPAGES` (§17.16.4.1) — total document page count

When `FieldContext` has no value for a field (e.g., body text without per-page context), the cached result is used as fallback.

## §17.16.19 MERGEFORMAT

The `\* MERGEFORMAT` switch preserves the formatting of the first result run when the field is updated. Our substitution honors this: the first `TextRun` between Separate and End provides font family, size, bold, italic, color — the substituted text replaces only the content while preserving the style.

Fallback (no result TextRun present): paragraph default font properties are used via `make_field_text_fragment`.