rustyfi_backend/vbox.rs
1use crate::hbox::{DecoId, HookId, PureHorzBox};
2use crate::length::Length;
3
4/// A milestone-1 subset of `vert_box`: a typeset line or vertical space.
5///
6/// See [`crate::hbox::PureHorzBox`] for what the `#[subast]` list means and
7/// what checks it.
8#[derive(Clone, Debug, PartialEq, syan::visit::Ast)]
9#[subast(crate::hbox::PureHorzBox)]
10pub enum VertBox {
11 /// One typeset line: boxes with their x offsets from the line start.
12 Line {
13 height: Length,
14 depth: Length,
15 /// Baseline-to-baseline distance to the *next* line: the `leading`
16 /// a `context` was set to when this line was assembled. `page-break`
17 /// takes no context, so the property rides the line itself, set by
18 /// `break_into_lines` from `ctx.leading`.
19 leading: Length,
20 contents: Vec<(Length, PureHorzBox)>,
21 },
22 /// Fixed vertical space (`block-skip`, frame padding, a paragraph's
23 /// *bottom* margin) — NOT padded by `min_first_line_ascender`.
24 Skip(Length),
25 /// A paragraph's TOP margin (`prim_line_break` prepends this from
26 /// `ctx.paragraph_top`). Distinguished from `Skip` because SATySFi pads
27 /// only the paragraph `margin_top` up to `min_first_line_ascender`
28 /// (`lineBreak.ml:857`) — `block-skip`s and frame pads get no such pad.
29 /// Accumulates into `pending_skip` exactly like `Skip`; the difference is
30 /// only that its presence enables the ascender pad on the following line.
31 ParagTop(Length),
32 /// A `block-frame-breakable` frame's internal top/bottom padding. Unlike
33 /// `Skip` (a margin, which max-COLLAPSES with adjacent margins), frame
34 /// padding is ADDITIVE — SATySFi adds it to the running height inside the
35 /// frame (`pageBreak.ml:323` `hgttotal +% pads.paddingT`, `:380`
36 /// `+% pads.paddingB`), so it stacks ON TOP of the surrounding
37 /// paragraph margin rather than being absorbed by it.
38 FramePad(Length),
39 /// `clear-page`'s marker (`primitives.cppo.ml`'s `VertClearPage`,
40 /// `horzBox.ml:346`) — forces the current page to end right here:
41 /// `chop_page` closes the page as soon as at least one real `Line` has
42 /// been placed, leaving everything from this marker onward for the next
43 /// page. Contributes zero height (`measure_block`), mirroring
44 /// `pageBreak.ml`'s `PBClearPage` (`solidify`'s
45 /// `ImVertFixedEmpty(Fixed, Length.zero)`).
46 ClearPage,
47 /// `hook-page-break-block`'s marker (`vminst.ml:632`
48 /// `BackendHookPageBreakBlock` / `horzBox.ml:347`'s `VertHookPageBreak`)
49 /// — the block-level analog of `PureHorzBox::HookPageBreak`: an opaque
50 /// index into a lang-side hook table, fired by `fire_hooks` with the
51 /// page's `pbinfo` and the point where it sits in the flow. Contributes
52 /// zero height, same as `ClearPage`.
53 HookPageBreak(HookId),
54 /// `block-frame-breakable`'s frame-extent markers: the frame's
55 /// indented contents sit between a `FrameStart(id)`/`FrameEnd(id)` pair;
56 /// `chop_page`/`place_block_at` place each as a zero-height marker
57 /// `PlacedLine` (the `HookPageBreak` pattern) and `fire_hooks` derives
58 /// each page fragment's rect from the real lines between them. The
59 /// frame's pads/width/deco-set live lang-side (`DecoEntry::Block`).
60 FrameStart(DecoId),
61 FrameEnd(DecoId),
62 /// an INERT reflow marker for list (`itemize`/`enumerate`) structure,
63 /// emitted by the `list-mark` primitive from
64 /// `lib-rustyfi/dist-v01/packages/itemize.satyh`'s
65 /// `listing`/`listing-item`/`listing-item-breakable`/`enumerate`/
66 /// `enumerate-item`. Zero height/depth, contributes nothing to any
67 /// measurement (`measure_block`) or placement (`chop_page`/
68 /// `place_block_at`) — it never reaches a `PlacedLine`, so PDF output is
69 /// byte-identical whether or not a document's stdlib emits these. Read
70 /// only by the reflow HTML walker (`rustyfi-html/src/reflow/block.rs`'s
71 /// `walk_vboxes`), which uses the Start/End nesting to rebuild real
72 /// `<ul>`/`<ol>`/`<li>` structure.
73 ListMark(ListMarkKind),
74}
75
76/// The marker kind a `VertBox::ListMark` carries. `ordered` on `ListStart`
77/// is the ONE piece of real data these markers carry (`listing` vs
78/// `enumerate` are distinct stdlib commands, so this is an exact bit, not a
79/// heuristic); nesting depth is recovered structurally by the reflow
80/// walker's stack from how markers are nested in the flat box stream, not
81/// stored here.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum ListMarkKind {
84 /// Opens a `<ul>` (`ordered = false`) or `<ol>` (`ordered = true`).
85 ListStart { ordered: bool },
86 /// Closes the innermost open list.
87 ListEnd,
88 /// Opens one `<li>`.
89 ItemStart,
90 /// Closes the innermost open `<li>`.
91 ItemEnd,
92}