How to avoid duplicate footnotes with same content?

Hi everyone,

I need to automatically deduplicate identical footnotes that appear multiple times on the same page. Instead of showing the same footnote content repeatedly, I want subsequent instances to reference the first occurrence with the same footnote number. The manual approach using labels isn’t suitable for my use case since I need this to work automatically without manually tracking which footnotes have already been used. Is there a way to implement this automatic deduplication behavior in Typst?

Maybe this is a way to do it, or a start.

I’m using metadata and a global show rule for style. The style rule figures out which footnotes are unique on each page and shows them.

We want to avoid using state and toggling footnotes from visible to not visible. I guess some layout convergence problems might show up anyway, but maybe this can be a way to avoid the largest problems.

#set page(width: 5cm, height: 5cm)

#let dedupfootnotes(body) = context {
  let sel = selector.and(metadata, <_dedup_footnote_item>)
  // find every first unique footnote on each page
  let footnotes = query(sel).dedup(key: mt => (mt.value.footnote, mt.location().page()))

  show sel: mt => {
    if footnotes.any(elt => elt.location() == mt.location()) {
      // this is the first footnote
      footnote(mt.value.footnote)
    } else {
      // duplicate footnote, get the previous one's number.
      let previous-numbers = query(sel.before(here()))
        .filter(elt => elt.location().page() == here().page())
        .filter(elt => elt.value.footnote == mt.value.footnote)
      for prev in previous-numbers {
        super([#{counter(footnote).at(prev.location()).first() + 1}])
        break
      }
    }
    mt
  }
  body
}

#show: dedupfootnotes

#let ft(body) = {
  [#metadata((footnote: body))<_dedup_footnote_item>]
}

One#ft[one] two#ft[one] and three#ft[one] and four#ft[two]

#pagebreak()

Again#ft[one] and again#ft[two] and again#ft[one]



2 Likes

Thanks for your effort, but somehow this solution doesn’t work at all in my document. On one hand, I tried to put the code in one file and to change all the existing footnotes to #ft and on the other hand I tried to put the code in the main.typ and to change all existing footnotes in the whole document. Both solution didn’t work.
Maybe there is some interference with another workaround. I had to hide the footnote in the table and image outlines with this code:

#show footnote.entry: hide
#set footnote.entry(separator: none)
#show ref: none
#show footnote: none

Additionally, after the outlines there is a counter definitions which sets the footnote counter to zero:

#counter(footnote).update(0)

Could this be the problem?

I don’t know if that can be a problem, would be best to have a reduced example that demonstrates it, unfortunately.

When we talk about problems, even if it doesn’t “work at all” it would help to report what the expected document result was and what the actual document result was, and why that’s a problem :slightly_smiling_face:

Hi there, I’m sorry for my late reply.
I tried many other options to get this thing running and finally got the mistake.
I didn’t know, that I have to put the function

#let ft(body) = {[#metadata((footnote: body))<_dedup_footnote_item>}

at the beginning of every .typ file in my document.

Is there a solution in what I can define this function globally in the main.typ?

You can add common functions in a .typ file and then import from that file in every document part.

I also think you can select a longer or better name for the ft function, as needed, whatever suits your document. It was written quite short just for the forum code.

Also, if you have problems like that, you can share the error message on the forum so that others can help. If fact, when “it does not work” is uttered, it should come with an error message :wink:

Sure, that would be helpful ;)

Could you please elaborate how the #import function would work in this case? Simply putting the code in the .typ file wouldn’t work, I guess?