ratex_layout/layout_box.rs
1use ratex_types::color::Color;
2use ratex_types::path_command::PathCommand;
3
4/// A TeX box: the fundamental unit of layout.
5///
6/// Every mathematical element is represented as a box with three dimensions:
7/// - `width`: horizontal extent
8/// - `height`: ascent above baseline
9/// - `depth`: descent below baseline
10///
11/// All values are in **em** units relative to the current font size.
12#[derive(Debug, Clone)]
13pub struct LayoutBox {
14 pub width: f64,
15 pub height: f64,
16 pub depth: f64,
17 pub content: BoxContent,
18 pub color: Color,
19}
20
21/// What a LayoutBox contains.
22#[derive(Debug, Clone)]
23pub enum BoxContent {
24 /// Horizontal list of child boxes laid out left-to-right.
25 HBox(Vec<LayoutBox>),
26
27 /// Vertical list of child boxes laid out top-to-bottom.
28 VBox(Vec<VBoxChild>),
29
30 /// A single glyph character.
31 Glyph {
32 font_id: ratex_font::FontId,
33 char_code: u32,
34 },
35
36 /// Filled rectangle from `\rule[<raise>]{width}{height}`.
37 /// `thickness` is the ink height; `raise` is the distance (in em) from the baseline
38 /// to the bottom edge of the rectangle, positive toward the top of the line.
39 Rule {
40 thickness: f64,
41 raise: f64,
42 },
43
44 /// Empty space (kern).
45 Kern,
46
47 /// A fraction: numerator over denominator with optional bar.
48 Fraction {
49 numer: Box<LayoutBox>,
50 denom: Box<LayoutBox>,
51 numer_shift: f64,
52 denom_shift: f64,
53 bar_thickness: f64,
54 numer_scale: f64,
55 denom_scale: f64,
56 },
57
58 /// Superscript/subscript layout.
59 SupSub {
60 base: Box<LayoutBox>,
61 sup: Option<Box<LayoutBox>>,
62 sub: Option<Box<LayoutBox>>,
63 sup_shift: f64,
64 sub_shift: f64,
65 sup_scale: f64,
66 sub_scale: f64,
67 /// When true, place scripts centered on the base width (e.g. `\overbrace` / `\underbrace`).
68 center_scripts: bool,
69 /// Italic correction of the base character (em). Superscript x is offset by this amount
70 /// beyond base.width, matching KaTeX's margin-right on italic math symbols.
71 italic_correction: f64,
72 /// Horizontal kern (em) applied to the subscript: KaTeX uses `margin-left: -base.italic` on
73 /// `SymbolNode` bases so subscripts are not pushed out by the base's italic correction.
74 sub_h_kern: f64,
75 },
76
77 /// A radical (square root).
78 Radical {
79 body: Box<LayoutBox>,
80 index: Option<Box<LayoutBox>>,
81 /// Horizontal offset (in em) of the surd/body from the left edge when index is present.
82 index_offset: f64,
83 /// `scriptscript` size relative to the surrounding math style (for drawing the index).
84 index_scale: f64,
85 rule_thickness: f64,
86 inner_height: f64,
87 },
88
89 /// An operator with limits above/below (e.g. \sum_{i=0}^{n}).
90 OpLimits {
91 base: Box<LayoutBox>,
92 sup: Option<Box<LayoutBox>>,
93 sub: Option<Box<LayoutBox>>,
94 base_shift: f64,
95 sup_kern: f64,
96 sub_kern: f64,
97 slant: f64,
98 sup_scale: f64,
99 sub_scale: f64,
100 },
101
102 /// An accent above or below its base.
103 Accent {
104 base: Box<LayoutBox>,
105 accent: Box<LayoutBox>,
106 clearance: f64,
107 skew: f64,
108 is_below: bool,
109 /// KaTeX `accentunder.js`: extra em gap between base bottom and under-accent (e.g. 0.12 for `\\utilde`).
110 under_gap_em: f64,
111 },
112
113 /// A stretchy delimiter (\left, \right) wrapping inner content.
114 LeftRight {
115 left: Box<LayoutBox>,
116 right: Box<LayoutBox>,
117 inner: Box<LayoutBox>,
118 },
119
120 /// A matrix/array: rows × columns of cells.
121 Array {
122 cells: Vec<Vec<LayoutBox>>,
123 col_widths: Vec<f64>,
124 /// Per-column alignment: b'l', b'c', or b'r'.
125 col_aligns: Vec<u8>,
126 row_heights: Vec<f64>,
127 row_depths: Vec<f64>,
128 col_gap: f64,
129 offset: f64,
130 /// Extra x padding before the first column (= arraycolsep when hskip_before_and_after is true).
131 content_x_offset: f64,
132 /// For each column boundary (0 = before col 0, ..., num_cols = after last col),
133 /// the vertical rule separator type: None = no rule, Some(false) = solid '|', Some(true) = dashed ':'.
134 col_separators: Vec<Option<bool>>,
135 /// For each row boundary (0 = before row 0, ..., num_rows = after last row),
136 /// the list of hlines: false = solid, true = dashed.
137 hlines_before_row: Vec<Vec<bool>>,
138 /// Thickness of array rules in em.
139 rule_thickness: f64,
140 /// Gap between consecutive \hline or \hdashline rules (= \doublerulesep, in em).
141 double_rule_sep: f64,
142 /// Width of the cell grid including `content_x_offset` padding (em); excludes tag column.
143 array_inner_width: f64,
144 /// Horizontal gap between grid and tag column (em).
145 tag_gap_em: f64,
146 /// Width reserved for tags; tags are right-aligned in this column (em).
147 tag_col_width: f64,
148 /// Per-row tag layout; length matches number of rows.
149 row_tags: Vec<Option<LayoutBox>>,
150 /// When true, tags sit left of the grid (leqno-style).
151 tags_left: bool,
152 },
153
154 /// An SVG-style path (arrows, braces, etc.).
155 SvgPath {
156 commands: Vec<PathCommand>,
157 fill: bool,
158 },
159
160 /// A framed/colored box (fbox, colorbox, fcolorbox).
161 /// body is the inner content; padding and border add to the outer dimensions.
162 Framed {
163 body: Box<LayoutBox>,
164 padding: f64,
165 border_thickness: f64,
166 has_border: bool,
167 bg_color: Option<Color>,
168 border_color: Color,
169 },
170
171 /// A raised/lowered box (raisebox).
172 /// shift > 0 moves content up, shift < 0 moves content down.
173 RaiseBox {
174 body: Box<LayoutBox>,
175 shift: f64,
176 },
177
178 /// A scaled box (for \scriptstyle, \scriptscriptstyle in inline context).
179 /// The child is rendered at child_scale relative to the parent.
180 Scaled {
181 body: Box<LayoutBox>,
182 child_scale: f64,
183 },
184
185 /// Actuarial angle \angl{body}: path (horizontal roof + vertical bar) and body share the same baseline.
186 Angl {
187 path_commands: Vec<PathCommand>,
188 body: Box<LayoutBox>,
189 },
190
191 /// \overline{body}: body with a horizontal rule drawn above it.
192 /// The rule sits `2 * rule_thickness` above the body's top (clearance), and is `rule_thickness` thick.
193 Overline {
194 body: Box<LayoutBox>,
195 rule_thickness: f64,
196 },
197
198 /// \underline{body}: body with a horizontal rule drawn below it.
199 /// The rule sits `2 * rule_thickness` below the body's bottom (clearance), and is `rule_thickness` thick.
200 Underline {
201 body: Box<LayoutBox>,
202 rule_thickness: f64,
203 },
204
205 /// Bussproofs-style proof tree with absolutely placed child boxes and inference rules.
206 ProofTree {
207 children: Vec<PlacedBox>,
208 rules: Vec<ProofRule>,
209 },
210
211 /// Empty placeholder.
212 Empty,
213}
214
215#[derive(Debug, Clone)]
216pub struct PlacedBox {
217 pub box_: LayoutBox,
218 pub x: f64,
219 pub baseline_y: f64,
220}
221
222#[derive(Debug, Clone)]
223pub struct ProofRule {
224 pub x: f64,
225 pub y: f64,
226 pub width: f64,
227 pub thickness: f64,
228 pub dashed: bool,
229}
230
231/// A child element in a vertical box.
232#[derive(Debug, Clone)]
233pub struct VBoxChild {
234 pub kind: VBoxChildKind,
235 pub shift: f64,
236}
237
238#[derive(Debug, Clone)]
239pub enum VBoxChildKind {
240 Box(Box<LayoutBox>),
241 Kern(f64),
242}
243
244impl LayoutBox {
245 pub fn new_empty() -> Self {
246 Self {
247 width: 0.0,
248 height: 0.0,
249 depth: 0.0,
250 content: BoxContent::Empty,
251 color: Color::BLACK,
252 }
253 }
254
255 pub fn new_kern(width: f64) -> Self {
256 Self {
257 width,
258 height: 0.0,
259 depth: 0.0,
260 content: BoxContent::Kern,
261 color: Color::BLACK,
262 }
263 }
264
265 pub fn new_rule(width: f64, height: f64, depth: f64, thickness: f64, raise: f64) -> Self {
266 Self {
267 width,
268 height,
269 depth,
270 content: BoxContent::Rule { thickness, raise },
271 color: Color::BLACK,
272 }
273 }
274
275 pub fn total_height(&self) -> f64 {
276 self.height + self.depth
277 }
278
279 pub fn with_color(mut self, color: Color) -> Self {
280 self.color = color;
281 self
282 }
283
284 /// Adjust height/depth for a delimiter to match a target size.
285 pub fn with_adjusted_delim(mut self, height: f64, depth: f64) -> Self {
286 self.height = height;
287 self.depth = depth;
288 self
289 }
290}