diff --git a/crates/typst/src/layout/flow/compose.rs b/crates/typst/src/layout/flow/compose.rs index 003e4e72c..6f14618ee 100644 --- a/crates/typst/src/layout/flow/compose.rs +++ b/crates/typst/src/layout/flow/compose.rs @@ -1,6 +1,8 @@ use std::num::NonZeroUsize; -use super::{distribute, Config, FlowResult, PlacedChild, Skip, Stop, Work}; +use super::{ + distribute, Config, FlowResult, LineNumberConfig, PlacedChild, Skip, Stop, Work, +}; use crate::diag::SourceResult; use crate::engine::Engine; use crate::foundations::{Content, NativeElement, Packed, Resolve, Smart}; @@ -13,7 +15,7 @@ use crate::layout::{ OuterHAlignment, PlacementScope, Point, Region, Regions, Rel, Size, }; use crate::model::{ - FootnoteElem, FootnoteEntry, LineNumberingScope, Numbering, ParLine, ParLineMarker, + FootnoteElem, FootnoteEntry, LineNumberingScope, Numbering, ParLineMarker, }; use crate::syntax::Span; use crate::utils::NonZeroExt; @@ -198,10 +200,11 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> { let mut output = insertions.finalize(self.work, self.config, inner); // Lay out per-column line numbers. - if self.config.root { + if let Some(line_config) = &self.config.line_numbers { layout_line_numbers( self.engine, self.config, + line_config, locator, self.column, &mut output, @@ -647,6 +650,7 @@ impl<'a, 'b> Insertions<'a, 'b> { fn layout_line_numbers( engine: &mut Engine, config: &Config, + line_config: &LineNumberConfig, locator: Locator, column: usize, output: &mut Frame, @@ -654,9 +658,7 @@ fn layout_line_numbers( let mut locator = locator.split(); // Reset page-scoped line numbers if currently at the first column. - if column == 0 - && ParLine::numbering_scope_in(config.shared) == LineNumberingScope::Page - { + if column == 0 && line_config.scope == LineNumberingScope::Page { let reset = layout_line_number_reset(engine, config, &mut locator)?; output.push_frame(Point::zero(), reset); } @@ -711,9 +713,11 @@ fn layout_line_numbers( .resolve(config.shared) }; - // Compute the marker's horizontal position. Will be adjusted based on - // the maximum number width later. - let clearance = marker.number_clearance.resolve(config.shared); + // Determine how much space to leave between the column and the number. + let clearance = match marker.number_clearance { + Smart::Auto => line_config.default_clearance, + Smart::Custom(rel) => rel.resolve(config.shared), + }; // Compute the base X position. let x = match margin { diff --git a/crates/typst/src/layout/flow/mod.rs b/crates/typst/src/layout/flow/mod.rs index 8a10b05dd..5db70ecb2 100644 --- a/crates/typst/src/layout/flow/mod.rs +++ b/crates/typst/src/layout/flow/mod.rs @@ -19,14 +19,14 @@ use self::compose::{compose, Composer}; use self::distribute::distribute; use crate::diag::{bail, At, SourceDiagnostic, SourceResult}; use crate::engine::{Engine, Route, Sink, Traced}; -use crate::foundations::{Content, Packed, StyleChain}; +use crate::foundations::{Content, Packed, Resolve, StyleChain}; use crate::introspection::{ Introspector, Location, Locator, LocatorLink, SplitLocator, Tag, }; use crate::layout::{ - Abs, Dir, Fragment, Frame, PlacementScope, Region, Regions, Rel, Size, + Abs, Dir, Em, Fragment, Frame, PageElem, PlacementScope, Region, Regions, Rel, Size, }; -use crate::model::{FootnoteElem, FootnoteEntry}; +use crate::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine}; use crate::realize::{realize, Arenas, Pair, RealizationKind}; use crate::text::TextElem; use crate::utils::{NonZeroExt, Numeric}; @@ -187,6 +187,18 @@ pub(crate) fn layout_flow( gap: FootnoteEntry::gap_in(shared), expand: regions.expand.x, }, + line_numbers: root.then(|| LineNumberConfig { + scope: ParLine::numbering_scope_in(shared), + default_clearance: { + let width = if PageElem::flipped_in(shared) { + PageElem::height_in(shared) + } else { + PageElem::width_in(shared) + }; + (0.026 * width.unwrap_or_default()) + .clamp(Em::new(0.75).resolve(shared), Em::new(2.5).resolve(shared)) + }, + }), }; // Collect the elements into pre-processed children. These are much easier @@ -311,6 +323,8 @@ struct Config<'x> { columns: ColumnConfig, /// Settings for footnotes. footnote: FootnoteConfig, + /// Settings for line numbers. + line_numbers: Option, } /// Configuration of footnotes. @@ -338,6 +352,14 @@ struct ColumnConfig { dir: Dir, } +/// Configuration of line numbers. +struct LineNumberConfig { + /// Where line numbers are reset. + scope: LineNumberingScope, + /// The default clearance for `auto`. + default_clearance: Abs, +} + /// The result type for flow layout. /// /// The `Err(_)` variant incorporate control flow events for finishing and diff --git a/crates/typst/src/model/par.rs b/crates/typst/src/model/par.rs index 2e23bd74c..b50fca8bf 100644 --- a/crates/typst/src/model/par.rs +++ b/crates/typst/src/model/par.rs @@ -7,7 +7,7 @@ use crate::foundations::{ StyleVec, Unlabellable, }; use crate::introspection::{Count, CounterUpdate, Locatable}; -use crate::layout::{Abs, Em, HAlignment, Length, OuterHAlignment}; +use crate::layout::{Em, HAlignment, Length, OuterHAlignment}; use crate::model::Numbering; use crate::utils::singleton; @@ -219,17 +219,27 @@ impl Unlabellable for Packed {} /// /// This element is exclusively used for line number configuration and cannot /// be placed. -#[elem(name = "line", title = "Paragraph Line", Construct, Locatable)] +/// +/// ```example +/// >>> #set page(margin: (left: 3em)) +/// #set par.line(numbering: "1") +/// +/// Roses are red. \ +/// Violets are blue. \ +/// Typst is there for you. +/// ``` +#[elem(name = "line", title = "Paragraph Line", keywords = ["line numbering"], Construct, Locatable)] pub struct ParLine { /// How to number each line. Accepts a /// [numbering pattern or function]($numbering). /// /// ```example - /// #set par.line(numbering: "1") + /// >>> #set page(margin: (left: 3em)) + /// #set par.line(numbering: "I") /// /// Roses are red. \ /// Violets are blue. \ - /// Typst is awesome. + /// Typst is there for you. /// ``` #[ghost] pub numbering: Option, @@ -241,6 +251,7 @@ pub struct ParLine { /// the current text direction. /// /// ```example + /// >>> #set page(margin: (left: 3em)) /// #set par.line(numbering: "I", number-align: left) /// /// Hello world! \ @@ -253,6 +264,7 @@ pub struct ParLine { /// The margin at which line numbers appear. /// /// ```example + /// >>> #set page(margin: (right: 3em)) /// #set par.line(numbering: "1", number-margin: right) /// /// = Report @@ -265,10 +277,14 @@ pub struct ParLine { /// The distance between line numbers and text. /// + /// The default value of `{auto}` results in a clearance that is adaptive to + /// the page width and yields reasonable results in most cases. + /// /// ```example + /// >>> #set page(margin: (left: 3em)) /// #set par.line( /// numbering: "1", - /// number-clearance: 0.5pt + /// number-clearance: 4pt /// ) /// /// Typesetting \ @@ -276,8 +292,8 @@ pub struct ParLine { /// Layout /// ``` #[ghost] - #[default(Length::from(Abs::cm(1.0)))] - pub number_clearance: Length, + #[default] + pub number_clearance: Smart, /// Controls when to reset line numbering. /// @@ -285,8 +301,9 @@ pub struct ParLine { /// is never reset, or `"page"`, indicating it is reset on every page. /// /// ```example + /// >>> #set page(margin: (left: 3em)) /// #set par.line( - /// numbering: "1.", + /// numbering: "1", /// numbering-scope: "page" /// ) /// @@ -339,7 +356,7 @@ pub struct ParLineMarker { #[internal] #[required] - pub number_clearance: Length, + pub number_clearance: Smart, } impl Construct for ParLineMarker { diff --git a/tests/ref/line-numbers-default-alignment.png b/tests/ref/line-numbers-default-alignment.png index 4445ddf30..c2e9f3d4b 100644 Binary files a/tests/ref/line-numbers-default-alignment.png and b/tests/ref/line-numbers-default-alignment.png differ diff --git a/tests/ref/line-numbers-enable.png b/tests/ref/line-numbers-enable.png index 0437624e3..95088f23f 100644 Binary files a/tests/ref/line-numbers-enable.png and b/tests/ref/line-numbers-enable.png differ diff --git a/tests/ref/line-numbers-page-scope.png b/tests/ref/line-numbers-page-scope.png index 0cdffd28d..4b29cd4b8 100644 Binary files a/tests/ref/line-numbers-page-scope.png and b/tests/ref/line-numbers-page-scope.png differ diff --git a/tests/ref/line-numbers-rtl.png b/tests/ref/line-numbers-rtl.png index 6e0fef837..d4d9dc916 100644 Binary files a/tests/ref/line-numbers-rtl.png and b/tests/ref/line-numbers-rtl.png differ diff --git a/tests/ref/line-numbers-start-alignment.png b/tests/ref/line-numbers-start-alignment.png index 3f7a65234..9df2c6ef0 100644 Binary files a/tests/ref/line-numbers-start-alignment.png and b/tests/ref/line-numbers-start-alignment.png differ diff --git a/tests/suite/layout/line-numbers.typ b/tests/suite/layout/line-numbers.typ index 5ee53e25a..25f96d98d 100644 --- a/tests/suite/layout/line-numbers.typ +++ b/tests/suite/layout/line-numbers.typ @@ -1,5 +1,5 @@ --- line-numbers-enable --- -#set page(margin: (left: 1.5cm)) +#set page(margin: (left: 2.5em)) #set par.line(numbering: "1") First line \ @@ -23,13 +23,13 @@ Second line \ Third line --- line-numbers-default-alignment --- -#set page(margin: (left: 2cm)) +#set page(margin: (left: 3em)) #set par.line(numbering: "1") a #([\ a] * 15) --- line-numbers-start-alignment --- -#set page(margin: (left: 2cm)) +#set page(margin: (left: 3em)) #set par.line(numbering: "i", number-align: start) a \ a @@ -47,7 +47,7 @@ Second line \ Third line --- line-numbers-rtl --- -#set page(margin: (right: 2cm)) +#set page(margin: (right: 3em)) #set text(dir: rtl) #set par.line(numbering: "1") a @@ -120,7 +120,7 @@ In the \ Sky --- line-numbers-page-scope --- -#set page(margin: (left: 2cm)) +#set page(margin: (left: 2.5em)) #set par.line(numbering: "1", numbering-scope: "page") First line \