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