How to auto-label a figure?

I have functions to create figures from images and another for tables.
For the former, I use context to obtain the figure index, for example:

// Auto-figure for images: builds "prefix + <n> + ext" from the image-figure counter.
#let autoimagefig(
  caption: none,
  width: 100%,
  prefix: "plot_",
  ext: ".png",
  placement: none,
  gap: 0.65em,
) = {
  figure(
    // Everything that needs layout info (counter) goes inside `context`.
    context {
      let n = counter(figure.where(kind: image)).display("1")
      let fname = prefix + str(n) + ext
      image(fname, width: width)
    },
    caption: caption,
    kind: image,
    placement: placement,
    gap: gap,
  )
}

This is because I am generating plots sequentially in Python, and the Typst template mirrors that sequence.

Now I am wondering how to add an auto-label using the same index, so I could generate something like <figure_#n> and later refer to it via @figure_1.
I tried several approaches, but all failed because I cannot make n from inside the context block available outside the figure (so I cannot do figure(...)<#auto-label>).

Is this a dead end, or is there a workable solution?

Here is my current approach for my table function, where the label is set manually:

#let qc_table(caption, file: "table_1.tsv", precision: 1, fsize: 9pt, rot: false, label: none) = {
  set-round(precision: precision)
  set-num(math: false)
  let data = csv(file, delimiter: "\t")
  let clen = data.at(0).len()
  let formats = (auto,) * clen
  let angle = if rot { -90deg } else { 0deg }

  show table.cell.where(y: 0): strong
  show table.cell: set text(size: fsize)
  show table: format-table(..formats)
  set table(
    fill: (_, y) => if calc.odd(y) { rgb("#a2d0e043") },
    stroke: none, //0.6pt + black,
  )

  let fig = figure(
    rotate(
      angle,
      reflow: true,
      table(
        table.hline(y: 0),
        table.hline(y: 1),
        table.vline(x: 1, start: 1),
        columns: clen,
        align: (left,) + (right,) * (clen - 1),
        ..data.flatten(),
      ),
    ),
    caption: [_#caption _],
  )

  if label != none [
    #fig#label
  ] else [
    #fig
  ]
}

Hi! You can also create a label with the function.

Would this work?

#let autoimagefig(
  caption: none,
  width: 100%,
  prefix: "plot_",
  ext: ".png",
  placement: none,
  gap: 0.65em,
) = context {
  let n = counter(figure.where(kind: image)).display("1")
  let fname = prefix + str(n) + ext
  let label-name = "figure-" + prefix + str(n)

  // Currently, labels can only be attached to elements in markup mode, not in code mode. This might change in the future.
  [#figure(
      image(fname, width: width),
      caption: caption,
      kind: image,
      placement: placement,
      gap: gap,
    )#label(lable-name)]
}

Many thanks @Y.D.X. I just needed to tweak a few things but it’s now working.

// Auto-figure for images: builds "prefix + <n> + ext" from the image-figure counter.
#let autoimagefig(
  caption: none,
  width: 100%,
  prefix: "plot_",
  ext: ".png",
  placement: none,
  gap: 0.65em,
) = context {
  let n = counter(figure.where(kind: image)).get().first() + 1 // <-- main change
  let fname = prefix + str(n) + ext
  let label-name = prefix + str(n)

  // Currently, labels can only be attached to elements in markup mode, not in code mode. This might change in the future.
  [#figure(
      image(fname, width: width),
      caption: caption,
      kind: image,
      placement: placement,
      gap: gap,
    )#label(label-name)]
}
1 Like