Handle paragraph indent at a later stage

This commit is contained in:
Laurenz 2022-11-17 12:40:58 +01:00
parent bf59c08a0a
commit cabd0908e2
3 changed files with 46 additions and 38 deletions

View File

@ -1,3 +1,5 @@
use typst::model::{Property, StyleEntry};
use super::{AlignNode, ColbreakNode, PlaceNode, Spacing, VNode}; use super::{AlignNode, ColbreakNode, PlaceNode, Spacing, VNode};
use crate::prelude::*; use crate::prelude::*;
use crate::text::ParNode; use crate::text::ParNode;
@ -58,6 +60,8 @@ struct FlowLayouter {
used: Size, used: Size,
/// The sum of fractions in the current region. /// The sum of fractions in the current region.
fr: Fr, fr: Fr,
/// Whether the last block was a paragraph.
last_block_was_par: bool,
/// Spacing and layouted blocks. /// Spacing and layouted blocks.
items: Vec<FlowItem>, items: Vec<FlowItem>,
/// Finished frames for previous regions. /// Finished frames for previous regions.
@ -92,6 +96,7 @@ impl FlowLayouter {
full, full,
used: Size::zero(), used: Size::zero(),
fr: Fr::zero(), fr: Fr::zero(),
last_block_was_par: false,
items: vec![], items: vec![],
finished: vec![], finished: vec![],
} }
@ -150,8 +155,18 @@ impl FlowLayouter {
.unwrap_or(Align::Top), .unwrap_or(Align::Top),
); );
// Disable paragraph indent if this is not a consecutive paragraph.
let reset;
let is_par = block.is::<ParNode>();
let mut chained = styles;
if !self.last_block_was_par && is_par && !styles.get(ParNode::INDENT).is_zero() {
let property = Property::new(ParNode::INDENT, Length::zero());
reset = StyleEntry::Property(property);
chained = reset.chain(&styles);
}
// Layout the block itself. // Layout the block itself.
let frames = block.layout_block(world, &self.regions, styles)?; let frames = block.layout_block(world, &self.regions, chained)?;
let len = frames.len(); let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later. // Grow our size, shrink the region and save the frame for later.
@ -166,6 +181,8 @@ impl FlowLayouter {
} }
} }
self.last_block_was_par = is_par;
Ok(()) Ok(())
} }

View File

@ -474,13 +474,14 @@ struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
impl<'a> FlowBuilder<'a> { impl<'a> FlowBuilder<'a> {
fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool { fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool {
let last_was_parbreak = self.1; let last_was_parbreak = std::mem::replace(&mut self.1, false);
self.1 = false;
if content.is::<ParbreakNode>() { if content.is::<ParbreakNode>() {
self.1 = true; self.1 = true;
return true;
} else if content.is::<VNode>() || content.is::<ColbreakNode>() { } else if content.is::<VNode>() || content.is::<ColbreakNode>() {
self.0.push(content.clone(), styles); self.0.push(content.clone(), styles);
return true;
} else if content.has::<dyn LayoutBlock>() { } else if content.has::<dyn LayoutBlock>() {
if !last_was_parbreak { if !last_was_parbreak {
let tight = if let Some(node) = content.downcast::<ListNode>() { let tight = if let Some(node) = content.downcast::<ListNode>() {
@ -505,11 +506,10 @@ impl<'a> FlowBuilder<'a> {
self.0.push(above.pack(), styles); self.0.push(above.pack(), styles);
self.0.push(content.clone(), styles); self.0.push(content.clone(), styles);
self.0.push(below.pack(), styles); self.0.push(below.pack(), styles);
} else { return true;
return false;
} }
true false
} }
fn finish(self, doc: &mut DocBuilder<'a>, styles: StyleChain<'a>) { fn finish(self, doc: &mut DocBuilder<'a>, styles: StyleChain<'a>) {
@ -545,39 +545,10 @@ impl<'a> ParBuilder<'a> {
} }
fn finish(self, parent: &mut Builder<'a>) { fn finish(self, parent: &mut Builder<'a>) {
let (mut children, shared) = self.0.finish(); let (children, shared) = self.0.finish();
if children.is_empty() { if !children.is_empty() {
return; parent.flow.accept(&ParNode(children).pack(), shared);
} }
// Paragraph indent should only apply if the paragraph starts with
// text and follows directly after another paragraph.
let indent = shared.get(ParNode::INDENT);
if !indent.is_zero()
&& children
.items()
.find_map(|child| {
if child.is::<TextNode>() || child.is::<SmartQuoteNode>() {
Some(true)
} else if child.has::<dyn LayoutInline>() {
Some(false)
} else {
None
}
})
.unwrap_or_default()
&& parent
.flow
.0
.items()
.rev()
.find(|child| child.has::<dyn LayoutBlock>())
.map_or(false, |child| child.is::<ParNode>())
{
children.push_front(HNode::strong(indent.into()).pack());
}
parent.flow.accept(&ParNode(children).pack(), shared);
} }
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {

View File

@ -393,6 +393,26 @@ fn collect<'a>(
let mut segments = vec![]; let mut segments = vec![];
let mut iter = par.0.iter().peekable(); let mut iter = par.0.iter().peekable();
let indent = styles.get(ParNode::INDENT);
if !indent.is_zero()
&& par
.0
.items()
.find_map(|child| {
if child.is::<TextNode>() || child.is::<SmartQuoteNode>() {
Some(true)
} else if child.has::<dyn LayoutInline>() {
Some(false)
} else {
None
}
})
.unwrap_or_default()
{
full.push(SPACING_REPLACE);
segments.push((Segment::Spacing(indent.into()), *styles));
}
while let Some((child, map)) = iter.next() { while let Some((child, map)) = iter.next() {
let styles = map.chain(styles); let styles = map.chain(styles);
let segment = if child.is::<SpaceNode>() { let segment = if child.is::<SpaceNode>() {