core/
lib.rs

1//! Core types and abstractions used in Texcraft.
2//!
3//!
4
5use std::fmt::Write;
6
7/// Trait satisfied by font formats (like .tfm files).
8pub trait FontFormat: Sized {
9    const DEFAULT_FILE_EXTENSION: &'static str;
10    type Error: std::error::Error + 'static;
11
12    /// Parse binary data into a font.
13    fn parse(b: &[u8]) -> Result<Self, Self::Error>;
14}
15
16/// Scaled numbers.
17///
18/// This is a fixed-width numeric type used in throughout TeX.
19/// This type is defined and described in part 7 "arithmetic with scaled
20/// dimensions" starting at TeX.2021.99.
21///
22/// This numeric type has 15 bits for the integer part,
23/// 16 bits for the fractional part, and a single signed bit.
24/// The inner value is the number multiplied by 2^16.
25#[derive(Default, PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Hash)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Scaled(pub i32);
28
29impl Scaled {
30    /// Representation of the number 0 as a [Scaled].
31    pub const ZERO: Scaled = Scaled(0);
32
33    /// Representation of the number 1 as a [Scaled].
34    pub const ONE: Scaled = Scaled(1 << 16);
35
36    /// Representation of the number 2 as a [Scaled].
37    pub const TWO: Scaled = Scaled(1 << 17);
38
39    /// Maximum possible dimension in TeX, which is (2^30-1)/2^16.
40    ///
41    /// This is _not_ the maximum size of the Rust scaled number type, which is (2^31-1)/2^16.
42    ///
43    /// Defined in TeX.2021.421.
44    pub const MAX_DIMEN: Scaled = Scaled((1 << 30) - 1);
45
46    /// Create a scaled number corresponding the provided positive integer.
47    ///
48    /// Scaled numbers are in the range `(-2^14, 2^14)`.
49    /// If _i_ is outside this range an overflow error is returned.
50    pub fn from_integer(i: i32) -> Result<Scaled, OverflowError> {
51        if i >= (1 << 14) || i <= -(1 << 14) {
52            Err(OverflowError {})
53        } else {
54            Ok(Scaled(Scaled::ONE.0 * i))
55        }
56    }
57
58    /// Creates a scaled number from a decimal fraction.
59    ///
60    /// TeX.2021.102.
61    pub fn from_decimal_digits(digits: &[u8]) -> Scaled {
62        let mut a = 0;
63        for d in digits.iter().rev() {
64            a = (a + (*d as i32) * Scaled::TWO.0) / 10
65        }
66        Scaled((a + 1) / 2)
67    }
68
69    /// Creates a scaled number from the provided components.
70    ///
71    /// TeX.2021.458
72    pub fn new(
73        integer_part: i32,
74        fractional_part: Scaled,
75        scaled_unit: ScaledUnit,
76    ) -> Result<Scaled, OverflowError> {
77        if scaled_unit == ScaledUnit::ScaledPoint {
78            return if integer_part > Scaled::MAX_DIMEN.0 {
79                Err(OverflowError)
80            } else {
81                // For sp units, the fractional part is silently dropped.
82                Ok(Scaled(integer_part))
83            };
84        }
85        let (n, d) = scaled_unit.conversion_fraction();
86        // xn_over_d, but with integer arguments
87        let (Scaled(i), Scaled(remainder)) = Scaled(integer_part).xn_over_d(n, d)?;
88        let f =
89            fractional_part.nx_plus_y(n, Scaled::from_integer(remainder).expect("remainder<d<=7200<2^13, so a valid scaled number"))
90            .expect("fractional_part<2^16, remainder<2^16*d, so nx_plus_y<2^16(n+d). Each (n,d) makes this <2^30")
91            / d;
92        let integer_part = Scaled::from_integer(i + f.integer_part())?;
93        Ok(integer_part + f.fractional_part())
94    }
95
96    /// Calculates the integer division _xn_/_d_ and remainder, where _x_ is this scaled number
97    /// and _n_ and _d_ are integers in the range `[0,2^16]`.
98    ///
99    /// This function appears in TeX.2021.107. Knuth is working with 32-bit integers
100    /// and so calculating this number is tricky without overflowing. E.g. _xn_ may
101    /// be larger than `2^32-1` even if the final result is in range.
102    /// TeX has an algorithm that calculates the exact value without overflowing,
103    /// in the case when the final result is in range.
104    ///
105    /// Our implementation simply uses 64-bit integers.
106    pub fn xn_over_d(&self, n: i32, d: i32) -> Result<(Scaled, Scaled), OverflowError> {
107        debug_assert!(n <= 0o200000);
108        debug_assert!(d <= 0o200000);
109        let mut b: i64 = self.0.into();
110        b *= n as i64; // can't overflow because |b|<=2^31 and |n|<=2^16
111        let remainder: i32 = (b % (d as i64)).try_into().expect("d<=2^16 so b%d<2^16");
112        b /= d as i64;
113        if b < -(Scaled::MAX_DIMEN.0 as i64) || b > Scaled::MAX_DIMEN.0 as i64 {
114            return Err(OverflowError {});
115        }
116        let b: i32 = b.try_into().expect("b in (-2^30, +2^30)");
117        Ok((Scaled(b), Scaled(remainder)))
118    }
119
120    /// TeX.2021.105
121    pub fn nx_plus_y(self, mut n: i32, y: Scaled) -> Result<Scaled, OverflowError> {
122        let max_answer = Scaled::MAX_DIMEN;
123        if n == 0 {
124            return Ok(y);
125        }
126        let mut x = self;
127        if n < 0 {
128            n = -n;
129            x = -x;
130        }
131        if x <= (max_answer - y) / n && -x <= (max_answer + y) / n {
132            Ok(x * n + y)
133        } else {
134            Err(OverflowError {})
135        }
136    }
137
138    pub fn integer_part(self) -> i32 {
139        self.0 / Scaled::ONE.0
140    }
141
142    pub fn fractional_part(self) -> Scaled {
143        self % Scaled::ONE.0
144    }
145
146    pub fn abs(self) -> Scaled {
147        Scaled(self.0.abs())
148    }
149
150    pub fn wrapping_add(self, rhs: Scaled) -> Self {
151        Scaled(self.0.wrapping_add(rhs.0))
152    }
153    pub fn checked_add(self, rhs: Scaled) -> Option<Self> {
154        Some(Scaled(self.0.checked_add(rhs.0)?))
155    }
156    pub fn wrapping_mul(self, rhs: i32) -> Self {
157        Scaled(self.0.wrapping_mul(rhs))
158    }
159    pub fn checked_mul(self, rhs: i32) -> Option<Self> {
160        // TODO: need to really probe the overflow behavior here!
161        // I actually think it's correct, but we should add tests.
162        self.nx_plus_y(rhs, Scaled::ZERO).ok()
163    }
164    pub fn checked_div(self, rhs: i32) -> Option<Self> {
165        Some(Scaled(self.0.checked_div(rhs)?))
166    }
167    // TeX.2021.103 print_scaled
168    pub fn display_no_units(self) -> impl std::fmt::Display {
169        struct D {
170            s: Scaled,
171        }
172        impl std::fmt::Display for D {
173            fn fmt(&self, fm: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174                if self.s.0 < 0 {
175                    write!(fm, "-")?;
176                }
177                write!(fm, "{}.", self.s.integer_part().abs())?;
178                // Fractional part
179                let mut f = self.s.abs().fractional_part() * 10 + Scaled(5);
180                let mut delta = Scaled(10);
181                loop {
182                    if delta > Scaled::ONE {
183                        // round the last digit
184                        f += Scaled(0o100000 - 50000);
185                    }
186                    fm.write_char(
187                        char::from_digit(f.integer_part().try_into().unwrap(), 10).unwrap(),
188                    )?;
189                    f = f.fractional_part() * 10;
190                    delta = delta * 10;
191                    if f <= delta {
192                        break;
193                    }
194                }
195                Ok(())
196            }
197        }
198        D { s: self }
199    }
200}
201
202#[derive(Debug)]
203pub struct OverflowError;
204
205impl std::fmt::Display for Scaled {
206    fn fmt(&self, fm: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        // Integer part
208        write!(fm, "{}", self.display_no_units())?;
209        // Units
210        write!(fm, "pt")?;
211        Ok(())
212    }
213}
214
215impl std::iter::Sum for Scaled {
216    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
217        Self(iter.map(|s| s.0).sum())
218    }
219}
220
221impl std::ops::Add<Scaled> for Scaled {
222    type Output = Scaled;
223    fn add(self, rhs: Scaled) -> Self::Output {
224        Scaled(self.0 + rhs.0)
225    }
226}
227
228impl std::ops::AddAssign<Scaled> for Scaled {
229    fn add_assign(&mut self, rhs: Scaled) {
230        self.0 += rhs.0;
231    }
232}
233impl std::ops::Sub<Scaled> for Scaled {
234    type Output = Scaled;
235    fn sub(self, rhs: Scaled) -> Self::Output {
236        Scaled(self.0 - rhs.0)
237    }
238}
239
240impl std::ops::Mul<i32> for Scaled {
241    type Output = Scaled;
242    fn mul(self, rhs: i32) -> Self::Output {
243        Scaled(self.0 * rhs)
244    }
245}
246
247impl std::ops::Div<i32> for Scaled {
248    type Output = Scaled;
249    fn div(self, rhs: i32) -> Self::Output {
250        Scaled(self.0 / rhs)
251    }
252}
253
254impl std::ops::DivAssign<i32> for Scaled {
255    fn div_assign(&mut self, rhs: i32) {
256        self.0 = self.0 / rhs
257    }
258}
259
260impl std::ops::Rem<i32> for Scaled {
261    type Output = Scaled;
262    fn rem(self, rhs: i32) -> Self::Output {
263        Scaled(self.0 % rhs)
264    }
265}
266
267impl std::ops::Neg for Scaled {
268    type Output = Scaled;
269    fn neg(self) -> Self::Output {
270        Scaled(-self.0)
271    }
272}
273
274/// Unit used to define a scaled integer
275///
276/// Defined in TeX.2021.458 and chapter 10 of the TeX book.
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278pub enum ScaledUnit {
279    Point,
280    Pica,
281    Inch,
282    BigPoint,
283    Centimeter,
284    Millimeter,
285    DidotPoint,
286    Cicero,
287    ScaledPoint,
288}
289
290impl ScaledUnit {
291    /// Parses a unit from a two character abbreviation.
292    ///
293    /// E.g., `"pc"` is parsed to [`ScaledUnit::Pica`].
294    /// These are abreviations are defined in TeX.2021.458 and chapter 10 of the TeX book.
295    pub fn parse(s: &str) -> Option<Self> {
296        use ScaledUnit::*;
297        Some(match s {
298            "pt" => Point,
299            "pc" => Pica,
300            "in" => Inch,
301            "bp" => BigPoint,
302            "cm" => Centimeter,
303            "mm" => Millimeter,
304            "dd" => DidotPoint,
305            "cc" => Cicero,
306            "sp" => ScaledPoint,
307            _ => return None,
308        })
309    }
310
311    /// Returns the fraction needed to convert to/from this unit to points.
312    ///
313    /// The return value is of the form (_n_, _d_).
314    /// If a scaled number represents _x_in these units (e.g. y [`ScaledUnit::Pica`]),
315    ///     then it is _y_=_nx_/_d_ points.
316    ///
317    /// Defined in TeX.2021.458.
318    pub fn conversion_fraction(&self) -> (i32, i32) {
319        use ScaledUnit::*;
320        match self {
321            Point => (1, 1),
322            Pica => (12, 1),
323            Inch => (7227, 100),
324            BigPoint => (7227, 7200),
325            Centimeter => (7227, 254),
326            Millimeter => (7227, 2540),
327            DidotPoint => (1238, 1157),
328            Cicero => (14856, 1157),
329            ScaledPoint => (1, 1 << 16),
330        }
331    }
332}
333
334/// Glue.
335///
336/// In Knuth's TeX this struct is not passed around directly; instead
337/// Knuth essentially uses `std::rc::Rc<Glue>`.
338/// This optimization is based on the fact that very few distinct glue
339/// values appear in a document, and that the pointer takes up less
340/// space than the struct.
341/// We might consider performing such an optimization.
342///
343/// Described in TeX.2021.150.
344#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
345#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
346pub struct Glue {
347    pub width: Scaled,
348    pub stretch: Scaled,
349    pub stretch_order: GlueOrder,
350    pub shrink: Scaled,
351    pub shrink_order: GlueOrder,
352}
353
354impl std::ops::Mul<i32> for Glue {
355    type Output = Glue;
356    fn mul(self, rhs: i32) -> Self::Output {
357        Glue {
358            width: self.width * rhs,
359            stretch: self.stretch * rhs,
360            stretch_order: self.stretch_order,
361            shrink: self.shrink * rhs,
362            shrink_order: self.shrink_order,
363        }
364    }
365}
366
367impl Glue {
368    /// TeX.2021.1239
369    pub fn wrapping_add(self, rhs: Glue) -> Self {
370        use std::cmp::Ordering::*;
371        Glue {
372            width: self.width.wrapping_add(rhs.width),
373            stretch: match self.stretch_order.cmp(&rhs.stretch_order) {
374                Less => rhs.stretch,
375                Equal => self.stretch.wrapping_add(rhs.stretch),
376                Greater => self.stretch,
377            },
378            stretch_order: self.stretch_order.max(rhs.stretch_order),
379            shrink: match self.shrink_order.cmp(&rhs.shrink_order) {
380                Less => rhs.shrink,
381                Equal => self.shrink.wrapping_add(rhs.shrink),
382                Greater => self.shrink,
383            },
384            shrink_order: self.shrink_order.max(rhs.shrink_order),
385        }
386    }
387    pub fn checked_add(self, rhs: Glue) -> Option<Self> {
388        use std::cmp::Ordering::*;
389        Some(Glue {
390            width: self.width.checked_add(rhs.width)?,
391            stretch: match self.stretch_order.cmp(&rhs.stretch_order) {
392                Less => rhs.stretch,
393                Equal => self.stretch.checked_add(rhs.stretch)?,
394                Greater => self.stretch,
395            },
396            stretch_order: self.stretch_order.max(rhs.stretch_order),
397            shrink: match self.shrink_order.cmp(&rhs.shrink_order) {
398                Less => rhs.shrink,
399                Equal => self.shrink.checked_add(rhs.shrink)?,
400                Greater => self.shrink,
401            },
402            shrink_order: self.shrink_order.max(rhs.shrink_order),
403        })
404    }
405    pub fn checked_mul(self, rhs: i32) -> Option<Self> {
406        Some(Glue {
407            width: self.width.checked_mul(rhs)?,
408            stretch: self.stretch.checked_mul(rhs)?,
409            stretch_order: self.stretch_order,
410            shrink: self.shrink.checked_mul(rhs)?,
411            shrink_order: self.shrink_order,
412        })
413    }
414    pub fn wrapping_mul(self, rhs: i32) -> Self {
415        Glue {
416            width: self.width.wrapping_mul(rhs),
417            stretch: self.stretch.wrapping_mul(rhs),
418            stretch_order: self.stretch_order,
419            shrink: self.shrink.wrapping_mul(rhs),
420            shrink_order: self.shrink_order,
421        }
422    }
423    pub fn checked_div(self, rhs: i32) -> Option<Self> {
424        Some(Glue {
425            width: self.width.checked_div(rhs)?,
426            stretch: self.stretch.checked_div(rhs)?,
427            stretch_order: self.stretch_order,
428            shrink: self.shrink.checked_div(rhs)?,
429            shrink_order: self.shrink_order,
430        })
431    }
432}
433
434impl std::fmt::Display for Glue {
435    // TeX.2021.177 print_spec with s="pt"
436    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437        write!(f, "{}", self.width)?;
438        if self.stretch != Scaled::ZERO {
439            write!(f, " plus ")?;
440            write!(f, "{}", self.stretch.display_no_units())?;
441            write!(f, "{}", self.stretch_order)?;
442        }
443        if self.shrink != Scaled::ZERO {
444            write!(f, " minus ")?;
445            write!(f, "{}", self.shrink.display_no_units())?;
446            write!(f, "{}", self.shrink_order)?;
447        }
448        Ok(())
449    }
450}
451
452/// Order of infinity of a glue stretch or shrink.
453///
454/// When setting a list of boxes, TeX stretches or shrinks glue boxes.
455/// In some cases it is desirable that TeX only stretches some subset of the
456/// glue boxes.
457/// For example, when setting centered text, TeX only stretches the two glue
458/// boxes at each end of the list and leaves all other glue intact.
459///
460/// To achieve this, each glue stretch or shrink has an order of infinity.
461/// If a list contains glue of some order (e.g. [GlueOrder::Fil]),
462/// then glues of a lower order (e.g. [GlueOrder::Normal]) are not stretched
463/// or shrunk.
464#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PartialOrd, Ord)]
465#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
466pub enum GlueOrder {
467    #[default]
468    Normal,
469    Fil,
470    Fill,
471    Filll,
472}
473
474impl GlueOrder {
475    /// Parses an infinite glue order from a keyword.
476    pub fn parse(s: &str) -> Option<Self> {
477        use GlueOrder::*;
478        Some(match s {
479            "fil" => Fil,
480            "fill" => Fill,
481            "filll" => Filll,
482            _ => return None,
483        })
484    }
485    pub fn inf_str(&self) -> Option<&'static str> {
486        use GlueOrder::*;
487        match self {
488            Normal => None,
489            Fil => Some("fil"),
490            Fill => Some("fill"),
491            Filll => Some("filll"),
492        }
493    }
494    /// Returns the next highest glue order.
495    pub fn next(&self) -> Option<Self> {
496        use GlueOrder::*;
497        match self {
498            Normal => Some(Fil),
499            Fil => Some(Fill),
500            Fill => Some(Filll),
501            Filll => None,
502        }
503    }
504}
505
506impl std::fmt::Display for GlueOrder {
507    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508        write!(f, "{}", self.inf_str().unwrap_or("pt"))
509    }
510}
511
512#[cfg(test)]
513mod tests {
514    use super::*;
515
516    #[test]
517    fn type_sizes() {
518        assert_eq!(16, std::mem::size_of::<Glue>());
519    }
520}