1use std::fmt::Write;
6pub mod font;
7
8pub trait FontFormat: Sized {
10 const DEFAULT_FILE_EXTENSION: &'static str;
11 type Error: std::error::Error + 'static;
12
13 fn parse(b: &[u8]) -> Result<Self, Self::Error>;
15}
16
17#[derive(Default, PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Scaled(pub i32);
29
30impl Scaled {
31 pub const ZERO: Scaled = Scaled(0);
33
34 pub const ONE: Scaled = Scaled(1 << 16);
36
37 pub const TWO: Scaled = Scaled(1 << 17);
39
40 pub fn is_zero(&self) -> bool {
41 *self == Scaled::ZERO
42 }
43
44 pub const MAX_DIMEN: Scaled = Scaled((1 << 30) - 1);
50
51 pub fn from_integer(i: i32) -> Result<Scaled, OverflowError> {
56 if i >= (1 << 14) || i <= -(1 << 14) {
57 Err(OverflowError {})
58 } else {
59 Ok(Scaled(Scaled::ONE.0 * i))
60 }
61 }
62
63 pub fn from_decimal_digits(digits: &[u8]) -> Scaled {
67 let mut a = 0;
68 for d in digits.iter().rev() {
69 a = (a + (*d as i32) * Scaled::TWO.0) / 10
70 }
71 Scaled((a + 1) / 2)
72 }
73
74 pub fn new(
78 integer_part: i32,
79 fractional_part: Scaled,
80 scaled_unit: ScaledUnit,
81 ) -> Result<Scaled, OverflowError> {
82 if scaled_unit == ScaledUnit::ScaledPoint {
83 return if integer_part > Scaled::MAX_DIMEN.0 {
84 Err(OverflowError)
85 } else {
86 Ok(Scaled(integer_part))
88 };
89 }
90 let (n, d) = scaled_unit.conversion_fraction();
91 let (Scaled(i), Scaled(remainder)) = Scaled(integer_part).xn_over_d(n, d)?;
93 let f =
94 fractional_part.nx_plus_y(n, Scaled::from_integer(remainder).expect("remainder<d<=7200<2^13, so a valid scaled number"))
95 .expect("fractional_part<2^16, remainder<2^16*d, so nx_plus_y<2^16(n+d). Each (n,d) makes this <2^30")
96 / d;
97 let integer_part = Scaled::from_integer(i + f.integer_part())?;
98 Ok(integer_part + f.fractional_part())
99 }
100
101 pub fn xn_over_d(&self, n: i32, d: i32) -> Result<(Scaled, Scaled), OverflowError> {
112 debug_assert!(n <= 0o200000);
113 debug_assert!(d <= 0o200000);
114 let mut b: i64 = self.0.into();
115 b *= n as i64; let remainder: i32 = (b % (d as i64)).try_into().expect("d<=2^16 so b%d<2^16");
117 b /= d as i64;
118 if b < -(Scaled::MAX_DIMEN.0 as i64) || b > Scaled::MAX_DIMEN.0 as i64 {
119 return Err(OverflowError {});
120 }
121 let b: i32 = b.try_into().expect("b in (-2^30, +2^30)");
122 Ok((Scaled(b), Scaled(remainder)))
123 }
124
125 pub fn nx_plus_y(self, mut n: i32, y: Scaled) -> Result<Scaled, OverflowError> {
127 let max_answer = Scaled::MAX_DIMEN;
128 if n == 0 {
129 return Ok(y);
130 }
131 let mut x = self;
132 if n < 0 {
133 n = -n;
134 x = -x;
135 }
136 if x <= (max_answer - y) / n && -x <= (max_answer + y) / n {
137 Ok(x * n + y)
138 } else {
139 Err(OverflowError {})
140 }
141 }
142
143 pub fn parse_from_string(s: &str) -> Result<Scaled, String> {
148 if s.len() < 3 {
149 return Err(format!(
150 "invalid dimension \"{s}\": expected <number><unit> (e.g. 100pt)"
151 ));
152 }
153 let (value_str, unit_str) = s.split_at(s.len() - 2);
154 let unit = ScaledUnit::parse(unit_str)
155 .ok_or_else(|| format!("invalid unit \"{unit_str}\" in dimension \"{s}\""))?;
156 Self::parse_from_string_with_unit(value_str, unit)
157 }
158
159 fn parse_from_string_with_unit(s: &str, unit: ScaledUnit) -> Result<Scaled, String> {
160 let (int_str, frac_str) = match s.find('.') {
161 Some(pos) => (&s[..pos], &s[pos + 1..]),
162 None => (s, ""),
163 };
164 let integer_part: i32 = int_str
165 .parse()
166 .map_err(|_| format!("invalid number \"{int_str}\" in dimension \"{s}{unit}\""))?;
167 let frac_digits: Vec<u8> = frac_str.chars().map(|c| c as u8 - b'0').collect();
168 if frac_digits.iter().any(|&d| d > 9) {
169 return Err(format!(
170 "invalid fractional part \"{frac_str}\" in dimension \"{s}{unit}\""
171 ));
172 }
173 let fractional_part = Scaled::from_decimal_digits(&frac_digits);
174 Scaled::new(integer_part, fractional_part, unit)
175 .map_err(|_| format!("dimension \"{s}{unit}\" is out of range"))
176 }
177
178 pub fn parse_no_units(s: &str) -> Result<Scaled, OverflowError> {
197 let (neg, s) = match s.strip_prefix('-') {
198 Some(s) => (true, s),
199 None => (false, s),
200 };
201 let mut parts = s.split('.');
202 let i: i32 = parts
203 .next()
204 .expect("scaled has an integer part")
205 .parse()
206 .expect("integer part is an integer");
207 let mut f = [0_u8; 17];
208 for (k, c) in parts
209 .next()
210 .expect("scaled has a fractional part")
211 .chars()
212 .enumerate()
213 {
214 f[k] = c
215 .to_digit(10)
216 .expect("fractional part are digits")
217 .try_into()
218 .expect("digits are in the range [0,10) and always fit in u8");
219 }
220 let f = Scaled::from_decimal_digits(&f);
221 let sc = Scaled::new(i, f, ScaledUnit::Point)?;
222 Ok(if neg { -sc } else { sc })
223 }
224
225 pub fn integer_part(self) -> i32 {
226 self.0 / Scaled::ONE.0
227 }
228
229 pub fn fractional_part(self) -> Scaled {
230 self % Scaled::ONE.0
231 }
232
233 pub fn abs(self) -> Scaled {
234 Scaled(self.0.abs())
235 }
236
237 pub fn wrapping_add(self, rhs: Scaled) -> Self {
238 Scaled(self.0.wrapping_add(rhs.0))
239 }
240 pub fn checked_add(self, rhs: Scaled) -> Option<Self> {
241 Some(Scaled(self.0.checked_add(rhs.0)?))
242 }
243 pub fn wrapping_mul(self, rhs: i32) -> Self {
244 Scaled(self.0.wrapping_mul(rhs))
245 }
246 pub fn checked_mul(self, rhs: i32) -> Option<Self> {
247 self.nx_plus_y(rhs, Scaled::ZERO).ok()
250 }
251 pub fn checked_div(self, rhs: i32) -> Option<Self> {
252 Some(Scaled(self.0.checked_div(rhs)?))
253 }
254 pub fn display_no_units(self) -> impl std::fmt::Display {
256 struct D {
257 s: Scaled,
258 }
259 impl std::fmt::Display for D {
260 fn fmt(&self, fm: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 if self.s.0 < 0 {
262 write!(fm, "-")?;
263 }
264 write!(fm, "{}.", self.s.integer_part().abs())?;
265 let mut f = self.s.fractional_part().abs() * 10 + Scaled(5);
267 let mut delta = Scaled(10);
268 loop {
269 if delta > Scaled::ONE {
270 f += Scaled(0o100000 - 50000);
272 }
273 fm.write_char(
274 char::from_digit(f.integer_part().try_into().unwrap(), 10).unwrap(),
275 )?;
276 f = f.fractional_part() * 10;
277 delta = delta * 10;
278 if f <= delta {
279 break;
280 }
281 }
282 Ok(())
283 }
284 }
285 D { s: self }
286 }
287}
288
289#[derive(Debug, PartialEq, Eq)]
290pub struct OverflowError;
291
292impl std::fmt::Display for Scaled {
293 fn fmt(&self, fm: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 write!(fm, "{}", self.display_no_units())?;
296 write!(fm, "pt")?;
298 Ok(())
299 }
300}
301
302impl std::iter::Sum for Scaled {
303 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
304 Self(iter.map(|s| s.0).sum())
305 }
306}
307
308impl std::ops::Add<Scaled> for Scaled {
309 type Output = Scaled;
310 fn add(self, rhs: Scaled) -> Self::Output {
311 Scaled(self.0 + rhs.0)
312 }
313}
314
315impl std::ops::AddAssign<Scaled> for Scaled {
316 fn add_assign(&mut self, rhs: Scaled) {
317 self.0 += rhs.0;
318 }
319}
320
321impl std::ops::Sub<Scaled> for Scaled {
322 type Output = Scaled;
323 fn sub(self, rhs: Scaled) -> Self::Output {
324 Scaled(self.0 - rhs.0)
325 }
326}
327
328impl std::ops::SubAssign<Scaled> for Scaled {
329 fn sub_assign(&mut self, rhs: Scaled) {
330 self.0 -= rhs.0;
331 }
332}
333
334impl std::ops::Mul<i32> for Scaled {
335 type Output = Scaled;
336 fn mul(self, rhs: i32) -> Self::Output {
337 Scaled(self.0 * rhs)
338 }
339}
340
341impl std::ops::Div<i32> for Scaled {
342 type Output = Scaled;
343 fn div(self, rhs: i32) -> Self::Output {
344 Scaled(self.0 / rhs)
345 }
346}
347
348impl std::ops::DivAssign<i32> for Scaled {
349 fn div_assign(&mut self, rhs: i32) {
350 self.0 = self.0 / rhs
351 }
352}
353
354impl std::ops::Rem<i32> for Scaled {
355 type Output = Scaled;
356 fn rem(self, rhs: i32) -> Self::Output {
357 Scaled(self.0 % rhs)
358 }
359}
360
361impl std::ops::Neg for Scaled {
362 type Output = Scaled;
363 fn neg(self) -> Self::Output {
364 Scaled(-self.0)
365 }
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum ScaledUnit {
373 Point,
374 Pica,
375 Inch,
376 BigPoint,
377 Centimeter,
378 Millimeter,
379 DidotPoint,
380 Cicero,
381 ScaledPoint,
382}
383
384impl std::fmt::Display for ScaledUnit {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 write!(f, "{}", self.abbreviation())
387 }
388}
389
390impl ScaledUnit {
391 pub fn abbreviation(&self) -> &'static str {
393 use ScaledUnit::*;
394 match self {
395 Point => "pt",
396 Pica => "pc",
397 Inch => "in",
398 BigPoint => "bp",
399 Centimeter => "cm",
400 Millimeter => "mm",
401 DidotPoint => "dd",
402 Cicero => "cc",
403 ScaledPoint => "sp",
404 }
405 }
406
407 pub fn parse(s: &str) -> Option<Self> {
412 use ScaledUnit::*;
413 Some(match s {
414 "pt" => Point,
415 "pc" => Pica,
416 "in" => Inch,
417 "bp" => BigPoint,
418 "cm" => Centimeter,
419 "mm" => Millimeter,
420 "dd" => DidotPoint,
421 "cc" => Cicero,
422 "sp" => ScaledPoint,
423 _ => return None,
424 })
425 }
426
427 pub fn conversion_fraction(&self) -> (i32, i32) {
435 use ScaledUnit::*;
436 match self {
437 Point => (1, 1),
438 Pica => (12, 1),
439 Inch => (7227, 100),
440 BigPoint => (7227, 7200),
441 Centimeter => (7227, 254),
442 Millimeter => (7227, 2540),
443 DidotPoint => (1238, 1157),
444 Cicero => (14856, 1157),
445 ScaledPoint => (1, 1 << 16),
446 }
447 }
448}
449
450#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
461#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
462pub struct Glue {
463 pub width: Scaled,
464 pub stretch: Scaled,
465 pub stretch_order: GlueOrder,
466 pub shrink: Scaled,
467 pub shrink_order: GlueOrder,
468}
469
470impl Glue {
471 pub const ZERO: Glue = Glue {
472 width: Scaled::ZERO,
473 stretch: Scaled::ZERO,
474 stretch_order: GlueOrder::Normal,
475 shrink: Scaled::ZERO,
476 shrink_order: GlueOrder::Normal,
477 };
478 pub fn is_zero(&self) -> bool {
479 self.width == Scaled::ZERO && self.stretch == Scaled::ZERO && self.shrink == Scaled::ZERO
480 }
481}
482
483impl std::ops::Mul<i32> for Glue {
484 type Output = Glue;
485 fn mul(self, rhs: i32) -> Self::Output {
486 Glue {
487 width: self.width * rhs,
488 stretch: self.stretch * rhs,
489 stretch_order: self.stretch_order,
490 shrink: self.shrink * rhs,
491 shrink_order: self.shrink_order,
492 }
493 }
494}
495
496impl Glue {
497 pub fn wrapping_add(self, rhs: Glue) -> Self {
499 use std::cmp::Ordering::*;
500 Glue {
501 width: self.width.wrapping_add(rhs.width),
502 stretch: match self.stretch_order.cmp(&rhs.stretch_order) {
503 Less => rhs.stretch,
504 Equal => self.stretch.wrapping_add(rhs.stretch),
505 Greater => self.stretch,
506 },
507 stretch_order: self.stretch_order.max(rhs.stretch_order),
508 shrink: match self.shrink_order.cmp(&rhs.shrink_order) {
509 Less => rhs.shrink,
510 Equal => self.shrink.wrapping_add(rhs.shrink),
511 Greater => self.shrink,
512 },
513 shrink_order: self.shrink_order.max(rhs.shrink_order),
514 }
515 }
516 pub fn checked_add(self, rhs: Glue) -> Option<Self> {
517 use std::cmp::Ordering::*;
518 Some(Glue {
519 width: self.width.checked_add(rhs.width)?,
520 stretch: match self.stretch_order.cmp(&rhs.stretch_order) {
521 Less => rhs.stretch,
522 Equal => self.stretch.checked_add(rhs.stretch)?,
523 Greater => self.stretch,
524 },
525 stretch_order: self.stretch_order.max(rhs.stretch_order),
526 shrink: match self.shrink_order.cmp(&rhs.shrink_order) {
527 Less => rhs.shrink,
528 Equal => self.shrink.checked_add(rhs.shrink)?,
529 Greater => self.shrink,
530 },
531 shrink_order: self.shrink_order.max(rhs.shrink_order),
532 })
533 }
534 pub fn checked_mul(self, rhs: i32) -> Option<Self> {
535 Some(Glue {
536 width: self.width.checked_mul(rhs)?,
537 stretch: self.stretch.checked_mul(rhs)?,
538 stretch_order: self.stretch_order,
539 shrink: self.shrink.checked_mul(rhs)?,
540 shrink_order: self.shrink_order,
541 })
542 }
543 pub fn wrapping_mul(self, rhs: i32) -> Self {
544 Glue {
545 width: self.width.wrapping_mul(rhs),
546 stretch: self.stretch.wrapping_mul(rhs),
547 stretch_order: self.stretch_order,
548 shrink: self.shrink.wrapping_mul(rhs),
549 shrink_order: self.shrink_order,
550 }
551 }
552 pub fn checked_div(self, rhs: i32) -> Option<Self> {
553 Some(Glue {
554 width: self.width.checked_div(rhs)?,
555 stretch: self.stretch.checked_div(rhs)?,
556 stretch_order: self.stretch_order,
557 shrink: self.shrink.checked_div(rhs)?,
558 shrink_order: self.shrink_order,
559 })
560 }
561
562 pub fn parse_from_string(s: &str) -> Result<Glue, String> {
563 let mut glue = Glue::ZERO;
564 let (width_str, rest) = match s.find(" plus ").or_else(|| s.find(" minus ")) {
565 Some(pos) => (&s[..pos], s[pos..].trim()),
566 None => (s, ""),
567 };
568 glue.width = Scaled::parse_from_string(width_str.trim())?;
569 let rest = if let Some(r) = rest.strip_prefix("plus ") {
570 let (stretch_str, minus_rest) = match r.find(" minus ") {
571 Some(pos) => (&r[..pos], r[pos..].trim()),
572 None => (r, ""),
573 };
574 let (stretch, order) = Glue::parse_scaled_inf(stretch_str.trim())?;
575 glue.stretch = stretch;
576 glue.stretch_order = order;
577 minus_rest
578 } else {
579 rest
580 };
581 if let Some(shrink_str) = rest.strip_prefix("minus ") {
582 let (shrink, order) = Glue::parse_scaled_inf(shrink_str.trim())?;
583 glue.shrink = shrink;
584 glue.shrink_order = order;
585 } else if !rest.is_empty() {
586 return Err(format!("invalid glue {s:?}"));
587 }
588 Ok(glue)
589 }
590
591 fn parse_scaled_inf(s: &str) -> Result<(Scaled, GlueOrder), String> {
592 for (suffix, order) in [
593 ("filll", GlueOrder::Filll),
594 ("fill", GlueOrder::Fill),
595 ("fil", GlueOrder::Fil),
596 ] {
597 if let Some(num_str) = s.strip_suffix(suffix) {
598 return Ok((
599 Scaled::parse_from_string_with_unit(num_str, ScaledUnit::Point)?,
600 order,
601 ));
602 }
603 }
604 Ok((Scaled::parse_from_string(s)?, GlueOrder::Normal))
605 }
606}
607
608impl std::fmt::Display for Glue {
609 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611 write!(f, "{}", self.width)?;
612 if self.stretch != Scaled::ZERO {
613 write!(f, " plus ")?;
614 write!(f, "{}", self.stretch.display_no_units())?;
615 write!(f, "{}", self.stretch_order)?;
616 }
617 if self.shrink != Scaled::ZERO {
618 write!(f, " minus ")?;
619 write!(f, "{}", self.shrink.display_no_units())?;
620 write!(f, "{}", self.shrink_order)?;
621 }
622 Ok(())
623 }
624}
625
626#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PartialOrd, Ord)]
639#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
640pub enum GlueOrder {
641 #[default]
642 Normal,
643 Fil,
644 Fill,
645 Filll,
646}
647
648impl GlueOrder {
649 pub fn parse(s: &str) -> Option<Self> {
651 use GlueOrder::*;
652 Some(match s {
653 "fil" => Fil,
654 "fill" => Fill,
655 "filll" => Filll,
656 _ => return None,
657 })
658 }
659 pub fn inf_str(&self) -> Option<&'static str> {
660 use GlueOrder::*;
661 match self {
662 Normal => None,
663 Fil => Some("fil"),
664 Fill => Some("fill"),
665 Filll => Some("filll"),
666 }
667 }
668 pub fn next(&self) -> Option<Self> {
670 use GlueOrder::*;
671 match self {
672 Normal => Some(Fil),
673 Fil => Some(Fill),
674 Fill => Some(Filll),
675 Filll => None,
676 }
677 }
678}
679
680impl std::fmt::Display for GlueOrder {
681 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
682 write!(f, "{}", self.inf_str().unwrap_or("pt"))
683 }
684}
685
686#[cfg(test)]
687mod tests {
688 use super::*;
689
690 #[test]
691 fn type_sizes() {
692 assert_eq!(16, std::mem::size_of::<Glue>());
693 }
694
695 macro_rules! parse_from_string_tests {
696 ( $( $name:ident : $input:expr => $expected:expr, )* ) => {
697 $(
698 #[test]
699 fn $name() {
700 assert_eq!(Scaled::parse_from_string($input), $expected);
701 }
702 )*
703 };
704 }
705
706 parse_from_string_tests! {
707 integer_pt: "100pt" => Ok(Scaled::new(100, Scaled::ZERO, ScaledUnit::Point).unwrap()),
709 integer_pc: "6pc" => Ok(Scaled::new(6, Scaled::ZERO, ScaledUnit::Pica).unwrap()),
710 integer_in: "2in" => Ok(Scaled::new(2, Scaled::ZERO, ScaledUnit::Inch).unwrap()),
711 integer_bp: "72bp" => Ok(Scaled::new(72, Scaled::ZERO, ScaledUnit::BigPoint).unwrap()),
712 integer_cm: "10cm" => Ok(Scaled::new(10, Scaled::ZERO, ScaledUnit::Centimeter).unwrap()),
713 integer_mm: "25mm" => Ok(Scaled::new(25, Scaled::ZERO, ScaledUnit::Millimeter).unwrap()),
714 integer_dd: "10dd" => Ok(Scaled::new(10, Scaled::ZERO, ScaledUnit::DidotPoint).unwrap()),
715 integer_cc: "3cc" => Ok(Scaled::new(3, Scaled::ZERO, ScaledUnit::Cicero).unwrap()),
716 integer_sp: "65536sp" => Ok(Scaled(65536)),
717 fractional_pt: "72.27pt" => Ok(Scaled::new(72, Scaled::from_decimal_digits(&[2, 7]), ScaledUnit::Point).unwrap()),
719 fractional_in: "6.5in" => Ok(Scaled::new(6, Scaled::from_decimal_digits(&[5]), ScaledUnit::Inch).unwrap()),
720 zero_pt: "0pt" => Ok(Scaled::ZERO),
722 empty: "" => Err("invalid dimension \"\": expected <number><unit> (e.g. 100pt)".to_string()),
724 bad_unit: "10xx" => Err("invalid unit \"xx\" in dimension \"10xx\"".to_string()),
725 bad_number: "abpt" => Err("invalid number \"ab\" in dimension \"abpt\"".to_string()),
726 bad_fraction: "1.xpt" => Err("invalid fractional part \"x\" in dimension \"1.xpt\"".to_string()),
727 }
728
729 macro_rules! parse_no_units_tests {
730 ( $( $name:ident : $input:expr => $expected:expr, )* ) => {
731 $(
732 #[test]
733 fn $name() {
734 assert_eq!(Scaled::parse_no_units($input), $expected);
735 }
736 )*
737 };
738 }
739
740 parse_no_units_tests! {
741 no_units_one: "1.0" => Ok(Scaled::ONE),
742 no_units_zero: "0.0" => Ok(Scaled::ZERO),
743 no_units_negative: "-2.5" => Ok(-(Scaled::ONE * 5) / 2),
744 no_units_fraction: "0.27779" => Ok(Scaled::from_decimal_digits(&[2, 7, 7, 7, 9])),
745 no_units_overflow: "16384.0" => Err(OverflowError),
746 }
747
748 #[test]
749 fn parse_no_units_round_trips_display_no_units() {
750 for sc in [Scaled(1), Scaled(-1), Scaled(18205), Scaled::MAX_DIMEN] {
751 let printed = format!("{}", sc.display_no_units());
752 assert_eq!(Scaled::parse_no_units(&printed), Ok(sc));
753 }
754 }
755
756 #[test]
757 fn print_smallest_scaled() {
758 assert_eq!("-32768.0pt", format!("{}", Scaled(i32::MIN)));
759 }
760
761 #[test]
762 fn glue_order_ordering() {
763 assert!(GlueOrder::Normal < GlueOrder::Fil);
764 assert!(GlueOrder::Fil < GlueOrder::Fill);
765 assert!(GlueOrder::Fill < GlueOrder::Filll);
766 }
767}