From d74c9378b81618419dc8c6315e391b6012955218 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 3 Apr 2021 17:36:33 +0200 Subject: =?UTF-8?q?New=20paragraph=20layout=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous paragraph layout algorithm had a couple of flaws: - It always produced line break opportunities between runs although on the textual level there might have been none. - It didn't handle trailing spacing correctly in some cases. - It wouldn't have been easily adaptable to Knuth-Plass style optimal line breaking because it was fundamentally structured first-fit run-by-run. The new paragraph layout algorithm fixes these flaws. It proceeds roughly in the following stages: 1. Collect all text in the paragraph. 2. Compute BiDi embedding levels. 3. Shape all runs, layout all children and store the resulting items in a reusable (possibly even cacheable) `ParLayout`. 3. Iterate over all line breaks in the concatenated text. 4. Construct lightweight `LineLayout` objects for full lines instead of runs. These mostly borrow from the `ParLayout` and only reshape the first and last run if necessary. The design allows to use Harfbuzz's UNSAFE_TO_BREAK mechanism to make reshaping more efficient. The size of a `LineLayout` can be measured without building the line's frame. 5. Build only the selected line's frames and stack them. --- src/exec/context.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/exec') diff --git a/src/exec/context.rs b/src/exec/context.rs index 987f9f7f..d33d62ef 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -6,7 +6,7 @@ use crate::env::Env; use crate::eval::TemplateValue; use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size}; use crate::layout::{ - AnyNode, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, TextNode, Tree, + AnyNode, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, Tree, }; use crate::syntax::Span; @@ -129,7 +129,7 @@ impl<'a> ExecContext<'a> { fn make_text_node(&self, text: impl Into) -> ParChild { let align = self.state.aligns.cross; let props = self.state.font.resolve_props(); - ParChild::Text(TextNode { text: text.into(), props }, align) + ParChild::Text(text.into(), props, align) } } @@ -238,10 +238,12 @@ impl ParBuilder { } fn push_inner(&mut self, child: ParChild) { - if let ParChild::Text(curr, curr_align) = &child { - if let Some(ParChild::Text(prev, prev_align)) = self.children.last_mut() { - if prev_align == curr_align && prev.props == curr.props { - prev.text.push_str(&curr.text); + if let ParChild::Text(curr_text, curr_props, curr_align) = &child { + if let Some(ParChild::Text(prev_text, prev_props, prev_align)) = + self.children.last_mut() + { + if prev_align == curr_align && prev_props == curr_props { + prev_text.push_str(&curr_text); return; } } -- cgit v1.2.3