How to layout glossary into two separate tables (symbols and abbreviations)

Hi there,

I am writing a template for my master’s thesis. On requirement of the template is that the symbols and abbreviations are layed out into a table as depicted in this image:

I took the suggestions from a previous discussion about controlling the look of the glossary and tried to adjust it to my needs. So far, I arrived at this:

let symbols = latin-symbols + greek-symbols
show: make-glossary
register-glossary(symbols)
register-glossary(abbreviations)

print-glossary(
  show-all: false,
  user-print-glossary: (entries, groups, ..) => {
    table(
      // Invisible fourth column for the figure caption
      columns: (1fr, 1fr, 3fr, 0pt),
      table.header([Formelzeichen], [Einheit], [Beschreibung]),
      ..for group in ("latin", "greek") {
        (
          ..for entry in entries.filter(x => x.group == group) {
            (
              entry.short,
              entry.custom.unit,
              entry.description,
              [
                // #show figure.caption: none
                #figure(kind: "glossarium_figure", supplement: "", caption: none)[]
                #label(entry.key)
              ],
            )
          },
        )
      },
    )
  },
  symbols,
)

print-glossary(
  show-all: false,
  user-print-glossary: (entries, groups, ..) => {
    table(
      // Invisible third column for the figure caption
      columns: (1fr, 3fr, 0pt),
      table.header([Abkürzung], [Beschreibung]),
      ..for group in ("abbreviations",) {
        (
          ..for entry in entries.filter(x => x.group == group) {
            (
              entry.short,
              entry.long,
              [
                // #show figure.caption: none
                #figure(kind: "glossarium_figure", supplement: "", caption: none)[]
                #label(entry.key)
              ],
            )
          },
        )
      },
    )
  },
  abbreviations,
)

where my entries look like this:

let _latin-symbols = (
  (
    key: "sym:ap",
    short: $a_p$,
    description: "Schnitttiefe",
    custom: (
      unit: "mm",
    ),
  ),
  (
    key: "sym:ae",
    short: $a_e$,
    description: "Eingriffsweite",
    custom: (
      unit: "mm",
    ),
  ),
  ...
)

let latin-symbols = _latin-symbols.map(item => item + (group: "latin"))

and

let _abbreviations = (
  (
    key: "dp",
    short: "DP",
    long: "polykristalliner Diamant",
  ),
  (
    key: "llm",
    short: "LLM",
    long: "Large Language Model",
  ),
)

let abbreviations = _abbreviations.map(item => item + (group: "abbreviations"))

This kind of works, but there are a few things that are broken, unfortunately (see last image):

  1. The symbols (and abbreviations) are not automatically sorted alphabetically
  2. All symbols and abbreviations appear in the table, even though there are no references to all but one of them, show-all: false has no effect.

Styling the table is just something I didn’t do yet, that’ll come later, when I’ve got the core functionality working.

Is what I’m trying to achieve even possible? :sweat_smile: I would really like to avoid having to keep track of all symbols and abbreviations manually throughout my thesis, so glossarium seemed like the right choice for me.

Hi @engifar! Thanks for reaching out. Glossarium is a bit complicated to manipulate, but one good example to start with would be possible this test glossarium/tests/glossary-table/test.typ at 785e0dbebfa44b5cae844e5e11437157be888f6e · typst-community/glossarium · GitHub

I wrote it based on the previous forum topic. Tell me if you’re able to tweak it to your preferences!

Because it sounded interesting, here’s a quick attempt from me:

#import "@preview/glossarium:0.5.10": *

#let _latin-symbols = (
  (
    key: "sym-ap",
    short: $a_p$,
    description: "Schnitttiefe",
    custom: (
      unit: "mm",
    ),
  ),
  (
    key: "sym-ae",
    short: $a_e$,
    description: "Eingriffsweite",
    custom: (
      unit: "mm",
    ),
  ),
)

#let latin-symbols = _latin-symbols.map(item => item + (group: "latin"))

#let _abbreviations = (
  (
    key: "dp",
    short: "DP",
    long: "polykristalliner Diamant",
  ),
  (
    key: "llm",
    short: "LLM",
    long: "Large Language Model",
  ),
)

#let abbreviations = _abbreviations.map(item => item + (group: "abbreviations"))

#let symbols = latin-symbols
#show: make-glossary
#register-glossary(symbols)
#register-glossary(abbreviations)

#print-glossary(
  user-print-glossary: (entries, groups, show-all: false, ..) => {
    show table.cell.where(y: 0): set text(weight: "bold")
    set table(inset: (top: 10pt, bottom: 15pt),)
    table(
      // Invisible third column for the figure caption
      columns: (1fr, 1fr, 3fr, 0pt),
      table.header([Formelzeichen], [Einheit], [Beschreibung]),
      ..for group in groups {
        let a = entries.map(e => (e.key, count-refs(e.at("key")))).to-dict()
        let b = count-all-refs().to-dict()
        (
          ..for entry in entries.filter(x => x.group == group ) {
            let display-entry = show-all == true or count-refs(entry.at("key")) >= 1
            if display-entry {(
              entry.short,
              entry.custom.unit,
              entry.description,
              [
                #figure(kind: "glossarium_entry", supplement: none, numbering: none, none,) 
                #label(entry.key)
              ]
            )} else {(
                table.cell(inset: 0%+0pt, none),
                table.cell(inset: 0%+0pt, none),
                table.cell(inset: 0%+0pt, none),
                table.cell(inset: 0%+0pt)[
                 #figure(kind: "glossarium_entry", supplement: none, numbering: none, none,) 
                 #label(entry.key)
                ]
              )}
          },
        )
      },
    )
  },
  symbols,
)

#print-glossary(
  user-print-glossary: (entries, groups, show-all: false, ..) => {
    show table.cell.where(y: 0): set text(weight: "bold")
    set table(inset: (top: 10pt, bottom: 15pt),)
    table(
      // Invisible third column for the figure caption
      columns: (1fr, 3fr, 0pt),
      table.header([Abkürzung], [Beschreibung]),
      ..for group in groups {
        let a = entries.map(e => (e.key, count-refs(e.at("key")))).to-dict()
        let b = count-all-refs().to-dict()
        (
          ..for entry in entries.filter(x => x.group == group ) {
            let display-entry = show-all == true or count-refs(entry.at("key")) >= 1
            if display-entry {(
              entry.short,
              entry.long,
              [
                #figure(kind: "glossarium_entry", supplement: none, numbering: none, none,) 
                #label(entry.key)
              ]
            )} else {(
                table.cell(inset: 0%+0pt, none),
                table.cell(inset: 0%+0pt, none),
                table.cell(inset: 0%+0pt)[
                 #figure(kind: "glossarium_entry", supplement: none, numbering: none, none,) 
                 #label(entry.key)
                ]
              )}
          },
        )
      },
    )
  },
  abbreviations,
)


@dp

@llm

@llm

@sym-ae
1 Like

Hey guys, thank you very much for your answers. I actually ended up adjusting the code that @quachpas linked to to fit my needs. The code is super long, so I wrapped it into a function which I can import from a dedicted glossary.typ file. Here’s the code in its entirety for anyone interested. glossarium-default.typ is also taken from the glossarium repo.

#import "deps.typ"
#import deps.glossarium: make-glossary, print-glossary, register-glossary
#import "assets/glossarium-default.typ": *

#import "globals.typ": text-size


#let glossary(
  lang-map,
  latin-symbols: (),
  greek-symbols: (),
  abbreviations: (),
) = {
  heading()[#lang-map.at("sym-abb-title")]
  let symbols = latin-symbols + greek-symbols

  show: make-glossary
  register-glossary(symbols)
  register-glossary(abbreviations)

  // Definitions for the tables
  let show-all = false
  let left-padding = 0.7cm
  let tbl-stroke = 0.5pt
  let tbl-sym-top-space = -1.135em
  let tbl-abb-top-space = 1.21em
  let tbl-row1-inset-bottom = -0.09em
  let tbl-inset-top = 0.79em
  let tbl-inset-bottom = 1.272em
  let left-col-width = 3.37cm
  let desc-col-width = 9.84cm

  // Styling of tables
  show table: set text(size: text-size)
  set table(
    stroke: (_, y) => (
      bottom: if y == 0 { tbl-stroke },
    ),
    align: left + top,
  )
  // Move the whole table to the right a bit
  show table: it => { pad(left: left-padding)[#it] }

  show table.cell.where(y: 0): set text(weight: "bold")
  show table.cell.where(y: 0): set block(inset: (bottom: tbl-row1-inset-bottom))

  // Symbols
  v(tbl-sym-top-space)
  // Original code from https://github.com/typst-community/glossarium/blob/785e0dbebfa44b5cae844e5e11437157be888f6e/tests/glossary-table/test.typ, adjusted to fit the needs of the template
  print-glossary(
    show-all: show-all,
    deduplicate-back-references: true,
    description-separator: none,
    user-print-glossary: (entries, groups, ..kwargs) => {
      kwargs = kwargs.named()
      let user-print-gloss = kwargs.at("user-print-gloss")
      let show-all = kwargs.at("show-all")
      let minimum-refs = kwargs.at("minimum-refs")
      for kw in (
        // remove unused kwargs for user-print-gloss
        "group-heading-level",
        "group-sortkey",
        "entry-sortkey",
        "user-print-reference",
        "user-print-group-heading",
        "user-group-break",
        "user-print-gloss",
      ) {
        _ = kwargs.remove(kw)
      }
      table(
        columns: (left-col-width, 1fr, desc-col-width),
        table.header(..lang-map.at("sym-tbl-header")),
        ..for group in groups {
          let group-entries = entries.filter(x => x.at(GROUP) == group)
          let group-ref-counts = group-entries.map(x => count-refs(x.at(KEY)))
          let print-group = (
            // ? group-heading-pagebreak Layout divergence if location is conditional on print-group
            group != "" and (show-all == true or group-ref-counts.any(x => x >= minimum-refs))
          )
          (
            // Group heading
            ..for entry in entries.filter(x => x.group == group) {
              let display-entry = show-all == true or count-refs(entry.at(KEY)) >= minimum-refs
              // If the entry is not used, make row's total height 0%+0pt
              let inset = if display-entry { (top: tbl-inset-top, bottom: tbl-inset-bottom) } else { 0% + 0pt }
              (
                table.cell(inset: inset, {
                  if display-entry {
                    entry.short
                  }
                }),
                table.cell(inset: inset, {
                  [
                    // default-print-reference
                    #__glossarium_figure(
                      entry.at(KEY),
                      body: user-print-gloss(entry, ..kwargs),
                    )
                    // The line below adds a ref shorthand for plural form, e.g., "@term:pl"
                    #__glossarium_figure(entry.at(KEY) + ":pl")
                    // Same as above, but for capitalized form, e.g., "@Term"
                    // Skip if key is already capitalized
                    #if upper(entry.at(KEY).at(0)) != entry.at(KEY).at(0) {
                      [
                        #__glossarium_figure(
                          default-capitalize(entry.at(KEY)),
                        )
                        #__glossarium_figure(
                          default-capitalize(entry.at(KEY)) + ":pl",
                        )
                      ]
                    }
                  ]
                }),
                table.cell(inset: inset, {
                  if display-entry {
                    entry.long
                  }
                }),
              )
            },
          )
        },
      )
    },
    user-print-gloss: (entry, ..kwargs) => {
      let show-all = kwargs.at("show-all")
      let minimum-refs = kwargs.at("minimum-refs")
      let user-print-back-references = kwargs.at("user-print-back-references")
      let deduplicate-back-references = kwargs.at("deduplicate-back-references")
      let display-entry = show-all == true or count-refs(entry.at(KEY)) >= minimum-refs
      if display-entry {
        table.cell(entry.description)
      }
    },
    symbols,
  )

  v(tbl-abb-top-space)

  // Abbreviations
  print-glossary(
    show-all: show-all,
    deduplicate-back-references: true,
    description-separator: none,
    user-print-glossary: (entries, groups, ..kwargs) => {
      kwargs = kwargs.named()
      let user-print-gloss = kwargs.at("user-print-gloss")
      let show-all = kwargs.at("show-all")
      let minimum-refs = kwargs.at("minimum-refs")
      for kw in (
        // remove unused kwargs for user-print-gloss
        "group-heading-level",
        "group-sortkey",
        "entry-sortkey",
        "user-print-reference",
        "user-print-group-heading",
        "user-group-break",
        "user-print-gloss",
      ) {
        _ = kwargs.remove(kw)
      }
      table(
        columns: (1fr, desc-col-width),
        table.header(..lang-map.at("abb-tbl-header")),
        ..for group in groups {
          let group-entries = entries.filter(x => x.at(GROUP) == group)
          let group-ref-counts = group-entries.map(x => count-refs(x.at(KEY)))
          let print-group = (
            // ? group-heading-pagebreak Layout divergence if location is conditional on print-group
            group != "" and (show-all == true or group-ref-counts.any(x => x >= minimum-refs))
          )
          (
            // Group heading
            ..for entry in entries.filter(x => x.group == group) {
              let display-entry = show-all == true or count-refs(entry.at(KEY)) >= minimum-refs
              // If the entry is not used, make row's total height 0%+0pt
              let inset = if display-entry { (top: tbl-inset-top, bottom: tbl-inset-bottom) } else { 0% + 0pt }
              (
                table.cell(inset: inset, {
                  if display-entry {
                    entry.short
                  }
                }),
                table.cell(inset: inset, {
                  [
                    // default-print-reference
                    #__glossarium_figure(
                      entry.at(KEY),
                      body: user-print-gloss(entry, ..kwargs),
                    )
                    // The line below adds a ref shorthand for plural form, e.g., "@term:pl"
                    #__glossarium_figure(entry.at(KEY) + ":pl")
                    // Same as above, but for capitalized form, e.g., "@Term"
                    // Skip if key is already capitalized
                    #if upper(entry.at(KEY).at(0)) != entry.at(KEY).at(0) {
                      [
                        #__glossarium_figure(
                          default-capitalize(entry.at(KEY)),
                        )
                        #__glossarium_figure(
                          default-capitalize(entry.at(KEY)) + ":pl",
                        )
                      ]
                    }
                  ]
                }),
              )
            },
          )
        },
      )
    },
    user-print-gloss: (entry, ..kwargs) => {
      let show-all = kwargs.at("show-all")
      let minimum-refs = kwargs.at("minimum-refs")
      let user-print-back-references = kwargs.at("user-print-back-references")
      let deduplicate-back-references = kwargs.at("deduplicate-back-references")
      let display-entry = show-all == true or count-refs(entry.at(KEY)) >= minimum-refs
      if display-entry {
        table.cell(entry.long)
      }
    },
    abbreviations,
  )
}

1 Like