oxedyne/fe2o3/fe2o3_austenite/tests/oracle/fixtures/float_marginalia_fixture.typ
4.4 KiB, 1 run
created by r1870400018:52171, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // The oracle harness's float+marginalia fixture -- the regression root for the anchor-region-membership |
| 2 | // bug: a margin note (or any other nested anchor -- a ref, an index entry) recorded INSIDE a float's own |
| 3 | // body must inherit that float's page-region membership, not default to `Region::Body`, or a later float |
| 4 | // insertion on the same page shifts the note away from its own line while the float's own ink (correctly |
| 5 | // region-tagged) stays put. `float_fixture.typ` exercises float placement alone and `marginalia_fixture.typ` |
| 6 | // exercises the margin note alone; neither puts one inside the other, which is why this class of bug slipped |
| 7 | // a green oracle. Modelled closely on `float_fixture.typ` (same self-contained `#let aside-box`, same A4 |
| 8 | // geometry) plus `marginalia_fixture.typ`'s `#let claim-label`, so it compiles under typst 0.15.1 with no |
| 9 | // symbol-modifier patch, the same idiom as both. |
| 10 | // |
| 11 | // Two top-placed `#aside-box` floats stack in the top band; the FIRST one's body carries a |
| 12 | // `#claim-label(<A1>)`, so the margin-note anchor is recorded not as a direct child of the float's node list |
| 13 | // but nested inside it, through the same `place_line`/`place_vbox`/`place_leaf` helpers ordinary body text |
| 14 | // goes through. When the second aside is inserted, `try_insert_float` shifts every `Region::Body` anchor on |
| 15 | // the page down by the second float's band -- correct for a body anchor, wrong for this one, which sits |
| 16 | // inside the first float's own already-placed material and must stay put. Before the anchor-region-membership |
| 17 | // fix this note landed beside the SECOND aside instead of its own line, at the same physical y the second |
| 18 | // aside's own text sits at (both anchors dragged down as `Region::Body`, since the first aside's material had |
| 19 | // already been laid and stamped `Region::Top` by the time the shift ran). |
| 20 | // |
| 21 | // NO LEVEL-1 HEADING, for the same reason `float_fixture.typ` carries none: a lone-file `= Heading` lowers as |
| 22 | // austenite's ~136pt chapter-opener grid (tracked as A18), which is a heading-rendering confound, not |
| 23 | // something this root gates. |
| 24 | // |
| 25 | // The oracle compares page count and anchor order against Typst as every root does; `expected.json` pins no |
| 26 | // per-anchor y for this root (only page count, anchor count and the whole-PDF sha), so the real gate on the |
| 27 | // nested anchor's position is that pinned PDF sha -- a regression that moves the note's drawn y changes the |
| 28 | // hashed content stream. `float-fixture`'s per-float y, by contrast, IS checked live against a numeric |
| 29 | // baseline, but that baseline is Typst's own in-float `<fp>` probes read via `mod.rs` (see the `float-fixture` |
| 30 | // branch there), not `expected.json`, since Typst's plain `query` reports an anchor's IN-FLOW position, not |
| 31 | // the position either fixture is checking (a floated or nested anchor's position after layout has moved it). |
| 32 | |
| 33 | #set page(width: 595.276pt, height: 841.89pt, margin: 56.9pt) |
| 34 | #set text(size: 11pt, font: "Libertinus Serif") |
| 35 | #set par(justify: true, leading: 0.65em, spacing: 1.2em, first-line-indent: 0pt) |
| 36 | |
| 37 | #let aside-box(float: true, body) = { |
| 38 | let inner = box( |
| 39 | width: 100%, |
| 40 | inset: (x: 10pt, y: 10pt), |
| 41 | fill: luma(240), |
| 42 | stroke: (left: 2pt + luma(80)), |
| 43 | [#body], |
| 44 | ) |
| 45 | if float { figure(placement: auto, inner, caption: none) } else { inner } |
| 46 | } |
| 47 | #let claim-refs(..codes) = { } |
| 48 | #let claim-label(..codes) = { |
| 49 | let strs = codes.pos().map(c => if type(c) == label { str(c) } else { str(c) }) |
| 50 | let display = strs.join(" ") |
| 51 | box(width: 0pt, clip: false, context { |
| 52 | let pos = here().position() |
| 53 | let content = text(size: 6.5pt, fill: luma(90), hyphenate: false, display) |
| 54 | let m = measure(content) |
| 55 | place(dx: 595.276pt - 56.9pt - pos.x, box(width: m.width, content)) |
| 56 | }) |
| 57 | } |
| 58 | |
| 59 | An opening paragraph of plain prose, long enough to be a paragraph, so the first aside is met high on the |
| 60 | page and its midpoint sits in the upper half, drawing it to the top of the page above the prose. |
| 61 | |
| 62 | #aside-box[A first aside carrying a claim code#claim-label(<A1>) inside its body, so a nested margin-note |
| 63 | anchor is recorded inside the float's own material rather than as a direct child of the float list.] |
| 64 | |
| 65 | A second paragraph of plain prose keeps the cursor high enough that the next aside also goes to the top, |
| 66 | stacking beneath the first one in the top band and shifting the body down once more. |
| 67 | |
| 68 | #aside-box[A second aside, met high as well, so it stacks in the top band under the first one.] |
| 69 | |
| 70 | A closing paragraph of plain prose ends the document beneath the two stacked top asides. |