Skip to content

Commit

Permalink
Use line height based spacing model
Browse files Browse the repository at this point in the history
  • Loading branch information
Enter-tainer committed Jun 16, 2024
1 parent 9a45d94 commit 89fd452
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 71 deletions.
10 changes: 8 additions & 2 deletions crates/typst/src/layout/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ impl<'a> FlowLayouter<'a> {
styles: StyleChain,
) -> SourceResult<()> {
let align = AlignElem::alignment_in(styles).resolve(styles);
let leading = ParElem::leading_in(styles);
let line_height = ParElem::line_height_in(styles);
let consecutive = self.last_was_par;
let lines = par
.layout(
Expand Down Expand Up @@ -303,8 +303,14 @@ impl<'a> FlowLayouter<'a> {
}
}

for (i, mut frame) in lines.into_iter().enumerate() {
for (i, mut frame) in lines.clone().into_iter().enumerate() {
if i > 0 {
let leading =
if lines[i - 1].descent().abs() + frame.ascent() > line_height {
Abs::pt(1.0)
} else {
line_height - lines[i - 1].descent().abs() - frame.ascent()
};
self.layout_item(engine, FlowItem::Absolute(leading, true))?;
}

Expand Down
21 changes: 15 additions & 6 deletions crates/typst/src/layout/inline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ struct Preparation<'a> {
cjk_latin_spacing: bool,
/// Whether font fallback is enabled for this paragraph.
fallback: bool,
/// The leading of the paragraph.
leading: Abs,
/// The line height of the paragraph.
line_height: Abs,
/// How to determine line breaks.
linebreaks: Smart<Linebreaks>,
/// The text size.
Expand Down Expand Up @@ -682,7 +682,7 @@ fn prepare<'a>(
hang: ParElem::hanging_indent_in(styles),
cjk_latin_spacing,
fallback: TextElem::fallback_in(styles),
leading: ParElem::leading_in(styles),
line_height: ParElem::line_height_in(styles),
linebreaks: ParElem::linebreaks_in(styles),
size: TextElem::size_in(styles),
})
Expand Down Expand Up @@ -1292,7 +1292,7 @@ fn finalize(
if frames.len() >= 2 && !frames[1].is_empty() {
let second = frames.remove(1);
let first = &mut frames[0];
merge(first, second, p.leading);
merge(first, second, p.line_height, true);
}
}
if p.costs.widow().get() > 0.0 {
Expand All @@ -1301,19 +1301,28 @@ fn finalize(
if len >= 2 && !frames[len - 2].is_empty() {
let second = frames.pop().unwrap();
let first = frames.last_mut().unwrap();
merge(first, second, p.leading);
merge(first, second, p.line_height, false);
}
}

Ok(Fragment::frames(frames))
}

/// Merge two line frames
fn merge(first: &mut Frame, second: Frame, leading: Abs) {
fn merge(first: &mut Frame, second: Frame, line_height: Abs, based_on_second: bool) {
let leading = if first.descent().abs() + second.ascent().abs() > line_height {
Abs::pt(1.0)
} else {
line_height - first.descent().abs() - second.ascent().abs()
};
let offset = first.height() + leading;
let total = offset + second.height();
let second_baseline = offset + second.baseline();
first.push_frame(Point::with_y(offset), second);
first.size_mut().y = total;
if based_on_second {
first.set_baseline(second_baseline);
}
}

/// Commit to a line and build its frame.
Expand Down
11 changes: 3 additions & 8 deletions crates/typst/src/math/equation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::layout::{
use crate::math::{
scaled_font_size, LayoutMath, MathContext, MathRunFrameBuilder, MathSize, MathVariant,
};
use crate::model::{Numbering, Outlinable, ParElem, Refable, Supplement};
use crate::model::{Numbering, Outlinable, Refable, Supplement};
use crate::syntax::Span;
use crate::text::{
families, variant, Font, FontFamily, FontList, FontWeight, LocalName, TextElem,
Expand Down Expand Up @@ -297,13 +297,8 @@ fn layout_equation_inline(
let InlineItem::Frame(frame) = item else { continue };

let font_size = scaled_font_size(&ctx, styles);
let slack = ParElem::leading_in(styles) * 0.7;
let top_edge = TextElem::top_edge_in(styles).resolve(font_size, &font, None);
let bottom_edge =
-TextElem::bottom_edge_in(styles).resolve(font_size, &font, None);

let ascent = top_edge.max(frame.ascent() - slack);
let descent = bottom_edge.max(frame.descent() - slack);
let ascent = TextElem::top_edge_in(styles).resolve(font_size, &font, None);
let descent = -TextElem::bottom_edge_in(styles).resolve(font_size, &font, None);
frame.translate(Point::with_y(ascent - frame.baseline()));
frame.size_mut().y = ascent + descent;
}
Expand Down
29 changes: 14 additions & 15 deletions crates/typst/src/math/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ use std::iter::once;
use unicode_math_class::MathClass;

use crate::foundations::{Resolve, StyleChain};
use crate::layout::{Abs, AlignElem, Em, Frame, InlineItem, Point, Size};
use crate::math::{
alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext,
MathFragment, MathSize,
};
use crate::layout::{Abs, AlignElem, Frame, InlineItem, Point, Size};
use crate::math::{alignments, spacing, FrameFragment, MathContext, MathFragment};
use crate::model::ParElem;

use super::fragment::SpacingFragment;

pub const TIGHT_LEADING: Em = Em::new(0.25);

/// A linear collection of [`MathFragment`]s.
#[derive(Debug, Default, Clone)]
pub struct MathRun(Vec<MathFragment>);
Expand Down Expand Up @@ -170,27 +165,31 @@ impl MathRun {
ctx: &MathContext,
styles: StyleChain,
) -> MathRunFrameBuilder {
let _ = ctx;
let rows: Vec<_> = self.rows();
let row_count = rows.len();
let alignments = alignments(&rows);

let leading = if EquationElem::size_in(styles) >= MathSize::Text {
ParElem::leading_in(styles)
} else {
let font_size = scaled_font_size(ctx, styles);
TIGHT_LEADING.at(font_size)
};
let line_height = ParElem::line_height_in(styles);

let align = AlignElem::alignment_in(styles).resolve(styles).x;
let mut frames: Vec<(Frame, Point)> = vec![];
let mut size = Size::zero();
for (i, row) in rows.into_iter().enumerate() {
for (i, row) in rows.clone().into_iter().enumerate() {
if i == row_count - 1 && row.0.is_empty() {
continue;
}

let sub = row.into_line_frame(&alignments.points, LeftRightAlternator::Right);
let sub = row
.clone()
.into_line_frame(&alignments.points, LeftRightAlternator::Right);
if i > 0 {
let leading =
if rows[i - 1].descent().abs() + row.ascent().abs() > line_height {
Abs::pt(1.0)
} else {
line_height - rows[i - 1].descent().abs() - row.ascent().abs()
};
size.y += leading;
}

Expand Down
10 changes: 6 additions & 4 deletions crates/typst/src/model/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
Length, Regions, Sizing, VAlignment, VElem,
Abs, Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter,
HAlignment, Length, Regions, Sizing, Spacing, VAlignment, VElem,
};
use crate::model::{Numbering, NumberingPattern, ParElem};
use crate::text::TextElem;
Expand Down Expand Up @@ -223,7 +223,8 @@ impl Show for Packed<EnumElem> {
.spanned(self.span());

if self.tight(styles) {
let leading = ParElem::leading_in(styles);
// TODO(mgt): fix this
let leading: Abs = ParElem::line_height_in(styles);
let spacing = VElem::list_attach(leading.into()).pack();
realized = spacing + realized;
}
Expand All @@ -246,7 +247,8 @@ fn layout_enum(
let body_indent = elem.body_indent(styles);
let gutter = elem.spacing(styles).unwrap_or_else(|| {
if elem.tight(styles) {
ParElem::leading_in(styles).into()
// TODO(mgt): fix this
ParElem::line_height_in(styles).into()
} else {
ParElem::spacing_in(styles).into()
}
Expand Down
4 changes: 2 additions & 2 deletions crates/typst/src/model/footnote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ impl Show for Packed<FootnoteEntry> {
impl ShowSet for Packed<FootnoteEntry> {
fn show_set(&self, _: StyleChain) -> Styles {
let text_size = Em::new(0.85);
let leading = Em::new(0.5);
let line_height = Em::new(1.158203125);
let mut out = Styles::new();
out.set(ParElem::set_leading(leading.into()));
out.set(ParElem::set_line_height(line_height.into()));
out.set(TextElem::set_size(TextSize(text_size.into())));
out
}
Expand Down
6 changes: 4 additions & 2 deletions crates/typst/src/model/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ impl Show for Packed<ListElem> {
.spanned(self.span());

if self.tight(styles) {
let leading = ParElem::leading_in(styles);
// TODO(mgt): fix this
let leading = ParElem::line_height_in(styles);
let spacing = VElem::list_attach(leading.into()).pack();
realized = spacing + realized;
}
Expand All @@ -169,7 +170,8 @@ fn layout_list(
let body_indent = elem.body_indent(styles);
let gutter = elem.spacing(styles).unwrap_or_else(|| {
if elem.tight(styles) {
ParElem::leading_in(styles).into()
// TODO(mgt): fix this
ParElem::line_height_in(styles).into()
} else {
ParElem::spacing_in(styles).into()
}
Expand Down
33 changes: 3 additions & 30 deletions crates/typst/src/model/par.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,11 @@ use crate::realize::StyleVec;
/// ```
#[elem(title = "Paragraph", Debug, Construct)]
pub struct ParElem {
/// The spacing between lines.
///
/// Leading defines the spacing between the [bottom edge]($text.bottom-edge)
/// of one line and the [top edge]($text.top-edge) of the following line. By
/// default, these two properties are up to the font, but they can also be
/// configured manually with a text set rule.
///
/// By setting top edge, bottom edge, and leading, you can also configure a
/// consistent baseline-to-baseline distance. You could, for instance, set
/// the leading to `{1em}`, the top-edge to `{0.8em}`, and the bottom-edge
/// to `-{0.2em}` to get a baseline gap of exactly `{2em}`. The exact
/// distribution of the top- and bottom-edge values affects the bounds of
/// the first and last line.
#[resolve]
#[ghost]
#[default(Em::new(0.65).into())]
pub leading: Length,

/// The spacing between paragraphs.
///
/// Just like leading, this defines the spacing between the bottom edge of a
/// paragraph's last line and the top edge of the next paragraph's first
/// line.
///
/// When a paragraph is adjacent to a [`block`] that is not a paragraph,
/// that block's [`above`]($block.above) or [`below`]($block.below) property
/// takes precedence over the paragraph spacing. Headings, for instance,
/// reduce the spacing below them by default for a better look.
/// The spacing between baselines.
#[resolve]
#[ghost]
#[default(Em::new(1.2).into())]
pub spacing: Length,
#[default(Em::new(1.308203125).into())]
pub line_height: Length,

/// Whether to justify text in its line.
///
Expand Down
6 changes: 4 additions & 2 deletions crates/typst/src/model/terms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ impl Show for Packed<TermsElem> {
let hanging_indent = self.hanging_indent(styles);
let gutter = self.spacing(styles).unwrap_or_else(|| {
if self.tight(styles) {
ParElem::leading_in(styles).into()
// TODO(mgt): fix this
ParElem::line_height_in(styles).into()
} else {
ParElem::spacing_in(styles).into()
}
Expand Down Expand Up @@ -149,7 +150,8 @@ impl Show for Packed<TermsElem> {
.padded(padding);

if self.tight(styles) {
let leading = ParElem::leading_in(styles);
// TODO(mgt): fix this
let leading = ParElem::line_height_in(styles);
let spacing = VElem::list_attach(leading.into()).pack();
realized = spacing + realized;
}
Expand Down
Binary file added tests/ref/par-inline-math-overlap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions tests/suite/model/par.typ
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,10 @@ Welcome \ here. Does this work well?
#set text(dir: rtl)
لآن وقد أظلم الليل وبدأت النجوم
تنضخ وجه الطبيعة التي أعْيَتْ من طول ما انبعثت في النهار

--- par-inline-math-overlap ---
#lorem(1) $1/123456$ #lorem(2) $12345667/1/1$

#lorem(3) $sum_(x=0)^n$ #lorem(3)

#lorem(3) $display((a+b)/(c+d))$ #lorem(2) $display((a/b+1)/c+1)$ #lorem(7)

0 comments on commit 89fd452

Please sign in to comment.