Skip to content

Commit d21cb2c

Browse files
committed
Fix scales for unit conversion (#5169)
1 parent 250223c commit d21cb2c

File tree

7 files changed

+54
-49
lines changed

7 files changed

+54
-49
lines changed

crates/typst-ide/src/jump.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ mod tests {
205205

206206
macro_rules! assert_approx_eq {
207207
($l:expr, $r:expr) => {
208-
assert!(($l.to_raw() - $r.to_raw()).abs() < 0.1, "{:?} ≉ {:?}", $l, $r);
208+
assert!(($l - $r).abs() < Abs::pt(0.1), "{:?} ≉ {:?}", $l, $r);
209209
};
210210
}
211211

crates/typst-render/src/text.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,26 @@ use crate::{shape, AbsExt, State};
1313

1414
/// Render a text run into the canvas.
1515
pub fn render_text(canvas: &mut sk::Pixmap, state: State, text: &TextItem) {
16-
let mut x = 0.0;
16+
let mut x = Abs::zero();
1717
for glyph in &text.glyphs {
1818
let id = GlyphId(glyph.id);
19-
let offset = x + glyph.x_offset.at(text.size).to_f32();
19+
let offset = x + glyph.x_offset.at(text.size);
2020

2121
if should_outline(&text.font, glyph) {
22-
let state =
23-
state.pre_translate(Point::new(Abs::raw(offset as _), Abs::raw(0.0)));
22+
let state = state.pre_translate(Point::with_x(offset));
2423
render_outline_glyph(canvas, state, text, id);
2524
} else {
2625
let upem = text.font.units_per_em();
27-
let text_scale = Abs::raw(text.size.to_raw() / upem);
26+
let text_scale = text.size / upem;
2827
let state = state
29-
.pre_translate(Point::new(Abs::raw(offset as _), -text.size))
28+
.pre_translate(Point::new(offset, -text.size))
3029
.pre_scale(Axes::new(text_scale, text_scale));
3130

3231
let (glyph_frame, _) = glyph_frame(&text.font, glyph.id);
3332
crate::render_frame(canvas, state, &glyph_frame);
3433
}
3534

36-
x += glyph.x_advance.at(text.size).to_f32();
35+
x += glyph.x_advance.at(text.size);
3736
}
3837
}
3938

crates/typst-svg/src/text.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ impl SVGRenderer {
5555
scale: f64,
5656
) -> Option<()> {
5757
let data_url = convert_svg_glyph_to_base64_url(&text.font, id)?;
58-
let upem = Abs::raw(text.font.units_per_em());
59-
let origin_ascender = text.font.metrics().ascender.at(upem).to_pt();
58+
let upem = text.font.units_per_em();
59+
let origin_ascender = text.font.metrics().ascender.at(Abs::pt(upem));
6060

6161
let glyph_hash = hash128(&(&text.font, id));
6262
let id = self.glyphs.insert_with(glyph_hash, || RenderedGlyph::Image {
6363
url: data_url,
64-
width: upem.to_pt(),
65-
height: upem.to_pt(),
66-
ts: Transform::translate(Abs::zero(), Abs::pt(-origin_ascender))
64+
width: upem,
65+
height: upem,
66+
ts: Transform::translate(Abs::zero(), -origin_ascender)
6767
.post_concat(Transform::scale(Ratio::new(scale), Ratio::new(-scale))),
6868
});
6969

@@ -260,9 +260,10 @@ fn convert_svg_glyph_to_base64_url(font: &Font, id: GlyphId) -> Option<EcoString
260260
data = &decoded;
261261
}
262262

263-
let upem = Abs::raw(font.units_per_em());
264-
let (width, height) = (upem.to_pt(), upem.to_pt());
265-
let origin_ascender = font.metrics().ascender.at(upem).to_pt();
263+
let upem = font.units_per_em();
264+
let width = upem;
265+
let height = upem;
266+
let origin_ascender = font.metrics().ascender.at(Abs::pt(upem));
266267

267268
// Parse XML.
268269
let mut svg_str = std::str::from_utf8(data).ok()?.to_owned();
@@ -296,7 +297,8 @@ fn convert_svg_glyph_to_base64_url(font: &Font, id: GlyphId) -> Option<EcoString
296297
// make sure the glyph is rendered at the correct position
297298
svg_str.insert_str(
298299
start_span.unwrap().range().end,
299-
format!(r#" viewBox="0 {} {width} {height}""#, -origin_ascender).as_str(),
300+
format!(r#" viewBox="0 {} {width} {height}""#, -origin_ascender.to_pt())
301+
.as_str(),
300302
);
301303
}
302304

crates/typst/src/layout/abs.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ use ecow::EcoString;
77
use crate::foundations::{cast, repr, Fold, Repr, Value};
88
use crate::utils::{Numeric, Scalar};
99

10-
/// The epsilon for approximate comparisons.
11-
const EPS: f64 = 1e-6;
12-
1310
/// An absolute length.
1411
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
1512
pub struct Abs(Scalar);
@@ -113,17 +110,17 @@ impl Abs {
113110
/// Whether the other absolute length fits into this one (i.e. is smaller).
114111
/// Allows for a bit of slack.
115112
pub fn fits(self, other: Self) -> bool {
116-
self.0 + EPS >= other.0
113+
self.0 + AbsUnit::EPS >= other.0
117114
}
118115

119116
/// Compares two absolute lengths for whether they are approximately equal.
120117
pub fn approx_eq(self, other: Self) -> bool {
121-
self == other || (self - other).to_raw().abs() < EPS
118+
self == other || (self - other).to_raw().abs() < AbsUnit::EPS
122119
}
123120

124121
/// Whether the size is close to zero or negative.
125122
pub fn approx_empty(self) -> bool {
126-
self.to_raw() <= EPS
123+
self.to_raw() <= AbsUnit::EPS
127124
}
128125

129126
/// Returns a number that represent the sign of this length
@@ -254,13 +251,19 @@ pub enum AbsUnit {
254251
}
255252

256253
impl AbsUnit {
254+
/// The epsilon for approximate length comparisons.
255+
const EPS: f64 = 1e-4;
256+
257257
/// How many raw units correspond to a value of `1.0` in this unit.
258-
fn raw_scale(self) -> f64 {
258+
const fn raw_scale(self) -> f64 {
259+
// We choose a raw scale which has an integer conversion value to all
260+
// four units of interest, so that whole numbers in all units can be
261+
// represented accurately.
259262
match self {
260-
AbsUnit::Pt => 1.0,
261-
AbsUnit::Mm => 2.83465,
262-
AbsUnit::Cm => 28.3465,
263-
AbsUnit::In => 72.0,
263+
AbsUnit::Pt => 127.0,
264+
AbsUnit::Mm => 360.0,
265+
AbsUnit::Cm => 3600.0,
266+
AbsUnit::In => 9144.0,
264267
}
265268
}
266269
}

crates/typst/src/text/font/color.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,8 @@ fn draw_colr_glyph(
186186
)
187187
.ok()?;
188188

189-
let y_shift = Abs::raw(upem.to_raw() - y_max);
190-
191-
let position = Point::new(Abs::raw(x_min), y_shift);
189+
let y_shift = Abs::pt(upem.to_pt() - y_max);
190+
let position = Point::new(Abs::pt(x_min), y_shift);
192191
let size = Size::new(Abs::pt(width), Abs::pt(height));
193192
frame.push(position, FrameItem::Image(image, size, Span::detached()));
194193

tests/ref/gradient-presets.png

4 Bytes
Loading

tests/suite/layout/length.typ

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,26 @@
99

1010
--- length-to-unit ---
1111
// Test length unit conversions.
12-
#test((500.934pt).pt(), 500.934)
13-
#test((3.3453cm).cm(), 3.3453)
14-
#test((4.3452mm).mm(), 4.3452)
15-
#test((5.345in).inches(), 5.345)
16-
#test((500.333666999pt).pt(), 500.333666999)
17-
#test((3.5234354cm).cm(), 3.5234354)
18-
#test((4.12345678mm).mm(), 4.12345678)
19-
#test((5.333666999in).inches(), 5.333666999)
20-
#test((4.123456789123456mm).mm(), 4.123456789123456)
21-
#test((254cm).mm(), 2540.0)
22-
#test(calc.round((254cm).inches(), digits: 2), 100.0)
23-
#test((2540mm).cm(), 254.0)
24-
#test(calc.round((2540mm).inches(), digits: 2), 100.0)
25-
#test((100in).pt(), 7200.0)
26-
#test(calc.round((100in).cm(), digits: 2), 254.0)
27-
#test(calc.round((100in).mm(), digits: 2), 2540.0)
28-
#test(5em.abs.cm(), 0.0)
29-
#test((5em + 6in).abs.inches(), 6.0)
12+
#let t(a, b) = assert(calc.abs(a - b) < 1e-6)
13+
14+
#t((500.934pt).pt(), 500.934)
15+
#t((3.3453cm).cm(), 3.3453)
16+
#t((4.3452mm).mm(), 4.3452)
17+
#t((5.345in).inches(), 5.345)
18+
#t((500.333666999pt).pt(), 500.333666999)
19+
#t((3.523435cm).cm(), 3.523435)
20+
#t((4.12345678mm).mm(), 4.12345678)
21+
#t((5.333666999in).inches(), 5.333666999)
22+
#t((4.123456789123456mm).mm(), 4.123456789123456)
23+
#t((254cm).mm(), 2540.0)
24+
#t((254cm).inches(), 100.0)
25+
#t((2540mm).cm(), 254.0)
26+
#t((2540mm).inches(), 100.0)
27+
#t((100in).pt(), 7200.0)
28+
#t((100in).cm(), 254.0)
29+
#t((100in).mm(), 2540.0)
30+
#t(5em.abs.cm(), 0.0)
31+
#t((5em + 6in).abs.inches(), 6.0)
3032

3133
--- length-to-absolute ---
3234
// Test length `to-absolute` method.

0 commit comments

Comments
 (0)