dxpdf 0.2.3

A fast DOCX-to-PDF converter powered by Skia
# Headers and Footers — §17.10

## Resolution

§17.10.5: sections without explicit header/footer references inherit from the previous section.

Footer types: `default`, `even`, `first`. Currently only `default` is resolved (via `footerReference` → `RelId` → `footer{N}.xml`).

## Content Structure

Headers/footers can contain:
- Paragraphs (text, fields, hyperlinks)
- Tables (address blocks, multi-column layouts)
- Floating images (logos, decorative elements)
- VML shapes with absolute positioning

## Building (`build_header_footer_content`)

Produces `HeaderFooterContent`:

```rust
pub struct HeaderFooterContent {
    pub blocks: Vec<LayoutBlock>,           // paragraphs + tables
    pub absolute_position: Option<(Pt, Pt)>, // VML override
    pub floating_images: Vec<FloatingImage>,  // page-relative
}
```

- **Paragraphs**: built via `build_fragments` (same as body paragraphs)
- **Tables**: built via `build_table` (full table style cascade applies)
- **Floating images**: extracted separately — they use page-relative coordinates, not stack-relative
- **VML absolute positioning**: detected from `Pict` inlines; overrides the default margin-based position

### §17.10.1: Empty Paragraph Handling

Empty non-last paragraphs still occupy vertical space (line height from the paragraph mark's font). A `Fragment::LineBreak` is inserted. The last empty paragraph produces no height.

## Layout (`render_headers_footers`)

Uses `stack_blocks` (same engine as table cells) to vertically stack paragraphs and tables.

### Header Positioning

```
offset_x = margins.left  (or VML abs_x)
offset_y = header_margin  (or VML abs_y)
```

Commands from `stack_blocks` are shifted by `(offset_x, offset_y)` and prepended before body content.

### Footer Positioning

```
footer_y = page_height - footer_margin - content_height
```

Commands shifted by `(margins.left, footer_y)` and appended after body content.

### Floating Images in Headers/Footers

Handled separately from `stack_blocks` because they use page-absolute coordinates:
- `FloatingImageY::Absolute(y)` — used as-is (no shift)
- `FloatingImageY::RelativeToParagraph(offset)` — shifted by header/footer offset

## Per-Page Field Evaluation

Headers/footers are built **per-page** to evaluate PAGE and NUMPAGES fields correctly.

### Two-Phase Layout (lib.rs)

1. **Phase 1**: layout all sections → determine total page count
2. **Phase 2**: render headers/footers with correct `page_number` and `total_pages`

`SectionHfInfo` stores the page range and raw block references for each section. Phase 2 iterates these, setting `field_ctx_cell` per-page before building content.

```rust
ctx.field_ctx_cell.set(FieldContext {
    page_number: Some(page_base + page_idx + 1),
    num_pages: Some(total_pages),
});
let hf = build_header_footer_content(blocks, ctx);
```

The field context is reset to default after header/footer rendering to prevent leakage into body layout.