How can I customize the outline entries based on the different parts' labels?

Hello, friends. I am making a customized outline of a book, I want to skip numbering the part entries and indent the chapter entries, how can I make it?

The reasons that I use figures rather than simply headings are

  • for Part-I and Part-II, I need them to be new pages
  • for Chapter titles, I want to
    • make them more styled
    • write the body from heading level 1

In my project, I make every chapter a single file and combine them together using include like the way I write LaTeX.

I tried to compare the counters with integers, for example,
I can get the following two counts, which has some features, but it is not allowed…

#let chap-index = context counter(label-chapter).at(loc).at(0)
#let part-index = context counter(label-part).at(loc).at(0)

The complete code is big and made of multiple files, here I provide the runnable example.

#let label-chapter = <chapter>
#let label-part = <part>

#let contents-style(body, depth: 2) = {
  assert(depth in (1, 2), message: "depth can only be either 1 or 2")


  show link: set text(black)
  show heading.where(level: 1): it => {
    set text(22pt)
    it
    v(0.1em)
  }

  set outline(
    title: {
      heading(
        outlined: true,
        level: 1,
        "Contents",
      )
    },
  )

  show outline.entry: x => {
    let loc = x.element.location()
    let prefix = x.prefix()
    let func = x.element.func()

    let chap-index = context counter(label-chapter).at(loc).at(0)
    let part-index = context counter(label-part).at(loc).at(0)

    if (depth >= 1) and (func == figure) {
      link(
        loc,
        {
          (
            chap-index + "." + h(.5em) + smallcaps(strong(x.body())) + strong(x.page()) + v(0em)
          )
        },
      )
    } else if (depth == 2) and (x.level == 1) and (prefix != none) {
      link(
        loc,
        {
          strong(
            if prefix.has("children") {
              h(1.2em) + chap-index + "." + prefix.children.at(1) + h(.5em)
            } else if prefix.has("text") {
              prefix + h(.5em)
            }
              + x.body(),
          )
          + strong(x.page())
        },
      )
      v(0em)
    }
  }
  body
}

#let contents(depth: 2) = {
  show: contents-style.with(depth: depth)
  outline(target: selector(heading).or(label-part).or(label-chapter), depth: depth)
  pagebreak(to: "odd")
}

#let chapter-style(body, title: "") = {
  align(
    center,
    [#figure(
        text(
          36pt,
          title,
          style: "italic",
          weight: "bold",
        ),
        kind: "title",
        supplement: none,
        numbering: _ => none,
        caption: title,
      )#label-chapter],
  )

  show pagebreak.where(weak: true): it => {
    counter(heading).update(0)
    it
  }

  show figure.caption.where(kind: "title"): none
  pagebreak()

  body
}

#let part(title) = {
  show figure.caption: none

  align(
    center + horizon,
    [
      #figure(
        text(36pt, strong(title)),
        kind: "part",
        supplement: none,
        numbering: _ => none,
        caption: title,
      ) #label-part
    ],
  )

  pagebreak()
}

#contents(depth: 2)

#part("Part 1")

#show: chapter-style.with(title: "Chapter 1")

#show: chapter-style.with(title: "Chapter 2")

#part("Part 2")

#show: chapter-style.with(title: "Chapter 3")

#show: chapter-style.with(title: "Chapter 4")

Did I understand it correctly that you would only like to modify the outline entries for level 1 headings (parts)? In that case a show rule that skips the prefix of the outline entry is sufficient. I slightly modified this example of the default show rule. Wrapping it.inner() in a block() was required to get the vertical spacing right. Without the block there was some additional space above every outline entry of a level 1 heading.

#set heading(numbering: "1.1")

#show outline.entry.where(level: 1): it => link(
  it.element.location(),
  block(it.inner())
)

#outline(target: heading)

= Part I
== Chapter 1
== Chapter 2

= Part II
== Chapter 3
== Chapter 4
This is what the outline will look like

If you provide a code example, please only include the stuff that actually matters for your question. I looked at some parts of the code and it didn’t really make sense to me. Why are you using figures to create the parts and the chapters? The heading element is meant to be used for the general structure of your document. If you have some follow-up question regarding the styling of the outline, feel free to add it to this post. For more general questions please create a new post.

To actually answer your question in the title. I couldn’t find any comparison of the counter values to an integer in your code, did you remove this because it was raising an error? If you try to compare the value of the counter outside of the context where you evaluate the counter, this will not work because context always returns content. See Why is the value I receive from context always content? - #2 by laurmaedje for an explanation.

1 Like

Hello. You probably should read about context from Why is the value I receive from context always content? - #2 by laurmaedje. Your example is very big and from what you are asking, most of the stuff is irrelevant.

Moreover, some things in your code don’t do anything, and it’s probably better to use headings and everything around them, than reinventing almost everything from scratch. If the question is just about context and counter, then you can just remove the whole code example. It adds a lot of unnecessary complexity to the question at hand.

The reasons that I use figures rather than simply headings are

  • for Part-I and Part-II, I need them to be new pages
  • for Chapter titles, I want to
    • write the body from heading level 1
    • make them more styled

The table of contents is for a book, and I have read the post about content values as well as any other thing I can on this forum, but they don’t give me a clue to solve my problem. I posted this question to seek some workaround.

Yes, I remove the conditions as they raised the error, I wrote

#let chap-index = context counter(label-chapter).at(loc).at(0)
#let part-index = context counter(label-part).at(loc).at(0)
#show outline.entry: x => {
  if (depth >= 1) and (x.element.func() == figure) {
    if (part-index > 0) {
      link(
        loc,
        {
          (
            smallcaps(strong(x.body())) + strong(x.page()) + v(0em)
          )
        },
      )
    } else {
      link(
        loc,
        {
          (
            chap-index + "." + h(.5em) + smallcaps(strong(x.body())) + strong(x.page()) + v(0em)
          )
        },
      )
    }
  }
}

Thanks for pointing out, I changed the question and add more explanation of my aim.

1 Like

I understand that you would like to modify the headings and you can achieve this with show rules targeting the different heading levels. Please take a look at the show rules below as a starting point. You can add more set rules inside the show rules to change the style of the part and chapter titles. Or you can directly pass the titles to styling functions as I did with emph().

#show heading.where(level: 1): it => {
  set align(center + horizon)
  pagebreak()
  block(it.body)
  pagebreak()
}

#show heading.where(level: 2): it => {
  set align(center)
  let part = query(heading.where(level: 1).before(it.location())).last()
  pagebreak(weak: true)
  block({
    part.body
    linebreak()
    emph(it.body)
  })
}

= Part I
== Chapter 1
== Chapter 2

= Part II
== Chapter 3
== Chapter 4
1 Like

For part/chapter/normal headings, you can re-use Part independent section - #6 by Andrew. I don’t understand if the numbering is supposed to be automatic or not exist at all (to confuse readers).

#set heading(numbering: (..nums) => {
  let chapter-count = counter("chapter counter")
  if nums.pos().len() == 1 {
    numbering("I.", ..nums) // Part
  } else if nums.pos().len() == 2 {
    chapter-count.step()
    numbering("1.", chapter-count.get().first() + 1) // Chapter
  } else {
    numbering("1.", ..chapter-count.get(), ..nums.pos().slice(2))
  }
})

#show heading.where(level: 1): it => if it.body == [Contents] { it } else {
  set align(center + horizon)
  set text(36pt)
  pagebreak(weak: true) + it
}

#show heading.where(level: 2): set text(22pt)
#show heading.where(level: 2): set align(center)
#show heading.where(level: 2): it => pagebreak(weak: true) + emph(it)

#outline(target: selector.or(heading.where(level: 1), heading.where(level: 2)))

= Part One
== Chapter One
=== Section
== Chapter Two
= Part Two
== Chapter Three
=== Section
=== Section
==== Subsection
==== Subsection
= Part Three
== Chapter Four
1 Like

Finally, I figured out that the way to solve my problems is to avoid using context keyword.

Thank again for your examples.