Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use line height based spacing model #4236

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
};
Comment on lines -177 to -182
Copy link
Contributor Author

@Enter-tainer Enter-tainer May 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anyidea what is tight leading? Is it a workaround or something?

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)
Loading