From 2501da4bb62948e63c68c76cb45af3b7c8a57a0c Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 21 Aug 2025 16:21:56 +0200 Subject: [PATCH] Fix Unicode mapping of hyphenation artifacts (#6799) --- crates/typst-layout/src/inline/line.rs | 8 +++++--- crates/typst-layout/src/inline/shaping.rs | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/typst-layout/src/inline/line.rs b/crates/typst-layout/src/inline/line.rs index 9b5b656e5..f89795de6 100644 --- a/crates/typst-layout/src/inline/line.rs +++ b/crates/typst-layout/src/inline/line.rs @@ -178,7 +178,8 @@ pub fn line<'a>( && pred.dash == Some(Dash::Hard) && let Some(base) = pred.items.last_text() && should_repeat_hyphen(base.lang, full) - && let Some(hyphen) = ShapedText::hyphen(engine, p.config.fallback, base, trim) + && let Some(hyphen) = + ShapedText::hyphen(engine, p.config.fallback, base, trim, false) { items.push(Item::Text(hyphen), START_HYPHEN_IDX); } @@ -188,7 +189,8 @@ pub fn line<'a>( // Add a hyphen at the line end, if we ended on a soft hyphen. if dash == Some(Dash::Soft) && let Some(base) = items.last_text() - && let Some(hyphen) = ShapedText::hyphen(engine, p.config.fallback, base, trim) + && let Some(hyphen) = + ShapedText::hyphen(engine, p.config.fallback, base, trim, true) { items.push(Item::Text(hyphen), END_HYPHEN_IDX); } @@ -659,7 +661,7 @@ fn overhang(c: char) -> f64 { match c { // Dashes. '–' | '—' => 0.2, - '-' => 0.55, + '-' | '\u{ad}' => 0.55, // Punctuation. '.' | ',' => 0.8, diff --git a/crates/typst-layout/src/inline/shaping.rs b/crates/typst-layout/src/inline/shaping.rs index 287bca9a5..66987b2ad 100644 --- a/crates/typst-layout/src/inline/shaping.rs +++ b/crates/typst-layout/src/inline/shaping.rs @@ -21,6 +21,11 @@ use unicode_script::{Script, UnicodeScript}; use super::{Item, Range, SpanMapper, decorate}; use crate::modifiers::FrameModifyText; +const SHY: char = '\u{ad}'; +const SHY_STR: &str = "\u{ad}"; +const HYPHEN: char = '-'; +const HYPHEN_STR: &str = "-"; + /// The result of shaping text. /// /// This type contains owned or borrowed shaped text runs, which can be @@ -441,11 +446,15 @@ impl<'a> ShapedText<'a> { } /// Creates shaped text containing a hyphen. + /// + /// If `soft` is true, the item will map to plain text as a soft hyphen. + /// Otherwise, it will map to a normal hyphen. pub fn hyphen( engine: &Engine, fallback: bool, base: &ShapedText<'a>, pos: usize, + soft: bool, ) -> Option { let world = engine.world; let book = world.book(); @@ -466,10 +475,11 @@ impl<'a> ShapedText<'a> { let glyph_id = ttf.glyph_index('-')?; let x_advance = font.to_em(ttf.glyph_hor_advance(glyph_id)?); let size = base.styles.resolve(TextElem::size); + let (c, text) = if soft { (SHY, SHY_STR) } else { (HYPHEN, HYPHEN_STR) }; Some(ShapedText { base: pos, - text: "", + text, dir: base.dir, lang: base.lang, region: base.region, @@ -484,9 +494,9 @@ impl<'a> ShapedText<'a> { y_offset: Em::zero(), size, adjustability: Adjustability::default(), - range: pos..pos, + range: pos..pos + text.len(), safe_to_break: true, - c: '-', + c, is_justifiable: false, script: Script::Common, }]),