From 2f390c53173707671387292b18cc40e4c3f12a60 Mon Sep 17 00:00:00 2001 From: bluebear94 Date: Mon, 6 May 2024 11:23:32 -0400 Subject: [PATCH] Add `auto` option for page headers/footers (#4051) Co-authored-by: Laurenz --- crates/typst/src/layout/page.rs | 54 ++++++++++++------ .../ref/page-suppress-headers-and-footers.png | Bin 0 -> 1339 bytes tests/suite/layout/page.typ | 9 +++ 3 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 tests/ref/page-suppress-headers-and-footers.png diff --git a/crates/typst/src/layout/page.rs b/crates/typst/src/layout/page.rs index f62218873..c354dc81a 100644 --- a/crates/typst/src/layout/page.rs +++ b/crates/typst/src/layout/page.rs @@ -228,6 +228,11 @@ pub struct PageElem { /// The page's header. Fills the top margin of each page. /// + /// - Content: Shows the content as the header. + /// - `{auto}`: Shows the page number if a `numbering` is set and + /// `number-align` is `top`. + /// - `{none}`: Suppresses the header. + /// /// ```example /// #set par(justify: true) /// #set page( @@ -242,7 +247,7 @@ pub struct PageElem { /// #lorem(19) /// ``` #[borrowed] - pub header: Option, + pub header: Smart>, /// The amount the header is raised into the top margin. #[resolve] @@ -251,8 +256,13 @@ pub struct PageElem { /// The page's footer. Fills the bottom margin of each page. /// - /// For just a page number, the `numbering` property, typically suffices. If - /// you want to create a custom footer, but still display the page number, + /// - Content: Shows the content as the footer. + /// - `{auto}`: Shows the page number if a `numbering` is set and + /// `number-align` is `bottom`. + /// - `{none}`: Suppresses the footer. + /// + /// For just a page number, the `numbering` property typically suffices. If + /// you want to create a custom footer but still display the page number, /// you can directly access the [page counter]($counter). /// /// ```example @@ -273,7 +283,7 @@ pub struct PageElem { /// #lorem(48) /// ``` #[borrowed] - pub footer: Option, + pub footer: Smart>, /// The amount the footer is lowered into the bottom margin. #[resolve] @@ -402,17 +412,15 @@ impl Packed { } let fill = self.fill(styles); - let foreground = Cow::Borrowed(self.foreground(styles)); - let background = Cow::Borrowed(self.background(styles)); + let foreground = self.foreground(styles); + let background = self.background(styles); let header_ascent = self.header_ascent(styles); let footer_descent = self.footer_descent(styles); let numbering = self.numbering(styles); let number_align = self.number_align(styles); - let mut header = Cow::Borrowed(self.header(styles)); - let mut footer = Cow::Borrowed(self.footer(styles)); // Construct the numbering (for header or footer). - let numbering_marginal = Cow::Owned(numbering.as_ref().map(|numbering| { + let numbering_marginal = numbering.as_ref().map(|numbering| { let both = match numbering { Numbering::Pattern(pattern) => pattern.pieces() >= 2, Numbering::Func(_) => true, @@ -433,13 +441,21 @@ impl Packed { } counter - })); + }); - if matches!(number_align.y(), Some(OuterVAlignment::Top)) { - header = if header.is_some() { header } else { numbering_marginal }; + let header = self.header(styles); + let footer = self.footer(styles); + let (header, footer) = if matches!(number_align.y(), Some(OuterVAlignment::Top)) { + ( + header.as_ref().unwrap_or(&numbering_marginal), + footer.as_ref().unwrap_or(&None), + ) } else { - footer = if footer.is_some() { footer } else { numbering_marginal }; - } + ( + header.as_ref().unwrap_or(&None), + footer.as_ref().unwrap_or(&numbering_marginal), + ) + }; // Post-process pages. let mut pages = Vec::with_capacity(frames.len()); @@ -463,16 +479,16 @@ impl Packed { let size = frame.size(); // Realize overlays. - for marginal in [&header, &footer, &background, &foreground] { - let Some(content) = &**marginal else { continue }; + for marginal in [header, footer, background, foreground] { + let Some(content) = marginal.as_ref() else { continue }; let (pos, area, align); - if ptr::eq(marginal, &header) { + if ptr::eq(marginal, header) { let ascent = header_ascent.relative_to(margin.top); pos = Point::with_x(margin.left); area = Size::new(pw, margin.top - ascent); align = Alignment::BOTTOM; - } else if ptr::eq(marginal, &footer) { + } else if ptr::eq(marginal, footer) { let descent = footer_descent.relative_to(margin.bottom); pos = Point::new(margin.left, size.y - margin.bottom + descent); area = Size::new(pw, margin.bottom - descent); @@ -490,7 +506,7 @@ impl Packed { .layout(engine, styles, pod)? .into_frame(); - if ptr::eq(marginal, &header) || ptr::eq(marginal, &background) { + if ptr::eq(marginal, header) || ptr::eq(marginal, background) { frame.prepend_frame(pos, sub); } else { frame.push_frame(pos, sub); diff --git a/tests/ref/page-suppress-headers-and-footers.png b/tests/ref/page-suppress-headers-and-footers.png new file mode 100644 index 0000000000000000000000000000000000000000..6ce4f7214bc4c90d115202ddc4b9e609fad4a0f0 GIT binary patch literal 1339 zcmV-B1;qM^P)rYb$7zS|vjU{G@Gj0=^L7hS8kce&)GH;vWWr{elgBF5_WdapCK&Y0RT!fYtiiLuV zt8!m*K;%vpwIK6S3$zpvY2_ll(9>rn$cTKwGD|Z(@24kk)BKY5ocF`C0ydfz6imSs z`~kz7TiRuUMV}SC1#cW#CK#*m*0_x>7yKmvRHnxaqbd2yGczots{x?%P>oMv3T?zB z<~INUlzWNb7`*0Y3IRymOV8XyU0EU^&I#clU!1lSSakr>AE$O;cpKb$6Uf;Q*7tSq z1h-64t8<%O9I1cokn|x(r7Xf^?`$CG`C%MhkbZopkFfJ0rdH--ri^bo)%==V%-gE!HCRR)yq#tVaGaGq}x zI~+}kDdUiGQ-&W0)TsQW=XwB%UwvL+A3L%?tehp`mjKTLbEJM%hQy2t0B{hbDUg(z z9k1I@Kb!|XtMYvukQ=52i)sxP%*MG8-vWd#-{oprmH#G5ylxaJs9XL2f~W7kvJI?p z=~(zAg)R#WpUwq~3J)v5MVtc0BRNe?oCm8Y;X-S0DSWO(v_q2}uWCM<$r}=cz37g# z0{m)?y7D);4r2pyJ+ZIG=nS7m-Rg-Wt(o5Vv}Z7`xJNUVX$4s8f(}3IeE~-l#^U5- zIL`dkJLM}oJA2I;gL}LTXtM%5_AQ!dINq&ZO^P`6-wLl?!>!;*a zfb%_`CVLM_g0jyLAxt?j5S!kQo-;|$2=`K?3oh&KCsCJ+f+?7SKVsNcL;c4T zOu-cV-r=q?{ak!q{Sp>USEFUHXbmqpVth8=);AO9Tfe_jli zbqgcZz{tVsuuyQ)Cx2~fIdO|tmP8wueZyx@&J=A1T88^XRwnOV;MCyUU}6;+&L<@kKKWgjjQvY!waLC^@h z(S*Q>>FuxYzYEOH^8G`pgc+{QI#vTmGzR_)a|g2U?w9>Faw{?sX!a2nkkQ>pE`|Bf8HdZ49a-oUyRt; z9PS-PYgSM{i=&DZ)dGB0)vJr)>cJ@9{r`GEE*HEBFX=$y$g*87&Hj^Pv&nQSy||IO z)f7y@ixh0=NlxnTIQ8ICI3jbc56>5DMrRB<;pl^ogWwsh56>5Dy}N;R