diff --git a/crates/typst-layout/src/math/accent.rs b/crates/typst-layout/src/math/accent.rs index 0f464c78d..159703b8e 100644 --- a/crates/typst-layout/src/math/accent.rs +++ b/crates/typst-layout/src/math/accent.rs @@ -42,7 +42,8 @@ pub fn layout_accent( styles }; - let mut glyph = GlyphFragment::new(ctx.font, accent_styles, accent.0, elem.span()); + let mut glyph = + GlyphFragment::new_char(ctx.font, accent_styles, accent.0, elem.span())?; // Forcing the accent to be at least as large as the base makes it too wide // in many cases. diff --git a/crates/typst-layout/src/math/frac.rs b/crates/typst-layout/src/math/frac.rs index cfbd90e8e..091f328f6 100644 --- a/crates/typst-layout/src/math/frac.rs +++ b/crates/typst-layout/src/math/frac.rs @@ -109,12 +109,12 @@ fn layout_frac_like( frame.push_frame(denom_pos, denom); if binom { - let mut left = GlyphFragment::new(ctx.font, styles, '(', span); + let mut left = GlyphFragment::new_char(ctx.font, styles, '(', span)?; left.stretch_vertical(ctx, height - short_fall); left.center_on_axis(); ctx.push(left); ctx.push(FrameFragment::new(styles, frame)); - let mut right = GlyphFragment::new(ctx.font, styles, ')', span); + let mut right = GlyphFragment::new_char(ctx.font, styles, ')', span)?; right.stretch_vertical(ctx, height - short_fall); right.center_on_axis(); ctx.push(right); diff --git a/crates/typst-layout/src/math/fragment.rs b/crates/typst-layout/src/math/fragment.rs index 526e5a3e3..3fd335af2 100644 --- a/crates/typst-layout/src/math/fragment.rs +++ b/crates/typst-layout/src/math/fragment.rs @@ -4,7 +4,7 @@ use az::SaturatingAs; use rustybuzz::{BufferFlags, UnicodeBuffer}; use ttf_parser::math::{GlyphAssembly, GlyphConstruction, GlyphPart}; use ttf_parser::GlyphId; -use typst_library::diag::warning; +use typst_library::diag::{bail, warning, SourceResult}; use typst_library::foundations::StyleChain; use typst_library::introspection::Tag; use typst_library::layout::{ @@ -258,21 +258,25 @@ pub struct GlyphFragment { } impl GlyphFragment { - /// Calls `try_new` with the given character, assuming the font has a glyph - /// for it. - pub fn new(font: &Font, styles: StyleChain, c: char, span: Span) -> Self { - Self::try_new(font, styles, c.encode_utf8(&mut [0; 4]), span).unwrap() + /// Calls `new` with the given character. + pub fn new_char( + font: &Font, + styles: StyleChain, + c: char, + span: Span, + ) -> SourceResult { + Self::new(font, styles, c.encode_utf8(&mut [0; 4]), span) } - /// Try to create a new glyph out of the given string. Will return None if - /// the result from shaping the string is not a single glyph or is a tofu. + /// Try to create a new glyph out of the given string. Will bail if the + /// result from shaping the string is not a single glyph or is a tofu. #[comemo::memoize] - pub fn try_new( + pub fn new( font: &Font, styles: StyleChain, text: &str, span: Span, - ) -> Option { + ) -> SourceResult { let mut buffer = UnicodeBuffer::new(); buffer.push_str(text); buffer.set_language(language(styles)); @@ -296,7 +300,7 @@ impl GlyphFragment { let buffer = rustybuzz::shape_with_plan(font.rusty(), &plan, buffer); if buffer.len() != 1 { - return None; + bail!(span, "did not get a single glyph after shaping {}", text); } let info = buffer.glyph_infos()[0]; @@ -304,7 +308,7 @@ impl GlyphFragment { // TODO: add support for coverage and fallback, like in normal text shaping. if info.glyph_id == 0 { - return None; + bail!(span, "current font is missing a glyph for {}", text); } let cluster = info.cluster as usize; @@ -353,7 +357,7 @@ impl GlyphFragment { modifiers: FrameModifiers::get_in(styles), }; fragment.update_glyph(); - Some(fragment) + Ok(fragment) } /// Sets element id and boxes in appropriate way without changing other diff --git a/crates/typst-layout/src/math/mat.rs b/crates/typst-layout/src/math/mat.rs index 3333d1d32..278b1343e 100644 --- a/crates/typst-layout/src/math/mat.rs +++ b/crates/typst-layout/src/math/mat.rs @@ -183,8 +183,12 @@ fn layout_body( // We pad ascent and descent with the ascent and descent of the paren // to ensure that normal matrices are aligned with others unless they are // way too big. - let paren = - GlyphFragment::new(ctx.font, styles.chain(&denom_style), '(', Span::detached()); + let paren = GlyphFragment::new_char( + ctx.font, + styles.chain(&denom_style), + '(', + Span::detached(), + )?; for (column, col) in columns.iter().zip(&mut cols) { for (cell, (ascent, descent)) in column.iter().zip(&mut heights) { @@ -313,7 +317,7 @@ fn layout_delimiters( frame.set_baseline(height / 2.0 + axis); if let Some(left_c) = left { - let mut left = GlyphFragment::new(ctx.font, styles, left_c, span); + let mut left = GlyphFragment::new_char(ctx.font, styles, left_c, span)?; left.stretch_vertical(ctx, target - short_fall); left.center_on_axis(); ctx.push(left); @@ -322,7 +326,7 @@ fn layout_delimiters( ctx.push(FrameFragment::new(styles, frame)); if let Some(right_c) = right { - let mut right = GlyphFragment::new(ctx.font, styles, right_c, span); + let mut right = GlyphFragment::new_char(ctx.font, styles, right_c, span)?; right.stretch_vertical(ctx, target - short_fall); right.center_on_axis(); ctx.push(right); diff --git a/crates/typst-layout/src/math/root.rs b/crates/typst-layout/src/math/root.rs index b18e1d204..91b9b16af 100644 --- a/crates/typst-layout/src/math/root.rs +++ b/crates/typst-layout/src/math/root.rs @@ -49,7 +49,7 @@ pub fn layout_root( // Layout root symbol. let target = radicand.height() + thickness + gap; - let mut sqrt = GlyphFragment::new(ctx.font, styles, '√', span); + let mut sqrt = GlyphFragment::new_char(ctx.font, styles, '√', span)?; sqrt.stretch_vertical(ctx, target); let sqrt = sqrt.into_frame(); diff --git a/crates/typst-layout/src/math/text.rs b/crates/typst-layout/src/math/text.rs index 192380f18..67dc0a2c8 100644 --- a/crates/typst-layout/src/math/text.rs +++ b/crates/typst-layout/src/math/text.rs @@ -70,7 +70,7 @@ fn layout_inline_text( let mut fragments = vec![]; for unstyled_c in text.chars() { let c = styled_char(styles, unstyled_c, false); - let glyph = GlyphFragment::new(ctx.font, styles, c, span); + let glyph = GlyphFragment::new_char(ctx.font, styles, c, span)?; fragments.push(glyph.into()); } let frame = MathRun::new(fragments).into_frame(styles); @@ -125,23 +125,19 @@ pub fn layout_symbol( _ => (elem.text, styles), }; let c = styled_char(styles, unstyled_c, true); - let fragment: MathFragment = match GlyphFragment::try_new( - ctx.font, - symbol_styles, - c.encode_utf8(&mut [0; 4]), - elem.span(), - ) { - Some(mut glyph) => { - adjust_glyph_layout(&mut glyph, ctx, styles); - glyph.into() - } - None => { - // Not in the math font, fallback to normal inline text layout. - // TODO: Should replace this with proper fallback in [`GlyphFragment::try_new`]. - layout_inline_text(c.encode_utf8(&mut [0; 4]), elem.span(), ctx, styles)? - .into() - } - }; + let fragment: MathFragment = + match GlyphFragment::new_char(ctx.font, symbol_styles, c, elem.span()) { + Ok(mut glyph) => { + adjust_glyph_layout(&mut glyph, ctx, styles); + glyph.into() + } + Err(_) => { + // Not in the math font, fallback to normal inline text layout. + // TODO: Should replace this with proper fallback in [`GlyphFragment::new`]. + layout_inline_text(c.encode_utf8(&mut [0; 4]), elem.span(), ctx, styles)? + .into() + } + }; ctx.push(fragment); Ok(()) } diff --git a/crates/typst-layout/src/math/underover.rs b/crates/typst-layout/src/math/underover.rs index 00eb403a0..c29d99478 100644 --- a/crates/typst-layout/src/math/underover.rs +++ b/crates/typst-layout/src/math/underover.rs @@ -285,7 +285,7 @@ fn layout_underoverspreader( let body = ctx.layout_into_run(body, styles)?; let body_class = body.class(); let body = body.into_fragment(styles); - let mut glyph = GlyphFragment::new(ctx.font, styles, c, span); + let mut glyph = GlyphFragment::new_char(ctx.font, styles, c, span)?; glyph.stretch_horizontal(ctx, body.width()); let mut rows = vec![];