How can I draw an arc below two words?

In writing down music lyrics sometimes an arc is drawn below two adjacent words to indicate that these words are to be sung faster. Basically I’d like to achieve the following effect, but without changing to math mode (as my font does not support math):

Hello, #math.underparen("it is") me again.

Typst-Playground01

Is there some way to achieve that? There is the #curve() function, but how to position it properly below text?

Using a content block ([…]) instead of a string (`“…”) will keep the font. Would this satisfy you?

Hello, #math.underparen[it is] me again.

Function Type – Typst Documentation:

You can call a function by writing a comma-separated list of function arguments enclosed in parentheses directly after the function name. Additionally, you can pass any number of trailing content blocks arguments to a function after the normal argument list. If the normal argument list would become empty, it can be omitted. Typst supports positional and named arguments. The former are identified by position and type, while the latter are written as name: value.

2 Likes

I tried that also, but still the math font is used for the part in the content block. It gets more obvious when you use a different default text font (I used PT Sans)

1 Like

Indeed… How about using math.underparen for an empty block, and placing it properly?

#set text(font: "Fira Sans")

Hello, #arc[it is] me again.

#arc[To be]

#arc[To be oh]

#arc[To be oh yeah]
#let arc(body) = context {
  let width = measure(body).width

  // Make a parenthesis for an empty block with a similar width
  let annotation = math.underparen(
    block(
      width: calc.max(
        width - 1em.to-absolute(),
        width * 80%,
      ),
      height: 1em,
    ),
  )

  // Make sure the layout won't be affected
  // https://typst.app/docs/reference/layout/place/#effect-on-other-elements
  box(place(bottom + center, dx: width / 2, annotation))
  sym.wj
  h(0pt, weak: true)

  body
}

This doesn’t work if body is broken into two lines, but I assume that’s acceptable for lyrics?

3 Likes

That works fine, thanks!

With my lyrics it is only two words, or even the syllables of a single word that occur on a single beat in following verses, and that’s where I want to place a mark below. So luckily linebreaks are not an issue.

2 Likes

I was learning about custom underlines and found this and decided to learn doing it, hope this helps. This one has the advantage than text copy doesn’t includes the arc character.

#let arc_words(height: 10pt, body) = {
    let arc_tiling(width, height) = tiling(size: (width, height),
        curve(stroke: 0.5pt,
            curve.move((0pt, 0pt)),
            curve.quad((50%, 50%), (100%, 0pt)),
        ),
    )

    context {
        let size = measure(body)
        underline(stroke: height + arc_tiling(size.width, height), offset: 2pt, body)
    }
}

Test of #arc_words("Hello World!") with an underline arc.

It looks this way, note it has the same problems if the text expand more than one line.

image

1 Like