pulldown_latex/event.rs
1//! The definition of the [`Event`] enum, which is used as a logical
2//! representation of `LaTeX` content.
3//!
4//! A stream of `Result<Event, ParserError>`s is produced by the [`Parser`], which can then be typeset/rendered
5//! by a renderer. This crate only provides a simple `mathml` renderer available through the
6//! [`push_mathml`] and [`write_mathml`] functions.
7//!
8//! This module tries to be comprehensive in explaining the invariants that are be upheld by the [`Parser`].
9//! If a user of this crate, or a renderer implementor finds a case where the invariants are not
10//! satisfied, then it is a bug in the parser, and should be reported.
11//!
12//! [`Parser`]: crate::parser::Parser
13//! [`push_mathml`]: crate::mathml::push_mathml
14//! [`write_mathml`]: crate::mathml::write_mathml
15
16use std::fmt::Display;
17
18/// All events that can be produced by the parser.
19///
20/// # For Renderer Implementors
21///
22/// When an [`Event`] is referreing to an "_element_", it is referring to the next logical unit of
23/// content in the stream. This can be a single [`Event::Content`] element, a group marked
24/// by [`Event::Begin`] and [`Event::End`], an [`Event::Visual`] or an [`Event::Script`] element,
25/// an [`Event::Space`], or an [`Event::StateChange`].
26///
27/// [`EnvironmentFlow::Alignment`] and [`EnvironmentFlow::NewLine`] events are not considered
28/// elements, and must never occur when an element is expected.
29///
30/// ### Examples
31///
32/// The following examples all constitute a single element:
33///
34/// __Input__: `\text{Hello, world!}`
35/// ```
36/// # use pulldown_latex::event::{Event, Content};
37/// [Event::Content(Content::Text("Hello, world!"))];
38/// ```
39///
40/// __Input__: `x^2_{\text{max}}`
41/// ```
42/// # use pulldown_latex::event::{Event, Content, Grouping, ScriptType, ScriptPosition};
43/// [
44/// Event::Script {
45/// ty: ScriptType::SubSuperscript,
46/// position: ScriptPosition::Right,
47/// },
48/// Event::Begin(Grouping::Normal),
49/// Event::Content(Content::Text("max")),
50/// Event::End,
51/// Event::Content(Content::Ordinary {
52/// content: 'x',
53/// stretchy: false,
54/// }),
55/// ];
56/// ```
57#[derive(Debug, Clone, PartialEq)]
58pub enum Event<'a> {
59 /// The event is a [`Content`] element.
60 Content(Content<'a>),
61 /// The events following this one constitute a "group" which counts as a single _element_
62 /// (i.e., a set of elements within `{}` in `LaTeX`), until the [`Event::End`] event
63 /// is reached.
64 Begin(Grouping),
65 /// Marks the end of a group initiated with [`Event::Begin`].
66 End,
67 /// The `n` elements following this one constitute the content of the [`Visual`] element,
68 /// where `n` is specified in the documentation of for each of the [`Visual`] variants.
69 Visual(Visual),
70 /// The `n` elements following this one constitute a base and its script(s), where `n` is
71 /// specified in the documentation for each of the [`ScriptType`] variants.
72 Script {
73 /// The type of the script.
74 ty: ScriptType,
75 /// The position of the script.
76 position: ScriptPosition,
77 },
78 /// This event specifes a custom spacing. This is produced by commands such as
79 /// `\kern`, `\hspace`, etc.
80 ///
81 /// If any of the components are `None`, then the spacing is set to 0 for that component.
82 Space {
83 /// The amount of space to add before the element.
84 width: Option<Dimension>,
85 /// The amount of space to add after the element.
86 height: Option<Dimension>,
87 /// The amount of depth (space below the baseline) to add.
88 depth: Option<Dimension>,
89 },
90 /// This event specifies a state change in the renderer.
91 ///
92 /// This state change only applies to the current group nesting and deeper groups.
93 StateChange(StateChange),
94
95 /// This is a flow event that is emitted in mathematical environments such as `align`,
96 /// `cases`, `array`, etc.
97 EnvironmentFlow(EnvironmentFlow),
98}
99
100/// Base events that produce `mathml` nodes
101#[derive(Debug, Clone, Copy, PartialEq)]
102pub enum Content<'a> {
103 /// Text content that should be typeset following the rules of `LaTeX`'s `text` mode.
104 Text(&'a str),
105 /// A number, which can include decimal points and commas.
106 Number(&'a str),
107 /// A function identifier, such as `sin`, `lim`, or a custom function with
108 /// `\operatorname{arccotan}`.
109 Function(&'a str),
110 /// A variable identifier, such as `x`, `\theta`, `\aleph`, etc., and other stuff that do not have
111 /// any spacing around them. This includes things that normally go in under and overscripts
112 /// which may be stretchy, e.g., `→`, `‾`, etc.
113 Ordinary {
114 /// The content character.
115 content: char,
116 /// Whether the character is stretchy.
117 ///
118 /// This applies to characters that are in under and overscripts, such as `→`.
119 stretchy: bool,
120 },
121 /// A large operator, e.g., `\sum`, `\int`, `\prod`, etc.
122 LargeOp {
123 /// The content character.
124 content: char,
125 /// Whether the operator is a small variant, e.g., `\smallint`.
126 small: bool,
127 },
128 /// A binary operator, e.g., `+`, `*`, `⊗`, `?`, etc.
129 BinaryOp {
130 /// The content character.
131 content: char,
132 /// Whether the operator is a small variant, e.g., `\smallsetminus`.
133 small: bool,
134 },
135 /// A relation, e.g., `=`, `≠`, `≈`, etc.
136 Relation {
137 /// The content of the relation.
138 content: RelationContent,
139 /// Whether the relation is a small variant, e.g., `\shortparallel`.
140 small: bool,
141 },
142 /// An opening, closing, or fence delimiter, e.g., `(`, `[`, `{`, `|`, `)`, `]`, `}`, etc.
143 Delimiter {
144 /// The delimiter character.
145 content: char,
146 /// The size of the delimiter, if any.
147 size: Option<DelimiterSize>,
148 /// The type of the delimiter.
149 ty: DelimiterType,
150 },
151 /// A punctuation character, such as `,`, `.`, or `;`.
152 Punctuation(char),
153}
154
155/// Modifies the visual representation of the following element(s)
156#[derive(Debug, Clone, Copy, PartialEq)]
157pub enum Visual {
158 /// The following element is the content of the root.
159 SquareRoot,
160 /// The 2 following elements are the radicand and the index of the root.
161 Root,
162 /// The 2 following elements are the numerator and denominator of the fraction.
163 ///
164 /// If the content of the variant is `None`, then the size of the middle line is set to the
165 /// default size, otherwise the size is set to the specified size.
166 Fraction(Option<Dimension>),
167 /// The "negation" operator as in "not equal" (≠) or "does not exist" (∄). This applies to the
168 /// next event in the stream.
169 ///
170 /// This event can occur before an arbitrary event, not just a `Content` event. It is left to
171 /// the renderer to determine how to apply the negation. In `LaTeX`, the renderer usually
172 /// generates an akward looking negation across the next element, when it does not correspond
173 /// to a commonly negated element.
174 Negation,
175}
176
177/// Logical type of the script. This is used to determine how to render the scripts.
178///
179/// Things like subscripts, underscripts, and movable scripts can be represented when using this
180/// `enum` in conjunction with the [`ScriptPosition`] `enum`.
181#[derive(Debug, Clone, Copy, PartialEq)]
182pub enum ScriptType {
183 /// The 2 following elements are the base and and the subscript
184 Subscript,
185 /// The 2 following elements are the base and and the superscript
186 Superscript,
187 /// The 3 following elements are the base, subscript and superscript
188 SubSuperscript,
189}
190
191/// Position of the script. This is used to determine how to render the scripts.
192#[derive(Debug, Clone, Copy, PartialEq)]
193pub enum ScriptPosition {
194 /// The scripts are rendered to the (bottom and top) right of the operator.
195 Right,
196 /// The scripts are rendered above and below the operator instead of to the right.
197 AboveBelow,
198 /// Is set to `AboveBelow` by preference, but should be changed to `Right` when rendering in
199 /// inline mode.
200 ///
201 /// This is used by the `lim` and `sum` (Σ) operators for example.
202 Movable,
203}
204
205/// Represents a state change for the following content.
206///
207/// State changes take effect for the current group nesting and all deeper groups.
208/// State changes are not maintained across `NewLine` and `Alignment` events, and are also reset
209/// when entering a new group that is not a `Grouping::Normal`, or a `Grouping::LeftRight`. The
210/// exception to the latter is `StateChange::Style`, which is maintained across all groups.
211#[derive(Debug, Clone, Copy, PartialEq)]
212pub enum StateChange {
213 /// Changes the font of the content.
214 ///
215 /// If the font is `None`, then the default renderer font is used, otherwise the font is set to
216 /// the specified font.
217 Font(Option<Font>),
218 /// Changes the color of the content.
219 Color(ColorChange),
220 /// Changes the style of the content (mostly affects the sizing of the content).
221 ///
222 /// __Important__: This state change does not affect scripts and root indices.
223 Style(Style),
224}
225
226/// Available font styles from LaTeX.
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum Font {
229 /// The bold and calligraphic font-face.
230 BoldScript,
231 /// The bold and italic font-face.
232 BoldItalic,
233 /// The bold font-face.
234 Bold,
235 /// The `\boldsymbol` font-face: bold italic for characters that are italic
236 /// by default (Latin letters, lowercase Greek), bold upright for characters
237 /// that are upright by default (capital Greek, digits).
238 BoldSymbol,
239 /// The fraktur font-face.
240 Fraktur,
241 /// The calligraphic font-face.
242 Script,
243 /// The monospace font-face.
244 Monospace,
245 /// The sans-serif font-face.
246 SansSerif,
247 /// The double-struck font-face.
248 DoubleStruck,
249 /// The double-struck italic font-face. Covers the 5 letters at U+2145–U+2149
250 /// (ⅅ, ⅆ, ⅇ, ⅈ, ⅉ); no other characters have double-struck italic forms in Unicode.
251 DoubleStruckItalic,
252 /// The italic font-face.
253 Italic,
254 /// The bold fraktur font-face.
255 BoldFraktur,
256 /// The bold sans-serif font-face.
257 SansSerifBoldItalic,
258 /// The sans-serif italic font-face.
259 SansSerifItalic,
260 /// The sans-serif bold font-face.
261 BoldSansSerif,
262 /// The normal font-face.
263 UpRight,
264}
265
266/// The style of the content.
267///
268/// This is analogous to the different "modes" in `LaTeX`, such as `display`, `text`, etc., which
269/// are set by commands like `\displaystyle`, `\textstyle`, etc.
270#[derive(Debug, Clone, Copy, PartialEq)]
271pub enum Style {
272 /// Set by the `\displaystyle` command.
273 Display,
274 /// Set by the `\textstyle` command.
275 Text,
276 /// Set by the `\scriptstyle` command.
277 Script,
278 /// Set by the `\scriptscriptstyle` command.
279 ScriptScript,
280}
281
282/// Represents a color change.
283#[derive(Debug, Clone, Copy, PartialEq)]
284pub struct ColorChange {
285 /// The color to change to.
286 ///
287 /// A string that represents the color to change to, either as a hex RGB color in the form #RRGGBB,
288 /// or as one of the color names existing as part of CSS3 (e.g., "red").
289 pub color: (u8, u8, u8),
290 /// The target of the color change.
291 ///
292 /// Specifies which part of the content to change the color of.
293 pub target: ColorTarget,
294}
295
296/// The target of the color change.
297#[derive(Debug, Clone, Copy, PartialEq)]
298pub enum ColorTarget {
299 /// The text of the content.
300 Text,
301 /// The background of the content.
302 Background,
303 /// The border surrounding the content.
304 Border,
305}
306
307/// Represents a grouping of elements, which is itself a single logical element.
308///
309/// This can be created by a lot of different `LaTeX` commands, such as `{}`, `\left`, `\right`,
310/// `\begin{...}`, `\end{...}`, etc.
311#[derive(Debug, Clone, PartialEq)]
312pub enum Grouping {
313 /// A normal form of grouping, usually induced by `{}` or `\begingroup` and `\endgroup` in `LaTeX`.
314 Normal,
315 /// A grouping that is induced by `\left` and `\right` in `LaTeX`.
316 LeftRight(Option<char>, Option<char>),
317 /// The array environment of `LaTeX`.
318 ///
319 /// It's content is an array of columns, which represents the column specification in `LaTeX`.
320 ///
321 /// ## Example
322 ///
323 /// __Input__: `\begin{array}{lcr} ... \end{array}`
324 /// __Generates__:
325 /// ```
326 /// # use pulldown_latex::event::{ArrayColumn, ColumnAlignment, Grouping};
327 /// Grouping::Array(Box::new([
328 /// ArrayColumn::Column(ColumnAlignment::Left),
329 /// ArrayColumn::Column(ColumnAlignment::Center),
330 /// ArrayColumn::Column(ColumnAlignment::Right),
331 /// ]));
332 /// ```
333 /// ## Invariant
334 ///
335 /// The content of the `Array` variant is guaranteed to be non-empty, and contain at least one
336 /// [`ArrayColumn::Column`].
337 Array(Box<[ArrayColumn]>),
338 /// The `matrix` environment of `LaTeX`.
339 Matrix {
340 /// The default alignment is `ColumnAlignment::Center`, but it can be specified by in `LaTeX`
341 /// when using the `\begin{matrix*}[l] ... \end{matrix*}` syntax.
342 alignment: ColumnAlignment,
343 },
344 /// The `cases` environment of `LaTeX`.
345 Cases {
346 /// `left` is true if the environment is `cases` and false if the environment is `rcases`.
347 left: bool,
348 },
349 /// The `equation` environment of `LaTeX`.
350 Equation {
351 /// If `eq_numbers` is true, then equation numbers are displayed.
352 eq_numbers: bool,
353 },
354 /// The `align` environment of `LaTeX`.
355 Align {
356 /// If `eq_numbers` is true, then equation numbers are displayed.
357 eq_numbers: bool,
358 },
359 /// The `aligned` environment of `LaTeX`.
360 Aligned,
361 /// The `subarray` environment of `LaTeX`.
362 SubArray {
363 /// The alignment of the columns in the subarray.
364 alignment: ColumnAlignment,
365 },
366 /// The `alignat` environment of `LaTeX`.
367 Alignat {
368 /// `pairs` specifies the number of left-right column pairs specified in the environment
369 /// declaration.
370 pairs: u16,
371 /// If `eq_numbers` is true, then equation numbers are displayed.
372 eq_numbers: bool,
373 },
374 /// The `alignedat` environment of `LaTeX`.
375 Alignedat {
376 /// `pairs` specifies the number of left-right column pairs specified in the environment
377 pairs: u16,
378 },
379 /// The `gather` environment of `LaTeX`.
380 Gather {
381 /// If `eq_numbers` is true, then equation numbers are displayed.
382 eq_numbers: bool,
383 },
384 /// The `gathered` environment of `LaTeX`.
385 Gathered,
386 /// The `multline` environment of `LaTeX`.
387 Multline,
388 /// The `split` environment of `LaTeX`.
389 Split,
390}
391
392impl Grouping {
393 pub(crate) fn is_math_env(&self) -> bool {
394 !matches!(self, Self::Normal | Self::LeftRight(_, _))
395 }
396}
397
398#[derive(Debug, Clone, PartialEq)]
399pub enum EnvironmentFlow {
400 /// This event specifies an alignment mark in a mathematical environment.
401 ///
402 /// This event is only emitted when inside a `Grouping` that allows it.
403 Alignment,
404 /// This event specifies a line break in a mathematical environment.
405 ///
406 /// This event is only emitted when inside a of `Grouping` that allows it.
407 NewLine {
408 /// The amount of space to add after the line break.
409 spacing: Option<Dimension>,
410 /// The horizontal lines to draw after the line break.
411 horizontal_lines: Box<[Line]>,
412 },
413
414 /// This event is emitted specifically when an environment begins with horizontal lines
415 /// as the first element.
416 ///
417 /// ### Examples
418 /// ```
419 /// use pulldown_latex::{
420 /// event::{ArrayColumn, ColumnAlignment, Content, EnvironmentFlow, Event, Grouping, Line},
421 /// Parser, Storage,
422 /// };
423 ///
424 /// const INPUT: &str = r#"\begin{array}{|c|c|c|} \hline a & b & c \\ \hline d & e & f \\
425 /// \hline \end{array}"#;
426 ///
427 /// let storage = Storage::new();
428 /// let mut parser = Parser::new(INPUT, &storage);
429 /// let events = parser.collect::<Result<Vec<_>, _>>().unwrap();
430 ///
431 /// assert_eq!(
432 /// events,
433 /// vec![
434 /// Event::Begin(Grouping::Array(Box::new([
435 /// ArrayColumn::Separator(Line::Solid),
436 /// ArrayColumn::Column(ColumnAlignment::Center),
437 /// ArrayColumn::Separator(Line::Solid),
438 /// ArrayColumn::Column(ColumnAlignment::Center),
439 /// ArrayColumn::Separator(Line::Solid),
440 /// ArrayColumn::Column(ColumnAlignment::Center),
441 /// ArrayColumn::Separator(Line::Solid),
442 /// ]))),
443 /// Event::EnvironmentFlow(EnvironmentFlow::StartLines {
444 /// lines: Box::new([Line::Solid]),
445 /// }),
446 /// Event::Content(Content::Ordinary {
447 /// content: 'a',
448 /// stretchy: false,
449 /// }),
450 /// Event::EnvironmentFlow(EnvironmentFlow::Alignment),
451 /// Event::Content(Content::Ordinary {
452 /// content: 'b',
453 /// stretchy: false,
454 /// }),
455 /// Event::EnvironmentFlow(EnvironmentFlow::Alignment),
456 /// Event::Content(Content::Ordinary {
457 /// content: 'c',
458 /// stretchy: false,
459 /// }),
460 /// Event::EnvironmentFlow(EnvironmentFlow::NewLine {
461 /// spacing: None,
462 /// horizontal_lines: Box::new([Line::Solid]),
463 /// }),
464 /// Event::Content(Content::Ordinary {
465 /// content: 'd',
466 /// stretchy: false,
467 /// }),
468 /// Event::EnvironmentFlow(EnvironmentFlow::Alignment),
469 /// Event::Content(Content::Ordinary {
470 /// content: 'e',
471 /// stretchy: false,
472 /// }),
473 /// Event::EnvironmentFlow(EnvironmentFlow::Alignment),
474 /// Event::Content(Content::Ordinary {
475 /// content: 'f',
476 /// stretchy: false,
477 /// }),
478 /// Event::EnvironmentFlow(EnvironmentFlow::NewLine {
479 /// spacing: None,
480 /// horizontal_lines: Box::new([Line::Solid]),
481 /// }),
482 /// Event::End,
483 /// ]
484 /// );
485 /// ```
486 StartLines { lines: Box<[Line]> },
487}
488
489#[derive(Debug, Clone, Copy)]
490pub(crate) enum GroupingKind {
491 Normal,
492 OptionalArgument,
493 BeginEnd,
494 LeftRight,
495 Array { display: bool },
496 Matrix { ty: MatrixType, column_spec: bool },
497 Cases { left: bool, display: bool },
498 Equation { eq_numbers: bool },
499 Align { eq_numbers: bool },
500 Aligned,
501 SubArray,
502 Alignat { eq_numbers: bool },
503 Alignedat,
504 Gather { eq_numbers: bool },
505 Gathered,
506 Multline,
507 Split,
508}
509
510impl GroupingKind {
511 pub fn opening_str(&self) -> &'static str {
512 match self {
513 Self::Normal => "{",
514 Self::OptionalArgument => "[",
515 Self::BeginEnd => "\\begin",
516 Self::LeftRight => "\\left",
517 Self::Array { display: false } => "\\begin{array}",
518 Self::Array { display: true } => "\\begin{darray}",
519 Self::Matrix { ty, column_spec } => match (ty, column_spec) {
520 (MatrixType::Normal, true) => "\\begin{matrix*}",
521 (MatrixType::Normal, false) => "\\begin{matrix}",
522 (MatrixType::Small, true) => "\\begin{smallmatrix*}",
523 (MatrixType::Small, false) => "\\begin{smallmatrix}",
524 (MatrixType::Parens, true) => "\\begin{pmatrix*}",
525 (MatrixType::Parens, false) => "\\begin{pmatrix}",
526 (MatrixType::Brackets, true) => "\\begin{bmatrix*}",
527 (MatrixType::Brackets, false) => "\\begin{bmatrix}",
528 (MatrixType::Braces, true) => "\\begin{Bmatrix*}",
529 (MatrixType::Braces, false) => "\\begin{Bmatrix}",
530 (MatrixType::Vertical, true) => "\\begin{vmatrix*}",
531 (MatrixType::Vertical, false) => "\\begin{vmatrix}",
532 (MatrixType::DoubleVertical, true) => "\\begin{Vmatrix*}",
533 (MatrixType::DoubleVertical, false) => "\\begin{Vmatrix}",
534 },
535 Self::Cases { left, display } => match (left, display) {
536 (true, false) => "\\begin{cases}",
537 (true, true) => "\\begin{dcases}",
538 (false, false) => "\\begin{rcases}",
539 (false, true) => "\\begin{drcases}",
540 },
541 Self::Equation { eq_numbers: true } => "\\begin{equation}",
542 Self::Equation { eq_numbers: false } => "\\begin{equation*}",
543 Self::Align { eq_numbers: true } => "\\begin{align}",
544 Self::Align { eq_numbers: false } => "\\begin{align*}",
545 Self::Aligned => "\\begin{aligned}",
546 Self::SubArray => "\\begin{subarray}",
547 Self::Alignat { eq_numbers: true } => "\\begin{alignat}",
548 Self::Alignat { eq_numbers: false } => "\\begin{alignat*}",
549 Self::Alignedat => "\\begin{alignedat}",
550 Self::Gather { eq_numbers: true } => "\\begin{gather}",
551 Self::Gather { eq_numbers: false } => "\\begin{gather*}",
552 Self::Gathered => "\\begin{gathered}",
553 Self::Multline => "\\begin{multline}",
554 Self::Split => "\\begin{split}",
555 }
556 }
557
558 pub fn closing_str(&self) -> &'static str {
559 match self {
560 Self::Normal => "}",
561 Self::OptionalArgument => "]",
562 Self::BeginEnd => "\\end",
563 Self::LeftRight => "\\right",
564 Self::Array { display: false } => "\\end{array}",
565 Self::Array { display: true } => "\\end{darray}",
566 Self::Matrix { ty, column_spec } => match (ty, column_spec) {
567 (MatrixType::Normal, true) => "\\end{matrix*}",
568 (MatrixType::Normal, false) => "\\end{matrix}",
569 (MatrixType::Small, true) => "\\end{smallmatrix*}",
570 (MatrixType::Small, false) => "\\end{smallmatrix}",
571 (MatrixType::Parens, true) => "\\end{pmatrix*}",
572 (MatrixType::Parens, false) => "\\end{pmatrix}",
573 (MatrixType::Brackets, true) => "\\end{bmatrix*}",
574 (MatrixType::Brackets, false) => "\\end{bmatrix}",
575 (MatrixType::Braces, true) => "\\end{Bmatrix*}",
576 (MatrixType::Braces, false) => "\\end{Bmatrix}",
577 (MatrixType::Vertical, true) => "\\end{vmatrix*}",
578 (MatrixType::Vertical, false) => "\\end{vmatrix}",
579 (MatrixType::DoubleVertical, true) => "\\end{Vmatrix*}",
580 (MatrixType::DoubleVertical, false) => "\\end{Vmatrix}",
581 },
582 Self::Cases { left, display } => match (left, display) {
583 (true, false) => "\\end{cases}",
584 (true, true) => "\\end{dcases}",
585 (false, false) => "\\end{rcases}",
586 (false, true) => "\\end{drcases}",
587 },
588 Self::Equation { eq_numbers: true } => "\\end{equation}",
589 Self::Equation { eq_numbers: false } => "\\end{equation*}",
590 Self::Align { eq_numbers: true } => "\\end{align}",
591 Self::Align { eq_numbers: false } => "\\end{align*}",
592 Self::Aligned => "\\end{aligned}",
593 Self::SubArray => "\\end{subarray}",
594 Self::Alignat { eq_numbers: true } => "\\end{alignat}",
595 Self::Alignat { eq_numbers: false } => "\\end{alignat*}",
596 Self::Alignedat => "\\end{alignedat}",
597 Self::Gather { eq_numbers: true } => "\\end{gather}",
598 Self::Gather { eq_numbers: false } => "\\end{gather*}",
599 Self::Gathered => "\\end{gathered}",
600 Self::Multline => "\\end{multline}",
601 Self::Split => "\\end{split}",
602 }
603 }
604}
605
606#[derive(Debug, Clone, Copy)]
607pub(crate) enum MatrixType {
608 Normal,
609 Small,
610 Parens,
611 Brackets,
612 Braces,
613 Vertical,
614 DoubleVertical,
615}
616
617/// Represents a column in a matrix or array environment.
618#[derive(Debug, Clone, Copy, PartialEq)]
619pub enum ColumnAlignment {
620 /// Content in the column is left-aligned.
621 Left,
622 /// Content in the column is center-aligned.
623 Center,
624 /// Content in the column is right-aligned.
625 Right,
626}
627
628/// Represents a column in an array environment specification.
629///
630/// It can either be a column specification or a vertical separator specification.
631#[derive(Debug, Clone, Copy, PartialEq)]
632pub enum ArrayColumn {
633 /// A column specification.
634 Column(ColumnAlignment),
635 /// A vertical separator specification.
636 Separator(Line),
637}
638
639/// Represents a delimiter size.
640#[derive(Debug, Clone, Copy, PartialEq)]
641pub enum DelimiterSize {
642 /// Corresponds to `\bigl`, `\bigr`, etc.
643 Big,
644 /// Corresponds to `\Bigl`, `\Bigr`, etc.
645 BIG,
646 /// Corresponds to `\biggl`, `\biggr`, etc.
647 Bigg,
648 /// Corresponds to `\Biggl`, `\Biggr`, etc.
649 BIGG,
650}
651
652impl DelimiterSize {
653 pub(crate) fn to_em(self) -> f32 {
654 match self {
655 DelimiterSize::Big => 1.2,
656 DelimiterSize::BIG => 1.8,
657 DelimiterSize::Bigg => 2.4,
658 DelimiterSize::BIGG => 3.,
659 }
660 }
661}
662
663/// Whether the delimiter is an opening, closing, or fence delimiter.
664#[derive(Debug, Clone, Copy, PartialEq)]
665pub enum DelimiterType {
666 /// Corresponds to the left delimiter.
667 Open,
668 /// Corresponds to a delimiter that is introduced by the command `\middle`.
669 Fence,
670 /// Corresponds to the right delimiter.
671 Close,
672}
673
674/// Represents a line in a `LaTeX` environment.
675#[derive(Debug, Clone, Copy, PartialEq)]
676pub enum Line {
677 /// A solid line.
678 Solid,
679 /// A dashed line.
680 Dashed,
681}
682
683/// Sometimes mathematical relations can be made of more than one character, so we need a way to
684/// represent them when one character is not enough.
685#[derive(Debug, Clone, Copy, PartialEq)]
686pub struct RelationContent {
687 content: (char, Option<char>),
688}
689
690impl RelationContent {
691 pub(crate) fn single_char(content: char) -> Self {
692 Self {
693 content: (content, None),
694 }
695 }
696
697 pub(crate) fn double_char(first: char, second: char) -> Self {
698 Self {
699 content: (first, Some(second)),
700 }
701 }
702
703 /// Write the content of the relation to a buffer, and output the filled slice of that
704 /// buffer.
705 ///
706 /// To ensure a successful operation, the buffer must be at least 8 bytes long.
707 pub fn encode_utf8_to_buf<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
708 let mut len = self.content.0.encode_utf8(buf).len();
709 if let Some(second) = self.content.1 {
710 len += second.encode_utf8(&mut buf[len..]).len();
711 }
712 &buf[..len]
713 }
714}
715
716/// Represents a glue specification.
717pub type Glue = (Dimension, Option<Dimension>, Option<Dimension>);
718
719/// Represents a LaTeX dimension.
720#[derive(Debug, Clone, Copy, PartialEq)]
721pub struct Dimension {
722 /// The value of the dimension.
723 pub value: f32,
724 /// The unit of the dimension.
725 pub unit: DimensionUnit,
726}
727
728impl Dimension {
729 /// Creates a new dimension.
730 pub fn new(value: f32, unit: DimensionUnit) -> Self {
731 Self { value, unit }
732 }
733}
734
735/// Displays a LaTeX dimension into a CSS dimension string.
736///
737/// The conversion is done using the following relations between the TeX and CSS units:
738/// | TeX unit | CSS unit |
739/// | ---------------- | ----------------- |
740/// | 1 em | 1 em |
741/// | 18 mu | 1 em |
742/// | 1 ex | 1 ex |
743/// | 1 mm | 1 mm |
744/// | 1 cm | 1 cm |
745/// | 1 in | 1 in |
746/// | 1 bp | 1 pt |
747/// | 72.27 pt | 72 pt |
748/// | 72.27 pc | 72 pc |
749/// | 65536 * 72.27 sp | 1 pt |
750/// | 1157 * 72.27 dd | 1238 * 72 pt |
751/// | 1157 * 72.27 cc | 12 * 1238 * 72 pt |
752impl Display for Dimension {
753 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
754 const BP_PER_PT: f32 = 72. / 72.27;
755 const BP_PER_SP: f32 = 1. / 65536. * BP_PER_PT;
756 const BP_PER_DD: f32 = 1238. / 1157. * BP_PER_PT;
757 const BP_PER_CC: f32 = 12. * BP_PER_DD;
758 const EM_PER_MU: f32 = 1. / 18.;
759 let mut value = self.value;
760 let unit = match self.unit {
761 DimensionUnit::Em => "em",
762 DimensionUnit::Mu => {
763 value *= EM_PER_MU;
764 "mu"
765 }
766 DimensionUnit::Ex => "ex",
767 DimensionUnit::Mm => "mm",
768 DimensionUnit::Cm => "cm",
769 DimensionUnit::In => "in",
770 DimensionUnit::Bp => "pt",
771 DimensionUnit::Pt => {
772 value *= BP_PER_PT;
773 "pt"
774 }
775 DimensionUnit::Pc => {
776 value *= BP_PER_PT;
777 "pc"
778 }
779 DimensionUnit::Sp => {
780 value *= BP_PER_SP;
781 "pt"
782 }
783 DimensionUnit::Dd => {
784 value *= BP_PER_DD;
785 "pt"
786 }
787 DimensionUnit::Cc => {
788 value *= BP_PER_CC;
789 "pt"
790 }
791 };
792 write!(f, "{}{}", value, unit)
793 }
794}
795
796// From the TeXbook, p. 57, 60, 167.
797/// Represents a dimension unit in LaTeX.
798#[derive(Debug, Clone, Copy, PartialEq, Eq)]
799pub enum DimensionUnit {
800 /// The `em` unit.
801 Em,
802 /// The "math" unit.
803 Mu,
804 /// The `ex` unit.
805 Ex,
806 /// The "point" unit.
807 Pt,
808 /// The "picas" unit.
809 Pc,
810 /// The "inch" unit.
811 In,
812 /// The "big point" unit.
813 Bp,
814 /// The "centimeter" unit.
815 Cm,
816 /// The "millimeter" unit.
817 Mm,
818 /// The "didot point" unit.
819 Dd,
820 /// The "cicero" unit.
821 Cc,
822 /// The "scaled point" unit.
823 Sp,
824}