1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Configuration options for the renderer.
//!
//! The configuration of the `mathml` renderer is done through the [`RenderConfig`] struct.
use std::fmt::Display;

/// Configuration for the `mathml` renderer.
///
/// The default value is: [`RenderConfig::default`].
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RenderConfig<'a> {
    /// See [`DisplayMode`].
    pub display_mode: DisplayMode,
    /// If Some, the `mathml` generated includes an `<annotation>` element that contains the
    /// provided string. It is commonly used to include the LaTeX source code in the generated `mathml`.
    pub annotation: Option<&'a str>,
    /// A RGB color. This option determines the color in which errors and invalid LaTeX commands are rendered in.
    pub error_color: (u8, u8, u8),
    /// If true, a `xml` namespace (`xmlns="http://www.w3.org/1998/Math/MathML"`) will be written
    /// into the `<math>` element.
    ///
    /// This namespace is unnecessary for modern browsers but can be helpful for other user agents,
    /// such as Microsoft Word.
    pub xml: bool,
    /// See [`MathStyle`].
    pub math_style: MathStyle,
}

impl<'a> RenderConfig<'a> {
    /// Create a new `RenderConfig` with the provided annotation, and default values for other fields.
    pub fn with_annotation(annotation: &'a str) -> Self {
        Self {
            annotation: Some(annotation),
            ..Self::default()
        }
    }
}

impl<'a> Default for RenderConfig<'a> {
    /// # Default Value
    /// ```rust
    /// # use pulldown_latex::config::{RenderConfig, DisplayMode, MathStyle};
    /// const DEFAULT: RenderConfig = RenderConfig {
    ///     display_mode: DisplayMode::Inline,
    ///     annotation: None,
    ///     error_color: (178, 34, 34),
    ///     xml: false,
    ///     math_style: MathStyle::TeX,
    /// };
    /// assert_eq!(RenderConfig::default(), DEFAULT);
    /// ```
    fn default() -> Self {
        Self {
            display_mode: DisplayMode::Inline,
            annotation: None,
            error_color: (178, 34, 34),
            xml: false,
            math_style: MathStyle::TeX,
        }
    }
}

/// The way in which math variables are displayed.
///
/// This is used to determine how single-letter variables are displayed. This affects lowercase and
/// uppercase latin letters (__a-z__ and __A-Z__), and uppercase and lowercase greek letters
/// (__α-ω__ and __Α-Ω__). Style differences are shown in the table below.
///
/// ## Math Styles
///
/// | Style     | Low. Latin | Upp. Latin | Low. Greek | Upp. Greek |
/// | -----     | ---------- | ---------- | ---------- | ---------- |
/// | `TeX`     | _italic_   | _italic_   | _italic_   | upright    |
/// | `ISO`     | _italic_   | _italic_   | _italic_   | _italic_   |
/// | `French`  | _italic_   | upright    | upright    | upright    |
/// | `Upright` | upright    | upright    | upright    | upright    |
///
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum MathStyle {
    /// The default style used in TeX.
    ///
    /// Makes everything italic except for uppercase greek letters.
    ///
    /// __This is the default value.__
    #[default]
    TeX,
    /// The style used in `ISO 80000-2:2019`.
    ///
    /// Makes everything italic.
    ISO,
    /// The style used in French typography.
    ///
    /// Makes everything upright except for lowercase latin letters.
    French,
    /// Makes everything upright.
    Upright,
}

impl MathStyle {
    /// Whether or not the style should __manually__ be set to upright for the given character.
    ///
    /// The mathml spec states that the default style for math variables is italic for all
    /// characters, following the ISO 80000-2:2019 standard. If the style is set to something other
    /// than `TeX`, then the style should be set to upright for some characters. This function
    /// returns `true` if the style should be set to upright for the given character.
    pub(crate) fn should_be_upright(self, c: char) -> bool {
        match self {
            MathStyle::TeX => c.is_uppercase() && !c.is_ascii_uppercase(),
            MathStyle::ISO => false,
            MathStyle::French => !c.is_ascii_lowercase(),
            MathStyle::Upright => true,
        }
    }
}

/// How the math is displayed.
///
/// Semantically, this affects the [`display`] attribute of the [`<math>`] tag in the mathml
/// output. The attribute will be set to `block` or `inline` depending on the value of this enum.
///
/// [`<math>`]: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math
/// [`display`]: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#display
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DisplayMode {
    /// The math is rendered inline.
    ///
    /// The equation is displayed inline within a sentence or paragraph, and elements such as
    /// `\int` and `\sum` are minimized to fit within the line.
    ///
    /// __This is the default value.__
    #[default]
    Inline,
    /// The math is rendered in display/block mode (`displaystyle` in LaTeX).
    ///
    /// The equation is centered on its own line
    /// and elements such as`\int` and `\sum` are displayed bigger.
    Block,
}

impl Display for DisplayMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            DisplayMode::Inline => f.write_str("inline"),
            DisplayMode::Block => f.write_str("block"),
        }
    }
}