From 79948c0c5e7e8df49a0f2f47ca44c7b6643fbb63 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 30 Mar 2021 12:47:34 +0200 Subject: [PATCH] =?UTF-8?q?BiDi=20reordering=20=F0=9F=94=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Martin --- Cargo.toml | 1 + src/layout/par.rs | 237 ++++++++++++++++++++++++++----------- tests/ref/text/shaping.png | Bin 12273 -> 12247 bytes 3 files changed, 172 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0d0d77d2..4eef9f8cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ miniz_oxide = "0.3" pdf-writer = { path = "../pdf-writer" } rustybuzz = "0.3" ttf-parser = "0.9" +unicode-bidi = "0.3" unicode-xid = "0.2" xi-unicode = "0.3" anyhow = { version = "1", optional = true } diff --git a/src/layout/par.rs b/src/layout/par.rs index 6a226fb4d..de3b1bab7 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -1,6 +1,8 @@ use std::fmt::{self, Debug, Formatter}; use std::mem; +use std::ops::Range; +use unicode_bidi::{BidiInfo, Level}; use xi_unicode::LineBreakIterator; use super::*; @@ -29,18 +31,6 @@ pub enum ParChild { Any(AnyNode, Align), } -impl Debug for ParChild { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Spacing(amount) => write!(f, "Spacing({:?})", amount), - Self::Text(node, align) => write!(f, "Text({:?}, {:?})", node.text, align), - Self::Any(any, align) => { - f.debug_tuple("Any").field(any).field(align).finish() - } - } - } -} - /// A consecutive, styled run of text. #[derive(Clone, PartialEq)] pub struct TextNode { @@ -50,26 +40,73 @@ pub struct TextNode { pub props: FontProps, } -impl Debug for TextNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Text({:?})", self.text) - } -} - impl Layout for ParNode { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec { - let mut layouter = ParLayouter::new(self.dir, self.line_spacing, areas.clone()); + let mut text = String::new(); + let mut ranges = vec![]; + for child in &self.children { + let start = text.len(); + match child { + ParChild::Spacing(_) => text.push(' '), + ParChild::Text(node, _) => text.push_str(&node.text), + ParChild::Any(_, _) => text.push('\u{FFFC}'), + } + ranges.push(start .. text.len()); + } + + let level = match self.dir { + Dir::LTR => Level::ltr(), + Dir::RTL => Level::rtl(), + _ => panic!("invalid paragraph direction"), + }; + + let bidi = BidiInfo::new(&text, Some(level)); + let mut layouter = + ParLayouter::new(self.dir, self.line_spacing, &bidi, areas.clone()); + + for (range, child) in ranges.into_iter().zip(&self.children) { match *child { - ParChild::Spacing(amount) => layouter.push_spacing(amount), - ParChild::Text(ref node, align) => layouter.push_text(ctx, node, align), + ParChild::Spacing(amount) => { + layouter.push_spacing(range, amount); + } + ParChild::Text(ref node, align) => { + let mut start = range.start; + let mut last = None; + for (idx, level) in bidi.levels[range.clone()].iter().enumerate() { + let idx = range.start + idx; + + if last.map_or(false, |last| last != level) { + // Push the text up until `idx` (exclusively). + layouter.push_text( + ctx, + start .. idx, + &text[start .. idx], + &node.props, + align, + ); + start = idx; + } + + last = Some(level); + } + + layouter.push_text( + ctx, + start .. range.end, + &text[start .. range.end], + &node.props, + align, + ); + } ParChild::Any(ref node, align) => { for frame in node.layout(ctx, &layouter.areas) { - layouter.push_frame(frame, align); + layouter.push_frame(range.clone(), frame, align); } } } } + layouter.finish() } } @@ -80,65 +117,92 @@ impl From for AnyNode { } } -struct ParLayouter { +struct ParLayouter<'a> { dir: Dir, line_spacing: Length, + bidi: &'a BidiInfo<'a>, areas: Areas, finished: Vec, stack: Vec<(Length, Frame, Align)>, stack_size: Size, - line: Vec<(Length, Frame, Align)>, - line_size: Size, - line_ruler: Align, + line: Line, hard: bool, } -impl ParLayouter { - fn new(dir: Dir, line_spacing: Length, areas: Areas) -> Self { +struct Line { + items: Vec, + size: Size, + ruler: Align, +} + +struct LineItem { + range: Range, + frame: Frame, + align: Align, +} + +impl<'a> ParLayouter<'a> { + fn new(dir: Dir, line_spacing: Length, bidi: &'a BidiInfo<'a>, areas: Areas) -> Self { Self { dir, line_spacing, + bidi, areas, finished: vec![], stack: vec![], stack_size: Size::ZERO, - line: vec![], - line_size: Size::ZERO, - line_ruler: Align::Start, + line: Line { + items: vec![], + size: Size::ZERO, + ruler: Align::Start, + }, hard: true, } } - fn push_spacing(&mut self, amount: Length) { - let max = self.areas.current.width; - self.line_size.width = (self.line_size.width + amount).min(max); + fn push_spacing(&mut self, range: Range, amount: Length) { + let amount = amount.min(self.areas.current.width - self.line.size.width); + self.line.size.width += amount; + self.line.items.push(LineItem { + range, + frame: Frame::new(Size::new(amount, Length::ZERO)), + align: Align::default(), + }) } - fn push_text(&mut self, ctx: &mut LayoutContext, node: &TextNode, align: Align) { + fn push_text( + &mut self, + ctx: &mut LayoutContext, + range: Range, + text: &str, + props: &FontProps, + align: Align, + ) { // Position in the text at which the current line starts. - let mut start = 0; + let mut start = range.start; // The current line attempt: Text shaped up to the previous line break // opportunity. let mut last = None; - let mut iter = LineBreakIterator::new(&node.text).peekable(); + let mut iter = LineBreakIterator::new(text).peekable(); while let Some(&(pos, mandatory)) = iter.peek() { - let line = &node.text[start .. pos]; + let line = &text[start - range.start .. pos]; // Remove trailing newline and spacing at the end of lines. let mut line = line.trim_end_matches(is_newline); - if pos != node.text.len() { + if pos != text.len() { line = line.trim_end(); } - let frame = shape(line, &mut ctx.env.fonts, &node.props); + let pos = range.start + pos; + let frame = shape(line, &mut ctx.env.fonts, props); if self.usable().fits(frame.size) { // Still fits into the line. if mandatory { // We have to break here. - self.push_frame(frame, align); + self.push_frame(start .. pos, frame, align); self.finish_line(true); start = pos; last = None; @@ -149,7 +213,7 @@ impl ParLayouter { // The line start..pos doesn't fit. So we write the line up to // the last position and retry writing just the single piece // behind it. - self.push_frame(frame, align); + self.push_frame(start .. pos, frame, align); self.finish_line(false); start = pos; continue; @@ -157,7 +221,7 @@ impl ParLayouter { // Since last is `None`, we are at the first piece behind a line // break and it still doesn't fit. Since we can't break it up // further, so we just have to push it. - self.push_frame(frame, align); + self.push_frame(start .. pos, frame, align); self.finish_line(false); start = pos; } @@ -166,12 +230,12 @@ impl ParLayouter { } // Leftovers. - if let Some((frame, _)) = last { - self.push_frame(frame, align); + if let Some((frame, pos)) = last { + self.push_frame(start .. pos, frame, align); } } - fn push_frame(&mut self, frame: Frame, align: Align) { + fn push_frame(&mut self, range: Range, frame: Frame, align: Align) { // When the alignment of the last pushed frame (stored in the "ruler") // is further to the end than the new `frame`, we need a line break. // @@ -184,12 +248,12 @@ impl ParLayouter { // | First | // | Second | // +----------------------------+ - if self.line_ruler > align { + if self.line.ruler > align { self.finish_line(false); } // Find out whether the area still has enough space for this frame. - if !self.usable().fits(frame.size) && self.line_size.width > Length::ZERO { + if !self.usable().fits(frame.size) && self.line.size.width > Length::ZERO { self.finish_line(false); // Here, we can directly check whether the frame fits into @@ -209,10 +273,10 @@ impl ParLayouter { // A line can contain frames with different alignments. Their exact // positions are calculated later depending on the alignments. let size = frame.size; - self.line.push((self.line_size.width, frame, align)); - self.line_size.width += size.width; - self.line_size.height = self.line_size.height.max(size.height); - self.line_ruler = align; + self.line.items.push(LineItem { range, frame, align }); + self.line.size.width += size.width; + self.line.size.height = self.line.size.height.max(size.height); + self.line.ruler = align; } fn usable(&self) -> Size { @@ -220,37 +284,60 @@ impl ParLayouter { // `areas.current`, but the width of the current line needs to be // subtracted to make sure the frame fits. let mut usable = self.areas.current; - usable.width -= self.line_size.width; + usable.width -= self.line.size.width; usable } fn finish_line(&mut self, hard: bool) { - if !mem::replace(&mut self.hard, hard) && self.line.is_empty() { + if !mem::replace(&mut self.hard, hard) && self.line.items.is_empty() { return; } + let mut items = mem::take(&mut self.line.items); + if let (Some(first), Some(last)) = (items.first(), items.last()) { + let range = first.range.start .. last.range.end; + let para = self + .bidi + .paragraphs + .iter() + .find(|para| para.range.contains(&range.start)) + .unwrap(); + + let (levels, ranges) = self.bidi.visual_runs(¶, range); + + items.sort_by_key(|item| { + let start = item.range.start; + let idx = ranges.iter().position(|r| r.contains(&start)).unwrap(); + let ltr = levels[start].is_ltr(); + let sec = start as isize * if ltr { 1 } else { -1 }; + (idx, sec) + }); + } + let full_size = { let expand = self.areas.expand.horizontal; let full = self.areas.full.width; Size::new( - expand.resolve(self.line_size.width, full), - self.line_size.height, + expand.resolve(self.line.size.width, full), + self.line.size.height, ) }; let mut output = Frame::new(full_size); - for (before, frame, align) in mem::take(&mut self.line) { + let mut offset = Length::ZERO; + + for item in items { // Align along the x axis. - let x = align.resolve(if self.dir.is_positive() { - before .. full_size.width - self.line_size.width + before + let x = item.align.resolve(if self.dir.is_positive() { + offset .. full_size.width - self.line.size.width + offset } else { - let before_with_self = before + frame.size.width; - full_size.width - before_with_self - .. self.line_size.width - before_with_self + full_size.width - self.line.size.width + offset .. offset }); + offset += item.frame.size.width; + let pos = Point::new(x, Length::ZERO); - output.push_frame(pos, frame); + output.push_frame(pos, item.frame); } // Add line spacing, but only between lines, not after the last line. @@ -259,12 +346,12 @@ impl ParLayouter { self.areas.current.height -= self.line_spacing; } - self.stack.push((self.stack_size.height, output, self.line_ruler)); + self.stack.push((self.stack_size.height, output, self.line.ruler)); self.stack_size.height += full_size.height; self.stack_size.width = self.stack_size.width.max(full_size.width); self.areas.current.height -= full_size.height; - self.line_size = Size::ZERO; - self.line_ruler = Align::Start; + self.line.size = Size::ZERO; + self.line.ruler = Align::Start; } fn finish_area(&mut self) { @@ -292,3 +379,21 @@ impl ParLayouter { self.finished } } + +impl Debug for ParChild { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Spacing(amount) => write!(f, "Spacing({:?})", amount), + Self::Text(node, align) => write!(f, "Text({:?}, {:?})", node.text, align), + Self::Any(any, align) => { + f.debug_tuple("Any").field(any).field(align).finish() + } + } + } +} + +impl Debug for TextNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "Text({:?})", self.text) + } +} diff --git a/tests/ref/text/shaping.png b/tests/ref/text/shaping.png index 9af49f1601a77f4e33f1efea9edc3ee1dd27a834..88e7b0ad3335cc651689a408f8c30e40d7e950d1 100644 GIT binary patch literal 12247 zcma)?1yCGco9-bbBqS3^@IeE?gAVTQ?(T!TyIX+Z7Ti5R@SqbUxVsY|$l&gBJKug) zckkZaf9+IL1x?TKKIeIV?-Q;dC;s*|-fIK|gtw9sB1#Adh|htalvmGyI}2Z%z9S$o zWJ!t$s<xH6B6aA9dA4gvhhOq>G%>Sg)3&pDEf5+&7epr68uk1qP?wfSJXuJWp#x`{qyc09yC|_t zx#Gdin#F@$1&RA34Y#Ge3#oW$<6@hh9V6}+5mH~+s%8CYRPQWPs!T*4N2Xl)^Z
  • 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
  • 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⪙d#L`e!WgJTu)ESUTwAO%WIvz!*%O0a-kzZjsFE-_yTRh4T~s*;$y@pP z=G<42{H6Y5eZ6a-oK-4E8FTSHRxnxOY?n(8X)1xG@cC@-g@HM1hse{ zy!1VPmqD3)FpgL7y=E}_gs$QvfmOxtChOD~cd8No&%(qEpXss^-vj1BN>n9RSn-rK zk(aC`c60bRVe|FxelK|1cOQD`Vuw!`Q@4dvy=+46O!^-NJnQ_=3i zz}E;Ani6!5)`&zr>jv|?iqq;-J(!{biedrlZ^W$!MsyAMoGW#=x4CS)xp_nNX0&P1Y`%hyR^1A7%#?~%(ysRz1=kR6z?6EAy>k8XlNW`1 zG1Y|)t;_qBk&SIw&~GULwKm4u3L=0>@$)s#-DlI_3n`D8hC%nwrrEO|LRc|V!thYf zGD-*oH4fDM$NS*)$DSJ~shZWAI)uUJ0IXBzc$PQOh|C4GC5Cw-cm>tMq7r`_|#7JnFF* zQF>-xHDwJrwfxvx)orfETN4ga2EyuhZ%j&V)>DnL=Fhsz5Z~NxC3pv)g?9o3w-+AC z+ih7lW1`*V0_qvk{8n53s=1!{+u&@8uM0J~))&N&2l8$Kx2$3_+)Xn=3!3w-x-ruF zDDn#88Mmf-@WGTi+GQQM^f6bhH7Sep$4`lRifMY>{0HUBTlR?}LP=RBldAnzPAYvv zzJ~^ZcJ?!;q0tWj?2uBtXW@Oi`Sa0V#Q$**mHm$(-#8E|A{FD zMEqK$wfY?vMEEXo@mKLcusY99NQP!=(8|(#pWG@K5ly;7AUM+x6)ilKm*~>6mYTXX vewTlN_6$vn`|1C>ti4j=-<-HnD%=X?IDL&4lcRLJKaZ1