How can you switch between "I," "1," and no page number in an elegant way?

I have a document that uses roman page numbers for the preface, numeric ones for the main matter and no page number for title pages in between the main matter. I would like to switch between these three states without setting the footer “from scratch” each time, like so:

#set page(width: 50mm, height: 50mm)
#show heading.where(level: 1): it => {
  pagebreak(weak: true)
  it
}

#let page-number(shown, format: none) = {
  context {
    if shown {
      set page(footer: align(center, counter(page).display(format))) 
    }
  }
}

#page-number(true, format: "I")
= Preface: Roman Page Number

#counter(page).update(1)

#page-number(true, format: "1")
= Main Section: Numeric Page Number

#page-number(false)
= Blank Page: No Page Number

#page-number(true, format: "1")
= Main Section: Numeric Page Number

But this example doesn’t work (this displays no page numbers at all), I suspect because context doesn’t work that way. How could I refactor the code to get it to work?

Simplest version that does the “same thing” (morally) of that code is to use page numbering:

#set page(numbering: "I")
= Preface: Roman Page Number

#counter(page).update(1)

#set page(numbering: "1")
= Main Section: Numeric Page Number

#set page(numbering: none)
= Blank Page: No Page Number

#set page(numbering: "1")
= Main Section: Numeric Page Number

If you want to configure the footer once and for all you can do that, but not necessary for this example.

#set page(footer: {
  context align(right)[
    #let pgn = here().page-numbering()
    #if pgn != none {
      counter(page).display(pgn)
    }
  ]
})

The set page rule only works the usual way: it works in the scope where it is, for all the following code. So a show rule would be how to refactor it to be closer to the original code (but this is not better for this simple case)

#let page-number(shown, format: none, it) = {
  set page(footer: align(center, context counter(page).display(format))) if shown
  set page(footer: []) if not shown
  it
}

#show: page-number.with(true, format: "I")

As you can see the function has the item it which it expands in the scope where the set rules have been applied.

4 Likes

Thanks so much, this is exactly what I’ve been looking for and couldn’t get right, so I’d have to style/setup the footer only once. :pray:

Generally speaking, working with code in markup mode can play dirty tricks on you, and you have to use # a lot, so it’s better to avoid it. You’re also using an unnecessary {} to wrap the context. You can also apply an early return pattern and get this:

#set page(footer: context {
  let page-numbering = here().page-numbering()
  if page-numbering == none { return }
  align(right, counter(page).display(page-numbering))
})

Although with a formatter the number of lines will be the same, but the syntax and logic is a little easier to read through.

Ah, great, thank you for elaborating further! I will use this apporach as it is indeed a bit easier to read :smiling_face:

1 Like