How can I hide page numbers according to my criteria?

I’m typesetting a document with multiple short stories. Each story has a title page on a recto, possibly with a quotation or poem on the corresponding verso, and the document has a bit of frontmatter. I want to hide page numbering:

  • Before the first story starts
  • On title pages
  • On blank pages
  • On verso quotes opposite title pages

I’ve achieved the first by doing this in the footer function:

#set page(
  footer: context {
    let abspage = locate(here()).page();
    if abspage < 2 {
      // nop
    } else if abspage == 2 {
      counter(page).update(0)
    } // snip, see below
  }
)

It feels like a bit of a hack, but it works. I’m updating the page counter to zero before the title page itself because otherwise the outline function doesn’t update correctly.

I’m hiding page numbers on title pages like so:

#set page(
 footer: context {
   let abspage = locate(here()).page();
   let has_h1 = query(heading.where(level: 1)).any(it => it.location().page() == abspage);
   if /*snip*/ {
     // snip, see above
   } else if not h1 {
     counter(page).display("1")
   }
 }
)

I’d worry about generalizing this, because if I were querying a lot of elements on every page it could get slow, but there are so few level one headers I think it’s fine. Happy to hear if there’s a better way, though.

I don’t think I can just #set page(numbering: ...) because I needed to set an explicit footer to handle the page counter. I guess I could do that in the header instead and just manually set the page numbering at the start and end of each short story, but it seems like there might be a better way to do it.

I would want to try to set up a query like I did for checking for the title pages, but I don’t think I can for the blank pages because I need to query for any element, and I don’t think that’s possible from what I can tell; and I don’t think I can for the verso quotations/poems because they’re not “locatable” elements (I could wrap them in a figure, I suppose…).

To suppress headers and footers on otherwise empty versos, I use:

// see https://forum.typst.app/t/how-can-i-suppress-headers-and-footers-on-blank-verso-trailing-pages/4384/9
// and referenced git page for limitations
#show selector.or(
  pagebreak.where(to: "odd"),
  pagebreak.where(to: "even"),
): set page(header: none, footer: none)

To place folios bottom center on h1 pages, and in the top margins on other content pages, I use:

#import "@preview/hydra:0.6.2": hydra
#let hydraver = "0.6.2"

// consider changing quads to nuts if page numbering goes
//    over 999, or creat a variable that adapts automatically
//    as page count grows
#let folio_recto(pageno) = context {
  box(width:0em, height: 1em)[
    #place(dx: marginalia.get-right().sep)[
      #box(height: 1em)[|]
      #box(height: 1em, width: marginalia.get-right().width)[
        #align(right)[#(counter(page).display())
        #sym.space.en]
      ]
    ]
  ]
}
#let folio_verso(pageno) = context {
  box(width:0em)[
    #align(right)[
      #place(dx: 0em - marginalia.get-left().width - marginalia.get-left().sep)[
        #sym.space.en
        #(counter(page).display())
      ]
      |
      #h(marginalia.get-left().sep)
    ]
  ]
}
// Why the 3pt offest on verso?
#let placefolio(pageno, is-odd) = {
  let headerbase = 2 * LINESIZE
  if is-odd {
    place(top + right,
      dy: headerbase - 6pt,
      folio_recto(pageno))
  } else {
    place(top + left,
      dy: headerbase - 3pt,
      folio_verso(pageno))
  }
}
// for the header test is-start-chapter, see
//   https://github.com/typst/typst/issues/1613#issuecomment-2437415579
#set page(
  footer-descent: 0pt,
  footer: context {
    let chapters = query(
      selector(heading.where(level: 1)).before(here())
    )
    let is-start-chapter() = chapters.len() > 0 and chapters.last().location().page() == here().page()
    if is-start-chapter() {
      align(center, counter(page).display())
     }
  },
  header: context {
    let is-start-chapter() = query(
      heading.where(level: 1).after(here())
    )
      .map(h => h.location().page())
      .at(0, default: 0) == here().page()
    if not is-start-chapter() {// always not start
      placefolio(
        counter(page).display(),
        calc.odd(here().page()))
      if calc.odd(here().page()) {
        h(1fr)
        hydra(2, display: (_, it) => { emph(it.body)})
      } else {
        hydra(1, display: (_, it) => { emph(it.body)})
        h(1fr)
      }
    }
  },
)

(LINESIZE is elsewhere defined as the FONTSIZE plus leading. I find the default Typst settings for these unusable and struggle to come up with a good alternative, but for now use

#set text(
  font: "Libertinus Serif",
  number-type: "old-style",
  size: FONTSIZE,
  top-edge: LINESIZE,
  bottom-edge: LINESIZE,
  hyphenate: true,
  costs: (hyphenation: 70%),
  lang: "en",
)

)

You can obviously chose not to place the folio on h1 pages.

Chapter epigraphs are simple. Just insert something like this before the chapter heading to which it applies:

#[
  #pagebreak(to: "even")
  #set page(
    footer: none,
    header: none)

  #v(1fr)

  #chapterEpigraph(
    label: <qAmis-K01>,
    width: 80%,
    attribution: [Kingsley Amis],
    note: [Kingsley Amis in "Letters to the Editor" in #emph[The
      Times] (of London) (1983, 11) on February 22],
    )[
      Sir. No, "more means worse" was not what I said 20 years ago
      about the expansion of higher education (leader, February 18).
      What I wrote in 1960 was "more will mean worse." I think the
      difference is substantial, but let that go for now. You show
      by your misquotation that you couldn't be bothered to look up
      the reference, thereby ignoring the context, any arguments or
      evidence put forward, etc.

      Having garbled my remark you say roundly that in the event I
      was wrong. Not altogether, perhaps. Laziness and incuriosity
      about sources are familiar symptoms of academic decline.
      #endnote[The Kingsey Amis letter was quoted, in part, by Nigel
        Rees in BrFQ at 8·2. The letter was in response to #emph[The
        Times] misquoting "MORE WILL MEAN WORSE" as "more is worse".
        According to Rees, Amis used the phrase in a July 1960
        #emph[Encounter] magazine article in reference to the
        expansion of higher education and what Amis described as the
        "delusion that there are thousands of young people who are
        capable of benefiting from university training but have
        somehow failed to find their way there." The phrase was in
        running caps in that article. Amis clearly intended it to
        indicate a shouted statement. How much of the letter is the
        result of editing by the paper and how much is Amis' work is
        not clear. Newspapers typically reserve the right to make
        changes that they feel improve the letter without notice to
        the author.

        Despite Amis' snarky protestation, the phrase "more means
        less" continues to be associated with him.]
      ]
  #v(2fr)
]

Where the chapterEpigraph function is whatever you want it to be to format the epigraph.

There may well be more efficient ways to accomplish all of these. I am just working my way into many parts of Typst.

1 Like

That worked, thanks!

If anyone else is struggling as I was, I realized I was having trouble with #set page() because it wasn’t the first element on the page (so it created a new one). It now works for the epigraph function because I just integrated the vertical spacing I wanted into it, but wasn’t before because I was keeping that separate and using another function that I reused within the body of a chapter. So the epigraph function now looks like:

#let epigraph(body, ..params) = {
  set page(footer: none);
  v(1fr) + central(body, ..params) + v(1fr)
}

where central is the function I was using previously.