mirror of
https://github.com/typst/typst
synced 2025-05-19 19:45:29 +08:00
Fix double leading 🐞
This commit is contained in:
parent
88da325c7d
commit
652986fd58
@ -1,6 +1,8 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use xi_unicode::LineBreakIterator;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::exec::FontProps;
|
use crate::exec::FontProps;
|
||||||
|
|
||||||
@ -87,6 +89,7 @@ struct ParLayouter {
|
|||||||
line: Vec<(Length, Frame, Align)>,
|
line: Vec<(Length, Frame, Align)>,
|
||||||
line_size: Size,
|
line_size: Size,
|
||||||
line_ruler: Align,
|
line_ruler: Align,
|
||||||
|
hard: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParLayouter {
|
impl ParLayouter {
|
||||||
@ -101,6 +104,7 @@ impl ParLayouter {
|
|||||||
line: vec![],
|
line: vec![],
|
||||||
line_size: Size::ZERO,
|
line_size: Size::ZERO,
|
||||||
line_ruler: Align::Start,
|
line_ruler: Align::Start,
|
||||||
|
hard: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,45 +114,50 @@ impl ParLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_text(&mut self, ctx: &mut LayoutContext, node: &TextNode, align: Align) {
|
fn push_text(&mut self, ctx: &mut LayoutContext, node: &TextNode, align: Align) {
|
||||||
// Text shaped up to the previous line break opportunity that can fit
|
|
||||||
// the current line.
|
|
||||||
let mut last = None;
|
|
||||||
|
|
||||||
// Position in the text at which the current line starts.
|
// Position in the text at which the current line starts.
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
|
|
||||||
let iter = xi_unicode::LineBreakIterator::new(&node.text);
|
// The current line attempt: Text shaped up to the previous line break
|
||||||
for (pos, mandatory) in iter {
|
// opportunity.
|
||||||
while start != pos {
|
let mut last = None;
|
||||||
|
|
||||||
|
let mut iter = LineBreakIterator::new(&node.text).peekable();
|
||||||
|
while let Some(&(pos, mandatory)) = iter.peek() {
|
||||||
let part = &node.text[start .. pos].trim_end();
|
let part = &node.text[start .. pos].trim_end();
|
||||||
let frame = shape(part, &mut ctx.env.fonts, &node.props);
|
let frame = shape(part, &mut ctx.env.fonts, &node.props);
|
||||||
|
|
||||||
if self.usable().fits(frame.size) {
|
if self.usable().fits(frame.size) {
|
||||||
|
// Still fits into the line.
|
||||||
if mandatory {
|
if mandatory {
|
||||||
// We have to break here.
|
// We have to break here.
|
||||||
self.push_frame(frame, align);
|
self.push_frame(frame, align);
|
||||||
self.finish_line();
|
self.finish_line(true);
|
||||||
start = pos;
|
start = pos;
|
||||||
last = None;
|
last = None;
|
||||||
} else {
|
} else {
|
||||||
// Still fits into the line.
|
|
||||||
last = Some((frame, pos));
|
last = Some((frame, pos));
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Some((frame, pos)) = last.take() {
|
||||||
// Doesn't fit into the line. If `last` was `None`, the single
|
// The line start..pos doesn't fit. So we write the line up to
|
||||||
// unbreakable piece of text didn't fit, but we can't break it
|
// the last position and retry writing just the single piece
|
||||||
// up further, so we just have to push it.
|
// behind it.
|
||||||
let (frame, pos) = last.take().unwrap_or((frame, pos));
|
|
||||||
self.push_frame(frame, align);
|
self.push_frame(frame, align);
|
||||||
self.finish_line();
|
self.finish_line(false);
|
||||||
start = pos;
|
start = pos;
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
// 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.finish_line(false);
|
||||||
|
start = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
iter.next();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leftovers.
|
||||||
if let Some((frame, _)) = last {
|
if let Some((frame, _)) = last {
|
||||||
self.push_frame(frame, align);
|
self.push_frame(frame, align);
|
||||||
}
|
}
|
||||||
@ -168,12 +177,12 @@ impl ParLayouter {
|
|||||||
// | Second |
|
// | Second |
|
||||||
// +----------------------------+
|
// +----------------------------+
|
||||||
if self.line_ruler > align {
|
if self.line_ruler > align {
|
||||||
self.finish_line();
|
self.finish_line(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find out whether the area still has enough space for this frame.
|
// Find out whether the area still has enough space for this frame.
|
||||||
if !self.usable().fits(frame.size) {
|
if !self.usable().fits(frame.size) && self.line_size.width > Length::ZERO {
|
||||||
self.finish_line();
|
self.finish_line(false);
|
||||||
|
|
||||||
// Here, we can directly check whether the frame fits into
|
// Here, we can directly check whether the frame fits into
|
||||||
// `areas.current` since we just called `finish_line`.
|
// `areas.current` since we just called `finish_line`.
|
||||||
@ -207,7 +216,11 @@ impl ParLayouter {
|
|||||||
usable
|
usable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_line(&mut self) {
|
fn finish_line(&mut self, hard: bool) {
|
||||||
|
if !mem::replace(&mut self.hard, hard) && self.line.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let full_size = {
|
let full_size = {
|
||||||
let expand = self.areas.expand.horizontal;
|
let expand = self.areas.expand.horizontal;
|
||||||
let full = self.areas.full.width;
|
let full = self.areas.full.width;
|
||||||
@ -266,7 +279,7 @@ impl ParLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish(mut self) -> Vec<Frame> {
|
fn finish(mut self) -> Vec<Frame> {
|
||||||
self.finish_line();
|
self.finish_line(false);
|
||||||
self.finish_area();
|
self.finish_area();
|
||||||
self.finished
|
self.finished
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user