Converting markups to strings (Extending LilyPond)

#### 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)

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)

`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)

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)

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.26.0 (stable-branch).

- 1 Scheme tutorial
  - 1.1 Introduction to Scheme
    - 1.1.1 Scheme sandbox
    - 1.1.2 Scheme variables
    - 1.1.3 Scheme simple data types
    - 1.1.4 Scheme compound data types
      - Pairs
      - Lists
      - Association lists (alists)
      - Hash tables
    - 1.1.5 Calculations in Scheme
    - 1.1.6 Scheme procedures
      - Defining procedures
      - Predicates
      - Return values
    - 1.1.7 Scheme conditionals
      - if
      - cond
  - 1.2 Scheme in LilyPond
    - 1.2.1 LilyPond Scheme syntax
    - 1.2.2 LilyPond variables
    - 1.2.3 Debugging Scheme code
    - 1.2.4 Input variables and Scheme
    - 1.2.5 Importing Scheme in LilyPond
    - 1.2.6 Object properties
    - 1.2.7 LilyPond compound variables
      - Offsets
      - Fractions
      - Extents
      - Property alists
      - Alist chains
    - 1.2.8 LilyPond’s box model
    - 1.2.9 Internal music representation
  - 1.3 Building complicated functions
    - 1.3.1 A first approach (example)
    - 1.3.2 Displaying music expressions
    - 1.3.3 Music properties
    - 1.3.4 Doubling a note with slurs (example)
    - 1.3.5 Adding articulation to notes (example)
- 2 Interfaces for programmers
  - 2.1 LilyPond code blocks
  - 2.2 Scheme functions
    - 2.2.1 Scheme function definitions
    - 2.2.2 Scheme function usage
    - 2.2.3 Void Scheme functions
  - 2.3 Music functions
    - 2.3.1 Music function definitions
    - 2.3.2 Music function usage
    - 2.3.3 Simple substitution functions
    - 2.3.4 Intermediate substitution functions
    - 2.3.5 Mathematics in functions
    - 2.3.6 Functions without arguments
    - 2.3.7 Void music functions
  - 2.4 Event functions
  - 2.5 Markup functions
    - 2.5.1 Markup construction in Scheme
    - 2.5.2 How markups work internally
    - 2.5.3 New markup command definition
      - Markup command definition syntax
      - On properties
      - A complete example
      - Adapting builtin commands
      - Converting markups to strings
    - 2.5.4 New markup list command definition
  - 2.6 Contexts for programmers
    - 2.6.1 Context evaluation
    - 2.6.2 Running a function on all layout objects
  - 2.7 Callback functions
  - 2.8 Unpure-pure containers
  - 2.9 Difficult tweaks
- 3 LilyPond Scheme interfaces
- Appendix A GNU Free Documentation License
- Appendix B Index