|
| 1 | +# print_matrix follow-up — Windows-side handoff (round 2) |
| 2 | + |
| 3 | +**Audience**: same Windows engineer who captured `print_matrix.golden.json` |
| 4 | +in commit `2780199`. |
| 5 | + |
| 6 | +**One-line ask**: extend the workbook driver to round-trip-read two |
| 7 | +additional things (page margins + a more reliable Pages.Count), then |
| 8 | +re-capture three suites whose current goldens are blocking further work. |
| 9 | + |
| 10 | +This document is throw-away. Delete it (and this paragraph) once the |
| 11 | +re-capture lands on `develop`. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## What the first matrix capture revealed |
| 16 | + |
| 17 | +The 14-case `print_matrix` round 1 capture took the workbook oracle |
| 18 | +from **20 fail / 46 pass / 1 skip** down to **8 fail / 57 pass / 1 |
| 19 | +skip** after `d5dd461 fix(print): Suppress auto-column breaks ...`. |
| 20 | + |
| 21 | +Empirical rules derived from your goldens: |
| 22 | + |
| 23 | +- **Block C (density)**: confirmed Excel never auto-paginates columns at |
| 24 | + scale=100. A wide print area renders on a single page-column, |
| 25 | + clipped at the right margin; `VPageBreaks` and `Pages.Count` both |
| 26 | + ignore the overflow. `src/print/pagination.cpp` now matches. |
| 27 | +- **Block B (fit)**: confirmed `FitToPages` correctly forces |
| 28 | + `Zoom=False` and produces predictable page counts. |
| 29 | + |
| 30 | +## The 8 residual failures break into 3 groups |
| 31 | + |
| 32 | +### Group 1: driver-artifact (3 cases) — declared in divergence.yaml |
| 33 | + |
| 34 | +| Case | Symptom | |
| 35 | +|---|---| |
| 36 | +| `print_fit.scale_25_extreme_shrink` | `pages=6` with `h_breaks=[], v_breaks=[]` (formula `(H+1)*(V+1)=1`) | |
| 37 | +| `print_matrix.zoom_50_dense_fit_off` | `pages=4` with empty break arrays | |
| 38 | +| `print_matrix.zoom_25_dense_fit_off` | `pages=6` with empty break arrays | |
| 39 | + |
| 40 | +These are now in `tests/divergence.yaml` with `mode: skip-oracle` |
| 41 | +scoped to `win-365-ja_JP`. **Re-capture goal**: change the driver to |
| 42 | +read `PrintPreview`-mode `Pages.Count` rather than the display-zoom |
| 43 | +property (whichever COM call the driver currently uses). The |
| 44 | +`PrintPreview` count is what reconciles with `HPageBreaks / |
| 45 | +VPageBreaks`. Once re-captured, remove those three entries from |
| 46 | +`divergence.yaml`. |
| 47 | + |
| 48 | +### Group 2: body-height calibration (4 cases) — needs driver extension |
| 49 | + |
| 50 | +| Case | Symptom | |
| 51 | +|---|---| |
| 52 | +| `print_basic.print_titles_repeat_rows` | `h_breaks=[]` vs want `[39]` | |
| 53 | +| `print_basic.print_titles_repeat_rows_and_cols` | `h_breaks=[]` vs want `[39]` | |
| 54 | +| `print_matrix.print_titles_repeat_1_row` | `h_breaks=[]` vs want `[39]` | |
| 55 | +| `print_matrix.print_titles_repeat_3_rows` | `h_breaks=[]` vs want `[39]` | |
| 56 | +| (`print_matrix.print_titles_repeat_5_rows` passes — accidentally, because the prior body-subtraction logic compensates for one bug with another at depth=5) | |
| 57 | + |
| 58 | +**Root cause hypothesis**: the body height our `compute_printable_area` |
| 59 | +returns (`663.2pt` for A4 portrait Normal margins) is incompatible |
| 60 | +with Excel's actual body height (`~595pt` inferred from the goldens). |
| 61 | +A 40-row × 15pt print area (`A1:D40`) needs body `< 600pt` to fire a |
| 62 | +break at row 39 -- and Excel does. The OOXML defaults we use (`top=0.75, |
| 63 | +bottom=0.75, left=0.7, right=0.7, header=0.3, footer=0.3` in inches) |
| 64 | +should yield body `= 691.2pt` minus our 28pt header/footer text strip |
| 65 | +reservation `= 663.2pt`. |
| 66 | + |
| 67 | +**Suspicion**: when the case YAML omits `page_margins`, Excel COM |
| 68 | +applies a per-user / per-workbook-template preset that differs from |
| 69 | +the OOXML spec defaults. **Wide** (1.0/1.0/0.5) would give body `~558pt` |
| 70 | +which is close to the inferred `~595pt`; some other preset may give |
| 71 | +exactly `~595pt`. |
| 72 | + |
| 73 | +**Driver extension needed** -- extend `_apply_and_read_print` to |
| 74 | +round-trip-read the actual margins and write them under a new |
| 75 | +`applied_page_setup.margins` field: |
| 76 | + |
| 77 | +```json |
| 78 | +"applied_page_setup": { |
| 79 | + "zoom": 100, |
| 80 | + "fit_to_width": false, |
| 81 | + "fit_to_height": false, |
| 82 | + "margins": { |
| 83 | + "left": 0.7, |
| 84 | + "right": 0.7, |
| 85 | + "top": 0.75, |
| 86 | + "bottom": 0.75, |
| 87 | + "header": 0.3, |
| 88 | + "footer": 0.3 |
| 89 | + } |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +Then re-capture all of `print_basic`, `print_matrix`, `print_pagination`, |
| 94 | +`print_fit`. With actual margins recorded, the C++ side can either |
| 95 | +(a) apply matching margins to its own `PageMargins` struct, or |
| 96 | +(b) we add a small set of cases that explicitly set margins |
| 97 | +(`{page_margins: {top: 1.0, ...}}` in the YAML) to exercise both |
| 98 | +defaults and overrides. Either way, the absolute body number stops |
| 99 | +being a moving target. |
| 100 | + |
| 101 | +### Group 3: scale_50_shrinks_breaks mystery (1 case) — needs investigation |
| 102 | + |
| 103 | +``` |
| 104 | +print_fit.scale_50_shrinks_breaks |
| 105 | + spec: A1:H1 (1 row, 8 cols * 30 chars), scale=50, no fit |
| 106 | + golden: pages=2, h_breaks=[], v_breaks=[] |
| 107 | + Formulon: pages=1, h_breaks=[], v_breaks=[] |
| 108 | +``` |
| 109 | + |
| 110 | +At `scale=50`, content width = `1260 * 0.5 = 630pt`, body width = `494pt`. |
| 111 | +Excel reports `pages=2` — geometrically consistent with an auto-column |
| 112 | +break that splits the 630pt content into two 494pt-wide page-columns, |
| 113 | +yet `v_breaks=[]` (consistent with the Block C rule that VPageBreaks |
| 114 | +hides automatic column breaks). |
| 115 | + |
| 116 | +But `print_matrix.zoom_75_dense_fit_off` has content width `1260 * 0.75 |
| 117 | += 945pt`, body `494pt`, ratio `1.91` (would need 2 page-columns), and |
| 118 | +reports `pages=1`. So the rule "count auto-col-pages at scale<100" is |
| 119 | +not universal. |
| 120 | + |
| 121 | +**Re-capture ask**: add 4 small cases to `print_matrix.yaml`: |
| 122 | + |
| 123 | +```yaml |
| 124 | +- id: scale_50_one_row_wide # mirror of scale_50_shrinks_breaks |
| 125 | + sheets: {Sheet1: {A1: x, H1: x}} |
| 126 | + column_widths: {A:H: 30} |
| 127 | + print: {sheet: Sheet1, print_area: A1:H1, |
| 128 | + page_setup: {orientation: portrait, paper: 9, scale: 50}} |
| 129 | + |
| 130 | +- id: scale_50_thirty_rows # mirror of zoom_50 but only 1-row content |
| 131 | + sheets: {Sheet1: {A1: x, H30: x}} |
| 132 | + column_widths: {A:H: 30} |
| 133 | + print: {sheet: Sheet1, print_area: A1:H30, |
| 134 | + page_setup: {orientation: portrait, paper: 9, scale: 50}} |
| 135 | + |
| 136 | +- id: scale_75_one_row_wide |
| 137 | + sheets: {Sheet1: {A1: x, H1: x}} |
| 138 | + column_widths: {A:H: 30} |
| 139 | + print: {sheet: Sheet1, print_area: A1:H1, |
| 140 | + page_setup: {orientation: portrait, paper: 9, scale: 75}} |
| 141 | + |
| 142 | +- id: scale_75_thirty_rows # mirror of zoom_75 but one shape change |
| 143 | + sheets: {Sheet1: {A1: x, H30: x}} |
| 144 | + column_widths: {A:H: 30} |
| 145 | + print: {sheet: Sheet1, print_area: A1:H30, |
| 146 | + page_setup: {orientation: portrait, paper: 9, scale: 75}} |
| 147 | +``` |
| 148 | +
|
| 149 | +The diff between `scale_50_one_row_wide` and `scale_50_thirty_rows` — |
| 150 | +and between `scale_75` variants — should reveal whether Excel's |
| 151 | +`Pages.Count` cares about row count when columns overflow at scale<100. |
| 152 | + |
| 153 | +## Capture procedure (re-affirming) |
| 154 | + |
| 155 | +```bash |
| 156 | +# On Windows: |
| 157 | +git pull origin develop # picks up driver + matrix updates |
| 158 | +python tools/oracle/cli.py workbook --suite print_basic print_fit print_matrix print_pagination |
| 159 | +git add tests/oracle/golden_wb/*.golden.json |
| 160 | +# Confirm divergence-skipped cases were dropped from the regenerated goldens: |
| 161 | +python tools/oracle/.venv/bin/python tools/oracle/workbook_case_schema.py \ |
| 162 | + tests/oracle/cases_wb/print_matrix.case.json \ |
| 163 | + tests/oracle/golden_wb/print_matrix.golden.json |
| 164 | +git commit -m "feat(oracle): Capture margins and re-run pagination matrix" |
| 165 | +git push origin develop |
| 166 | +``` |
| 167 | + |
| 168 | +The Mac side picks it up via `git pull` and re-runs `make oracle-verify`. |
| 169 | + |
| 170 | +## Acceptance criteria |
| 171 | + |
| 172 | +After this round, the workbook track should be at **0 fail** for the |
| 173 | +print suites (modulo the 3 divergence-skipped Block A entries). If |
| 174 | +any case still fails, the failure mode is no longer "we don't know what |
| 175 | +Excel is doing" -- it's a specific gap with documented evidence. |
0 commit comments