Page function docs and more

This commit is contained in:
Martin Haug 2022-12-21 12:01:28 +01:00
parent 633d0b62c3
commit 959df6da3b
12 changed files with 330 additions and 94 deletions

View File

@ -43,7 +43,7 @@ pub fn lorem(args: &mut Args) -> SourceResult<Value> {
/// ## Example /// ## Example
/// ``` /// ```
/// #numbering("1.1)", 1, 2, 3) \ /// #numbering("1.1)", 1, 2, 3) \
/// #numbering("1.b.i", 1, 2) \ /// #numbering("1.a.i", 1, 2) \
/// #numbering("I 1", 12, 2) /// #numbering("I 1", 12, 2)
/// ``` /// ```
/// ///

View File

@ -32,6 +32,7 @@ use crate::prelude::*;
/// ///
/// ### Example /// ### Example
/// ``` /// ```
/// #set page(height: 6cm)
/// #set text(lang: "ar") /// #set text(lang: "ar")
/// ///
/// مثال /// مثال

View File

@ -4,7 +4,7 @@ use crate::text::TextNode;
/// # Columns /// # Columns
/// Separate a region into multiple equally sized columns. /// Separate a region into multiple equally sized columns.
/// ///
/// The column function allows to separate the interior of any container into /// The `column` function allows to separate the interior of any container into
/// multiple columns. It will not equalize the height of the columns, instead, /// multiple columns. It will not equalize the height of the columns, instead,
/// the columns will take up the height of their container or the remaining /// the columns will take up the height of their container or the remaining
/// height on the page. The columns function can break across pages if /// height on the page. The columns function can break across pages if
@ -12,8 +12,7 @@ use crate::text::TextNode;
/// ///
/// ## Example /// ## Example
/// ``` /// ```
/// = [An extraordinarily /// = Towards Advanced Deep Learning
/// long heading]
/// ///
/// #box(height: 68pt, /// #box(height: 68pt,
/// columns(2, gutter: 11pt)[ /// columns(2, gutter: 11pt)[
@ -176,7 +175,7 @@ impl Layout for ColumnsNode {
/// ///
/// ## Parameters /// ## Parameters
/// - weak: bool (named) /// - weak: bool (named)
/// If true, the column break is skipped if the current column is already /// If `{true}`, the column break is skipped if the current column is already
/// empty. /// empty.
/// ///
/// ## Category /// ## Category

View File

@ -5,10 +5,14 @@ use crate::prelude::*;
/// # Box /// # Box
/// An inline-level container that sizes content. /// An inline-level container that sizes content.
/// ///
/// Normally, all elements except inline math, text, and boxes are block-level /// All elements except inline math, text, and boxes are block-level and cannot
/// and cannot occur inside of a paragraph. The box element allows you to create /// occur inside of a paragraph. The box element is an inline-level container.
/// inline-level containers. Boxes take the size of their contents by default /// Boxes take the size of their contents by default but can also be sized
/// but can also be sized explicitly. /// explicitly.
///
/// _Note:_ While the behavior above will be the default in the future, the
/// transformation functions [`scale`](@scale), [`rotate`](@rotate), and
/// [`move`](@move) will currently yield inline nodes within paragraphs.
/// ///
/// ## Example /// ## Example
/// ``` /// ```
@ -102,7 +106,7 @@ impl Inline for BoxNode {}
/// A block-level container that places content into a separate flow. /// A block-level container that places content into a separate flow.
/// ///
/// This can be used to force elements that would otherwise be inline to become /// This can be used to force elements that would otherwise be inline to become
/// block-level. This is especially useful when writing `{show}` rules. /// block-level. This is especially useful when writing show rules.
/// ///
/// ## Example /// ## Example
/// ``` /// ```

View File

@ -12,81 +12,76 @@ use super::Spacing;
/// ///
/// The sizing of the grid is determined by the track sizes specified in the /// The sizing of the grid is determined by the track sizes specified in the
/// arguments. Because each of the sizing parameters accepts the same values, we /// arguments. Because each of the sizing parameters accepts the same values, we
/// will explain them here: /// will explain them here. Each sizing argument accepts an array of track
/// sizes. A track size is either:
/// ///
/// Each sizing argument accepts an array of track sizes. A track size is either /// - a fixed length (e.g. `{10pt}`). The track will be exactly this size.
/// - a fixed length (e.g. `10pt`). The track will be exactly this size.
/// - `{auto}`. The track will be sized to fit its contents. It will be at most /// - `{auto}`. The track will be sized to fit its contents. It will be at most
/// as large as the remaining space. If there is more than one `auto` track /// as large as the remaining space. If there is more than one `{auto}` track
/// that, together, claim more than the available space, they will be resized /// which, together, claim more than the available space, the tracks will be
/// to fit the available space. /// resized to fit the available space.
/// - a `fractional` length (e.g. `{1fr}`). Once all other tracks have been /// - a fractional length (e.g. `{1fr}`). Once all other tracks have been sized,
/// sized, the remaining space will be divided among the fractional tracks /// the remaining space will be divided among the fractional tracks according
/// according to their fraction. For example, if there are two fractional /// to their fraction. For example, if there are two fractional tracks, each
/// tracks, each with a fraction of `1`, they will each take up half of the /// with a fraction of `{1fr}`, they will each take up half of the remaining
/// remaining space. /// space.
/// ///
/// To specify a single track, the array can be omitted in favor of a single /// To specify a single track, the array can be omitted in favor of a single
/// value. To specify multiple `{auto}` tracks, enter the number of tracks /// value. To specify multiple `{auto}` tracks, enter the number of tracks
/// instead of a value. For example, `columns: {3}` is equivalent to /// instead of a value. For example, `columns:` `{3}` is equivalent to
/// `columns: {(auto, auto, auto)}`. /// `columns:` `{(auto, auto, auto)}`.
/// ///
/// ## Example /// ## Example
/// ``` /// ```
/// #set text(hyphenate: true) /// #set text(10pt, weight: "bold")
/// #let cell = rect.with( /// #let cell = rect.with(
/// inset: 6pt, /// inset: 8pt,
/// fill: rgb("e4e5ea"), /// fill: rgb("e4e5ea"),
/// width: 100%, /// width: 100%,
/// radius: 6pt /// radius: 6pt
/// ) /// )
/// /// #grid(
/// #grid( /// columns: (60pt, 1fr, 60pt),
/// columns: (60pt, 1fr, 60pt), /// rows: (60pt, auto),
/// rows: (60pt, auto), /// gutter: 3pt,
/// gutter: 3pt, /// cell(height: 100%)[Easy to learn],
/// cell(height: 100%)[*Easy to learn*], /// cell(height: 100%)[Great output],
/// cell(height: 100%)[Great output], /// cell(height: 100%)[Intuitive],
/// cell(height: 100%)[*Intuitive*], /// cell[Our best Typst yet],
/// cell[*Our best Typst yet*], /// cell[
/// cell[ /// Responsive design in print
/// Responsive design in print /// for everyone
/// for everyone /// ],
/// ], /// cell[One more thing...],
/// cell[*One more thing...*], /// )
/// )
/// ``` /// ```
/// ///
/// ## Parameters /// ## Parameters
/// - cells: Content (positional, variadic) /// - cells: Content (positional, variadic) The contents of the table cells.
/// The contents of the table cells.
/// ///
/// The cells are populated in row-major order. /// The cells are populated in row-major order.
/// ///
/// - rows: TrackSizings (named) /// - rows: TrackSizings (named) Defines the row sizes.
/// Defines the row sizes.
/// ///
/// If there are more cells than fit the defined rows, the last row is /// If there are more cells than fit the defined rows, the last row is
/// repeated until there are no more cells. /// repeated until there are no more cells.
/// ///
/// - columns: TrackSizings (named) /// - columns: TrackSizings (named) Defines the column sizes.
/// Defines the column sizes.
/// ///
/// Either specify a track size array or provide an /// Either specify a track size array or provide an integer to create a grid
/// integer to create a grid with that many `{auto}`-sized columns. Note that /// with that many `{auto}`-sized columns. Note that opposed to rows and
/// opposed to rows and gutters, providing a single track size will only ever /// gutters, providing a single track size will only ever create a single
/// create a single column. /// column.
/// ///
/// - gutter: TrackSizings (named) /// - gutter: TrackSizings (named) Defines the gaps between rows & columns.
/// Defines the gaps between rows & columns.
/// ///
/// If there are more gutters than defined sizes, the last gutter is repeated. /// If there are more gutters than defined sizes, the last gutter is repeated.
/// ///
/// - column-gutter: TrackSizings (named) /// - column-gutter: TrackSizings (named) Defines the gaps between columns.
/// Defines the gaps between columns. Takes precedence over `gutter`. /// Takes precedence over `gutter`.
/// ///
/// - row-gutter: TrackSizings (named) /// - row-gutter: TrackSizings (named) Defines the gaps between rows. Takes
/// Defines the gaps between rows. Takes precedence over `gutter`. /// precedence over `gutter`.
/// ///
/// ## Category /// ## Category
/// layout /// layout

View File

@ -4,9 +4,9 @@ use crate::prelude::*;
/// Hide content without affecting layout. /// Hide content without affecting layout.
/// ///
/// The `hide` function allows you to hide content while the layout still 'sees' /// The `hide` function allows you to hide content while the layout still 'sees'
/// it. This is useful to create layout items that are exactly as large as some /// it. This is useful to create to create whitespace that is exactly as large
/// content. It may also be useful to redact content because its arguments are /// as some content. It may also be useful to redact content because its
/// not included in the output. /// arguments are not included in the output.
/// ///
/// ## Example /// ## Example
/// ``` /// ```

View File

@ -11,7 +11,7 @@ use crate::prelude::*;
/// ``` /// ```
/// #set align(center) /// #set align(center)
/// ///
/// #pad(16pt, image("typing.jpg")) /// #pad(x: 16pt, image("typing.jpg"))
/// _Typing speeds can be /// _Typing speeds can be
/// measured in words per minute._ /// measured in words per minute._
/// ``` /// ```

View File

@ -7,12 +7,31 @@ use crate::text::TextNode;
/// # Page /// # Page
/// Layouts its child onto one or multiple pages. /// Layouts its child onto one or multiple pages.
/// ///
/// Although this function is primarily used in set rules to affect page
/// properties, it can also be used to explicitly render its argument onto
/// a set of pages of its own.
///
/// Pages can be set to use `{auto}` as their width or height. In this case,
/// the pages will grow to fit their content on the respective axis.
///
/// ## Parameters /// ## Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The contents of the page(s). /// The contents of the page(s).
/// ///
/// Multiple pages will be created if the content does not fit on a single
/// page. A new page with the page properties prior to the function invocation
/// will be created after the body has been typeset.
///
/// - paper: Paper (positional, settable) /// - paper: Paper (positional, settable)
/// The paper size. /// A standard paper size to set width and height. When this is not specified,
/// Typst defaults to `{"a4"}` paper.
///
/// ## Example
/// ```
/// #set page("us-letter", margin: auto)
///
/// There you go, US friends!
/// ```
/// ///
/// ## Category /// ## Category
/// layout /// layout
@ -24,33 +43,207 @@ pub struct PageNode(pub Content);
#[node] #[node]
impl PageNode { impl PageNode {
/// The unflipped width of the page. /// The unflipped width of the page.
///
/// # Example
/// ```
/// #set page(
/// width: 3cm,
/// margin: (x: 0cm),
/// )
///
/// #for i in range(3) {
/// box(square(width: 1cm))
/// }
/// ```
#[property(resolve)] #[property(resolve)]
pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width().into()); pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width().into());
/// The unflipped height of the page. /// The unflipped height of the page.
///
/// If this is set to `{auto}`, page breaks can only be triggered manually
/// by inserting a [page break](@pagebreak). Most examples throughout this
/// documentation use `{auto}` for the height of the page to dynamically
/// grow and shrink to fit their content.
#[property(resolve)] #[property(resolve)]
pub const HEIGHT: Smart<Length> = Smart::Custom(Paper::A4.height().into()); pub const HEIGHT: Smart<Length> = Smart::Custom(Paper::A4.height().into());
/// Whether the page is flipped into landscape orientation. /// Whether the page is flipped into landscape orientation.
///
/// # Example
/// ```
/// #set page(
/// "us-business-card",
/// flipped: true,
/// fill: rgb("f2e5dd"),
/// )
///
/// #set align(bottom + end)
/// #text(14pt)[*Sam H. Richards*] \
/// _Procurement Manager_
///
/// #set text(10pt)
/// 17 Main Street \
/// New York, NY 10001 \
/// +1 555 555 5555
/// ```
pub const FLIPPED: bool = false; pub const FLIPPED: bool = false;
/// The page's margins. /// The page's margins.
///
/// - A single length: The same margin on all sides.
/// - `{auto}`: The margin is set to the default value for the page's size.
/// - `{none}`: The page will be stripped of its margins.
/// - A dictionary: With a dictionary, the margins can be set individually.
/// The dictionary can contain the following keys in order of precedence:
/// - `top`: The top margin.
/// - `right`: The right margin.
/// - `bottom`: The bottom margin.
/// - `left`: The left margin.
/// - `x`: The horizontal margins.
/// - `y`: The vertical margins.
/// - `rest`: The margins on all sides except the sides for which the
/// dictionary explicitly sets a size.
///
/// # Example
/// ```
/// #set page(
/// width: 3cm,
/// height: 4cm,
/// margin: (x: 8pt, y: 4pt),
/// )
///
/// #rect(
/// width: 100%,
/// height: 100%,
/// fill: aqua,
/// )
/// ```
#[property(fold)] #[property(fold)]
pub const MARGIN: Sides<Option<Smart<Rel<Length>>>> = Sides::splat(Smart::Auto); pub const MARGIN: Sides<Option<Smart<Rel<Length>>>> = Sides::splat(Smart::Auto);
/// How many columns the page has. /// How many columns the page has.
///
/// # Example
/// ```
/// #set page(columns: 2, height: 4.8cm)
/// Climate change is one of the
/// most pressing issues of our
/// time, with the potential to
/// devastate communities,
/// ecosystems, and economies
/// around the world. It's clear
/// that we need to take urgent
/// action to reduce our carbon
/// emissions and mitigate the
/// impacts of a rapidly changing
/// climate.
/// ```
pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap(); pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap();
/// The page's background color. /// The page's background color.
///
/// This instructs the printer to color the complete page with the given
/// color. If you are considering larger production runs, it may be more
/// environmentally friendly and cost-effective to source pre-dyed pages and
/// not set this property.
///
/// # Example
/// ```
/// #set page(fill: rgb("444352"))
/// #set text(fill: rgb("fdfdfd"))
/// *Dark mode enabled.*
/// ```
pub const FILL: Option<Paint> = None; pub const FILL: Option<Paint> = None;
/// The page's header. /// The page's header.
///
/// The header is placed at in the top margin of each page.
///
/// - Content: The content will be placed in the header.
/// - A function: The function will be called with the page number (starting
/// at one) as its only argument. The content it returns will be placed in
/// the header.
/// - `{none}`: The header will be empty.
///
/// # Example
/// ```
/// #set par(justify: true)
/// #set page(
/// margin: (x: 24pt, y: 32pt),
/// header: align(horizon + right, text(8pt)[_Exercise Sheet 3_]),
/// )
///
/// #lorem(18)
/// ```
#[property(referenced)] #[property(referenced)]
pub const HEADER: Marginal = Marginal::None; pub const HEADER: Marginal = Marginal::None;
/// The page's footer. /// The page's footer.
///
/// The footer is placed at in the bottom margin of each page.
///
/// - Content: The content will be placed in the footer.
/// - A function: The function will be called with the page number (starting
/// at one) as its only argument. The content it returns will be placed in
/// the footer.
/// - `{none}`: The footer will be empty.
///
/// # Example
/// ```
/// #set par(justify: true)
/// #set page(
/// margin: (x: 24pt, y: 32pt),
/// footer: i => align(horizon + right,
/// text(8pt, numbering("I", i))
/// )
/// )
///
/// #lorem(18)
/// ```
#[property(referenced)] #[property(referenced)]
pub const FOOTER: Marginal = Marginal::None; pub const FOOTER: Marginal = Marginal::None;
/// Content in the page's background. /// Content in the page's background.
///
/// This content will be placed behind the page's body. It can be
/// used to place a background image or a watermark.
///
/// # Example
/// ```
/// #set page(
/// background: align(
/// center + horizon,
/// rotate(24deg,
/// text(18pt, fill: rgb("FFCBC4"))[*CONFIDENTIAL*]
/// )
/// ),
/// )
///
/// = Typst's secret plans
///
/// In the year 2023, we plan to take over the world
/// (of typesetting).
/// ```
#[property(referenced)] #[property(referenced)]
pub const BACKGROUND: Marginal = Marginal::None; pub const BACKGROUND: Marginal = Marginal::None;
/// Content in the page's foreground. /// Content in the page's foreground.
///
/// This content will overlay the page's body.
///
/// # Example
/// ```
/// #set page(
/// foreground: align(
/// center + horizon,
/// text(24pt)[🥸]
/// ),
/// )
///
/// Reviewer 2 has marked our paper
/// "Weak Reject" because they did
/// not understand our approach...
/// ```
#[property(referenced)] #[property(referenced)]
pub const FOREGROUND: Marginal = Marginal::None; pub const FOREGROUND: Marginal = Marginal::None;
@ -180,7 +373,7 @@ impl Debug for PageNode {
/// ///
/// ## Parameters /// ## Parameters
/// - weak: bool (named) /// - weak: bool (named)
/// If true, the page break is skipped if the current page is already empty. /// If `{true}`, the page break is skipped if the current page is already empty.
/// ///
/// ## Category /// ## Category
/// layout /// layout

View File

@ -152,8 +152,8 @@ castable! {
/// A paragraph break. /// A paragraph break.
/// ///
/// This starts a new paragraph. Especially useful when used within code like /// This starts a new paragraph. Especially useful when used within code like
/// [for loops](/docs/reference/concepts#for-loop). Paragraph breaks in an empty /// [for loops](/docs/reference/concepts#for-loop). Multiple consecutive
/// paragraph are ignored. /// paragraph breaks collapse into a single one.
/// ///
/// ## Example /// ## Example
/// ``` /// ```
@ -790,7 +790,12 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
// has minimum cost. All breakpoints before this one become // has minimum cost. All breakpoints before this one become
// inactive since no line can span above the mandatory break. // inactive since no line can span above the mandatory break.
active = k; active = k;
MIN_COST + if attempt.justify { ratio.powi(3).abs() } else { 0.0 } MIN_COST
+ if attempt.justify {
ratio.powi(3).abs()
} else {
0.0
}
} else { } else {
// Normal line with cost of |ratio^3|. // Normal line with cost of |ratio^3|.
ratio.powi(3).abs() ratio.powi(3).abs()

View File

@ -3,6 +3,22 @@ use crate::prelude::*;
/// # Repeat /// # Repeat
/// Repeats content to fill a line. /// Repeats content to fill a line.
/// ///
/// This can be useful when implementing a custom index, reference, or outline.
///
/// Space may be inserted between the instances of the body parameter, so be
/// sure to include negative space if you need the instances to overlap.
///
/// ## Example
/// ```
/// Sign on the dotted line: #repeat[.]
///
/// #set text(10pt)
/// #v(8pt, weak: true)
/// #align(right)[
/// Berlin, the 22nd of December, 2022
/// ]
/// ```
///
/// ## Parameters /// ## Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to repeat. /// The content to repeat.

View File

@ -5,9 +5,9 @@ use crate::prelude::*;
/// # Spacing (H) /// # Spacing (H)
/// Insert horizontal spacing into a paragraph. /// Insert horizontal spacing into a paragraph.
/// ///
/// The spacing can be a length or a `fractional`. In the latter case, the /// The spacing can be a length or a fractional. In the latter case, the
/// remaining space on the line is distributed among all fractional spacings /// remaining space on the line is distributed among all fractional spacings
/// according to their relative size. /// according to their relative fractions.
/// ///
/// ## Example /// ## Example
/// ``` /// ```
@ -35,7 +35,7 @@ use crate::prelude::*;
/// manifest in most cases. However, /// manifest in most cases. However,
/// when #h(8pt, weak: true) /// when #h(8pt, weak: true)
/// supported /// supported
/// #h(8pt, weak: true) on all /// #h(8pt, weak: true) on both
/// sides, they do show up. /// sides, they do show up.
/// ``` /// ```
/// ///
@ -100,9 +100,9 @@ impl Behave for HNode {
/// # Spacing (V) /// # Spacing (V)
/// Insert vertical spacing. /// Insert vertical spacing.
/// ///
/// The spacing can be a length or a `fractional`. In the latter case, the /// The spacing can be a length or a fractional. In the latter case, the
/// remaining space on the page is distributed among all fractional spacings /// remaining space on the page is distributed among all fractional spacings
/// according to their relative size. /// according to their relative fractions.
/// ///
/// ## Example /// ## Example
/// ``` /// ```
@ -115,9 +115,7 @@ impl Behave for HNode {
/// #v(5mm) /// #v(5mm)
/// ///
/// - Informed consent /// - Informed consent
///
/// - Participant confidentiality /// - Participant confidentiality
///
/// - The use of /// - The use of
/// vulnerable populations. /// vulnerable populations.
/// ``` /// ```
@ -127,23 +125,20 @@ impl Behave for HNode {
/// How much spacing to insert. /// How much spacing to insert.
/// ///
/// - weak: bool (named) /// - weak: bool (named)
/// If true, the spacing collapses at the start or end of a flow. /// If true, the spacing collapses at the start or end of a flow. Moreover,
/// Moreover, from multiple adjacent weak spacings all but the largest one /// from multiple adjacent weak spacings all but the largest one collapse.
/// collapse. /// Weak spacings will always collapse adjacent paragraph spacing, even if the
/// paragraph spacing is larger.
/// ///
/// ### Example /// ### Example
/// ``` /// ```
/// Only paragraph spacing /// The following theorem is
/// /// foundational to the field:
/// Override paragraph spacing /// #v(4pt, weak: true)
/// with weak space /// $ x^2 + y^2 = r^2 $
/// #v(7mm, weak: true) /// #v(4pt, weak: true)
/// /// The proof is simple:
/// Add to paragraph spacing /// ```
/// #v(7mm, weak: false)
///
/// A secret, fourth thing
/// ```
/// ## Category /// ## Category
/// layout /// layout
#[func] #[func]

View File

@ -5,7 +5,7 @@ use crate::prelude::*;
/// # Move /// # Move
/// Move content without affecting layout. /// Move content without affecting layout.
/// ///
/// The `move` function allows you to hide content while the layout still 'sees' /// The `move` function allows you to move content while the layout still 'sees'
/// it at the original positions. Containers will still be sized as if the content /// it at the original positions. Containers will still be sized as if the content
/// was not moved. /// was not moved.
/// ///
@ -100,9 +100,8 @@ impl Inline for MoveNode {}
/// ``` /// ```
/// { /// {
/// range(16) /// range(16)
/// .map(i => /// .map(i => rotate(24deg * i)[X])
/// rotate(360deg / 15 * i)[X] /// .join(h(1fr))
/// ).join(h(1fr))
/// } /// }
/// ``` /// ```
/// ///
@ -113,6 +112,11 @@ impl Inline for MoveNode {}
/// - angle: Angle (named) /// - angle: Angle (named)
/// The amount of rotation. /// The amount of rotation.
/// ///
/// ### Example
/// ```
/// #rotate(angle: -1.571rad)[To space!]
/// ```
///
/// ## Category /// ## Category
/// layout /// layout
#[func] #[func]
@ -128,6 +132,22 @@ pub struct RotateNode {
#[node] #[node]
impl RotateNode { impl RotateNode {
/// The origin of the rotation. /// The origin of the rotation.
///
/// By default, the origin is the center of the rotated element. If,
/// however, you wanted the bottom left corner of the rotated element to
/// stay aligned with the baseline, you would set the origin to `bottom +
/// left`.
///
/// # Example
/// ```
/// #set text(spacing: 8pt)
/// #let square = square.with(width: 8pt)
///
/// #square()
/// #rotate(angle: 30deg, origin: center, square())
/// #rotate(angle: 30deg, origin: top + left, square())
/// #rotate(angle: 30deg, origin: bottom + right, square())
/// ```
#[property(resolve)] #[property(resolve)]
pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default(); pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default();
@ -211,6 +231,14 @@ pub struct ScaleNode {
#[node] #[node]
impl ScaleNode { impl ScaleNode {
/// The origin of the transformation. /// The origin of the transformation.
///
/// By default, the origin is the center of the scaled element.
///
/// # Example
/// ```
/// A#scale(75%)[A]A \
/// B#scale(75%, origin: bottom + left)[B]B
/// ```
#[property(resolve)] #[property(resolve)]
pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default(); pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default();