How to get a footer with alternating alignment and custom text per page?

I would like to have a footer in my document. The footer should consist of a text and the page number, separated by a pipe symbol. On odd-numbered pages, the page number should be on the right side, and the entire footer should be right-aligned. For example:

         lorem ipsum | 45

On even-numbered pages, the footer should be left-aligned, and the page number should be on the left:

46 | lorem ipsum

I would like to set the displayed text differently on each page.

You can set the footer to be a contextual expression and check for page parity manually, for example:

#set page(
  footer: context {
    let val = counter(page).get().first()
    if calc.even(val) [
      #counter(page).display("1") | #lorem(2)
    ]
    else [
      #h(1fr) #lorem(4).slice(12, -1) | #counter(page).display("1")
    ]
  },
)

Sounds like a duplicate of How to combine the title of the document with the page counter in the footer - #7 by sijo, even though the main topic and solution are different. The question uses the same problem.

Thank you very much for your answer. It solves one half of my problem. The other part is that I want to place an additional text next to the page number. I would like to be able to redefine this text per page.

@Olaf , is the text to set next to the page number a heading? Where is it coming from? Do you have an example to work with?

Perhaps have a look at the Typst documentation to give you some ideas? Some hints: How to use a double-sided document layout? - #2 by vmartel08

Both hydra and chic-hdr packages may be doing what you are looking for.

The footer text should be defined individually for each page. I don’t know the best way to do this. Do you set a variable for this? Do you call a function?

I’ve come this far now. The pseudo code is only intended to give you an idea of what I have in mind:

#set page(
  width: 10cm,
  height: 5cm,
  footer: context {
    let footer-text = [footer-text]
    let val = counter(page).get().first()
    if calc.even(val) [
      #counter(page).display("1") | #footer-text
    ]
    else [
      #h(1fr) #footer-text | #counter(page).display("1")
    ]
  },
)

= Page 1

// pseudo code:
// footer-text = [footer text for page 1] 

#pagebreak()

= Page 2

// pseudo code:
// footer-text = [footer text for page 2] 

@Olaf, there seems to be a pattern here. Are you going to use headings for that purpose?

Perhaps you could inspire yourself with How to add an invisible Heading to an outline?, which has 2 good solutions for that, using either:

  • a heading…
    • do you want to use one?
    • perhaps you could use a hidden one?
      or
  • a state variable.

In any case, although you have provided a bit more insight, it is still unclear what means you would like to use, which would help suggesting a more complete solution… and avoid duplication.

1 Like

A working solution, which involves state (avoids using a heading, which would have been showing in the outline by default).

Code (solution with `state`)
// Example using state
#let footer-text = state("footer-text", "oops we forgot to set the text")

#set page(
  width: 10cm,
  height: 5cm,
  footer: context {
    let val = counter(page).get().first()
    if calc.even(val) [
      #counter(page).display("1") | #footer-text.get()
    ] else [
      #h(1fr) #footer-text.get() | #counter(page).display("1")
    ]
  },
)

= Page 1
This page shows the default footer as we didn't set it.
//#footer-text.update([footer *text* for page 1])

#pagebreak()

= Page 2
#footer-text.update([footer _text_ for page 2])

#pagebreak()

= Page 3
#footer-text.update([footer _text_ for page 3])

#pagebreak()

= Page 4
This page still shows the footer from page 3 because we forgot to change it. 
//#footer-text.update([footer _text_ for page 4])
Output

2 Likes

For page specifically, if no custom numbering is needed, it can be skipped in the .display(). For heading, you probably want something other than "1.1", but the same thing applies.

1 Like

Many thanks for your support. I have tweaked the solution a little more:


#let footer-text = state("footer-text", none)

#let set-footer-text(content) = footer-text.update(content)

#set page(
  width: 10cm,
  height: 4cm,
  footer: context {
    let text = footer-text.get()
    footer-text.update(none)
    let (page-num,) = counter(page).get()
    let val = counter(page).get().first()

    if calc.even(page-num) {
      if text == none [
        #counter(page).display("1")
      ] else [
        #counter(page).display("1") | #text
      ]
    } else {
      if text == none [
        #h(1fr) #counter(page).display("1")
      ] else [
        #h(1fr) #text | #counter(page).display("1")
      ]
    }
  },
)

= Page 1

#set-footer-text([footer text for page 1])

#pagebreak()

= Page 2

#set-footer-text([footer text for page 2])

#pagebreak()

= Page 3

#pagebreak()

= Page 4

#pagebreak()

= Page 5

#set-footer-text([footer text for page 5])
1 Like

You can make it much simpler. There are also some variations that depend on the document structure.

code
#let footer-text = state("footer-text")

#let set-footer-text(content) = footer-text.update(content)

#set page(
  width: 10cm,
  height: 4cm,
  footer: context {
    let text = footer-text.get()
    footer-text.update(none)
    // let page-num = here().page() // Physical page
    let page-num = counter(page).get().first() // Logical page
    // let num = counter(page).display("I") // If custom numbering is needed.
    if calc.even(page-num) {
      text = if text != none [ | #text]
      [#page-num#text]
    } else {
      text = if text != none [#text | ]
      [#h(1fr)#text#page-num]
    }
  },
)

= Page 1

#set-footer-text[footer text for page 1]

#pagebreak()

= Page 2

#set-footer-text[footer text for page 2]

#pagebreak()

= Page 3

#pagebreak()

= Page 4

#pagebreak()

= Page 5

#set-footer-text[footer text for page 5]
1 Like