From 92aacdb480286a8a1962a49843e0c271f6ef93a8 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 10 Oct 2024 17:51:48 +0200 Subject: [PATCH] Fix scales for unit conversion (#5169) --- crates/typst-ide/src/jump.rs | 2 +- crates/typst-render/src/text.rs | 13 +++++----- crates/typst-svg/src/text.rs | 20 ++++++++------- crates/typst/src/layout/abs.rs | 25 ++++++++++-------- crates/typst/src/text/font/color.rs | 5 ++-- tests/ref/gradient-presets.png | Bin 17311 -> 17315 bytes tests/suite/layout/length.typ | 38 +++++++++++++++------------- 7 files changed, 54 insertions(+), 49 deletions(-) diff --git a/crates/typst-ide/src/jump.rs b/crates/typst-ide/src/jump.rs index b798defab..e48db9865 100644 --- a/crates/typst-ide/src/jump.rs +++ b/crates/typst-ide/src/jump.rs @@ -205,7 +205,7 @@ mod tests { macro_rules! assert_approx_eq { ($l:expr, $r:expr) => { - assert!(($l.to_raw() - $r.to_raw()).abs() < 0.1, "{:?} ≉ {:?}", $l, $r); + assert!(($l - $r).abs() < Abs::pt(0.1), "{:?} ≉ {:?}", $l, $r); }; } diff --git a/crates/typst-render/src/text.rs b/crates/typst-render/src/text.rs index 70d516426..1685e67dc 100644 --- a/crates/typst-render/src/text.rs +++ b/crates/typst-render/src/text.rs @@ -13,27 +13,26 @@ use crate::{shape, AbsExt, State}; /// Render a text run into the canvas. pub fn render_text(canvas: &mut sk::Pixmap, state: State, text: &TextItem) { - let mut x = 0.0; + let mut x = Abs::zero(); for glyph in &text.glyphs { let id = GlyphId(glyph.id); - let offset = x + glyph.x_offset.at(text.size).to_f32(); + let offset = x + glyph.x_offset.at(text.size); if should_outline(&text.font, glyph) { - let state = - state.pre_translate(Point::new(Abs::raw(offset as _), Abs::raw(0.0))); + let state = state.pre_translate(Point::with_x(offset)); render_outline_glyph(canvas, state, text, id); } else { let upem = text.font.units_per_em(); - let text_scale = Abs::raw(text.size.to_raw() / upem); + let text_scale = text.size / upem; let state = state - .pre_translate(Point::new(Abs::raw(offset as _), -text.size)) + .pre_translate(Point::new(offset, -text.size)) .pre_scale(Axes::new(text_scale, text_scale)); let (glyph_frame, _) = glyph_frame(&text.font, glyph.id); crate::render_frame(canvas, state, &glyph_frame); } - x += glyph.x_advance.at(text.size).to_f32(); + x += glyph.x_advance.at(text.size); } } diff --git a/crates/typst-svg/src/text.rs b/crates/typst-svg/src/text.rs index 6af933988..4a99b4b8e 100644 --- a/crates/typst-svg/src/text.rs +++ b/crates/typst-svg/src/text.rs @@ -55,15 +55,15 @@ impl SVGRenderer { scale: f64, ) -> Option<()> { let data_url = convert_svg_glyph_to_base64_url(&text.font, id)?; - let upem = Abs::raw(text.font.units_per_em()); - let origin_ascender = text.font.metrics().ascender.at(upem).to_pt(); + let upem = text.font.units_per_em(); + let origin_ascender = text.font.metrics().ascender.at(Abs::pt(upem)); let glyph_hash = hash128(&(&text.font, id)); let id = self.glyphs.insert_with(glyph_hash, || RenderedGlyph::Image { url: data_url, - width: upem.to_pt(), - height: upem.to_pt(), - ts: Transform::translate(Abs::zero(), Abs::pt(-origin_ascender)) + width: upem, + height: upem, + ts: Transform::translate(Abs::zero(), -origin_ascender) .post_concat(Transform::scale(Ratio::new(scale), Ratio::new(-scale))), }); @@ -260,9 +260,10 @@ fn convert_svg_glyph_to_base64_url(font: &Font, id: GlyphId) -> Option Option bool { - self.0 + EPS >= other.0 + self.0 + AbsUnit::EPS >= other.0 } /// Compares two absolute lengths for whether they are approximately equal. pub fn approx_eq(self, other: Self) -> bool { - self == other || (self - other).to_raw().abs() < EPS + self == other || (self - other).to_raw().abs() < AbsUnit::EPS } /// Whether the size is close to zero or negative. pub fn approx_empty(self) -> bool { - self.to_raw() <= EPS + self.to_raw() <= AbsUnit::EPS } /// Returns a number that represent the sign of this length @@ -254,13 +251,19 @@ pub enum AbsUnit { } impl AbsUnit { + /// The epsilon for approximate length comparisons. + const EPS: f64 = 1e-4; + /// How many raw units correspond to a value of `1.0` in this unit. - fn raw_scale(self) -> f64 { + const fn raw_scale(self) -> f64 { + // We choose a raw scale which has an integer conversion value to all + // four units of interest, so that whole numbers in all units can be + // represented accurately. match self { - AbsUnit::Pt => 1.0, - AbsUnit::Mm => 2.83465, - AbsUnit::Cm => 28.3465, - AbsUnit::In => 72.0, + AbsUnit::Pt => 127.0, + AbsUnit::Mm => 360.0, + AbsUnit::Cm => 3600.0, + AbsUnit::In => 9144.0, } } } diff --git a/crates/typst/src/text/font/color.rs b/crates/typst/src/text/font/color.rs index 0e6b0c1fa..e26d323cf 100644 --- a/crates/typst/src/text/font/color.rs +++ b/crates/typst/src/text/font/color.rs @@ -186,9 +186,8 @@ fn draw_colr_glyph( ) .ok()?; - let y_shift = Abs::raw(upem.to_raw() - y_max); - - let position = Point::new(Abs::raw(x_min), y_shift); + let y_shift = Abs::pt(upem.to_pt() - y_max); + let position = Point::new(Abs::pt(x_min), y_shift); let size = Size::new(Abs::pt(width), Abs::pt(height)); frame.push(position, FrameItem::Image(image, size, Span::detached())); diff --git a/tests/ref/gradient-presets.png b/tests/ref/gradient-presets.png index de4968944997368e7b55afe17c5b9537d4363ec5..7fdb235c2f95b5ef12e5e9941654ae99ef08aec1 100644 GIT binary patch literal 17315 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 literal 17311 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 P00000NkvXXu0mjf1kOpc diff --git a/tests/suite/layout/length.typ b/tests/suite/layout/length.typ index c40752f78..5ba70072d 100644 --- a/tests/suite/layout/length.typ +++ b/tests/suite/layout/length.typ @@ -9,24 +9,26 @@ --- length-to-unit --- // Test length unit conversions. -#test((500.934pt).pt(), 500.934) -#test((3.3453cm).cm(), 3.3453) -#test((4.3452mm).mm(), 4.3452) -#test((5.345in).inches(), 5.345) -#test((500.333666999pt).pt(), 500.333666999) -#test((3.5234354cm).cm(), 3.5234354) -#test((4.12345678mm).mm(), 4.12345678) -#test((5.333666999in).inches(), 5.333666999) -#test((4.123456789123456mm).mm(), 4.123456789123456) -#test((254cm).mm(), 2540.0) -#test(calc.round((254cm).inches(), digits: 2), 100.0) -#test((2540mm).cm(), 254.0) -#test(calc.round((2540mm).inches(), digits: 2), 100.0) -#test((100in).pt(), 7200.0) -#test(calc.round((100in).cm(), digits: 2), 254.0) -#test(calc.round((100in).mm(), digits: 2), 2540.0) -#test(5em.abs.cm(), 0.0) -#test((5em + 6in).abs.inches(), 6.0) +#let t(a, b) = assert(calc.abs(a - b) < 1e-6) + +#t((500.934pt).pt(), 500.934) +#t((3.3453cm).cm(), 3.3453) +#t((4.3452mm).mm(), 4.3452) +#t((5.345in).inches(), 5.345) +#t((500.333666999pt).pt(), 500.333666999) +#t((3.523435cm).cm(), 3.523435) +#t((4.12345678mm).mm(), 4.12345678) +#t((5.333666999in).inches(), 5.333666999) +#t((4.123456789123456mm).mm(), 4.123456789123456) +#t((254cm).mm(), 2540.0) +#t((254cm).inches(), 100.0) +#t((2540mm).cm(), 254.0) +#t((2540mm).inches(), 100.0) +#t((100in).pt(), 7200.0) +#t((100in).cm(), 254.0) +#t((100in).mm(), 2540.0) +#t(5em.abs.cm(), 0.0) +#t((5em + 6in).abs.inches(), 6.0) --- length-to-absolute --- // Test length `to-absolute` method.