Converting markups to strings

Markups are occasionally converted to plain strings, such as when outputting PDF metadata based on the title header field or for converting lyrics to MIDI. This conversion is inherently lossy, but tries to as accurate as feasible. The function used for this is markup->string.

composerName = \markup \box "Arnold Schönberg"

\markup \composerName

\markup \typewriter #(markup->string composerName)

[image of music]

For custom markup commands, the default behavior is to convert all markup or markup list arguments first, and join the results by spaces.

#(define-markup-command (authors-and layout props authorA authorB)
                        (markup? markup?)
   (interpret-markup layout props
    #{
      \markup \fill-line { \box #authorA and \box #authorB }
    #}))

defaultBehavior = \markup \authors-and "Bertolt Brecht" "Kurt Weill"

\markup \defaultBehavior

\markup \typewriter #(markup->string defaultBehavior)

[image of music]

markup->string can also receive the named arguments #:layout layout and #:props props, with the same meaning as in the definition of a markup command. However, they are optional because they cannot always be provided (such as the layout argument when converting to MIDI).

To support special conversions in custom markup commands, the #:as-string parameter can be given to define-markup-command. It expects an expression, which is evaluated by markup->string in order to yield the string result.

#(define-markup-command (authors-and layout props authorA authorB)
                        (markup? markup?)
   #:as-string (format #f "~a and ~a"
                       (markup->string authorA #:layout layout #:props props)
                       (markup->string authorB #:layout layout #:props props))
   (interpret-markup layout props
    #{
      \markup \fill-line { \box #authorA and \box #authorB }
    #}))

customized = \markup \authors-and "Bertolt Brecht" "Kurt Weill"

\markup \customized

\markup \typewriter #(markup->string customized)

[image of music]

Within the expression, the same bindings are available as in the main markup command body, namely layout and props, the command argument, and optionally the properties.

#(define-markup-command (authors-and layout props authorA authorB)
                        (markup? markup?)
   #:properties ((author-separator " and "))
   #:as-string (format #f "~a~a~a"
                       (markup->string authorA #:layout layout #:props props)
                       (markup->string author-separator #:layout layout #:props props)
                       (markup->string authorB #:layout layout #:props props))
   (interpret-markup layout props
    #{
      \markup { \box #authorA #author-separator \box #authorB }
    #}))

customized = \markup \override #'(author-separator . ", ")
                     \authors-and "Bertolt Brecht" "Kurt Weill"

\markup \customized

\markup \typewriter #(markup->string customized)

[image of music]

Most often, a custom handler only needs to call markup->string recursively on certain arguments, as demonstrated above. However, it can also make use of the layout and props directly, in the same way as in the main body. Care must be taken that the layout argument is #f if no #:layout has been given to markup->string. The props argument defaults to the empty list.


Extending LilyPond v2.25.22 (development-branch).