Basically I want to insert an invisible “heading” into the outline. The heading should not not effect the rest of the document. I am looking for some sort of heading-label that is only shown in the outline to mark a new section: the appendix.
The outline should look like this:
...
Appendix
<appendix 1>
...
Appendix should show the same page as <appendix 1>.
so that the heading is added to the document tree but not actually shown. As the outline queries for all headings that exist in the document tree, the fact that it’s not actually visible doesn’t matter for the outline entry.
Note that (if a numbering is set) the heading counter will still be stepped, though you probably want to reset the counter value and update the numbering anyway for the appendix.
Why do you want something invisible to be inserted in the outline, which also shouldn’t affect the rest of the document? Do you still need the heading to be seen in the document? Otherwise, just don’t add the heading in the first place.
As a real example, when I had the same problem, it was because of a complex document structure (nested/bundled documents). Basically I need to insert the headings to then use them in a global-scope outline (and in the PDF outline), but the headings themselves shouldn’t be visible in the document (outside the outline), because they serve a semantic purpose instead of a literal one. I fixed it like illustrated here.
It’s a bit verbose of a solution (and it’s not really a heading shown in the outline but an inserted block of text before an entry) but gives you more freedom to customise the appearance of your section marker.
I think they mean that the heading should be visible in the outline but invisible in the rest of the document, as they mention it should be visible in the outline.
A generic function would be nice to add random content to the outline … for example it would be easy if I could find out how to check it.body for strings (starts-with) (if it is a sequence)?
As @Blaz clarified I was looking for a “Heading” that is visible in the outline but invisible in the rest of the document. Also I didn’t want it to be effected by styling-rules. So basically a label I place somewhere in the document.
@Eric Unfortunately your solution wasn’t what I was looking for because it actually creates a heading.
@Blaz You lead me into the right direction. My solution is heavily inspired by your examples :)
This is what I came up with. It is kind of hacky but works pretty well:
#let APPENDIX = [
= A1 (Appendix starts here)
#lorem(25)
= A2
]
#let is_set = counter("is_set")
#is_set.update(0)
#show outline.entry: it => context {
if is_set.get().at(0) == 0 {
let pos_it = it.element.location().position()
let pos_appendix = locate(<appendix>).position()
let page_diff = pos_it.page - pos_appendix.page
if page_diff > 0 or (page_diff == 0 and pos_it.y > pos_appendix.y) {
block(link(<appendix>, strong({
[Appendix ]
box(width: 1fr, repeat[.])
counter(page).display()
})))
is_set.update(1)
}
}
it
}
#outline()\
= H1
#lorem(25)
= H2
#lorem(25)
<appendix>
#APPENDIX
Output
If you have any ideas on how to improve this, please let me know!
#let make-outline-entry(label, body) = {
let filler = box(width: 1fr, repeat[.])
let content = body + " " + filler + counter(page).display()
block(link(label, strong(content)))
}
#show outline.entry: it => {
let label = <appendix>
let is-set = state("is-set", false)
context if not is-set.get() {
let found = query(selector(label).before(it.element.location()))
let is-after-appendix = found.len() > 0
if is-after-appendix {
make-outline-entry(<appendix>)[Appendix]
is-set.update(true)
}
}
it
}
I made it more readable/maintainable. Or you can slap everything together to make the code smaller:
#show outline.entry: it => {
let label = <appendix>
let is-set = state("is-set", false)
context if not is-set.get() {
let found = query(selector(label).before(it.element.location()))
if found.len() > 0 {
block(link(label, strong({
"Appendix "
// box(width: 1fr, repeat[.])
box(width: 1fr, it.fill)
counter(page).display()
})))
is-set.update(true)
}
}
it
}
Some things (e.g., using "" instead of [] for the name) are pure preference, but most things are the recommended way of writing Typst code (or code in general):
using descriptive variable/function names;
using state() over counter() if you don’t need to count natural numbers;
btw, counter() is initially set to 0, so you don’t have to explicitly set it initially unless you need to reset it at some point;
using query(selector(pivot).before(location)).len() > 0 over manually comparing pages/y position;
there is also .after();
sometimes you need to use things like <element>.where() which is already a selector;
the recommended variable/function naming convention is to use kebab case (which is typically easier to type).
A little bit less “you should do it” things:
adding aliases for things that need to be used multiple times (i.e., label) to reduce chance of a “desync value” bug (and similar stuff);