From 244ad386ec271ff86a2101eb4cc38d37a55552b9 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 15 Dec 2021 12:49:20 +0100 Subject: [PATCH] Set Rules Episode VI: Return of the Refactor --- src/eval/node.rs | 233 +++++++++++++++++------------------ src/library/flow.rs | 32 ++--- src/library/page.rs | 6 +- src/library/placed.rs | 7 -- tests/ref/layout/placed.png | Bin 31901 -> 33917 bytes tests/ref/layout/spacing.png | Bin 1827 -> 2412 bytes tests/typ/layout/placed.typ | 13 ++ tests/typ/layout/spacing.typ | 7 ++ 8 files changed, 151 insertions(+), 147 deletions(-) diff --git a/src/eval/node.rs b/src/eval/node.rs index 4aacf4c1b..5653beff1 100644 --- a/src/eval/node.rs +++ b/src/eval/node.rs @@ -81,7 +81,7 @@ impl Node { if let Node::Block(packed) = self { packed } else { - let mut packer = NodePacker::new(true); + let mut packer = Packer::new(false); packer.walk(self, Styles::new()); packer.into_block() } @@ -89,7 +89,7 @@ impl Node { /// Lift to a document node, the root of the layout tree. pub fn into_document(self) -> DocumentNode { - let mut packer = NodePacker::new(false); + let mut packer = Packer::new(true); packer.walk(self, Styles::new()); packer.into_document() } @@ -126,49 +126,37 @@ impl AddAssign for Node { } /// Packs a [`Node`] into a flow or whole document. -struct NodePacker { - /// Whether packing should produce a block-level node. - block: bool, +struct Packer { + /// Whether this packer produces the top-level document. + top: bool, /// The accumulated page nodes. pages: Vec, /// The accumulated flow children. - flow: Vec, - /// The common style properties of all items on the current flow. - flow_styles: Styles, - /// The kind of thing that was last added to the current flow. - flow_last: Last, + flow: Builder, /// The accumulated paragraph children. - par: Vec, - /// The common style properties of all items in the current paragraph. - par_styles: Styles, - /// The kind of thing that was last added to the current paragraph. - par_last: Last, + par: Builder, } -impl NodePacker { +impl Packer { /// Start a new node-packing session. - fn new(block: bool) -> Self { + fn new(top: bool) -> Self { Self { - block, + top, pages: vec![], - flow: vec![], - flow_styles: Styles::new(), - flow_last: Last::None, - par: vec![], - par_styles: Styles::new(), - par_last: Last::None, + flow: Builder::default(), + par: Builder::default(), } } /// Finish up and return the resulting flow. fn into_block(mut self) -> PackedNode { - self.finish_par(); - FlowNode(self.flow).pack() + self.parbreak(None); + FlowNode(self.flow.children).pack() } /// Finish up and return the resulting document. fn into_document(mut self) -> DocumentNode { - self.pagebreak(true); + self.pagebreak(); DocumentNode(self.pages) } @@ -176,34 +164,49 @@ impl NodePacker { fn walk(&mut self, node: Node, styles: Styles) { match node { Node::Space => { - if self.is_flow_compatible(&styles) && self.is_par_compatible(&styles) { - self.par_last.soft(ParChild::text(' ', styles)); - } + // A text space is "soft", meaning that it can be eaten up by + // adjacent line breaks or explicit spacings. + self.par.last.soft(ParChild::text(' ', styles)); } Node::Linebreak => { - self.par_last.hard(); + // A line break eats up surrounding text spaces. + self.par.last.hard(); self.push_inline(ParChild::text('\n', styles)); - self.par_last.hard(); + self.par.last.hard(); } Node::Parbreak => { + // An explicit paragraph break is styled according to the active + // styles (`Some(_)`) whereas paragraph breaks forced by + // incompatibility take their styles from the preceding + // paragraph. self.parbreak(Some(styles)); } Node::Pagebreak => { - self.pagebreak(true); - self.flow_styles = styles; + // We must set the flow styles after the page break such that an + // empty page created by two page breaks in a row has styles at + // all. + self.pagebreak(); + self.flow.styles = styles; } Node::Text(text) => { self.push_inline(ParChild::text(text, styles)); } Node::Spacing(SpecAxis::Horizontal, kind) => { - self.par_last.hard(); + // Just like a line break, explicit horizontal spacing eats up + // surrounding text spaces. + self.par.last.hard(); self.push_inline(ParChild::Spacing(SpacingNode { kind, styles })); - self.par_last.hard(); + self.par.last.hard(); } Node::Spacing(SpecAxis::Vertical, kind) => { - self.finish_par(); - self.flow.push(FlowChild::Spacing(SpacingNode { kind, styles })); - self.flow_last.hard(); + // Explicit vertical spacing ends the current paragraph and then + // discards the paragraph break. + self.parbreak(None); + self.make_flow_compatible(&styles); + self.flow + .children + .push(FlowChild::Spacing(SpacingNode { kind, styles })); + self.flow.last.hard(); } Node::Inline(inline) => { self.push_inline(ParChild::Node(inline.styled(styles))); @@ -212,6 +215,8 @@ impl NodePacker { self.push_block(block.styled(styles)); } Node::Sequence(list) => { + // For a list of nodes, we apply the list's styles to each node + // individually. for (node, mut inner) in list { inner.apply(&styles); self.walk(node, inner); @@ -222,22 +227,22 @@ impl NodePacker { /// Insert an inline-level element into the current paragraph. fn push_inline(&mut self, child: ParChild) { - if let Some(child) = self.par_last.any() { - self.push_inline_impl(child); + if let Some(child) = self.par.last.any() { + self.push_coalescing(child); } // The node must be both compatible with the current page and the // current paragraph. self.make_flow_compatible(child.styles()); self.make_par_compatible(child.styles()); - self.push_inline_impl(child); - self.par_last = Last::Any; + self.push_coalescing(child); + self.par.last.any(); } /// Push a paragraph child, coalescing text nodes with compatible styles. - fn push_inline_impl(&mut self, child: ParChild) { + fn push_coalescing(&mut self, child: ParChild) { if let ParChild::Text(right) = &child { - if let Some(ParChild::Text(left)) = self.par.last_mut() { + if let Some(ParChild::Text(left)) = self.par.children.last_mut() { if left.styles.compatible(&right.styles, TextNode::has_property) { left.text.push_str(&right.text); return; @@ -245,137 +250,122 @@ impl NodePacker { } } - self.par.push(child); + self.par.children.push(child); } /// Insert a block-level element into the current flow. fn push_block(&mut self, node: PackedNode) { - let mut is_placed = false; - if let Some(placed) = node.downcast::() { - is_placed = true; - - // This prevents paragraph spacing after the placed node if it - // is completely out-of-flow. - if placed.out_of_flow() { - self.flow_last = Last::None; - } - } + let placed = node.is::(); self.parbreak(None); self.make_flow_compatible(&node.styles); - - if let Some(child) = self.flow_last.any() { - self.flow.push(child); - } - - self.flow.push(FlowChild::Node(node)); + self.flow.children.extend(self.flow.last.any()); + self.flow.children.push(FlowChild::Node(node)); self.parbreak(None); - // This prevents paragraph spacing between the placed node and - // the paragraph below it. - if is_placed { - self.flow_last = Last::None; + // Prevent paragraph spacing between the placed node and the paragraph + // below it. + if placed { + self.flow.last.hard(); } } /// Advance to the next paragraph. fn parbreak(&mut self, break_styles: Option) { - let styles = break_styles.unwrap_or_else(|| self.par_styles.clone()); - self.finish_par(); + // Erase any styles that will be inherited anyway. + let Builder { mut children, styles, .. } = mem::take(&mut self.par); + for child in &mut children { + child.styles_mut().erase(&styles); + } - // Insert paragraph spacing. - self.flow_last.soft(FlowChild::Parbreak(styles)); - } + // For explicit paragraph breaks, `break_styles` is already `Some(_)`. + // For page breaks due to incompatibility, we fall back to the styles + // of the preceding paragraph. + let break_styles = break_styles.unwrap_or_else(|| styles.clone()); - fn finish_par(&mut self) { - let mut children = mem::take(&mut self.par); - let styles = mem::take(&mut self.par_styles); - self.par_last = Last::None; - - // No empty paragraphs. + // We don't want empty paragraphs. if !children.is_empty() { - // Erase any styles that will be inherited anyway. - for child in &mut children { - child.styles_mut().erase(&styles); - } - - if let Some(child) = self.flow_last.any() { - self.flow.push(child); - } - // The paragraph's children are all compatible with the page, so the // paragraph is too, meaning we don't need to check or intersect // anything here. - let node = ParNode(children).pack().styled(styles); - self.flow.push(FlowChild::Node(node)); + let par = ParNode(children).pack().styled(styles); + self.flow.children.extend(self.flow.last.any()); + self.flow.children.push(FlowChild::Node(par)); } + + // Insert paragraph spacing. + self.flow.last.soft(FlowChild::Break(break_styles)); } /// Advance to the next page. - fn pagebreak(&mut self, keep: bool) { - if self.block { - return; - } + fn pagebreak(&mut self) { + if self.top { + self.parbreak(None); - self.finish_par(); - - let styles = mem::take(&mut self.flow_styles); - let mut children = mem::take(&mut self.flow); - self.flow_last = Last::None; - - if keep || !children.is_empty() { - // Erase any styles that will be inherited anyway. + // Take the flow and erase any styles that will be inherited anyway. + let Builder { mut children, styles, .. } = mem::take(&mut self.flow); for child in &mut children { child.styles_mut().erase(&styles); } - let node = PageNode { node: FlowNode(children).pack(), styles }; - self.pages.push(node); + let flow = FlowNode(children).pack(); + let page = PageNode { child: flow, styles }; + self.pages.push(page); } } /// Break to a new paragraph if the `styles` contain paragraph styles that /// are incompatible with the current paragraph. fn make_par_compatible(&mut self, styles: &Styles) { - if self.par.is_empty() { - self.par_styles = styles.clone(); + if self.par.children.is_empty() { + self.par.styles = styles.clone(); return; } - if !self.is_par_compatible(styles) { + if !self.par.styles.compatible(&styles, ParNode::has_property) { self.parbreak(None); - self.par_styles = styles.clone(); + self.par.styles = styles.clone(); return; } - self.par_styles.intersect(&styles); + self.par.styles.intersect(&styles); } /// Break to a new page if the `styles` contain page styles that are /// incompatible with the current flow. fn make_flow_compatible(&mut self, styles: &Styles) { - if self.flow.is_empty() && self.par.is_empty() { - self.flow_styles = styles.clone(); + if self.flow.children.is_empty() && self.par.children.is_empty() { + self.flow.styles = styles.clone(); return; } - if !self.is_flow_compatible(styles) { - self.pagebreak(false); - self.flow_styles = styles.clone(); + if self.top && !self.flow.styles.compatible(&styles, PageNode::has_property) { + self.pagebreak(); + self.flow.styles = styles.clone(); return; } - self.flow_styles.intersect(styles); + self.flow.styles.intersect(styles); } +} - /// Whether the given styles are compatible with the current page. - fn is_par_compatible(&self, styles: &Styles) -> bool { - self.par_styles.compatible(&styles, ParNode::has_property) - } +/// Container for building a flow or paragraph. +struct Builder { + /// The intersection of the style properties of all `children`. + styles: Styles, + /// The accumulated flow or paragraph children. + children: Vec, + /// The kind of thing that was last added. + last: Last, +} - /// Whether the given styles are compatible with the current flow. - fn is_flow_compatible(&self, styles: &Styles) -> bool { - self.block || self.flow_styles.compatible(&styles, PageNode::has_property) +impl Default for Builder { + fn default() -> Self { + Self { + styles: Styles::new(), + children: vec![], + last: Last::None, + } } } @@ -383,6 +373,7 @@ impl NodePacker { enum Last { None, Any, + Hard, Soft(N), } @@ -401,6 +392,6 @@ impl Last { } fn hard(&mut self) { - *self = Self::None; + *self = Self::Hard; } } diff --git a/src/library/flow.rs b/src/library/flow.rs index eaa1811c2..de6ab8129 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -30,30 +30,30 @@ impl Debug for FlowNode { /// A child of a flow node. #[derive(Hash)] pub enum FlowChild { + /// A paragraph/block break. + Break(Styles), /// Vertical spacing between other children. Spacing(SpacingNode), /// An arbitrary node. Node(PackedNode), - /// A paragraph break. - Parbreak(Styles), } impl FlowChild { /// A reference to the child's styles. pub fn styles(&self) -> &Styles { match self { + Self::Break(styles) => styles, Self::Spacing(node) => &node.styles, Self::Node(node) => &node.styles, - Self::Parbreak(styles) => styles, } } /// A mutable reference to the child's styles. pub fn styles_mut(&mut self) -> &mut Styles { match self { + Self::Break(styles) => styles, Self::Spacing(node) => &mut node.styles, Self::Node(node) => &mut node.styles, - Self::Parbreak(styles) => styles, } } } @@ -61,14 +61,14 @@ impl FlowChild { impl Debug for FlowChild { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::Spacing(node) => node.fmt(f), - Self::Node(node) => node.fmt(f), - Self::Parbreak(styles) => { + Self::Break(styles) => { if f.alternate() { styles.fmt(f)?; } - write!(f, "Parbreak") + write!(f, "Break") } + Self::Spacing(node) => node.fmt(f), + Self::Node(node) => node.fmt(f), } } } @@ -132,6 +132,13 @@ impl<'a> FlowLayouter<'a> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for child in self.children { match child { + FlowChild::Break(styles) => { + let chain = styles.chain(&ctx.styles); + let amount = chain + .get(ParNode::SPACING) + .resolve(chain.get(TextNode::SIZE).abs); + self.layout_absolute(amount.into()); + } FlowChild::Spacing(node) => match node.kind { SpacingKind::Linear(v) => self.layout_absolute(v), SpacingKind::Fractional(v) => { @@ -146,13 +153,6 @@ impl<'a> FlowLayouter<'a> { self.layout_node(ctx, node); } - FlowChild::Parbreak(styles) => { - let chain = styles.chain(&ctx.styles); - let amount = chain - .get(ParNode::SPACING) - .resolve(chain.get(TextNode::SIZE).abs); - self.layout_absolute(amount.into()); - } } } @@ -248,7 +248,7 @@ impl<'a> FlowLayouter<'a> { output.push_frame(pos, frame); } FlowItem::Placed(frame) => { - output.push_frame(Point::with_y(offset), frame); + output.push_frame(Point::zero(), frame); } } } diff --git a/src/library/page.rs b/src/library/page.rs index 8905ffb65..4a4fab03a 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -62,7 +62,7 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult { #[derive(Hash)] pub struct PageNode { /// The node producing the content. - pub node: PackedNode, + pub child: PackedNode, /// The page's styles. pub styles: Styles, } @@ -116,7 +116,7 @@ impl PageNode { }; // Pad the child. - let padded = PadNode { child: self.node.clone(), padding }.pack(); + let padded = PadNode { child: self.child.clone(), padding }.pack(); // Layout the child. let expand = size.map(Length::is_finite); @@ -143,7 +143,7 @@ impl Debug for PageNode { self.styles.fmt(f)?; } f.write_str("Page(")?; - self.node.fmt(f)?; + self.child.fmt(f)?; f.write_str(")") } } diff --git a/src/library/placed.rs b/src/library/placed.rs index 3e2e1b26c..589a299bc 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -51,13 +51,6 @@ impl Layout for PlacedNode { let target = regions.expand.select(regions.current, Size::zero()); Rc::make_mut(frame).resize(target, Align::LEFT_TOP); - // Place relative to parent's base origin by offsetting our elements by - // the negative cursor position. - if out_of_flow { - let offset = (regions.current - regions.base).to_point(); - Rc::make_mut(frame).translate(offset); - } - // Set base constraint because our pod size is base and exact // constraints if we needed to expand or offset. *cts = Constraints::new(regions.expand); diff --git a/tests/ref/layout/placed.png b/tests/ref/layout/placed.png index 10a0bfd47a4da6c1815ed3ecd6b22688414d4b7e..4fdb1f704de4e75a8f51db222e2d54a17713305f 100644 GIT binary patch delta 15829 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 R|F!F$f{f}LnAF>#{{eFz{2%}T diff --git a/tests/ref/layout/spacing.png b/tests/ref/layout/spacing.png index c653df6d78d3951cb2ddd4abf17009fca1a5cc1d..6c7a4e63d48953355c3e2a612485b3fdc6cafdd4 100644 GIT binary patch literal 2412 zcmbVOc{J2}8)r=XvP?6!F|v%oSgvI($(m`#Fm85=;);-UkR>inwn6qKTS$p(-Zw&v zU0%i|Lye3?mMry#YD}^dU3EI=zW2TL{_&pk-Oh8K^L@V0^ZlI9=Si`#G85nx=jGtw z5HKg2*l}=hg7@b%uy5eFTD+yt!EsQ_+~l}JOyrZ=)lYfXBda%48(-T^Nnz< zL8s&u>WW_kR^KfGsc&;I@222$GPF2F>u5CYvX#W$=L4Cf(u32cxPmn<#nfA0M!!LK z0s+85^J`I3F<(&`zpU z=Nq0$sqA*p=~UW1b{N)LEcXKbtLwqu?3!w^ZP&Y?h!g)|4_K zMm&~P=zKHr;Os@js0(xXwN-14_F^aF9EF?b3KuwuOF?Ny;sVx!Xc+~HB)D@$9p_6!1D}X=%@)^2IE6%>mgtkpEVZB4T0FOw&4<&^QKqTWK=`5FGblKCb>4VTl{=(oaq#1$2+QPR^QV^UGj`sB+#$Ff zZ^Q{ab1uwT4GjnL92HhnF=rfb`{aPg#dA7@`f1!tke%n9$a!!eRi;i~COluGG7way zz!qX&b*`8zzu$E3u_g{mbEUY*{tdt1Q2j}p<}#=*Jsu@b+BV8M#`F3iASV#p#E{Qq z5@ifF4Pov6;!WPqE@uM1^<~gT+knCq)$GgZ(+wF~qZi~`GFe-C*vQUE-z$02}gLWiOyZf`Vxg?$KBggxUzuR_zy z0a$r8Xwec^F9y=9um(hh=u1Or>?MA8w0W3sl>LjK>7n2BWo3TnTry&cJ!Tk+Kqk%&Dbur10f$XGJ-kt5(uh_Hl?rd}_lALz5PM6}JF&A;U9_NZTE$83vy{$`rRr5Vj|`3yC?L9vr&ASUxiI@rrL7Y$U=>qEN-8j*1FNFW&$tsgO1hR9W*RoGS z(fZ|B%{r(P(vnvWFWy%G9fL1xP`NW#C1dx7_@CYQJEQz3&(QG5O7^LkE$}!Q2zRkw zdJ0I5?Dof-nTYYm7|$(S0lWs4^OK!-R@-H23fJF9$TaIFkxGe~_RS5}D?!JLW_1Iq zF=aN_{ID+c!x?c?32wvURdViD8A`!>@&K8zts9_Ts~C~->MpM}_lhmJGpGE~Cj)4I zoM$MC-fA8Wns>5%EKRsVP;PH!8!F=@ib8velV*E_h-rRgNL*FxBXo_^AF)zq#~Lj1Qf( z*s{@Eq0nMV(a&z*d)>OhCX8JSLeg)eM{l)Hs!izx?r6Vcv_XA>J2KEH?ep!|hbU%d zNWGGh5v2cp8j_e>(fzzj^^vfWtXQ%9-M z3fVlalHo4c`AFALl|#|c)>N1MolQU@Fxdfb@v;_9=B~3@=}$>@5VL)wTTR3BQ!V+) zMu1~8YkIQ3I~qnTyS>Ry-TMdNV*g8Lm7y}o`|8hKo*_RSdTae>aepA2|Jz}<5JmAi zT_8W;yR!eFsr9+VK6{X4)O=|hs_6xD2Rc`9wL8bVTOQ1}pHoO5>E)+ml$Cob13*bh z*UL7p*w8>Y?PXg+XkcJJ9-iz3$uzXomC45J0|y`IuSy2b#xM2(;S-Id6!g)A4OI4O zub78L;Y9e&y&v)8zUoiM-Tc-N#AFpe`Tz2wLce~Pt?<9uw)ao}0FSuMGZfPN)5&%7 z%8QVxw0f4n(eh*3@fRT@jB<6@b_H9aRb_x+kPM1sGRnvG@}7NJ9So8?OmwBm^G_Q| zInMbXa<1cfSQ5qFKY}7y=8|gs*Y+m$qyA?uBZ+ delta 1691 zcmV;M24wl{5~B`~7GnVK2mk;805O;KBLDyf>PbXFRCwC$-O+KgI1~lo(*bk<9Y6=7 z13a?@;0|yH;%Q$^2c!f34&)A00wt(~vkyWuv1hXroMc1Dxle{*kNE-@A+BUVp-?Ck z3WY+UcxUjJg_F?;6qBF`1b^^Nl3MY~dQ(gge`aByE+L9#MWyd@cj3HSTfxKcO9n3;@n1Ak1hY6k}^Sxgv zo<%TZKu?nBNr#zaKzEN2w^%6-dL*Fdx})okSLPuxb~(wmIA~%thNLMM@`^d_cwGA$ zVaCjW>yZ(Bfu=;yw-88X9VaY@MreC$J^u@#&skS{C-~UaA+?;|LLfb^m=29llM=Ci77dNM12`hTD+Z0gnhI-H><3LRf}n87^a37cRqJ82}W2 zkC%7@84<=IkbD^~W1xizVZ02>Q4r{G&8q^rCpn=o16oh!I=(3Vz)syVt$&gopoN$c@XG|oU%#%w|lWZ`Pp*jk}n&>CYWFScoOJ3tO z!o|}eHjS3pK$^Pj*8%LO~*Sd*}py=$PSL2Q|B}3 zVtP0VLfVlN(*M`NrnRA7uMQzA14DKqNp}hiSrfCwQ4r9b|GYHl$p!VF+e@1@6WQYO zXRP5;XA*jHLi6V~D+nCMCPM-2_Z4?<1s@YolVAP>2vlz z2IRd6Bn^PK_9a$nq##@PVXBEGCle-UdKChcJ^Q+BCc=@78B@DQ6^v zSm53v)i{D5uwlgeZ|plM94c9>3kTQ)60sm zT$;1rgy3Qg+c1$FrZ6_A`KKVvaO~Gv7;6-!=n-e~MrB~PAlNjADM^^RG8xW41feTP zAItXQjB?jlkCk~cm}e0HM}`jb&p^m~l2uKTWtDOQf9IGc=4TP&DHR^BJ9|Zlg*jI& zsSGdfK$=wRV+b_mk3eYAG)+_Enrg5<7Xd#3A)qkE7?YK0D!$)_kS1az*~7UHF*I7B~;|~}#k8JTY{D z@unvU;|c|SuondL-DThEV-%C22o@^BS0j*g8>huthK=84*jVc_ zZTv!R_$UHtH};6Stw+?>nKm{~eswqdm6Op36crG{|Anx%XKC3w8>#*3o+bNHgd>NO z(Fhcipa=vT@YM)g`xu{k$CjAfXT9UAdYhko$Cfbmv)&<-Km!#A@Kp%vSvq=?kq8zd ldp-?Ck3Wee^{s9`Zd%w$IzSIB!002ovPDHLkV1n&n0L%aY diff --git a/tests/typ/layout/placed.typ b/tests/typ/layout/placed.typ index 334ff80d2..017cdc20e 100644 --- a/tests/typ/layout/placed.typ +++ b/tests/typ/layout/placed.typ @@ -1,3 +1,6 @@ +// Test the `place` function. + +--- #page("a8") #place(bottom + center)[© Typst] @@ -20,3 +23,13 @@ the line breaks still had to be inserted manually. #place(center, dx: 7pt, dy: 5pt)[Hello] Hello #h(1fr) Hello ] + +--- +// Test how the placed node interacts with paragraph spacing around it. +#page("a8", height: 60pt) + +First + +#place(bottom + right)[Placed] + +Second diff --git a/tests/typ/layout/spacing.typ b/tests/typ/layout/spacing.typ index 7cf6760fa..78b778c5b 100644 --- a/tests/typ/layout/spacing.typ +++ b/tests/typ/layout/spacing.typ @@ -17,6 +17,13 @@ Add #h(10pt) #h(10pt) up // Fractional. | #h(1fr) | #h(2fr) | #h(1fr) | +--- +// Test that spacing has style properties. + +A[#par(align: right)#h(1cm)]B +[#page(height: 20pt)#v(1cm)] +B + --- // Missing spacing. // Error: 11-13 missing argument: spacing