"1 / 1" numbering: How to get correct max page number while allowing customization?

I am currently working on a template that has some template content after the users content. This leads to the max page number to be “wrong” in the content section when using “1 / 1” or similar as numbering format.

// content
#set page(numbering: "1 / 1")
#counter(page).update(1)


#lorem(9999)

// appendix
#set page(numbering: "i")
#counter(page).update(1)

#lorem(999)

This is a minimal version showing this problem: lorem(999) produces two pages, leading to the footer in the content section showing “x / 2”, even though the content section has 15 pages.

The easy solution would probably to use a label selector to find the last page of the content section and built the “1 / 1” numbering with a custom function. This is not viable in my situation, as the numbering should be customizable by the user. The user should be able to set the numbering to their liking with a single parameter (numbering-content).

How do I fix the wrong max page in “1 / 1” numbering styles, without receiving additional information from a template user?

The user should be able to set numbering-content to “1 / 1”, “I”, or whatever they want.

Hi @Marvin_Fuchs.

This has been resolved before (duplicate).

Multiple sections

Please have a look at the solution from How to reset the page counter after every section? - #10 by laurmaedje

Only one section - To switch from arabic to roman

You can also use a shorter solution from How can I switch from Roman to Arabic page numbers without breaking the total page count? - #2 by janekfleper

#let arabic-numbering(..n) = context {
  numbering("1 / 1", n.at(0), ..counter(page).at(<last-arabic-page>))
}
// content
#set page(numbering: arabic-numbering)
#counter(page).update(1)

#lorem(9999)
#metadata("last-arabic-page") <last-arabic-page>

// appendix
#set page(numbering: "i")
#counter(page).update(1)

#lorem(999)

3 Likes

Hey @vmartel08, I tried out your solution and this fixes the issue of having a wrong max-page count. However still does not fix the issue of allowing a template user to easily switch between a “1 / 1” and “1” numbering style. In your example when using “1” instead of “1 / 1” it will change a footer like “3 / 13” to “313” because

If numbering is a pattern and more numbers than counting symbols are given, the last counting symbol with its prefix is repeated. (Numbering Function – Typst Documentation)

Is there a nice approach to this problem?

I don’t think there is a way to have it as ‘user-friendly’ as you want it.
you want to allow the user to choose between quite different styles. typst gives you all the possibilities to allow all of these styles but it needs some programming as you can see vmartel08’s answer.

If you want to have it that user-friendly you would need to implement all of the ways that you want to be possible for the user and wrap them in a user-friendly way.
(Let me know if you need more help for that. It would then be beneficial to know what ways you want to enable for the user)

The numbering function would be the solution, yes. You could look at Page numbering: How to use characters as-is and not as counting symbols? (Page x of y) - #2 by vmartel08 or the following posts.

Thank @Xodarap this is what I expected unfortunately.

As a limiting “workaround” we’ll allow the user to make a choice between predefined numbering styles (“1” or “1 / 1” in our case) instead of letting the user define a numbering function/parameter.

If someone has a similar problem in the future there would be a different workaround that allows more customization for template users:

// user
#let custom-numbering-with-max-page(curr, max) = {
  numbering("1 / 1", curr, max)
}

#let custom-numbering-without-max-page(curr, max) = {
  numbering("1", curr)
}

// template property
#let used-numbering = custom-numbering-with-max-page


// template
// content
#set page(numbering: (..n) => {
  used-numbering(n.at(0), ..counter(page).at(<last-content-page>))
})
#counter(page).update(1)

#lorem(9999)
#metadata("last-content-page") <last-content-page>

// appendix
#set page(numbering: "i")
#counter(page).update(1)

#lorem(999)

The user provides a wrapped numbering function. The user implemented function receives the current page and the correct max page. This allows the user to call the numbering function with the correct arguments.

I fail to see where providing some preset numbering styles and applying a style by default could prevent the passing of custom numbering functions.

Rather than being a limitation or a workaround, allowing the user to choose between:

  • default numbering from the template
  • preset numbering with or without total number of pages (per section)
  • custom numbering (pattern or function)

feels more like a demonstration of Typst’s composability. This is a positive aspect and it allows easy configuration for most cases as well as full control in the case it is needed.

A few extra examples as I do agree the docs on numbering are not always easy to decipher especially with regards to numbering functions:

// Total number of pages for example only (need further treatment for section pages)
#let template-numberings = (
  "1",
  "1/1",
  (p, t) => numbering("i/i", p, t),
  (p, _) => numbering("i", p),
  (p, t) => [Page #numbering("i", p) of #numbering("i", t)],
  (p, _) => [Page #numbering("i", p)],
  (..nums) => [Page #numbering("i")],
  (..nums, last) => [-- 1 a i I -- #numbering("i") of #numbering("i", last)],
)

#for preset in template-numberings {
  page(
    paper: "a8",
    numbering: preset,
    lorem(50),
  )
}

From the user side, it would look like:

#import "template.typ": *

//User defined numbering not available in template presets
#let my-num-fr(p, t) = text(blue, [Page #numbering("1", p) de #numbering("1", ..section-total()) ])

#show: document.with(
  paper: "a8", // Pass any `page` arguments
  // Default numbering is "1 / 1" (section total)
  // numbering: numbering-presets.circles-numbering, // Preset
  // numbering: my-num-fr,  // User defined
  // numbering: "⓵",
  // numbering: none, 
)

= Content
#lorem(150)

#show: appendix.with(
  flipped: true // Pass any `page` arguments
  // Default numbering is "Page i of i" (section total)
  //numbering: my-num-fr, 
  //numbering: none,
) 
= Appendix
#lorem(100)

See this example project on Typst.app for a working solution of the Only one section case.

I believe this fulfills all of the requirements posted above:

  • a main document with appendix section
  • total section numbers per section
  • switching from 1 to 1/1 easily
  • capacity to use counting symbols in the functions (like a in page)
  • possibility of using some user defined patterns or functions
  • packaged as a template.

Follow-up from Add `repeat: bool` to `numbering` · Issue #8204 · typst/typst · GitHub which I replied with:

I am confident we can make it work for you.

1 Like

I opened a feature request for a property repeat, that would change the behavior of numbering to ignore additional provided numbers, if the pattern is satisfied.

This would allow users to pass simple patterns to a template, without any undesired or unexpected results, as shown in this example

We are continuing the discussion here, as @vmartel08 noted that the feature issue might not be the right place for it.

To make my goal more concise: Users should be able to modify page numbering by passing a simple numbering pattern (“1”, “1 / 1”, “i”, etc.)

All provided solutions depend on the user to provide a custom numbering function that implements the numbering pattern with the help of exposed variables like section-total as shown in examples of @vmartel08 here and here.

@vmartel08 also suggestions branching a provided content-numbering parameters and check whether a pattern or function is provided to the template. This would not solve the issue, as we still wouldn’t know whether one or two counting symbols are provided.

We could however analyze the numbering pattern an count counting symbols ourself. After all counting symbols are well defined (1, a, A, i, I, α, Α, 一, 壹, あ, い, ア, イ, א, 가, ㄱ, *, ١, ۱, १, ১, ক, ①, and ⓵.

I’ll draft a solution later and link it here. Nevertheless I still have the opinion that a simple repeat: bool parameter would be beneficial. But of course this is a opinion and if maintainers agree that this is not wanted, I’ll trust that their opinion is for the better of the community. After all they have deeper insight in the design and philosophy of Typst.

I am removing the solution marker to hopefully get more replies.

This example shows the matching counting symbols solution, while also supporting numbering functions.

The only thing I do not like about this approach, is that it is prone to changes in the counting symbols list.

After reading this thread I was using datetime’s display function which made me think that maybe numbering could take a similar format string.

//basic page number, number format based on language default(?)
numbering("[page]")
//basic page number, number format explicitly given
numbering("[page repr:1]")
numbering("[page repr:a]")
numbering("[page repr:가]")
//Some text around the number
numbering("Page number: [page]")
//Basic "current out of total" numbering - gets last page of document
numbering("[page] / [last]")
//More useful version that could get the last page of a given "section"
//Queries the document for the given metadata and uses the page number it was found on (lots of discussion needed about this.  Probably it's trying to fit too much functionality into the formatting parameter)
numbering("[page repr:I] / [query metadata: appendix]")

I realize this may be a bit off-topic, but it was inspired by the original question posed here.

2 Likes

This would certainly make page numbering patterns more descriptive. The problem is that numbering is not only used for page numbering but for ALL numbering, including headings, figures etc. This is why the repeating behavior makes sense, as you can define a pattern like “1.” and it will produce “2.3.” for level two headings.

2 Likes