Why do I get a `cannot reference context` error when referencing a custom figure?

Hello,

I wrote a wrapper function to have figure captions on the side instead of under the picture, where I’m re-implementing the image function in a show rule as to treat them separately. This function is defined in a separate template file.

Everything works fine, except for captions with tags in them: a regular figure function works,

#figure(
  image("path/to/img.png", width: 45%),
  caption: [Plot of @eq.],
)

while the custom function does not:

#scfigure(
  image("path/to/img.png", width: 45%),
  caption: [Plot of @eq.],
)

where I get the error: cannot reference context.

Here is a minimal working example of the function I wrote:

#let scfigure(
    caption-ratio: auto,
    caption-align: bottom + left,
    gutter: 1.6em,
    ..args,
  ) = context {

  let text-width = page.width - (page.margin.inside + page.margin.outside)
  let half-gutter = gutter.to-absolute() / 2

  show figure.caption: it => { align(caption-align)[#it] }

  show figure.where(kind: image): it => {
    let img-size = (width: 50% - half-gutter, height: auto)
    if "width" in it.body.fields() {
      img-size.width = it.body.width.ratio + (it.body.width.length.to-absolute() / text-width * 100%) - half-gutter
    }
    if "height" in it.body.fields() {
      img-size.height = it.body.height
    }
    let caption-width = img-size.width.ratio - half-gutter
    if img-size.width.ratio > 50% {
      caption-width = 100% - img-size.width.ratio - half-gutter
    }
    if caption-ratio != auto {
      caption-width = caption-width.ratio * caption-ratio - half-gutter
    }
    let columns-arrangement = (img-size.width, caption-width)

    align(center, block(breakable: false, above: 2.4em, below: 2.4em)[
      #grid(
        columns: columns-arrangement,
        column-gutter: gutter,
        image(
          it.body.source.trim("../"), // img folder is relative to this file now!!
          width: 100%,
          height: img-size.height,
        ),
        it.caption,
      )
    ])
  }
  figure(..args)
}

Is there a way to display tags correctly?

Hi there @9iovaferra , welcome to the forum.

Thank you for taking the time to format your code properly and to provide an example.

From what I can see, your code works. It does not compile directly, but here is an example that does:

Reworked Code Snippet that Compiles
// That was missing for the code to compile
#set page(margin: (inside: 2cm, outside: 2.5cm))
#set math.equation(numbering: "1.1")
$ x+1 $ <eq>

//Image to import 
#let svg = ```svg
<svg version="1.1" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
  <rect width="100%" height="100%" fill="black" />
  <text x="10" y="10" font-size="6" text-anchor="middle" fill="white">SVG</text>
</svg>
```.text

#let logo-b = bytes(svg)


#let scfigure(
  caption-ratio: auto,
  caption-align: bottom + left,
  gutter: 1.6em,
  ..args,
) = context {
  let text-width = page.width - (page.margin.inside + page.margin.outside)
  let half-gutter = gutter.to-absolute() / 2

  show figure.caption: it => { align(caption-align)[#it] }

  show figure.where(kind: image): it => {
    let img-size = (width: 50% - half-gutter, height: auto)
    if "width" in it.body.fields() {
      img-size.width = it.body.width.ratio + (it.body.width.length.to-absolute() / text-width * 100%) - half-gutter
    }
    if "height" in it.body.fields() {
      img-size.height = it.body.height
    }
    let caption-width = img-size.width.ratio - half-gutter
    if img-size.width.ratio > 50% {
      caption-width = 100% - img-size.width.ratio - half-gutter
    }
    if caption-ratio != auto {
      caption-width = caption-width.ratio * caption-ratio - half-gutter
    }
    let columns-arrangement = (img-size.width, caption-width)

    align(center, block(breakable: false, above: 2.4em, below: 2.4em)[
      #grid(
        columns: columns-arrangement,
        column-gutter: gutter,
        image(
          it.body.source, // had to comment this to compile 
                          // .trim("../"), 
                          // img folder is relative to this file now!!
          width: 100%,
          height: img-size.height,
        ),
        it.caption,
      )
    ])
  }
  figure(..args)
}

#figure(
  image(logo-b, width: 45%),
  caption: [Plot of @eq.],
)

#scfigure(
  image(logo-b, width: 45%),
  caption: [Plot of @eq.],
)

Look in your code for another context expression. You must have something else that you have not provided that creates the issue.

Could you try to provide an example that does bring that error you mentioned?

A good thing to do would be to read more about context, either in the docs or in this forum.

#set math.equation(numbering: "1.1.")
= Test
//Works
$ x + 1 $ <eq>
This is @eq and it works

//Works
#context [
  $ x + 2 $ <eq2>
  This is @eq2 and it works
]

//Does not work
#context $ x + 3 $ <eq3>
This is @eq3 and it dowes not work // <-- raises an error

Hi @vmartel08, thanks for your reply. I’ve checked again and realised the problematic tag was not the one inside the caption, but a different one labelling the figure itself… silly mistake.

The template file is quite long so I created a new file with just your working snippet and I was able to reproduce the error. The full function call is

#scfigure(
  image(logo-b, width: 45%),
  caption: [Plot of @eq.],
) <figure>

So, the error actually originates from the tag @figure somewhere else in the document. If I remove this reference it compiles without issue.

Edit: reading through the docs you linked, I thought the error might be due to the fact that the entire function is wrapped in context. However if I hard-code values text-width and half-gutter, which are the only lines requiring context, a different error is triggered: cannot reference styled.

That is the problem. The label attaches to the element returned by the function scfigure. Regardless if it’s wrapped in context or if you apply custom styles, in both cases it’s not a figure anymore unfortunately. The problem can be reproduced in a minimal example like this:

#let scfigure(..args) = {
  set text(weight: "bold")
  figure(..args)
}
#scfigure([A], caption: [My A])<fig>
See @fig

Error: Cannot reference styled

So for scfigure to work, it should return just a figure and the rest of the customization needs to be done through a template. That’s the first option. (If you want to smuggle parameters from the function to the template’s action, it would be possible through adding metadata to the caption or the figure body, I think.)

The second option is to change the interface and allow passing the label as an argument instead:

#let scfigure(..args, label: none) = {
  set text(weight: "bold")
  [#figure(..args)#label]
}
#scfigure([A], caption: [My A], label: <fig>)
See @fig

Which is one way to get it to work.

2 Likes

Also worth looking at How to reference styled figures? - #4 by quachpas

1 Like

Thank you @bluss and @vmartel08, now I understand the issue. I managed to implement your solutions and they work like a charm! I had to pick one to mark as solution but I got precious insight from both.

1 Like