diff --git a/crates/typst-layout/src/inline/line.rs b/crates/typst-layout/src/inline/line.rs index d02bf573e..c1ea7acc2 100644 --- a/crates/typst-layout/src/inline/line.rs +++ b/crates/typst-layout/src/inline/line.rs @@ -270,13 +270,12 @@ fn collect_range<'a>( items: &mut Items<'a>, fallback: &mut Option>, ) { - for (idx, run) in p.slice(range.clone()).enumerate() { + for (idx, (subrange, item)) in p.slice(range.clone()).enumerate() { // All non-text items are just kept, they can't be split. - let Item::Text(shaped) = &run.item else { - items.push(&run.item, idx); + let Item::Text(shaped) = item else { + items.push(item, idx); continue; }; - let subrange = &run.range; // The intersection range of the item, the subrange, and the line's // trimming. @@ -297,7 +296,7 @@ fn collect_range<'a>( items.push(Item::Text(reshaped), idx); } else { // When the item is fully contained, just keep it. - items.push(&run.item, idx); + items.push(item, idx); } } } diff --git a/crates/typst-layout/src/inline/linebreak.rs b/crates/typst-layout/src/inline/linebreak.rs index 8665706b9..ada048c7d 100644 --- a/crates/typst-layout/src/inline/linebreak.rs +++ b/crates/typst-layout/src/inline/linebreak.rs @@ -844,8 +844,8 @@ fn linebreak_link(link: &str, mut f: impl FnMut(usize)) { /// Whether hyphenation is enabled at the given offset. fn hyphenate_at(p: &Preparation, offset: usize) -> bool { p.config.hyphenate.unwrap_or_else(|| { - let run = p.get(offset); - match run.item.text() { + let (_, item) = p.get(offset); + match item.text() { Some(text) => TextElem::hyphenate_in(text.styles).unwrap_or(p.config.justify), None => false, } @@ -855,8 +855,8 @@ fn hyphenate_at(p: &Preparation, offset: usize) -> bool { /// The text language at the given offset. fn lang_at(p: &Preparation, offset: usize) -> Option { let lang = p.config.lang.or_else(|| { - let run = p.get(offset); - let styles = run.item.text()?.styles; + let (_, item) = p.get(offset); + let styles = item.text()?.styles; Some(TextElem::lang_in(styles)) })?; @@ -921,8 +921,8 @@ impl Estimates { let mut shrinkability = CumulativeVec::with_capacity(cap); let mut justifiables = CumulativeVec::with_capacity(cap); - for run in p.items.iter() { - if let Item::Text(shaped) = &run.item { + for (range, item) in p.items.iter() { + if let Item::Text(shaped) = item { for g in shaped.glyphs.iter() { let byte_len = g.range.len(); let stretch = g.stretchability().0 + g.stretchability().1; @@ -933,13 +933,13 @@ impl Estimates { justifiables.push(byte_len, g.is_justifiable() as usize); } } else { - widths.push(run.range.len(), run.item.natural_width()); + widths.push(range.len(), item.natural_width()); } - widths.adjust(run.range.end); - stretchability.adjust(run.range.end); - shrinkability.adjust(run.range.end); - justifiables.adjust(run.range.end); + widths.adjust(range.end); + stretchability.adjust(range.end); + shrinkability.adjust(range.end); + justifiables.adjust(range.end); } Self { diff --git a/crates/typst-layout/src/inline/prepare.rs b/crates/typst-layout/src/inline/prepare.rs index 12f85449a..5d7fcd7cb 100644 --- a/crates/typst-layout/src/inline/prepare.rs +++ b/crates/typst-layout/src/inline/prepare.rs @@ -3,11 +3,6 @@ use unicode_bidi::{BidiInfo, Level as BidiLevel}; use super::*; -pub struct Run<'a> { - pub item: Item<'a>, - pub range: Range, -} - /// A representation in which children are already layouted and text is already /// preshaped. /// @@ -25,22 +20,22 @@ pub struct Preparation<'a> { /// direction). pub bidi: Option>, /// Text runs, spacing and layouted elements. - pub items: Vec>, + pub items: Vec<(Range, Item<'a>)>, /// Maps from byte indices to item indices. pub indices: Vec, /// The span mapper. pub spans: SpanMapper, } -impl Preparation<'_> { +impl<'a> Preparation<'a> { /// Get the item that contains the given `text_offset`. - pub fn get(&self, offset: usize) -> &Run { + pub fn get(&self, offset: usize) -> &(Range, Item<'a>) { let idx = self.indices.get(offset).copied().unwrap_or(0); &self.items[idx] } /// Iterate over the items that intersect the given `sliced` range. - pub fn slice(&self, sliced: Range) -> impl Iterator { + pub fn slice(&self, sliced: Range) -> impl Iterator)> { // Usually, we don't want empty-range items at the start of the line // (because they will be part of the previous line), but for the first // line, we need to keep them. @@ -48,8 +43,8 @@ impl Preparation<'_> { 0 => 0, n => self.indices.get(n).copied().unwrap_or(0), }; - self.items[start..].iter().take_while(move |run| { - run.range.start < sliced.end || run.range.end <= sliced.end + self.items[start..].iter().take_while(move |(range, _)| { + range.start < sliced.end || range.end <= sliced.end }) } } @@ -89,9 +84,7 @@ pub fn prepare<'a>( Segment::Text(_, styles) => { shape_range(&mut items, engine, text, &bidi, range, styles); } - Segment::Item(item) => { - items.push(Run { range, item }); - } + Segment::Item(item) => items.push((range, item)), } cursor = end; @@ -99,8 +92,8 @@ pub fn prepare<'a>( // Build the mapping from byte to item indices. let mut indices = Vec::with_capacity(text.len()); - for (i, run) in items.iter().enumerate() { - indices.extend(run.range.clone().map(|_| i)); + for (i, (range, _)) in items.iter().enumerate() { + indices.extend(range.clone().map(|_| i)); } if config.cjk_latin_spacing { @@ -120,15 +113,15 @@ pub fn prepare<'a>( /// Add some spacing between Han characters and western characters. See /// Requirements for Chinese Text Layout, Section 3.2.2 Mixed Text Composition /// in Horizontal Written Mode -fn add_cjk_latin_spacing(items: &mut [Run]) { +fn add_cjk_latin_spacing(items: &mut [(Range, Item)]) { let mut items = items .iter_mut() - .filter(|run| !matches!(run.item, Item::Tag(_))) + .filter(|(_, x)| !matches!(x, Item::Tag(_))) .peekable(); let mut prev: Option<&ShapedGlyph> = None; - while let Some(run) = items.next() { - let Some(text) = run.item.text_mut() else { + while let Some((_, item)) = items.next() { + let Some(text) = item.text_mut() else { prev = None; continue; }; @@ -142,7 +135,7 @@ fn add_cjk_latin_spacing(items: &mut [Run]) { let next = glyphs.peek().map(|n| n as _).or_else(|| { items .peek() - .and_then(|run| run.item.text()) + .and_then(|(_, i)| i.text()) .and_then(|shaped| shaped.glyphs.first()) }); diff --git a/crates/typst-layout/src/inline/shaping.rs b/crates/typst-layout/src/inline/shaping.rs index bca0cc1f4..935a86b38 100644 --- a/crates/typst-layout/src/inline/shaping.rs +++ b/crates/typst-layout/src/inline/shaping.rs @@ -17,7 +17,6 @@ use typst_utils::SliceExt; use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_script::{Script, UnicodeScript}; -use super::prepare::Run; use super::{decorate, Item, Range, SpanMapper}; use crate::modifiers::FrameModifyText; @@ -593,7 +592,7 @@ impl Debug for ShapedText<'_> { /// Group a range of text by BiDi level and script, shape the runs and generate /// items for them. pub fn shape_range<'a>( - items: &mut Vec>, + items: &mut Vec<(Range, Item<'a>)>, engine: &Engine, text: &'a str, bidi: &BidiInfo<'a>, @@ -607,7 +606,7 @@ pub fn shape_range<'a>( let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL }; let shaped = shape(engine, range.start, &text[range.clone()], styles, dir, lang, region); - items.push(Run { range, item: Item::Text(shaped) }); + items.push((range, Item::Text(shaped))); }; let mut prev_level = BidiLevel::ltr();