Incorrect hanging indentation

The code below:

#set par(
  justify: true,
  leading: 12pt * 1.15 * 1.5,
)
#show figure.caption: set align(left)

#let figura(
  body,
  caption: none,
  source: none,
  ..args,
  ) = figure(
  [
    #context {
      body
      block(width: measure(body).width)[
        #if source != none [
          #align(left)[
            #text(size: 7pt)[
              #let prefix = [Source: ]
  
              #par(
                leading: 10pt * 1.15,
                hanging-indent: measure(prefix).width,
              )[
                #prefix#source
              ]
            ]
          ]
        ]
      ]
    }
  ],
  caption: if caption != none {
    figure.caption(
      position: top,
      separator: [ — ],
      [#caption]
    )
  },
  ..args,
)

#figura(
  rect(
    width: 5cm,
    height: 2cm,
    stroke: 10pt,
  ),
  caption: lorem(18),
  source: lorem(20),
)

produces the result:


Why isn’t the source paragraph correctly hanging? I used 7pt size instead of 10pt—which is what I need—to highlight the discrepancy.

The fact that there’s a couple of captions is more intriguing to me than the indentation.

So the only thing to notice here about indentation is the use of list.marker.

It’s for you to decide how to proceed when arguments are none, "", and so on.

Code
#set figure(gap: 2em)
#set figure.caption(position: top, separator: [ — ])
#show figure.caption: set align(left)
#show figure.caption: set par(justify: true)

#let figure-hung(..args, caption: "", source: "", body) = {
  set list(marker: "Source: ")
  show list.item: set align(left)
  show list.item: set par(justify: true)
  figure(
    ..args,
    caption: caption,
    body + context block(
      width: measure(body).width,
      list.item(source),
    ),
  )
}

#figure-hung(
  caption: lorem(50),
  source: lorem(10),
  rect(width: 5cm),
)

/* Alternative use of `terms`, as opposed to `list`:
#let figure-hung(..args, caption: "", source: "", body) = {
  let source-pre = "Source: "
  show terms.item: set align(left)
  show terms.item: set par(justify: true)
  // See https://forum.typst.app/t/how-to-style-terms-terms-only-not-definitions/5314/2
  show terms.item: it => { show strong: it => it.body; it }
  figure(
    ..args,
    caption: caption,
    body + context {
      set terms(
        separator: none,
        hanging-indent: measure(source-pre).width,
      )
      block(
        width: measure(body).width,
        terms.item(source-pre, source),
      )
    },
  )
}
*/
Output

2 Likes

Your code works, but the figures can no longer be referenced using “<>”.

Just pass the label to the figure-hung function:

#set figure(gap: 2em)
#set figure.caption(position: top, separator: [ — ])
#show figure.caption: set align(left)
#show figure.caption: set par(justify: true)

#let figure-hung(..args, caption: "", source: "", lbl: none, body) = {
  set list(marker: "Source: ")
  show list.item: set align(left)
  show list.item: set par(justify: true)
  [
    #figure(
      ..args,
      caption: caption,
      body + context block(
        width: measure(body).width,
        list.item(source),
      ),
    ) #if lbl != none { label(lbl)}
  ]
}

#figure-hung(
  caption: lorem(50),
  source: lorem(10),
  rect(width: 5cm),
  lbl: "hello"
)

Hey @hello
2 Likes

Your code using “lbl” works, but it wouldn’t be ideal for people coming from the tutorial. I also don’t want the original syntax used in some places and not others, so I wanted to ask for a solution that uses “<>”.

You can make it return a figure instead:

#let figure-hung(..args, caption: "", source: "", body) = figure(
  ..args,
  caption: caption,
  body + context block(
    width: measure(body).width,
  )[
    #set list(marker: "Source: ")
    #show list.item: set align(left)
    #show list.item: set par(justify: true)
    #list.item(source)
  ]
)
2 Likes

I tried adding the code below to your response so that the word "Source: " wouldn’t be displayed when “source” isn’t called, but it didn’t work.

    #if source != none {
      text(size: 10pt)[
        #list.item(source)
      ]
    }

Edit: I’m referring to Aarnent’s answer:

#let figure-hung(..args, caption: "", source: "", body) = figure(
  ..args,
  caption: caption,
  body + context block(
    width: measure(body).width,
  )[
    #set list(marker: "Source: ")
    #show list.item: set align(left)
    #show list.item: set par(justify: true)
    #list.item(source)
  ]
)

What happened: "Source: " appears even when the “source” field has not been called.

What I expect to happen: I want "Source: " not to appear when the “source” field has not been called.

You should paste the whole code when you encounter such a problem, often the bug isn’t in the code you think it is. Here from the code that aarnent posted, your error could be that the default value for source is "", not none so your test is always true (unless you explicitly pass source: none to the function).

And “didn’t work” isn’t a very useful description of a result, if possible, it would be better to explain what you expect and what you really get.

1 Like