From b9f3a95e03bd792bca4926959eab5f9f6edd1a9d Mon Sep 17 00:00:00 2001 From: +merlan #flirora Date: Fri, 27 Jun 2025 04:36:46 -0400 Subject: [PATCH] Sort line items by logical order when constructing frame (#5887) Co-authored-by: Laurenz --- crates/typst-layout/src/inline/line.rs | 58 ++++++++++++++---------- tests/ref/issue-5775-cite-order-rtl.png | Bin 0 -> 14786 bytes tests/suite/model/cite.typ | 12 +++++ 3 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 tests/ref/issue-5775-cite-order-rtl.png diff --git a/crates/typst-layout/src/inline/line.rs b/crates/typst-layout/src/inline/line.rs index f05189275..7bf4d4c73 100644 --- a/crates/typst-layout/src/inline/line.rs +++ b/crates/typst-layout/src/inline/line.rs @@ -219,7 +219,7 @@ fn collect_items<'a>( // Add fallback text to expand the line height, if necessary. if !items.iter().any(|item| matches!(item, Item::Text(_))) { if let Some(fallback) = fallback { - items.push(fallback); + items.push(fallback, usize::MAX); } } @@ -270,10 +270,10 @@ fn collect_range<'a>( items: &mut Items<'a>, fallback: &mut Option>, ) { - for (subrange, item) in p.slice(range.clone()) { + 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) = item else { - items.push(item); + items.push(item, idx); continue; }; @@ -293,10 +293,10 @@ fn collect_range<'a>( } else if split { // When the item is split in half, reshape it. let reshaped = shaped.reshape(engine, sliced); - items.push(Item::Text(reshaped)); + items.push(Item::Text(reshaped), idx); } else { // When the item is fully contained, just keep it. - items.push(item); + items.push(item, idx); } } } @@ -499,16 +499,16 @@ pub fn commit( // Build the frames and determine the height and baseline. let mut frames = vec![]; - for item in line.items.iter() { - let mut push = |offset: &mut Abs, frame: Frame| { + for &(idx, ref item) in line.items.indexed_iter() { + let mut push = |offset: &mut Abs, frame: Frame, idx: usize| { let width = frame.width(); top.set_max(frame.baseline()); bottom.set_max(frame.size().y - frame.baseline()); - frames.push((*offset, frame)); + frames.push((*offset, frame, idx)); *offset += width; }; - match item { + match &**item { Item::Absolute(v, _) => { offset += *v; } @@ -520,7 +520,7 @@ pub fn commit( layout_box(elem, engine, loc.relayout(), styles, region) })?; apply_baseline_shift(&mut frame, *styles); - push(&mut offset, frame); + push(&mut offset, frame, idx); } else { offset += amount; } @@ -532,15 +532,15 @@ pub fn commit( justification_ratio, extra_justification, ); - push(&mut offset, frame); + push(&mut offset, frame, idx); } Item::Frame(frame) => { - push(&mut offset, frame.clone()); + push(&mut offset, frame.clone(), idx); } Item::Tag(tag) => { let mut frame = Frame::soft(Size::zero()); frame.push(Point::zero(), FrameItem::Tag((*tag).clone())); - frames.push((offset, frame)); + frames.push((offset, frame, idx)); } Item::Skip(_) => {} } @@ -559,8 +559,13 @@ pub fn commit( add_par_line_marker(&mut output, marker, engine, locator, top); } + // Ensure that the final frame's items are in logical order rather than in + // visual order. This is important because it affects the order of elements + // during introspection and thus things like counters. + frames.sort_unstable_by_key(|(_, _, idx)| *idx); + // Construct the line's frame. - for (offset, frame) in frames { + for (offset, frame, _) in frames { let x = offset + p.config.align.position(remaining); let y = top - frame.baseline(); output.push_frame(Point::new(x, y), frame); @@ -627,7 +632,7 @@ fn overhang(c: char) -> f64 { } /// A collection of owned or borrowed inline items. -pub struct Items<'a>(Vec>); +pub struct Items<'a>(Vec<(usize, ItemEntry<'a>)>); impl<'a> Items<'a> { /// Create empty items. @@ -636,33 +641,38 @@ impl<'a> Items<'a> { } /// Push a new item. - pub fn push(&mut self, entry: impl Into>) { - self.0.push(entry.into()); + pub fn push(&mut self, entry: impl Into>, idx: usize) { + self.0.push((idx, entry.into())); } /// Iterate over the items. pub fn iter(&self) -> impl Iterator> { - self.0.iter().map(|item| &**item) + self.0.iter().map(|(_, item)| &**item) + } + + /// Iterate over the items with indices + pub fn indexed_iter(&self) -> impl Iterator)> { + self.0.iter() } /// Access the first item. pub fn first(&self) -> Option<&Item<'a>> { - self.0.first().map(|item| &**item) + self.0.first().map(|(_, item)| &**item) } /// Access the last item. pub fn last(&self) -> Option<&Item<'a>> { - self.0.last().map(|item| &**item) + self.0.last().map(|(_, item)| &**item) } /// Access the first item mutably, if it is text. pub fn first_text_mut(&mut self) -> Option<&mut ShapedText<'a>> { - self.0.first_mut()?.text_mut() + self.0.first_mut()?.1.text_mut() } /// Access the last item mutably, if it is text. pub fn last_text_mut(&mut self) -> Option<&mut ShapedText<'a>> { - self.0.last_mut()?.text_mut() + self.0.last_mut()?.1.text_mut() } /// Reorder the items starting at the given index to RTL. @@ -673,12 +683,12 @@ impl<'a> Items<'a> { impl<'a> FromIterator> for Items<'a> { fn from_iter>>(iter: I) -> Self { - Self(iter.into_iter().collect()) + Self(iter.into_iter().enumerate().collect()) } } impl<'a> Deref for Items<'a> { - type Target = Vec>; + type Target = Vec<(usize, ItemEntry<'a>)>; fn deref(&self) -> &Self::Target { &self.0 diff --git a/tests/ref/issue-5775-cite-order-rtl.png b/tests/ref/issue-5775-cite-order-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..982ceef3928f9a275611bcd3cc2969131056c623 GIT binary patch literal 14786 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 OA}y{URxe@{^8Wy!zYvQ6 literal 0 HcmV?d00001 diff --git a/tests/suite/model/cite.typ b/tests/suite/model/cite.typ index b328dda49..1a79b690d 100644 --- a/tests/suite/model/cite.typ +++ b/tests/suite/model/cite.typ @@ -147,3 +147,15 @@ B #cite() #cite(). // Error: 7-17 expected label, found string // Hint: 7-17 use `label("%@&#*!\\")` to create a label #cite("%@&#*!\\") + +--- issue-5775-cite-order-rtl --- +// Test citation order in RTL text. +#set page(width: 300pt) +@netwok +aaa +این است +@tolkien54 +و این یکی هست +@arrgh + +#bibliography("/assets/bib/works.bib")