]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
Issue 5148/1: Various chain-assoc-get -> #:properties
[lilypond.git] / scm / define-markup-commands.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; Copyright (C) 2000--2015  Han-Wen Nienhuys <hanwen@xs4all.nl>
4 ;;;;                  Jan Nieuwenhuizen <janneke@gnu.org>
5 ;;;;
6 ;;;; LilyPond is free software: you can redistribute it and/or modify
7 ;;;; it under the terms of the GNU General Public License as published by
8 ;;;; the Free Software Foundation, either version 3 of the License, or
9 ;;;; (at your option) any later version.
10 ;;;;
11 ;;;; LilyPond is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;;; GNU General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU General Public License
17 ;;;; along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;;
20 ;;; Markup commands and markup-list commands definitions.
21 ;;;
22 ;;; Markup commands which are part of LilyPond, are defined
23 ;;; in the (lily) module, which is the current module in this file,
24 ;;; using the `define-markup-command' macro.
25 ;;;
26 ;;; Usage:
27 ;;;
28 ;;; (define-markup-command (command-name layout props args...)
29 ;;;   args-signature
30 ;;;   [ #:category category ]
31 ;;;   [ #:properties property-bindings ]
32 ;;;   documentation-string
33 ;;;   ..body..)
34 ;;;
35 ;;; with:
36 ;;;   command-name
37 ;;;     the name of the markup command
38 ;;;
39 ;;;   layout and props
40 ;;;     arguments that are automatically passed to the command when it
41 ;;;     is interpreted.
42 ;;;     `layout' is an output def, which properties can be accessed
43 ;;;     using `ly:output-def-lookup'.
44 ;;;     `props' is a list of property settings which can be accessed
45 ;;;     using `chain-assoc-get' (more on that below)
46 ;;;
47 ;;;   args...
48 ;;;     the command arguments.
49 ;;;     There is no limitation on the order of command arguments.
50 ;;;     However, markup functions taking a markup as their last
51 ;;;     argument are somewhat special as you can apply them to a
52 ;;;     markup list, and the result is a markup list where the
53 ;;;     markup function (with the specified leading arguments) has
54 ;;;     been applied to every element of the original markup list.
55 ;;;
56 ;;;     Since replicating the leading arguments for applying a
57 ;;;     markup function to a markup list is cheap mostly for
58 ;;;     Scheme arguments, you avoid performance pitfalls by just
59 ;;;     using Scheme arguments for the leading arguments of markup
60 ;;;     functions that take a markup as their last argument.
61 ;;;
62 ;;;   args-signature
63 ;;;     the arguments signature, i.e., a list of type predicates which
64 ;;;     are used to type check the arguments, and also to define the general
65 ;;;     argument types (markup, markup-list, scheme) that the command is
66 ;;;     expecting.
67 ;;;     For instance, if a command expects a number, then a markup, the
68 ;;;     signature would be: (number? markup?)
69 ;;;
70 ;;;   category
71 ;;;     for documentation purpose, builtin markup commands are grouped by
72 ;;;     category.  This can be any symbol.  When documentation is generated,
73 ;;;     the symbol is converted to a capitalized string, where hyphens are
74 ;;;     replaced by spaces.
75 ;;;
76 ;;;   property-bindings
77 ;;;     this is used both for documentation generation, and to ease
78 ;;;     programming the command itself.  It is list of
79 ;;;        (property-name default-value)
80 ;;;     or (property-name)
81 ;;;     elements.  Each property is looked-up in the `props' argument, and
82 ;;;     the symbol naming the property is bound to its value.
83 ;;;     When the property is not found in `props', then the symbol is bound
84 ;;;     to the given default value.  When no default value is given, #f is
85 ;;;     used instead.
86 ;;;     Thus, using the following property bindings:
87 ;;;       ((thickness 0.1)
88 ;;;        (font-size 0))
89 ;;;     is equivalent to writing:
90 ;;;       (let ((thickness (chain-assoc-get 'thickness props 0.1))
91 ;;;             (font-size (chain-assoc-get 'font-size props 0)))
92 ;;;         ..body..)
93 ;;;     When a command `B' internally calls an other command `A', it may
94 ;;;     desirable to see in `B' documentation all the properties and
95 ;;;     default values used by `A'.  In that case, add `A-markup' to the
96 ;;;     property-bindings of B.  (This is used when generating
97 ;;;     documentation, but won't create bindings.)
98 ;;;
99 ;;;   documentation-string
100 ;;;     the command documentation string (used to generate manuals)
101 ;;;
102 ;;;   body
103 ;;;     the command body.  The function is supposed to return a stencil.
104 ;;;
105 ;;; Each markup command definition shall have a documentation string
106 ;;; with description, syntax and example.
107
108 (use-modules (ice-9 regex))
109
110 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
111 ;; utility functions
112 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
113
114 (define-public empty-stencil (ly:make-stencil '()
115                                               empty-interval empty-interval))
116 (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
117
118 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119 ;; line has to come early since it is often used implicitly from the
120 ;; markup macro since \markup { a b c } -> \markup \line { a b c }
121 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
122
123 (define-markup-command (line layout props args)
124   (markup-list?)
125   #:category align
126   #:properties ((word-space)
127                 (text-direction RIGHT))
128   "Put @var{args} in a horizontal line.  The property @code{word-space}
129 determines the space between markups in @var{args}.
130
131 @lilypond[verbatim,quote]
132 \\markup {
133   \\line {
134     one two three
135   }
136 }
137 @end lilypond"
138   (let ((stencils (interpret-markup-list layout props args)))
139     (if (= text-direction LEFT)
140         (set! stencils (reverse stencils)))
141     (stack-stencil-line word-space stencils)))
142
143 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
144 ;; geometric shapes
145 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
146
147 (define-markup-command (draw-line layout props dest)
148   (number-pair?)
149   #:category graphic
150   #:properties ((thickness 1))
151   "
152 @cindex drawing lines within text
153
154 A simple line.
155 @lilypond[verbatim,quote]
156 \\markup {
157   \\draw-line #'(4 . 4)
158   \\override #'(thickness . 5)
159   \\draw-line #'(-3 . 0)
160 }
161 @end lilypond"
162   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
163                thickness))
164         (x (car dest))
165         (y (cdr dest)))
166     (make-line-stencil th 0 0 x y)))
167
168 (define-markup-command (draw-dashed-line layout props dest)
169   (number-pair?)
170   #:category graphic
171   #:properties ((thickness 1)
172                 (on 1)
173                 (off 1)
174                 (phase 0)
175                 (full-length #t))
176   "
177 @cindex drawing dashed lines within text
178
179 A dashed line.
180
181 If @code{full-length} is set to #t (default) the dashed-line extends to the
182 whole length given by @var{dest}, without white space at beginning or end.
183 @code{off} will then be altered to fit.
184 To insist on the given (or default) values of @code{on}, @code{off} use
185 @code{\\override #'(full-length . #f)}
186 Manual settings for @code{on},@code{off} and @code{phase} are possible.
187 @lilypond[verbatim,quote]
188 \\markup {
189   \\draw-dashed-line #'(5.1 . 2.3)
190   \\override #'(on . 0.3)
191   \\override #'(off . 0.5)
192   \\draw-dashed-line #'(5.1 . 2.3)
193 }
194 @end lilypond"
195   (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
196          ;; Calculate the thickness to be used.
197          (th (* line-thickness thickness))
198          (half-thick (/ th 2))
199          ;; Get the extensions in x- and y-direction.
200          (x (car dest))
201          (y (cdr dest))
202          ;; Calculate the length of the dashed line.
203          (line-length (sqrt (+ (expt x 2) (expt y 2)))))
204
205     (if (and full-length (not (= (+ on off) 0)))
206         (begin
207           ;; Add double-thickness to avoid overlapping.
208           (set! off (+ (* 2 th) off))
209           (let* (;; Make a guess how often the off/on-pair should be printed
210                  ;; after the initial `onĀ“.
211                  ;; Assume a minimum of 1 to avoid division by zero.
212                  (guess (max 1 (round (/ (- line-length on) (+ off on)))))
213                  ;; Not sure about the value or why corr is necessary at all,
214                  ;; but it seems to be necessary.
215                  (corr (if (= on 0)
216                            (/ line-thickness 10)
217                            0))
218                  ;; Calculate a new value for off to fit the
219                  ;; line-length.
220                  (new-off (/ (- line-length corr (* (1+ guess) on)) guess))
221                  )
222             (cond
223
224              ;; Settings for (= on 0). Resulting in a dotted line.
225
226              ;; If line-length isn't shorter than `thĀ“, change the given
227              ;; value for `offĀ“ to fit the line-length.
228              ((and (= on 0) (< th line-length))
229               (set! off new-off))
230
231              ;; If the line-length is shorter than `thĀ“, it makes no
232              ;; sense to adjust `offĀ“. The rounded edges of the lines
233              ;; would prevent any nice output.
234              ;; Do nothing.
235              ;; This will result in a single dot for very short lines.
236              ((and (= on 0) (>= th line-length))
237               #f)
238
239              ;; Settings for (not (= on 0)). Resulting in a dashed line.
240
241              ;; If line-length isn't shorter than one go of on-off-on,
242              ;; change the given value for `offĀ“ to fit the line-length.
243              ((< (+ (* 2 on) off) line-length)
244               (set! off new-off))
245              ;; If the line-length is too short, but greater than
246              ;; (* 4 th) set on/off to (/ line-length 3)
247              ((< (* 4 th) line-length)
248               (set! on (/ line-length 3))
249               (set! off (/ line-length 3)))
250              ;; If the line-length is shorter than (* 4 th), it makes
251              ;; no sense trying to adjust on/off. The rounded edges of
252              ;; the lines would prevent any nice output.
253              ;; Simply set `onĀ“ to line-length.
254              (else
255               (set! on line-length))))))
256
257     ;; If `onĀ“ or `offĀ“ is negative, or the sum of `on' and `off' equals zero a
258     ;; ghostscript-error occurs while calling
259     ;; (ly:make-stencil (list 'dashed-line th on off x y phase) x-ext y-ext)
260     ;; Better be paranoid.
261     (if (or (= (+ on off) 0)
262             (negative? on)
263             (negative? off))
264         (begin
265           (ly:warning "Can't print a line - setting on/off to default")
266           (set! on 1)
267           (set! off 1)))
268
269     ;; To give the lines produced by \draw-line and \draw-dashed-line the same
270     ;; length, half-thick has to be added to the stencil-extensions.
271     (ly:make-stencil
272      (list 'dashed-line th on off x y phase)
273      (interval-widen (ordered-cons 0 x) half-thick)
274      (interval-widen (ordered-cons 0 y) half-thick))))
275
276 (define-markup-command (draw-dotted-line layout props dest)
277   (number-pair?)
278   #:category graphic
279   #:properties ((thickness 1)
280                 (off 1)
281                 (phase 0))
282   "
283 @cindex drawing dotted lines within text
284
285 A dotted line.
286
287 The dotted-line always extends to the whole length given by @var{dest}, without
288 white space at beginning or end.
289 Manual settings for @code{off} are possible to get larger or smaller space
290 between the dots.
291 The given (or default) value of @code{off} will be altered to fit the
292 line-length.
293 @lilypond[verbatim,quote]
294 \\markup {
295   \\draw-dotted-line #'(5.1 . 2.3)
296   \\override #'(thickness . 2)
297   \\override #'(off . 0.2)
298   \\draw-dotted-line #'(5.1 . 2.3)
299 }
300 @end lilypond"
301
302   (let ((new-props (prepend-alist-chain 'on 0
303                                         (prepend-alist-chain 'full-length #t props))))
304
305     (interpret-markup layout
306                       new-props
307                       (markup #:draw-dashed-line dest))))
308
309 (define-markup-command (draw-squiggle-line layout props sq-length dest eq-end?)
310   (number? number-pair? boolean?)
311   #:category graphic
312   #:properties ((thickness 0.5)
313                 (angularity 0)
314                 (height 0.5)
315                 (orientation 1))
316   "
317 @cindex drawing squiggled lines within text
318
319 A squiggled line.
320
321 If @code{eq-end?} is set to @code{#t}, it is ensured the squiggled line ends
322 with a bow in same direction as the starting one.  @code{sq-length} is the
323 length of the first bow.  @code{dest} is the end point of the squiggled line.
324 To match @code{dest} the squiggled line is scaled accordingly.
325 Its appearance may be customized by overrides for @code{thickness},
326 @code{angularity}, @code{height} and @code{orientation}.
327 @lilypond[verbatim,quote]
328 \\markup
329   \\column {
330     \\draw-squiggle-line #0.5 #'(6 . 0) ##t
331     \\override #'(orientation . -1)
332     \\draw-squiggle-line #0.5 #'(6 . 0) ##t
333     \\draw-squiggle-line #0.5 #'(6 . 0) ##f
334     \\override #'(height . 1)
335     \\draw-squiggle-line #0.5 #'(6 . 0) ##t
336     \\override #'(thickness . 5)
337     \\draw-squiggle-line #0.5 #'(6 . 0) ##t
338     \\override #'(angularity . 2)
339     \\draw-squiggle-line #0.5 #'(6 . 0) ##t
340   }
341 @end lilypond"
342   (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
343          (thick (* thickness line-thickness))
344          (x (car dest))
345          (y (cdr dest))
346          (length-to-print (magnitude (make-rectangular x y)))
347          ;; Make a guess how many bows may be needed
348          (guess (max 1 (truncate (/ length-to-print sq-length))))
349          ;; If `eq-end?' is set #t, make sure squiggle-line starts and ends
350          ;; with a bow in same direction
351          (amount (if (and (even? guess) eq-end?) (1+ guess) guess))
352          ;; The lined-up bows needs to fit `length-to-print'
353          ;; Thus scale the length of first bow accordingly
354          ;; Other bows are copies
355          (guessed-squiggle-line-length (* amount sq-length))
356          (line-length-diff (- length-to-print guessed-squiggle-line-length))
357          (line-length-diff-for-each-squiggle
358            (/ line-length-diff amount))
359          (first-bow-length (+ sq-length line-length-diff-for-each-squiggle))
360          ;; Get first bows
361          ;; TODO two bows are created via `make-bow-stencil'
362          ;;      cheaper to use `ly:stencil-scale'?
363          (first-bow-end-coord
364            (cons
365              (/ (* first-bow-length x) length-to-print)
366              (/ (* first-bow-length y) length-to-print)))
367          (init-bow
368            (lambda (o)
369              (make-bow-stencil
370                '(0 . 0)
371                first-bow-end-coord
372                thick angularity height o)))
373          (init-bow-up (init-bow orientation))
374          (init-bow-down (init-bow (- orientation)))
375          ;; Get a list of starting-points for the bows
376          (list-of-starts
377            (map
378              (lambda (n)
379                (cons
380                  (* n (car first-bow-end-coord))
381                  (* n (cdr first-bow-end-coord))))
382              (iota amount))))
383     ;; The final stencil: lined-up bows
384     (apply ly:stencil-add
385       (map
386         ly:stencil-translate
387         (circular-list init-bow-up init-bow-down)
388         list-of-starts))))
389
390 (define-markup-command (draw-hline layout props)
391   ()
392   #:category graphic
393   #:properties ((draw-line-markup)
394                 (line-width)
395                 (span-factor 1))
396   "
397 @cindex drawing a line across a page
398
399 Draws a line across a page, where the property @code{span-factor}
400 controls what fraction of the page is taken up.
401 @lilypond[verbatim,quote]
402 \\markup {
403   \\column {
404     \\draw-hline
405     \\override #'(span-factor . 1/3)
406     \\draw-hline
407   }
408 }
409 @end lilypond"
410   (interpret-markup layout
411                     props
412                     (markup #:draw-line (cons (* line-width
413                                                  span-factor)
414                                               0))))
415
416 (define-markup-command (draw-circle layout props radius thickness filled)
417   (number? number? boolean?)
418   #:category graphic
419   "
420 @cindex drawing circles within text
421
422 A circle of radius @var{radius} and thickness @var{thickness},
423 optionally filled.
424
425 @lilypond[verbatim,quote]
426 \\markup {
427   \\draw-circle #2 #0.5 ##f
428   \\hspace #2
429   \\draw-circle #2 #0 ##t
430 }
431 @end lilypond"
432   (make-circle-stencil radius thickness filled))
433
434 (define-markup-command (triangle layout props filled)
435   (boolean?)
436   #:category graphic
437   #:properties ((thickness 0.1)
438                 (font-size 0)
439                 (baseline-skip 2))
440   "
441 @cindex drawing triangles within text
442
443 A triangle, either filled or empty.
444
445 @lilypond[verbatim,quote]
446 \\markup {
447   \\triangle ##t
448   \\hspace #2
449   \\triangle ##f
450 }
451 @end lilypond"
452   (let ((ex (* (magstep font-size) 0.8 baseline-skip)))
453     (ly:make-stencil
454      `(polygon '(0.0 0.0
455                      ,ex 0.0
456                      ,(* 0.5 ex)
457                      ,(* 0.86 ex))
458                ,thickness
459                ,filled)
460      (cons 0 ex)
461      (cons 0 (* .86 ex)))))
462
463 (define-markup-command (circle layout props arg)
464   (markup?)
465   #:category graphic
466   #:properties ((thickness 1)
467                 (font-size 0)
468                 (circle-padding 0.2))
469   "
470 @cindex circling text
471
472 Draw a circle around @var{arg}.  Use @code{thickness},
473 @code{circle-padding} and @code{font-size} properties to determine line
474 thickness and padding around the markup.
475
476 @lilypond[verbatim,quote]
477 \\markup {
478   \\circle {
479     Hi
480   }
481 }
482 @end lilypond"
483   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
484                thickness))
485         (pad (* (magstep font-size) circle-padding))
486         (m (interpret-markup layout props arg)))
487     (circle-stencil m th pad)))
488
489 (define-markup-command (ellipse layout props arg)
490   (markup?)
491   #:category graphic
492   #:properties ((thickness 1)
493                 (font-size 0)
494                 (x-padding 0.2)
495                 (y-padding 0.2))
496   "
497 @cindex drawing ellipse around text
498
499 Draw an ellipse around @var{arg}.  Use @code{thickness},
500 @code{x-padding}, @code{y-padding} and @code{font-size} properties to determine
501 line thickness and padding around the markup.
502
503 @lilypond[verbatim,quote]
504 \\markup {
505   \\ellipse {
506     Hi
507   }
508 }
509 @end lilypond"
510   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
511                thickness))
512         (pad-x (* (magstep font-size) x-padding))
513         (pad-y (* (magstep font-size) y-padding))
514         (m (interpret-markup layout props arg)))
515     (ellipse-stencil m th pad-x pad-y)))
516
517 (define-markup-command (oval layout props arg)
518   (markup?)
519   #:category graphic
520   #:properties ((thickness 1)
521                 (font-size 0)
522                 (x-padding 0.75)
523                 (y-padding 0.75))
524   "
525 @cindex drawing oval around text
526
527 Draw an oval around @var{arg}.  Use @code{thickness},
528 @code{x-padding}, @code{x-padding} and @code{font-size} properties to determine
529 line thickness and padding around the markup.
530
531 @lilypond[verbatim,quote]
532 \\markup {
533   \\oval {
534     Hi
535   }
536 }
537 @end lilypond"
538   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
539                thickness))
540         (pad-x (* (magstep font-size) x-padding))
541         (pad-y (* (magstep font-size) y-padding))
542         (m (interpret-markup layout props arg)))
543     (oval-stencil m th pad-x pad-y)))
544
545 (define-markup-command (with-url layout props url arg)
546   (string? markup?)
547   #:category graphic
548   "
549 @cindex inserting URL links into text
550
551 Add a link to URL @var{url} around @var{arg}.  This only works in
552 the PDF backend.
553
554 @lilypond[verbatim,quote]
555 \\markup {
556   \\with-url #\"http://lilypond.org/\" {
557     LilyPond ... \\italic {
558       music notation for everyone
559     }
560   }
561 }
562 @end lilypond"
563   (let* ((stil (interpret-markup layout props arg))
564          (xextent (ly:stencil-extent stil X))
565          (yextent (ly:stencil-extent stil Y))
566          (old-expr (ly:stencil-expr stil))
567          (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
568
569     (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
570
571 (define-markup-command (page-link layout props page-number arg)
572   (number? markup?)
573   #:category other
574   "
575 @cindex referencing page numbers in text
576
577 Add a link to the page @var{page-number} around @var{arg}.  This only works
578 in the PDF backend.
579
580 @lilypond[verbatim,quote]
581 \\markup {
582   \\page-link #2  { \\italic { This links to page 2... } }
583 }
584 @end lilypond"
585   (let* ((stil (interpret-markup layout props arg))
586          (xextent (ly:stencil-extent stil X))
587          (yextent (ly:stencil-extent stil Y))
588          (old-expr (ly:stencil-expr stil))
589          (link-expr (list 'page-link page-number `(quote ,xextent) `(quote ,yextent))))
590
591     (ly:stencil-add (ly:make-stencil link-expr xextent yextent) stil)))
592
593 (define-public (book-first-page layout props)
594   "Return the @code{'first-page-number} of the entire book"
595   (define (ancestor layout)
596     "Return the topmost layout ancestor"
597     (let ((parent (ly:output-def-parent layout)))
598       (if (not (ly:output-def? parent))
599           layout
600           (ancestor parent))))
601   (ly:output-def-lookup (ancestor layout) 'first-page-number))
602
603 (define-markup-command (with-link layout props label arg)
604   (symbol? markup?)
605   #:category other
606   "
607 @cindex referencing page labels in text
608
609 Add a link to the page holding label @var{label} around @var{arg}.  This
610 only works in the PDF backend.
611
612 @lilypond[verbatim,quote]
613 \\markup {
614   \\with-link #'label {
615     \\italic { This links to the page containing the label... }
616   }
617 }
618 @end lilypond"
619   (let* ((arg-stencil (interpret-markup layout props arg))
620          (x-ext (ly:stencil-extent arg-stencil X))
621          (y-ext (ly:stencil-extent arg-stencil Y)))
622     (ly:stencil-add
623       (ly:make-stencil
624        `(delay-stencil-evaluation
625          ,(delay (let* ((table (ly:output-def-lookup layout 'label-page-table))
626                         (table-page-number
627                           (if (list? table)
628                               (assoc-get label table)
629                               #f))
630                         (first-page-number (book-first-page layout props))
631                         (current-page-number
632                           (if table-page-number
633                               (1+ (- table-page-number first-page-number))
634                               #f)))
635                  (list 'page-link current-page-number
636                        `(quote ,x-ext) `(quote ,y-ext)))))
637        x-ext
638        y-ext)
639       arg-stencil)))
640
641 (define-markup-command (beam layout props width slope thickness)
642   (number? number? number?)
643   #:category graphic
644   "
645 @cindex drawing beams within text
646
647 Create a beam with the specified parameters.
648 @lilypond[verbatim,quote]
649 \\markup {
650   \\beam #5 #1 #2
651 }
652 @end lilypond"
653   (let* ((y (* slope width))
654          (yext (cons (min 0 y) (max 0 y)))
655          (half (/ thickness 2)))
656
657     (ly:make-stencil
658      `(polygon ',(list
659                   0 (/ thickness -2)
660                   width (+ (* width slope)  (/ thickness -2))
661                   width (+ (* width slope)  (/ thickness 2))
662                   0 (/ thickness 2))
663                ,(ly:output-def-lookup layout 'blot-diameter)
664                #t)
665      (cons 0 width)
666      (cons (+ (- half) (car yext))
667            (+ half (cdr yext))))))
668
669 (define-markup-command (underline layout props arg)
670   (markup?)
671   #:category font
672   #:properties ((thickness 1) (offset 2))
673   "
674 @cindex underlining text
675
676 Underline @var{arg}.  Looks at @code{thickness} to determine line
677 thickness, and @code{offset} to determine line y-offset.
678
679 @lilypond[verbatim,quote]
680 \\markup \\fill-line {
681   \\underline \"underlined\"
682   \\override #'(offset . 5)
683   \\override #'(thickness . 1)
684   \\underline \"underlined\"
685   \\override #'(offset . 1)
686   \\override #'(thickness . 5)
687   \\underline \"underlined\"
688 }
689 @end lilypond"
690   (let* ((thick (ly:output-def-lookup layout 'line-thickness))
691          (underline-thick (* thickness thick))
692          (m (interpret-markup layout props arg))
693          (x1 (car (ly:stencil-extent m X)))
694          (x2 (cdr (ly:stencil-extent m X)))
695          (y (* thick (- offset)))
696          (line (make-line-stencil underline-thick x1 y x2 y)))
697     (ly:stencil-add m line)))
698
699 (define-markup-command (tie layout props arg)
700   (markup?)
701   #:category font
702   #:properties ((thickness 1)
703                 (offset 2)
704                 (direction UP)
705                 (shorten-pair '(0 . 0)))
706   "
707 @cindex tie-ing text
708
709 Adds a horizontal bow created with @code{make-tie-stencil} at bottom or top
710 of @var{arg}.  Looks at @code{thickness} to determine line thickness, and
711 @code{offset} to determine y-offset.  The added bow fits the extent of
712 @var{arg}, @code{shorten-pair} may be used to modify this.
713 @var{direction} may be set using an @code{override} or direction-modifiers or
714 @code{voiceOne}, etc.
715
716 @lilypond[verbatim,quote]
717 \\markup {
718   \\override #'(direction . 1)
719   \\tie \"above\"
720   \\override #'(direction . -1)
721   \\tie \"below\"
722 }
723 @end lilypond"
724   (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
725          (thick (* thickness line-thickness))
726          (stil (interpret-markup layout props arg))
727          (x1 (car (ly:stencil-extent stil X)))
728          (x2 (cdr (ly:stencil-extent stil X)))
729          (y-ext (ly:stencil-extent stil Y))
730          (y (+ (* line-thickness offset direction)
731                ;; we put out zero for positive text-direction, to make it
732                ;; consistent with `underline-markup'
733                ;; TODO: this will be problematic for args like "Eng"
734                ;;       fix it here _and_ in `underline-markup'
735                (if (negative? direction) 0 (cdr y-ext))))
736          (tie
737            (make-tie-stencil
738              (cons (+ x1 (car shorten-pair) line-thickness) y)
739              (cons (- x2 (cdr shorten-pair) line-thickness) y)
740              thick
741              direction)))
742     (ly:stencil-add stil tie)))
743
744 (define-markup-command (undertie layout props arg)
745   (markup?)
746   #:category font
747   #:properties (tie-markup)
748   "
749 @cindex undertie-ing text
750
751 @lilypond[verbatim,quote]
752 \\markup \\line {
753   \\undertie \"undertied\"
754   \\override #'(offset . 5)
755   \\override #'(thickness . 1)
756   \\undertie \"undertied\"
757   \\override #'(offset . 1)
758   \\override #'(thickness . 5)
759   \\undertie \"undertied\"
760 }
761 @end lilypond"
762   (interpret-markup layout (prepend-alist-chain 'direction DOWN props)
763     (make-tie-markup arg)))
764
765 (define-markup-command (overtie layout props arg)
766   (markup?)
767   #:category font
768   #:properties (tie-markup)
769   "
770 @cindex overtie-ing text
771
772 Overtie @var{arg}.
773
774 @lilypond[verbatim,quote]
775 \\markup \\line {
776   \\overtie \"overtied\"
777   \\override #'(offset . 5)
778   \\override #'(thickness . 1)
779   \\overtie \"overtied\"
780   \\override #'(offset . 1)
781   \\override #'(thickness . 5)
782   \\overtie \"overtied\"
783 }
784 @end lilypond"
785   (interpret-markup layout (prepend-alist-chain 'direction UP props)
786     (make-tie-markup arg)))
787
788 (define-markup-command (box layout props arg)
789   (markup?)
790   #:category font
791   #:properties ((thickness 1)
792                 (font-size 0)
793                 (box-padding 0.2))
794   "
795 @cindex enclosing text within a box
796
797 Draw a box round @var{arg}.  Looks at @code{thickness},
798 @code{box-padding} and @code{font-size} properties to determine line
799 thickness and padding around the markup.
800
801 @lilypond[verbatim,quote]
802 \\markup {
803   \\override #'(box-padding . 0.5)
804   \\box
805   \\line { V. S. }
806 }
807 @end lilypond"
808   (let* ((th (* (ly:output-def-lookup layout 'line-thickness)
809                 thickness))
810          (pad (* (magstep font-size) box-padding))
811          (m (interpret-markup layout props arg)))
812     (box-stencil m th pad)))
813
814 (define-markup-command (filled-box layout props xext yext blot)
815   (number-pair? number-pair? number?)
816   #:category graphic
817   "
818 @cindex drawing solid boxes within text
819 @cindex drawing boxes with rounded corners
820
821 Draw a box with rounded corners of dimensions @var{xext} and
822 @var{yext}.  For example,
823 @verbatim
824 \\filled-box #'(-.3 . 1.8) #'(-.3 . 1.8) #0
825 @end verbatim
826 creates a box extending horizontally from -0.3 to 1.8 and
827 vertically from -0.3 up to 1.8, with corners formed from a
828 circle of diameter@tie{}0 (i.e., sharp corners).
829
830 @lilypond[verbatim,quote]
831 \\markup {
832   \\filled-box #'(0 . 4) #'(0 . 4) #0
833   \\filled-box #'(0 . 2) #'(-4 . 2) #0.4
834   \\filled-box #'(1 . 8) #'(0 . 7) #0.2
835   \\with-color #white
836   \\filled-box #'(-4.5 . -2.5) #'(3.5 . 5.5) #0.7
837 }
838 @end lilypond"
839   (ly:round-filled-box
840    xext yext blot))
841
842 (define-markup-command (rounded-box layout props arg)
843   (markup?)
844   #:category graphic
845   #:properties ((thickness 1)
846                 (corner-radius 1)
847                 (font-size 0)
848                 (box-padding 0.5))
849   "@cindex enclosing text in a box with rounded corners
850    @cindex drawing boxes with rounded corners around text
851 Draw a box with rounded corners around @var{arg}.  Looks at @code{thickness},
852 @code{box-padding} and @code{font-size} properties to determine line
853 thickness and padding around the markup; the @code{corner-radius} property
854 makes it possible to define another shape for the corners (default is 1).
855
856 @lilypond[quote,verbatim,relative=2]
857 c4^\\markup {
858   \\rounded-box {
859     Overtura
860   }
861 }
862 c,8. c16 c4 r
863 @end lilypond"
864   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
865                thickness))
866         (pad (* (magstep font-size) box-padding))
867         (m (interpret-markup layout props arg)))
868     (ly:stencil-add (rounded-box-stencil m th pad corner-radius)
869                     m)))
870
871 (define-markup-command (rotate layout props ang arg)
872   (number? markup?)
873   #:category align
874   "
875 @cindex rotating text
876
877 Rotate object with @var{ang} degrees around its center.
878
879 @lilypond[verbatim,quote]
880 \\markup {
881   default
882   \\hspace #2
883   \\rotate #45
884   \\line {
885     rotated 45Ā°
886   }
887 }
888 @end lilypond"
889   (let* ((stil (interpret-markup layout props arg)))
890     (ly:stencil-rotate stil ang 0 0)))
891
892 (define-markup-command (whiteout layout props arg)
893   (markup?)
894   #:category other
895   #:properties ((style 'box)
896                 (thickness '()))
897   "
898 @cindex adding a white background to text
899
900 Provide a white background for @var{arg}.  The shape of the white
901 background is determined by @code{style}.  The default
902 is @code{box} which produces a rectangle.  @code{rounded-box}
903 produces a rounded rectangle.  @code{outline} approximates the
904 outline of the markup.
905
906 @lilypond[verbatim,quote]
907 \\markup {
908   \\combine
909     \\filled-box #'(-1 . 15) #'(-3 . 4) #1
910     \\override #'(thickness . 1.5)
911     \\whiteout whiteout-box
912 }
913 \\markup {
914   \\combine
915     \\filled-box #'(-1 . 24) #'(-3 . 4) #1
916     \\override #'(style . rounded-box)
917     \\override #'(thickness . 3)
918     \\whiteout whiteout-rounded-box
919 }
920 \\markup {
921   \\combine
922     \\filled-box #'(-1 . 18) #'(-3 . 4) #1
923     \\override #'(style . outline)
924     \\override #'(thickness . 3)
925     \\whiteout whiteout-outline
926 }
927 @end lilypond"
928   (stencil-whiteout
929     (interpret-markup layout props arg)
930     style
931     thickness
932     (ly:output-def-lookup layout 'line-thickness)))
933
934 (define-markup-command (pad-markup layout props amount arg)
935   (number? markup?)
936   #:category align
937   "
938 @cindex padding text
939 @cindex putting space around text
940
941 Add space around a markup object.
942 Identical to @code{pad-around}.
943
944 @lilypond[verbatim,quote]
945 \\markup {
946   \\box {
947     default
948   }
949   \\hspace #2
950   \\box {
951     \\pad-markup #1 {
952       padded
953     }
954   }
955 }
956 @end lilypond"
957   (let* ((m (interpret-markup layout props arg))
958          (x (interval-widen (ly:stencil-extent m X) amount))
959          (y (interval-widen (ly:stencil-extent m Y) amount)))
960     (ly:stencil-add (make-transparent-box-stencil x y)
961                     m)))
962
963 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
964 ;; space
965 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
966
967 (define-markup-command (strut layout props)
968   ()
969   #:category other
970   "
971 @cindex creating vertical spaces in text
972
973 Create a box of the same height as the space in the current font."
974   (let ((m (ly:text-interface::interpret-markup layout props " ")))
975     (ly:make-stencil (ly:stencil-expr m)
976                      '(0 . 0)
977                      (ly:stencil-extent m X)
978                      )))
979
980 (define-markup-command (hspace layout props amount)
981   (number?)
982   #:category align
983   "
984 @cindex creating horizontal spaces in text
985
986 Create an invisible object taking up horizontal space @var{amount}.
987
988 @lilypond[verbatim,quote]
989 \\markup {
990   one
991   \\hspace #2
992   two
993   \\hspace #8
994   three
995 }
996 @end lilypond"
997   (ly:make-stencil "" (cons 0 amount) empty-interval))
998
999 (define-markup-command (vspace layout props amount)
1000   (number?)
1001   #:category align
1002   "
1003 @cindex creating vertical spaces in text
1004
1005 Create an invisible object taking up vertical space
1006 of @var{amount} multiplied by 3.
1007
1008 @lilypond[verbatim,quote]
1009 \\markup {
1010     \\center-column {
1011     one
1012     \\vspace #2
1013     two
1014     \\vspace #5
1015     three
1016   }
1017 }
1018 @end lilypond"
1019   (let ((amount (* amount 3.0)))
1020     (ly:make-stencil "" empty-interval (cons 0 amount))))
1021
1022
1023 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1024 ;; importing graphics.
1025 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1026
1027 (define-markup-command (stencil layout props stil)
1028   (ly:stencil?)
1029   #:category other
1030   "
1031 @cindex importing stencils into text
1032
1033 Use a stencil as markup.
1034
1035 @lilypond[verbatim,quote]
1036 \\markup {
1037   \\stencil #(make-circle-stencil 2 0 #t)
1038 }
1039 @end lilypond"
1040   stil)
1041
1042 (define bbox-regexp
1043   (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)"))
1044
1045 (define (get-postscript-bbox string)
1046   "Extract the bbox from STRING, or return #f if not present."
1047   (let*
1048       ((match (regexp-exec bbox-regexp string)))
1049
1050     (if match
1051         (map (lambda (x)
1052                (string->number (match:substring match x)))
1053              (cdr (iota 5)))
1054
1055         #f)))
1056
1057 (define-markup-command (epsfile layout props axis size file-name)
1058   (number? number? string?)
1059   #:category graphic
1060   "
1061 @cindex inlining an Encapsulated PostScript image
1062
1063 Inline an EPS image.  The image is scaled along @var{axis} to
1064 @var{size}.
1065
1066 @lilypond[verbatim,quote]
1067 \\markup {
1068   \\general-align #Y #DOWN {
1069     \\epsfile #X #20 #\"context-example.eps\"
1070     \\epsfile #Y #20 #\"context-example.eps\"
1071   }
1072 }
1073 @end lilypond"
1074   (if (ly:get-option 'safe)
1075       (interpret-markup layout props "not allowed in safe")
1076       (eps-file->stencil axis size file-name)
1077       ))
1078
1079 (define-markup-command (postscript layout props str)
1080   (string?)
1081   #:category graphic
1082   "
1083 @cindex inserting PostScript directly into text
1084 This inserts @var{str} directly into the output as a PostScript
1085 command string.
1086
1087 @lilypond[verbatim,quote]
1088 ringsps = #\"
1089   0.15 setlinewidth
1090   0.9 0.6 moveto
1091   0.4 0.6 0.5 0 361 arc
1092   stroke
1093   1.0 0.6 0.5 0 361 arc
1094   stroke
1095   \"
1096
1097 rings = \\markup {
1098   \\with-dimensions #'(-0.2 . 1.6) #'(0 . 1.2)
1099   \\postscript #ringsps
1100 }
1101
1102 \\relative c'' {
1103   c2^\\rings
1104   a2_\\rings
1105 }
1106 @end lilypond"
1107   ;; FIXME
1108   (ly:make-stencil
1109    (list 'embedded-ps
1110          (format #f "
1111 gsave currentpoint translate
1112 0.1 setlinewidth
1113  ~a
1114 grestore
1115 "
1116                  str))
1117    '(0 . 0) '(0 . 0)))
1118
1119 (define-markup-command (path layout props thickness commands) (number? list?)
1120   #:category graphic
1121   #:properties ((line-cap-style 'round)
1122                 (line-join-style 'round)
1123                 (filled #f))
1124   "
1125 @cindex paths, drawing
1126 @cindex drawing paths
1127 Draws a path with line @var{thickness} according to the
1128 directions given in @var{commands}.  @var{commands} is a list of
1129 lists where the @code{car} of each sublist is a drawing command and
1130 the @code{cdr} comprises the associated arguments for each command.
1131
1132 There are seven commands available to use in the list
1133 @code{commands}: @code{moveto}, @code{rmoveto}, @code{lineto},
1134 @code{rlineto}, @code{curveto}, @code{rcurveto}, and
1135 @code{closepath}.  Note that the commands that begin with @emph{r}
1136 are the relative variants of the other three commands.
1137
1138 The commands @code{moveto}, @code{rmoveto}, @code{lineto}, and
1139 @code{rlineto} take 2 arguments; they are the X and Y coordinates
1140 for the destination point.
1141
1142 The commands @code{curveto} and @code{rcurveto} create cubic
1143 BĆ©zier curves, and take 6 arguments; the first two are the X and Y
1144 coordinates for the first control point, the second two are the X
1145 and Y coordinates for the second control point, and the last two
1146 are the X and Y coordinates for the destination point.
1147
1148 The @code{closepath} command takes zero arguments and closes the
1149 current subpath in the active path.
1150
1151 Note that a sequence of commands @emph{must} begin with a
1152 @code{moveto} or @code{rmoveto} to work with the SVG output.
1153
1154 Line-cap styles and line-join styles may be customized by
1155 overriding the @code{line-cap-style} and @code{line-join-style}
1156 properties, respectively.  Available line-cap styles are
1157 @code{'butt}, @code{'round}, and @code{'square}.  Available
1158 line-join styles are @code{'miter}, @code{'round}, and
1159 @code{'bevel}.
1160
1161 The property @code{filled} specifies whether or not the path is
1162 filled with color.
1163
1164 @lilypond[verbatim,quote]
1165 samplePath =
1166   #'((moveto 0 0)
1167      (lineto -1 1)
1168      (lineto 1 1)
1169      (lineto 1 -1)
1170      (curveto -5 -5 -5 5 -1 0)
1171      (closepath))
1172
1173 \\markup {
1174   \\path #0.25 #samplePath
1175
1176   \\override #'(line-join-style . miter) \\path #0.25 #samplePath
1177
1178   \\override #'(filled . #t) \\path #0.25 #samplePath
1179 }
1180 @end lilypond"
1181   (let* ((half-thickness (/ thickness 2))
1182          (current-point '(0 . 0))
1183          (set-point (lambda (lst) (set! current-point lst)))
1184          (relative? (lambda (x)
1185                       (string-prefix? "r" (symbol->string (car x)))))
1186          ;; For calculating extents, we want to modify the command
1187          ;; list so that all coordinates are absolute.
1188          (new-commands (map (lambda (x)
1189                               (cond
1190                                ;; for rmoveto, rlineto
1191                                ((and (relative? x) (= 3 (length x)))
1192                                 (let ((cp (cons
1193                                            (+ (car current-point)
1194                                               (second x))
1195                                            (+ (cdr current-point)
1196                                               (third x)))))
1197                                   (set-point cp)
1198                                   (list (car cp)
1199                                         (cdr cp))))
1200                                ;; for rcurveto
1201                                ((and (relative? x) (= 7 (length x)))
1202                                 (let* ((old-cp current-point)
1203                                        (cp (cons
1204                                             (+ (car old-cp)
1205                                                (sixth x))
1206                                             (+ (cdr old-cp)
1207                                                (seventh x)))))
1208                                   (set-point cp)
1209                                   (list (+ (car old-cp) (second x))
1210                                         (+ (cdr old-cp) (third x))
1211                                         (+ (car old-cp) (fourth x))
1212                                         (+ (cdr old-cp) (fifth x))
1213                                         (car cp)
1214                                         (cdr cp))))
1215                                ;; for moveto, lineto
1216                                ((= 3 (length x))
1217                                 (set-point (cons (second x)
1218                                                  (third x)))
1219                                 (drop x 1))
1220                                ;; for curveto
1221                                ((= 7 (length x))
1222                                 (set-point (cons (sixth x)
1223                                                  (seventh x)))
1224                                 (drop x 1))
1225                                ;; keep closepath for filtering;
1226                                ;; see `without-closepath'.
1227                                (else x)))
1228                             commands))
1229          ;; path-min-max does not accept 0-arg lists,
1230          ;; and since closepath does not affect extents, filter
1231          ;; out those commands here.
1232          (without-closepath (filter (lambda (x)
1233                                       (not (equal? 'closepath (car x))))
1234                                     new-commands))
1235          (extents (path-min-max
1236                    ;; set the origin to the first moveto
1237                    (list (list-ref (car without-closepath) 0)
1238                          (list-ref (car without-closepath) 1))
1239                    without-closepath))
1240          (X-extent (cons (list-ref extents 0) (list-ref extents 1)))
1241          (Y-extent (cons (list-ref extents 2) (list-ref extents 3)))
1242          (command-list (fold-right append '() commands)))
1243
1244     ;; account for line thickness
1245     (set! X-extent (interval-widen X-extent half-thickness))
1246     (set! Y-extent (interval-widen Y-extent half-thickness))
1247
1248     (ly:make-stencil
1249      `(path ,thickness `(,@',command-list)
1250             ',line-cap-style ',line-join-style ,filled)
1251      X-extent
1252      Y-extent)))
1253
1254 (define-markup-list-command (score-lines layout props score)
1255   (ly:score?)
1256   "This is the same as the @code{\\score} markup but delivers its
1257 systems as a list of lines.  Its @var{score} argument is entered in
1258 braces like it would be for @code{\\score}."
1259   (let ((output (ly:score-embedded-format score layout)))
1260
1261     (if (ly:music-output? output)
1262         (map
1263          (lambda (paper-system)
1264            ;; shift such that the refpoint of the bottom staff of
1265            ;; the first system is the baseline of the score
1266            (ly:stencil-translate-axis
1267             (paper-system-stencil paper-system)
1268             (- (car (paper-system-staff-extents paper-system)))
1269             Y))
1270          (vector->list (ly:paper-score-paper-systems output)))
1271         (begin
1272           (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
1273           '()))))
1274
1275 (define-markup-command (score layout props score)
1276   (ly:score?)
1277   #:category music
1278   #:properties ((baseline-skip))
1279   "
1280 @cindex inserting music into text
1281
1282 Inline an image of music.  The reference point (usually the middle
1283 staff line) of the lowest staff in the top system is placed on the
1284 baseline.
1285
1286 @lilypond[verbatim,quote]
1287 \\markup {
1288   \\score {
1289     \\new PianoStaff <<
1290       \\new Staff \\relative c' {
1291         \\key f \\major
1292         \\time 3/4
1293         \\mark \\markup { Allegro }
1294         f2\\p( a4)
1295         c2( a4)
1296         bes2( g'4)
1297         f8( e) e4 r
1298       }
1299       \\new Staff \\relative c {
1300         \\clef bass
1301         \\key f \\major
1302         \\time 3/4
1303         f8( a c a c a
1304         f c' es c es c)
1305         f,( bes d bes d bes)
1306         f( g bes g bes g)
1307       }
1308     >>
1309     \\layout {
1310       indent = 0.0\\cm
1311       \\context {
1312         \\Score
1313         \\override RehearsalMark
1314           #'break-align-symbols = #'(time-signature key-signature)
1315         \\override RehearsalMark
1316           #'self-alignment-X = #LEFT
1317       }
1318       \\context {
1319         \\Staff
1320         \\override TimeSignature
1321           #'break-align-anchor-alignment = #LEFT
1322       }
1323     }
1324   }
1325 }
1326 @end lilypond"
1327   (stack-stencils Y DOWN baseline-skip
1328                   (score-lines-markup-list layout props score)))
1329
1330 (define-markup-command (null layout props)
1331   ()
1332   #:category other
1333   "
1334 @cindex creating empty text objects
1335
1336 An empty markup with extents of a single point.
1337
1338 @lilypond[verbatim,quote]
1339 \\markup {
1340   \\null
1341 }
1342 @end lilypond"
1343   point-stencil)
1344
1345 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1346 ;; basic formatting.
1347 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1348
1349 (define-markup-command (simple layout props str)
1350   (string?)
1351   #:category font
1352   "
1353 @cindex simple text strings
1354
1355 A simple text string; @code{\\markup @{ foo @}} is equivalent with
1356 @code{\\markup @{ \\simple #\"foo\" @}}.
1357
1358 Note: for creating standard text markup or defining new markup commands,
1359 the use of @code{\\simple} is unnecessary.
1360
1361 @lilypond[verbatim,quote]
1362 \\markup {
1363   \\simple #\"simple\"
1364   \\simple #\"text\"
1365   \\simple #\"strings\"
1366 }
1367 @end lilypond"
1368   (interpret-markup layout props str))
1369
1370 (define-markup-command (first-visible layout props args)
1371   (markup-list?)
1372   #:category other
1373   "Use the first markup in @var{args} that yields a non-empty stencil
1374 and ignore the rest.
1375
1376 @lilypond[verbatim,quote]
1377 \\markup {
1378   \\first-visible {
1379     \\fromproperty #'header:composer
1380     \\italic Unknown
1381   }
1382 }
1383 @end lilypond"
1384   (define (false-if-empty stencil)
1385     (if (ly:stencil-empty? stencil) #f stencil))
1386   (or
1387    (any
1388     (lambda (m)
1389       (if (markup? m)
1390           (false-if-empty (interpret-markup layout props m))
1391           (any false-if-empty (interpret-markup-list layout props (list m)))))
1392     args)
1393    empty-stencil))
1394
1395 (define-public empty-markup
1396   (make-simple-markup ""))
1397
1398 ;; helper for justifying lines.
1399 (define (get-fill-space
1400           word-count line-width word-space text-widths constant-space?)
1401   "Calculate the necessary paddings between adjacent texts in a
1402 single justified line.  The lengths of all texts are stored in
1403 @var{text-widths}.
1404 When @var{constant-space?} is @code{#t}, the formula for the padding
1405 between texts is:
1406 padding = (line-width - total-text-width)/(word-count - 1)
1407 When @var{constant-space?} is @code{#f}, the formula for the
1408 padding between interior texts a and b is:
1409 padding = line-width/(word-count - 1) - (length(a) + length(b))/2
1410 In this case, the first and last padding have to be calculated
1411 specially using the whole length of the first or last text.
1412 All paddings are checked to be at least word-space, to ensure that
1413 no texts collide.
1414 Return a list of paddings."
1415   (cond
1416     ((null? text-widths) '())
1417     (constant-space?
1418      (make-list
1419        (1- word-count)
1420        ;; Ensure that space between words cannot be
1421        ;; less than word-space.
1422        (max
1423          word-space
1424          (/ (- line-width (apply + text-widths))
1425             (1- word-count)))))
1426
1427     ;; special case first padding
1428     ((= (length text-widths) word-count)
1429      (cons
1430        (- (- (/ line-width (1- word-count)) (car text-widths))
1431           (/ (cadr text-widths) 2))
1432        (get-fill-space
1433          word-count line-width word-space (cdr text-widths)
1434                                           constant-space?)))
1435     ;; special case last padding
1436     ((= (length text-widths) 2)
1437      (list (- (/ line-width (1- word-count))
1438               (+ (/ (car text-widths) 2) (cadr text-widths)))
1439            0))
1440     (else
1441       (let ((default-padding
1442               (- (/ line-width (1- word-count))
1443                  (/ (+ (car text-widths) (cadr text-widths)) 2))))
1444         (cons
1445           (if (> word-space default-padding)
1446               word-space
1447               default-padding)
1448           (get-fill-space
1449             word-count line-width word-space (cdr text-widths)
1450                                              constant-space?))))))
1451
1452 (define (justify-line-helper
1453           layout props args text-direction word-space line-width constant-space?)
1454   "Return a stencil which spreads @var{args} along a line of width
1455 @var{line-width}.  If @var{constant-space?} is set to @code{#t}, the
1456 space between words is constant.  If @code{#f}, the distance between
1457 words varies according to their relative lengths."
1458   (let* ((orig-stencils (interpret-markup-list layout props args))
1459          (stencils
1460            (map (lambda (stc)
1461                   (if (ly:stencil-empty? stc X)
1462                       (ly:make-stencil (ly:stencil-expr stc)
1463                                        '(0 . 0) (ly:stencil-extent stc Y))
1464                       stc))
1465                 orig-stencils))
1466          (text-widths
1467            (map (lambda (stc)
1468                   (interval-length (ly:stencil-extent stc X)))
1469                 stencils))
1470          (text-width (apply + text-widths))
1471          (word-count (length stencils))
1472          (line-width (or line-width (ly:output-def-lookup layout 'line-width)))
1473          (fill-space
1474            (cond
1475              ((= word-count 1)
1476               (list
1477                 (/ (- line-width text-width) 2)
1478                 (/ (- line-width text-width) 2)))
1479              ((= word-count 2)
1480               (list
1481                 (- line-width text-width)))
1482              (else
1483                (get-fill-space
1484                  word-count line-width word-space text-widths
1485                                                   constant-space?))))
1486          (line-contents (if (= word-count 1)
1487                             (list
1488                               point-stencil
1489                               (car stencils)
1490                               point-stencil)
1491                             stencils)))
1492
1493     (if (null? (remove ly:stencil-empty? orig-stencils))
1494         empty-stencil
1495         (begin
1496           (if (= text-direction LEFT)
1497               (set! line-contents (reverse line-contents)))
1498           (set! line-contents
1499                 (stack-stencils-padding-list
1500                   X RIGHT fill-space line-contents))
1501           (if (> word-count 1)
1502               ;; shift s.t. stencils align on the left edge, even if
1503               ;; first stencil had negative X-extent (e.g. center-column)
1504               ;; (if word-count = 1, X-extents are already normalized in
1505               ;; the definition of line-contents)
1506               (set! line-contents
1507                     (ly:stencil-translate-axis
1508                       line-contents
1509                       (- (car (ly:stencil-extent (car stencils) X)))
1510                       X)))
1511           line-contents))))
1512
1513 (define-markup-command (fill-line layout props args)
1514   (markup-list?)
1515   #:category align
1516   #:properties ((text-direction RIGHT)
1517                 (word-space 0.6)
1518                 (line-width #f))
1519   "Put @var{markups} in a horizontal line of width @var{line-width}.
1520 The markups are spaced or flushed to fill the entire line.
1521 If there are no arguments, return an empty stencil.
1522
1523 @lilypond[verbatim,quote]
1524 \\markup {
1525   \\column {
1526     \\fill-line {
1527       Words evenly spaced across the page
1528     }
1529     \\null
1530     \\fill-line {
1531       \\line { Text markups }
1532       \\line {
1533         \\italic { evenly spaced }
1534       }
1535       \\line { across the page }
1536     }
1537   }
1538 }
1539 @end lilypond"
1540   (justify-line-helper
1541     layout props args text-direction word-space line-width #f))
1542
1543 (define-markup-command (justify-line layout props args)
1544   (markup-list?)
1545   #:category align
1546   #:properties ((text-direction RIGHT)
1547                 (word-space 0.6)
1548                 (line-width #f))
1549   "Put @var{markups} in a horizontal line of width @var{line-width}.
1550 The markups are spread to fill the entire line and separated by equal
1551 space.  If there are no arguments, return an empty stencil.
1552
1553 @lilypond[verbatim,quote]
1554 \\markup {
1555   \\justify-line {
1556     Space between neighboring words is constant
1557   }
1558 }
1559 @end lilypond"
1560   (justify-line-helper
1561     layout props args text-direction word-space line-width #t))
1562
1563 (define-markup-command (concat layout props args)
1564   (markup-list?)
1565   #:category align
1566   "
1567 @cindex concatenating text
1568 @cindex ligatures in text
1569
1570 Concatenate @var{args} in a horizontal line, without spaces in between.
1571 Strings and simple markups are concatenated on the input level, allowing
1572 ligatures.  For example, @code{\\concat @{ \"f\" \\simple #\"i\" @}} is
1573 equivalent to @code{\"fi\"}.
1574
1575 @lilypond[verbatim,quote]
1576 \\markup {
1577   \\concat {
1578     one
1579     two
1580     three
1581   }
1582 }
1583 @end lilypond"
1584   (define (concat-string-args arg-list)
1585     (fold-right (lambda (arg result-list)
1586                   (let ((result (and (pair? result-list)
1587                                      (car result-list))))
1588                     (cond ((not (pair? arg)))
1589                           ((eq? (car arg) simple-markup)
1590                            (set! arg (cadr arg)))
1591                           ((eq? (car arg) char-markup)
1592                            (set! arg (ly:wide-char->utf-8 (cadr arg)))))
1593                     (if (and (string? result) (string? arg))
1594                         (cons (string-append arg result) (cdr result-list))
1595                         (cons arg result-list))))
1596                 '()
1597                 arg-list))
1598   (stack-stencil-line 0
1599                       (interpret-markup-list layout props
1600                                              (if (markup-command-list? args)
1601                                                  args
1602                                                  (concat-string-args args)))))
1603
1604 (define (wordwrap-stencils stencils
1605                            justify base-space line-width text-dir)
1606   "Perform simple wordwrap, return stencil of each line."
1607   (define space (if justify
1608                     ;; justify only stretches lines.
1609                     (* 0.7 base-space)
1610                     base-space))
1611   (define (stencil-len s)
1612     (interval-end (ly:stencil-extent s X)))
1613   (define (maybe-shift line)
1614     (if (= text-dir LEFT)
1615         (ly:stencil-translate-axis
1616          line
1617          (- line-width (stencil-len line))
1618          X)
1619         line))
1620   (if (null? stencils)
1621       '()
1622       (let loop ((lines '())
1623                  (todo stencils))
1624         (let word-loop
1625             ((line (first todo))
1626              (todo (cdr todo))
1627              (word-list (list (first todo))))
1628           (cond
1629            ((pair? todo)
1630             (let ((new (if (= text-dir LEFT)
1631                            (ly:stencil-stack (car todo) X RIGHT line space)
1632                            (ly:stencil-stack line X RIGHT (car todo) space))))
1633               (cond
1634                ((<= (stencil-len new) line-width)
1635                 (word-loop new (cdr todo)
1636                            (cons (car todo) word-list)))
1637                (justify
1638                 (let* ((word-list
1639                         ;; This depends on stencil stacking being
1640                         ;; associative so that stacking
1641                         ;; left-to-right and right-to-left leads to
1642                         ;; the same result
1643                         (if (= text-dir LEFT)
1644                             word-list
1645                             (reverse! word-list)))
1646                        (len (stencil-len line))
1647                        (stretch (- line-width len))
1648                        (spaces
1649                         (- (stencil-len
1650                             (stack-stencils X RIGHT (1+ space) word-list))
1651                            len)))
1652                   (if (zero? spaces)
1653                       ;; Uh oh, nothing to fill.
1654                       (loop (cons (maybe-shift line) lines) todo)
1655                       (loop (cons
1656                              (stack-stencils X RIGHT
1657                                              (+ space (/ stretch spaces))
1658                                              word-list)
1659                              lines)
1660                             todo))))
1661                (else ;; not justify
1662                 (loop (cons (maybe-shift line) lines) todo)))))
1663            ;; todo is null
1664            (justify
1665             ;; Now we have the last line assembled with space
1666             ;; which is compressed.  We want to use the
1667             ;; uncompressed version instead if it fits, and the
1668             ;; justified version if it doesn't.
1669             (let* ((word-list
1670                     ;; This depends on stencil stacking being
1671                     ;; associative so that stacking
1672                     ;; left-to-right and right-to-left leads to
1673                     ;; the same result
1674                     (if (= text-dir LEFT)
1675                         word-list
1676                         (reverse! word-list)))
1677                    (big-line (stack-stencils X RIGHT base-space word-list))
1678                    (big-len (stencil-len big-line))
1679                    (len (stencil-len line)))
1680               (reverse! lines
1681                         (list
1682                          (if (> big-len line-width)
1683                              (stack-stencils X RIGHT
1684                                              (/
1685                                               (+
1686                                                (* (- big-len line-width)
1687                                                   space)
1688                                                (* (- line-width len)
1689                                                   base-space))
1690                                               (- big-len len))
1691                                              word-list)
1692                              (maybe-shift big-line))))))
1693            (else ;; not justify
1694             (reverse! lines (list (maybe-shift line)))))))))
1695
1696
1697 (define-markup-list-command (wordwrap-internal layout props justify args)
1698   (boolean? markup-list?)
1699   #:properties ((line-width #f)
1700                 (word-space)
1701                 (text-direction RIGHT))
1702   "Internal markup list command used to define @code{\\justify} and @code{\\wordwrap}."
1703   (wordwrap-stencils (interpret-markup-list layout props args)
1704                      justify
1705                      word-space
1706                      (or line-width
1707                          (ly:output-def-lookup layout 'line-width))
1708                      text-direction))
1709
1710 (define-markup-command (justify layout props args)
1711   (markup-list?)
1712   #:category align
1713   #:properties ((baseline-skip)
1714                 wordwrap-internal-markup-list)
1715   "
1716 @cindex justifying text
1717
1718 Like @code{\\wordwrap}, but with lines stretched to justify the margins.
1719 Use @code{\\override #'(line-width . @var{X})} to set the line width;
1720 @var{X}@tie{}is the number of staff spaces.
1721
1722 @lilypond[verbatim,quote]
1723 \\markup {
1724   \\justify {
1725     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1726     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1727     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1728     laboris nisi ut aliquip ex ea commodo consequat.
1729   }
1730 }
1731 @end lilypond"
1732   (stack-lines DOWN 0.0 baseline-skip
1733                (wordwrap-internal-markup-list layout props #t args)))
1734
1735 (define-markup-command (wordwrap layout props args)
1736   (markup-list?)
1737   #:category align
1738   #:properties ((baseline-skip)
1739                 wordwrap-internal-markup-list)
1740   "Simple wordwrap.  Use @code{\\override #'(line-width . @var{X})} to set
1741 the line width, where @var{X} is the number of staff spaces.
1742
1743 @lilypond[verbatim,quote]
1744 \\markup {
1745   \\wordwrap {
1746     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1747     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1748     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1749     laboris nisi ut aliquip ex ea commodo consequat.
1750   }
1751 }
1752 @end lilypond"
1753   (stack-lines DOWN 0.0 baseline-skip
1754                (wordwrap-internal-markup-list layout props #f args)))
1755
1756 (define-markup-list-command (wordwrap-string-internal layout props justify arg)
1757   (boolean? string?)
1758   #:properties ((line-width)
1759                 (word-space)
1760                 (text-direction RIGHT))
1761   "Internal markup list command used to define @code{\\justify-string} and
1762 @code{\\wordwrap-string}."
1763   (let* ((para-strings (regexp-split
1764                         (string-regexp-substitute
1765                          "\r" "\n"
1766                          (string-regexp-substitute "\r\n" "\n" arg))
1767                         "\n[ \t\n]*\n[ \t\n]*"))
1768          (list-para-words (map (lambda (str)
1769                                  (regexp-split str "[ \t\n]+"))
1770                                para-strings))
1771          (para-lines (map (lambda (words)
1772                             (let* ((stencils
1773                                     (map (lambda (x)
1774                                            (interpret-markup layout props x))
1775                                          words)))
1776                               (wordwrap-stencils stencils
1777                                                  justify word-space
1778                                                  line-width text-direction)))
1779                           list-para-words)))
1780     (concatenate para-lines)))
1781
1782 (define-markup-command (wordwrap-string layout props arg)
1783   (string?)
1784   #:category align
1785   #:properties ((baseline-skip)
1786                 wordwrap-string-internal-markup-list)
1787   "Wordwrap a string.  Paragraphs may be separated with double newlines.
1788
1789 @lilypond[verbatim,quote]
1790 \\markup {
1791   \\override #'(line-width . 40)
1792   \\wordwrap-string #\"Lorem ipsum dolor sit amet, consectetur
1793       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1794       et dolore magna aliqua.
1795
1796
1797       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1798       laboris nisi ut aliquip ex ea commodo consequat.
1799
1800
1801       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1802       qui officia deserunt mollit anim id est laborum\"
1803 }
1804 @end lilypond"
1805   (stack-lines DOWN 0.0 baseline-skip
1806                (wordwrap-string-internal-markup-list layout props #f arg)))
1807
1808 (define-markup-command (justify-string layout props arg)
1809   (string?)
1810   #:category align
1811   #:properties ((baseline-skip)
1812                 wordwrap-string-internal-markup-list)
1813   "Justify a string.  Paragraphs may be separated with double newlines
1814
1815 @lilypond[verbatim,quote]
1816 \\markup {
1817   \\override #'(line-width . 40)
1818   \\justify-string #\"Lorem ipsum dolor sit amet, consectetur
1819       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1820       et dolore magna aliqua.
1821
1822
1823       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1824       laboris nisi ut aliquip ex ea commodo consequat.
1825
1826
1827       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1828       qui officia deserunt mollit anim id est laborum\"
1829 }
1830 @end lilypond"
1831   (stack-lines DOWN 0.0 baseline-skip
1832                (wordwrap-string-internal-markup-list layout props #t arg)))
1833
1834 (define-markup-command (wordwrap-field layout props symbol)
1835   (symbol?)
1836   #:category align
1837   "Wordwrap the data which has been assigned to @var{symbol}.
1838
1839 @lilypond[verbatim,quote]
1840 \\header {
1841   title = \"My title\"
1842   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1843     elit, sed do eiusmod tempor incididunt ut labore et dolore
1844     magna aliqua.  Ut enim ad minim veniam, quis nostrud
1845     exercitation ullamco laboris nisi ut aliquip ex ea commodo
1846     consequat.\"
1847 }
1848
1849 \\paper {
1850   bookTitleMarkup = \\markup {
1851     \\column {
1852       \\fill-line { \\fromproperty #'header:title }
1853       \\null
1854       \\wordwrap-field #'header:myText
1855     }
1856   }
1857 }
1858
1859 \\markup {
1860   \\null
1861 }
1862 @end lilypond"
1863   (let* ((m (chain-assoc-get symbol props)))
1864     (if (string? m)
1865         (wordwrap-string-markup layout props m)
1866         empty-stencil)))
1867
1868 (define-markup-command (justify-field layout props symbol)
1869   (symbol?)
1870   #:category align
1871   "Justify the data which has been assigned to @var{symbol}.
1872
1873 @lilypond[verbatim,quote]
1874 \\header {
1875   title = \"My title\"
1876   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1877     elit, sed do eiusmod tempor incididunt ut labore et dolore magna
1878     aliqua.  Ut enim ad minim veniam, quis nostrud exercitation ullamco
1879     laboris nisi ut aliquip ex ea commodo consequat.\"
1880 }
1881
1882 \\paper {
1883   bookTitleMarkup = \\markup {
1884     \\column {
1885       \\fill-line { \\fromproperty #'header:title }
1886       \\null
1887       \\justify-field #'header:myText
1888     }
1889   }
1890 }
1891
1892 \\markup {
1893   \\null
1894 }
1895 @end lilypond"
1896   (let* ((m (chain-assoc-get symbol props)))
1897     (if (string? m)
1898         (justify-string-markup layout props m)
1899         empty-stencil)))
1900
1901 (define-markup-command (combine layout props arg1 arg2)
1902   (markup? markup?)
1903   #:category align
1904   "
1905 @cindex merging text
1906
1907 Print two markups on top of each other.
1908
1909 Note: @code{\\combine} cannot take a list of markups enclosed in
1910 curly braces as an argument; for this purpose use @code{\\overlay} instead.
1911
1912 @lilypond[verbatim,quote]
1913 \\markup {
1914   \\fontsize #5
1915   \\override #'(thickness . 2)
1916   \\combine
1917     \\draw-line #'(0 . 4)
1918     \\arrow-head #Y #DOWN ##f
1919 }
1920 @end lilypond"
1921   (let* ((s1 (interpret-markup layout props arg1))
1922          (s2 (interpret-markup layout props arg2)))
1923     (ly:stencil-add s1 s2)))
1924
1925 (define-markup-command (overlay layout props args)
1926   (markup-list?)
1927   #:category align
1928   "
1929 @cindex merging text
1930
1931 Takes a list of markups combining them.
1932
1933 @lilypond[verbatim,quote]
1934 \\markup {
1935   \\fontsize #5
1936   \\override #'(thickness . 2)
1937   \\overlay {
1938     \\draw-line #'(0 . 4)
1939     \\arrow-head #Y #DOWN ##f
1940     \\translate #'(0 . 4)\\arrow-head #Y #UP ##f
1941   }
1942 }
1943 @end lilypond"
1944   (apply ly:stencil-add (interpret-markup-list layout props args)))
1945
1946 ;;
1947 ;; TODO: should extract baseline-skip from each argument somehow..
1948 ;;
1949 (define-markup-command (column layout props args)
1950   (markup-list?)
1951   #:category align
1952   #:properties ((baseline-skip))
1953   "
1954 @cindex stacking text in a column
1955
1956 Stack the markups in @var{args} vertically.  The property
1957 @code{baseline-skip} determines the space between markups
1958 in @var{args}.
1959
1960 @lilypond[verbatim,quote]
1961 \\markup {
1962   \\column {
1963     one
1964     two
1965     three
1966   }
1967 }
1968 @end lilypond"
1969   (let ((arg-stencils (interpret-markup-list layout props args)))
1970     (stack-lines -1 0.0 baseline-skip arg-stencils)))
1971
1972 (define-markup-command (dir-column layout props args)
1973   (markup-list?)
1974   #:category align
1975   #:properties ((direction)
1976                 (baseline-skip))
1977   "
1978 @cindex changing direction of text columns
1979
1980 Make a column of @var{args}, going up or down, depending on the
1981 setting of the @code{direction} layout property.
1982
1983 @lilypond[verbatim,quote]
1984 \\markup {
1985   \\override #`(direction . ,UP) {
1986     \\dir-column {
1987       going up
1988     }
1989   }
1990   \\hspace #1
1991   \\dir-column {
1992     going down
1993   }
1994   \\hspace #1
1995   \\override #'(direction . 1) {
1996     \\dir-column {
1997       going up
1998     }
1999   }
2000 }
2001 @end lilypond"
2002   (stack-lines (if (number? direction) direction -1)
2003                0.0
2004                baseline-skip
2005                (interpret-markup-list layout props args)))
2006
2007 (define (general-column align-dir baseline mols)
2008   "Stack @var{mols} vertically, aligned to  @var{align-dir} horizontally."
2009
2010   (let* ((aligned-mols (map (lambda (x) (ly:stencil-aligned-to x X align-dir)) mols))
2011          (stacked-stencil (stack-lines -1 0.0 baseline aligned-mols))
2012          (stacked-extent (ly:stencil-extent stacked-stencil X)))
2013     (ly:stencil-translate-axis stacked-stencil (- (car stacked-extent)) X )))
2014
2015 (define-markup-command (center-column layout props args)
2016   (markup-list?)
2017   #:category align
2018   #:properties ((baseline-skip))
2019   "
2020 @cindex centering a column of text
2021
2022 Put @code{args} in a centered column.
2023
2024 @lilypond[verbatim,quote]
2025 \\markup {
2026   \\center-column {
2027     one
2028     two
2029     three
2030   }
2031 }
2032 @end lilypond"
2033   (general-column CENTER baseline-skip (interpret-markup-list layout props args)))
2034
2035 (define-markup-command (left-column layout props args)
2036   (markup-list?)
2037   #:category align
2038   #:properties ((baseline-skip))
2039   "
2040 @cindex text columns, left-aligned
2041
2042 Put @code{args} in a left-aligned column.
2043
2044 @lilypond[verbatim,quote]
2045 \\markup {
2046   \\left-column {
2047     one
2048     two
2049     three
2050   }
2051 }
2052 @end lilypond"
2053   (general-column LEFT baseline-skip (interpret-markup-list layout props args)))
2054
2055 (define-markup-command (right-column layout props args)
2056   (markup-list?)
2057   #:category align
2058   #:properties ((baseline-skip))
2059   "
2060 @cindex text columns, right-aligned
2061
2062 Put @code{args} in a right-aligned column.
2063
2064 @lilypond[verbatim,quote]
2065 \\markup {
2066   \\right-column {
2067     one
2068     two
2069     three
2070   }
2071 }
2072 @end lilypond"
2073   (general-column RIGHT baseline-skip (interpret-markup-list layout props args)))
2074
2075 (define-markup-command (vcenter layout props arg)
2076   (markup?)
2077   #:category align
2078   "
2079 @cindex vertically centering text
2080
2081 Align @code{arg} to its Y@tie{}center.
2082
2083 @lilypond[verbatim,quote]
2084 \\markup {
2085   one
2086   \\vcenter
2087   two
2088   three
2089 }
2090 @end lilypond"
2091   (let* ((mol (interpret-markup layout props arg)))
2092     (ly:stencil-aligned-to mol Y CENTER)))
2093
2094 (define-markup-command (center-align layout props arg)
2095   (markup?)
2096   #:category align
2097   "
2098 @cindex horizontally centering text
2099
2100 Align @code{arg} to its X@tie{}center.
2101
2102 @lilypond[verbatim,quote]
2103 \\markup {
2104   \\column {
2105     one
2106     \\center-align
2107     two
2108     three
2109   }
2110 }
2111 @end lilypond"
2112   (let* ((mol (interpret-markup layout props arg)))
2113     (ly:stencil-aligned-to mol X CENTER)))
2114
2115 (define-markup-command (right-align layout props arg)
2116   (markup?)
2117   #:category align
2118   "
2119 @cindex right aligning text
2120
2121 Align @var{arg} on its right edge.
2122
2123 @lilypond[verbatim,quote]
2124 \\markup {
2125   \\column {
2126     one
2127     \\right-align
2128     two
2129     three
2130   }
2131 }
2132 @end lilypond"
2133   (let* ((m (interpret-markup layout props arg)))
2134     (ly:stencil-aligned-to m X RIGHT)))
2135
2136 (define-markup-command (left-align layout props arg)
2137   (markup?)
2138   #:category align
2139   "
2140 @cindex left aligning text
2141
2142 Align @var{arg} on its left edge.
2143
2144 @lilypond[verbatim,quote]
2145 \\markup {
2146   \\column {
2147     one
2148     \\left-align
2149     two
2150     three
2151   }
2152 }
2153 @end lilypond"
2154   (let* ((m (interpret-markup layout props arg)))
2155     (ly:stencil-aligned-to m X LEFT)))
2156
2157 (define-markup-command (general-align layout props axis dir arg)
2158   (integer? number? markup?)
2159   #:category align
2160   "
2161 @cindex controlling general text alignment
2162
2163 Align @var{arg} in @var{axis} direction to the @var{dir} side.
2164
2165 @lilypond[verbatim,quote]
2166 \\markup {
2167   \\column {
2168     one
2169     \\general-align #X #LEFT
2170     two
2171     three
2172     \\null
2173     one
2174     \\general-align #X #CENTER
2175     two
2176     three
2177     \\null
2178     \\line {
2179       one
2180       \\general-align #Y #UP
2181       two
2182       three
2183     }
2184     \\null
2185     \\line {
2186       one
2187       \\general-align #Y #3.2
2188       two
2189       three
2190     }
2191   }
2192 }
2193 @end lilypond"
2194   (let* ((m (interpret-markup layout props arg)))
2195     (ly:stencil-aligned-to m axis dir)))
2196
2197 (define-markup-command (halign layout props dir arg)
2198   (number? markup?)
2199   #:category align
2200   "
2201 @cindex setting horizontal text alignment
2202
2203 Set horizontal alignment.  If @var{dir} is @w{@code{-1}}, then it is
2204 left-aligned, while @code{+1} is right.  Values in between interpolate
2205 alignment accordingly.
2206
2207 @lilypond[verbatim,quote]
2208 \\markup {
2209   \\column {
2210     one
2211     \\halign #LEFT
2212     two
2213     three
2214     \\null
2215     one
2216     \\halign #CENTER
2217     two
2218     three
2219     \\null
2220     one
2221     \\halign #RIGHT
2222     two
2223     three
2224     \\null
2225     one
2226     \\halign #-5
2227     two
2228     three
2229   }
2230 }
2231 @end lilypond"
2232   (let* ((m (interpret-markup layout props arg)))
2233     (ly:stencil-aligned-to m X dir)))
2234
2235 (define-markup-command (with-dimensions layout props x y arg)
2236   (number-pair? number-pair? markup?)
2237   #:category other
2238   "
2239 @cindex setting extent of text objects
2240
2241 Set the dimensions of @var{arg} to @var{x} and@tie{}@var{y}."
2242   (let* ((expr (ly:stencil-expr (interpret-markup layout props arg))))
2243     (ly:stencil-add
2244      (make-transparent-box-stencil x y)
2245      (ly:make-stencil
2246       `(delay-stencil-evaluation ,(delay expr))
2247       x y))))
2248
2249 (define-markup-command (with-outline layout props outline arg)
2250   (markup? markup?)
2251   #:category other
2252   "
2253 Print @var{arg} with the outline and dimensions of @var{outline}."
2254   (ly:stencil-outline (interpret-markup layout props arg)
2255                       (interpret-markup layout props outline)))
2256
2257 (define-markup-command (with-dimensions-from layout props arg1 arg2)
2258   (markup? markup?)
2259   #:category other
2260   "
2261 Print @var{arg2} with the dimensions of @var{arg1}."
2262   (let* ((stil1 (interpret-markup layout props arg1))
2263          (x (ly:stencil-extent stil1 0))
2264          (y (ly:stencil-extent stil1 1)))
2265     (interpret-markup layout props (markup #:with-dimensions x y arg2))))
2266
2267 (define-markup-command (pad-around layout props amount arg)
2268   (number? markup?)
2269   #:category align
2270   "Add padding @var{amount} all around @var{arg}.
2271
2272 @lilypond[verbatim,quote]
2273 \\markup {
2274   \\box {
2275     default
2276   }
2277   \\hspace #2
2278   \\box {
2279     \\pad-around #0.5 {
2280       padded
2281     }
2282   }
2283 }
2284 @end lilypond"
2285   (let* ((m (interpret-markup layout props arg))
2286          (x (interval-widen (ly:stencil-extent m X) amount))
2287          (y (interval-widen (ly:stencil-extent m Y) amount)))
2288     (ly:stencil-add (make-transparent-box-stencil x y)
2289                     m)))
2290
2291 (define-markup-command (pad-x layout props amount arg)
2292   (number? markup?)
2293   #:category align
2294   "
2295 @cindex padding text horizontally
2296
2297 Add padding @var{amount} around @var{arg} in the X@tie{}direction.
2298
2299 @lilypond[verbatim,quote]
2300 \\markup {
2301   \\box {
2302     default
2303   }
2304   \\hspace #4
2305   \\box {
2306     \\pad-x #2 {
2307       padded
2308     }
2309   }
2310 }
2311 @end lilypond"
2312   (let* ((m (interpret-markup layout props arg))
2313          (x (ly:stencil-extent m X))
2314          (y (ly:stencil-extent m Y)))
2315     (ly:make-stencil (ly:stencil-expr m)
2316                      (interval-widen x amount)
2317                      y)))
2318
2319 (define-markup-command (put-adjacent layout props axis dir arg1 arg2)
2320   (integer? ly:dir? markup? markup?)
2321   #:category align
2322   "Put @var{arg2} next to @var{arg1}, without moving @var{arg1}."
2323   (let ((m1 (interpret-markup layout props arg1))
2324         (m2 (interpret-markup layout props arg2)))
2325     (ly:stencil-combine-at-edge m1 axis dir m2 0.0)))
2326
2327 (define-markup-command (transparent layout props arg)
2328   (markup?)
2329   #:category other
2330   "Make @var{arg} transparent.
2331
2332 @lilypond[verbatim,quote]
2333 \\markup {
2334   \\transparent {
2335     invisible text
2336   }
2337 }
2338 @end lilypond"
2339   (ly:stencil-outline empty-stencil (interpret-markup layout props arg)))
2340
2341 (define-markup-command (pad-to-box layout props x-ext y-ext arg)
2342   (number-pair? number-pair? markup?)
2343   #:category align
2344   "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space.
2345
2346 @lilypond[verbatim,quote]
2347 \\markup {
2348   \\box {
2349     default
2350   }
2351   \\hspace #4
2352   \\box {
2353     \\pad-to-box #'(0 . 10) #'(0 . 3) {
2354       padded
2355     }
2356   }
2357 }
2358 @end lilypond"
2359   (ly:stencil-add (make-transparent-box-stencil x-ext y-ext)
2360                   (interpret-markup layout props arg)))
2361
2362 (define-markup-command (hcenter-in layout props length arg)
2363   (number? markup?)
2364   #:category align
2365   "Center @var{arg} horizontally within a box of extending
2366 @var{length}/2 to the left and right.
2367
2368 @lilypond[quote,verbatim]
2369 \\new StaffGroup <<
2370   \\new Staff {
2371     \\set Staff.instrumentName = \\markup {
2372       \\hcenter-in #12
2373       Oboe
2374     }
2375     c''1
2376   }
2377   \\new Staff {
2378     \\set Staff.instrumentName = \\markup {
2379       \\hcenter-in #12
2380       Bassoon
2381     }
2382     \\clef tenor
2383     c'1
2384   }
2385 >>
2386 @end lilypond"
2387   (interpret-markup layout props
2388                     (make-pad-to-box-markup
2389                      (cons (/ length -2) (/ length 2))
2390                      '(0 . 0)
2391                      (make-center-align-markup arg))))
2392
2393 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2394 ;; property
2395 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2396
2397 (define-markup-command (property-recursive layout props symbol)
2398   (symbol?)
2399   #:category other
2400   "Print out a warning when a header field markup contains some recursive
2401 markup definition."
2402   (ly:warning "Recursive definition of property ~a detected!" symbol)
2403   empty-stencil)
2404
2405 (define-markup-command (fromproperty layout props symbol)
2406   (symbol?)
2407   #:category other
2408   "Read the @var{symbol} from property settings, and produce a stencil
2409 from the markup contained within.  If @var{symbol} is not defined, it
2410 returns an empty markup.
2411
2412 @lilypond[verbatim,quote]
2413 \\header {
2414   myTitle = \"myTitle\"
2415   title = \\markup {
2416     from
2417     \\italic
2418     \\fromproperty #'header:myTitle
2419   }
2420 }
2421 \\markup {
2422   \\null
2423 }
2424 @end lilypond"
2425   (let ((m (chain-assoc-get symbol props)))
2426     (if (markup? m)
2427         ;; prevent infinite loops by clearing the interpreted property:
2428         (interpret-markup layout (cons (list (cons symbol `(,property-recursive-markup ,symbol))) props) m)
2429         empty-stencil)))
2430
2431 (define-markup-command (on-the-fly layout props procedure arg)
2432   (procedure? markup?)
2433   #:category other
2434   "Apply the @var{procedure} markup command to @var{arg}.
2435 @var{procedure} takes the same arguments as @code{interpret-markup}
2436 and returns a stencil."
2437   (procedure layout props arg))
2438
2439 (define-markup-command (footnote layout props mkup note)
2440   (markup? markup?)
2441   #:category other
2442   "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
2443
2444 @lilypond[verbatim,quote]
2445 \\markup {
2446   \\auto-footnote a b
2447   \\override #'(padding . 0.2)
2448   \\auto-footnote c d
2449 }
2450 @end lilypond
2451 The footnote will not be annotated automatically."
2452   (ly:stencil-combine-at-edge
2453    (interpret-markup layout props mkup)
2454    X
2455    RIGHT
2456    (ly:make-stencil
2457     `(footnote (gensym "footnote") #f ,(interpret-markup layout props note))
2458     '(0 . 0)
2459     '(0 . 0))
2460    0.0))
2461
2462 (define-markup-command (auto-footnote layout props mkup note)
2463   (markup? markup?)
2464   #:category other
2465   #:properties ((raise 0.5)
2466                 (padding 0.0))
2467   "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
2468
2469 @lilypond[verbatim,quote]
2470 \\markup {
2471   \\auto-footnote a b
2472   \\override #'(padding . 0.2)
2473   \\auto-footnote c d
2474 }
2475 @end lilypond
2476 The footnote will be annotated automatically."
2477   (let* ((markup-stencil (interpret-markup layout props mkup))
2478          (footnote-hash (gensym "footnote"))
2479          (stencil-seed 0)
2480          (gauge-stencil (interpret-markup
2481                          layout
2482                          props
2483                          ((ly:output-def-lookup
2484                            layout
2485                            'footnote-numbering-function)
2486                           stencil-seed)))
2487          (x-ext (ly:stencil-extent gauge-stencil X))
2488          (y-ext (ly:stencil-extent gauge-stencil Y))
2489          (footnote-number
2490           `(delay-stencil-evaluation
2491             ,(delay
2492                (ly:stencil-expr
2493                 (let* ((table
2494                         (ly:output-def-lookup layout
2495                                               'number-footnote-table))
2496                        (footnote-stencil (if (list? table)
2497                                              (assoc-get footnote-hash
2498                                                         table)
2499                                              empty-stencil))
2500                        (footnote-stencil (if (ly:stencil? footnote-stencil)
2501                                              footnote-stencil
2502                                              (begin
2503                                                (ly:programming-error
2504                                                 "Cannot find correct footnote for a markup object.")
2505                                                empty-stencil)))
2506                        (gap (- (interval-length x-ext)
2507                                (interval-length
2508                                 (ly:stencil-extent footnote-stencil X))))
2509                        (y-trans (- (+ (cdr y-ext)
2510                                       raise)
2511                                    (cdr (ly:stencil-extent footnote-stencil
2512                                                            Y)))))
2513                   (ly:stencil-translate footnote-stencil
2514                                         (cons gap y-trans)))))))
2515          (main-stencil (ly:stencil-combine-at-edge
2516                         markup-stencil
2517                         X
2518                         RIGHT
2519                         (ly:make-stencil footnote-number x-ext y-ext)
2520                         padding)))
2521     (ly:stencil-add
2522      main-stencil
2523      (ly:make-stencil
2524       `(footnote ,footnote-hash #t ,(interpret-markup layout props note))
2525       '(0 . 0)
2526       '(0 . 0)))))
2527
2528 (define-markup-command (override layout props new-prop arg)
2529   (pair? markup?)
2530   #:category other
2531   "
2532 @cindex overriding properties within text markup
2533
2534 Add the argument @var{new-prop} to the property list.  Properties
2535 may be any property supported by @rinternals{font-interface},
2536 @rinternals{text-interface} and
2537 @rinternals{instrument-specific-markup-interface}.
2538
2539 @lilypond[verbatim,quote]
2540 \\markup {
2541   \\line {
2542     \\column {
2543       default
2544       baseline-skip
2545     }
2546     \\hspace #2
2547     \\override #'(baseline-skip . 4) {
2548       \\column {
2549         increased
2550         baseline-skip
2551       }
2552     }
2553   }
2554 }
2555 @end lilypond"
2556   (interpret-markup layout (cons (list new-prop) props) arg))
2557
2558 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2559 ;; files
2560 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2561
2562 (define-markup-command (verbatim-file layout props name)
2563   (string?)
2564   #:category other
2565   "Read the contents of file @var{name}, and include it verbatim.
2566
2567 @lilypond[verbatim,quote]
2568 \\markup {
2569   \\verbatim-file #\"simple.ly\"
2570 }
2571 @end lilypond"
2572   (interpret-markup layout props
2573                     (if  (ly:get-option 'safe)
2574                          "verbatim-file disabled in safe mode"
2575                          (let* ((str (ly:gulp-file name))
2576                                 (lines (string-split str #\nl)))
2577                            (make-typewriter-markup
2578                             (make-column-markup lines))))))
2579
2580 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2581 ;; fonts.
2582 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2583
2584
2585 (define-markup-command (smaller layout props arg)
2586   (markup?)
2587   #:category font
2588   "Decrease the font size relative to the current setting.
2589
2590 @lilypond[verbatim,quote]
2591 \\markup {
2592   \\fontsize #3.5 {
2593     some large text
2594     \\hspace #2
2595     \\smaller {
2596       a bit smaller
2597     }
2598     \\hspace #2
2599     more large text
2600   }
2601 }
2602 @end lilypond"
2603   (interpret-markup layout props
2604                     `(,fontsize-markup -1 ,arg)))
2605
2606 (define-markup-command (larger layout props arg)
2607   (markup?)
2608   #:category font
2609   "Increase the font size relative to the current setting.
2610
2611 @lilypond[verbatim,quote]
2612 \\markup {
2613   default
2614   \\hspace #2
2615   \\larger
2616   larger
2617 }
2618 @end lilypond"
2619   (interpret-markup layout props
2620                     `(,fontsize-markup 1 ,arg)))
2621
2622 (define-markup-command (finger layout props arg)
2623   (markup?)
2624   #:category font
2625   "Set @var{arg} as small numbers.
2626
2627 @lilypond[verbatim,quote]
2628 \\markup {
2629   \\finger {
2630     1 2 3 4 5
2631   }
2632 }
2633 @end lilypond"
2634   (interpret-markup layout
2635                     (cons '((font-size . -5) (font-encoding . fetaText)) props)
2636                     arg))
2637
2638 (define-markup-command (abs-fontsize layout props size arg)
2639   (number? markup?)
2640   #:properties ((word-space 0.6) (baseline-skip 3))
2641   #:category font
2642   "Use @var{size} as the absolute font size (in points) to display @var{arg}.
2643 Adjusts @code{baseline-skip} and @code{word-space} accordingly.
2644
2645 @lilypond[verbatim,quote]
2646 \\markup {
2647   default text font size
2648   \\hspace #2
2649   \\abs-fontsize #16 { text font size 16 }
2650   \\hspace #2
2651   \\abs-fontsize #12 { text font size 12 }
2652 }
2653 @end lilypond"
2654   (let* ((ref-size (ly:output-def-lookup layout 'text-font-size 12))
2655          (text-props (list (ly:output-def-lookup layout 'text-font-defaults)))
2656          (magnification (/ size ref-size)))
2657     (interpret-markup
2658      layout
2659      (cons
2660       `((baseline-skip . ,(* magnification baseline-skip))
2661         (word-space . ,(* magnification word-space))
2662         (font-size . ,(magnification->font-size magnification)))
2663       props)
2664      arg)))
2665
2666 (define-markup-command (fontsize layout props increment arg)
2667   (number? markup?)
2668   #:category font
2669   #:properties ((font-size 0)
2670                 (word-space 1)
2671                 (baseline-skip 2))
2672   "Add @var{increment} to the font-size.  Adjusts @code{baseline-skip}
2673 accordingly.
2674
2675 @lilypond[verbatim,quote]
2676 \\markup {
2677   default
2678   \\hspace #2
2679   \\fontsize #-1.5
2680   smaller
2681 }
2682 @end lilypond"
2683   (interpret-markup
2684    layout
2685    (cons
2686     `((baseline-skip . ,(* baseline-skip (magstep increment)))
2687       (word-space . ,(* word-space (magstep increment)))
2688       (font-size . ,(+ font-size increment)))
2689     props)
2690    arg))
2691
2692 (define-markup-command (magnify layout props sz arg)
2693   (number? markup?)
2694   #:category font
2695   "
2696 @cindex magnifying text
2697
2698 Set the font magnification for its argument.  In the following
2699 example, the middle@tie{}A is 10% larger:
2700
2701 @example
2702 A \\magnify #1.1 @{ A @} A
2703 @end example
2704
2705 Note: Magnification only works if a font name is explicitly selected.
2706 Use @code{\\fontsize} otherwise.
2707
2708 @lilypond[verbatim,quote]
2709 \\markup {
2710   default
2711   \\hspace #2
2712   \\magnify #1.5 {
2713     50% larger
2714   }
2715 }
2716 @end lilypond"
2717   (interpret-markup
2718    layout
2719    (prepend-alist-chain 'font-size (magnification->font-size sz) props)
2720    arg))
2721
2722 (define-markup-command (bold layout props arg)
2723   (markup?)
2724   #:category font
2725   "Switch to bold font-series.
2726
2727 @lilypond[verbatim,quote]
2728 \\markup {
2729   default
2730   \\hspace #2
2731   \\bold
2732   bold
2733 }
2734 @end lilypond"
2735   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
2736
2737 (define-markup-command (sans layout props arg)
2738   (markup?)
2739   #:category font
2740   "Switch to the sans serif font family.
2741
2742 @lilypond[verbatim,quote]
2743 \\markup {
2744   default
2745   \\hspace #2
2746   \\sans {
2747     sans serif
2748   }
2749 }
2750 @end lilypond"
2751   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
2752
2753 (define-markup-command (number layout props arg)
2754   (markup?)
2755   #:category font
2756   "Set font family to @code{number}, which yields the font used for
2757 time signatures and fingerings.  This font contains numbers and
2758 some punctuation; it has no letters.
2759
2760 @lilypond[verbatim,quote]
2761 \\markup {
2762   \\number {
2763     0 1 2 3 4 5 6 7 8 9 . ,
2764   }
2765 }
2766 @end lilypond"
2767   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2768
2769 (define-markup-command (roman layout props arg)
2770   (markup?)
2771   #:category font
2772   "Set font family to @code{roman}.
2773
2774 @lilypond[verbatim,quote]
2775 \\markup {
2776   \\sans \\bold {
2777     sans serif, bold
2778     \\hspace #2
2779     \\roman {
2780       text in roman font family
2781     }
2782     \\hspace #2
2783     return to sans
2784   }
2785 }
2786 @end lilypond"
2787   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
2788
2789 (define-markup-command (huge layout props arg)
2790   (markup?)
2791   #:category font
2792   "Set font size to +2.
2793
2794 @lilypond[verbatim,quote]
2795 \\markup {
2796   default
2797   \\hspace #2
2798   \\huge
2799   huge
2800 }
2801 @end lilypond"
2802   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
2803
2804 (define-markup-command (large layout props arg)
2805   (markup?)
2806   #:category font
2807   "Set font size to +1.
2808
2809 @lilypond[verbatim,quote]
2810 \\markup {
2811   default
2812   \\hspace #2
2813   \\large
2814   large
2815 }
2816 @end lilypond"
2817   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
2818
2819 (define-markup-command (normalsize layout props arg)
2820   (markup?)
2821   #:category font
2822   "Set font size to default.
2823
2824 @lilypond[verbatim,quote]
2825 \\markup {
2826   \\teeny {
2827     this is very small
2828     \\hspace #2
2829     \\normalsize {
2830       normal size
2831     }
2832     \\hspace #2
2833     teeny again
2834   }
2835 }
2836 @end lilypond"
2837   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
2838
2839 (define-markup-command (small layout props arg)
2840   (markup?)
2841   #:category font
2842   "Set font size to -1.
2843
2844 @lilypond[verbatim,quote]
2845 \\markup {
2846   default
2847   \\hspace #2
2848   \\small
2849   small
2850 }
2851 @end lilypond"
2852   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
2853
2854 (define-markup-command (tiny layout props arg)
2855   (markup?)
2856   #:category font
2857   "Set font size to -2.
2858
2859 @lilypond[verbatim,quote]
2860 \\markup {
2861   default
2862   \\hspace #2
2863   \\tiny
2864   tiny
2865 }
2866 @end lilypond"
2867   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
2868
2869 (define-markup-command (teeny layout props arg)
2870   (markup?)
2871   #:category font
2872   "Set font size to -3.
2873
2874 @lilypond[verbatim,quote]
2875 \\markup {
2876   default
2877   \\hspace #2
2878   \\teeny
2879   teeny
2880 }
2881 @end lilypond"
2882   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
2883
2884 (define-markup-command (fontCaps layout props arg)
2885   (markup?)
2886   #:category font
2887   "Set @code{font-shape} to @code{caps}
2888
2889 Note: @code{\\fontCaps} requires the installation and selection of
2890 fonts which support the @code{caps} font shape."
2891   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
2892
2893 ;; Poor man's caps
2894 (define-markup-command (smallCaps layout props arg)
2895   (markup?)
2896   #:category font
2897   "Emit @var{arg} as small caps.
2898
2899 Note: @code{\\smallCaps} does not support accented characters.
2900
2901 @lilypond[verbatim,quote]
2902 \\markup {
2903   default
2904   \\hspace #2
2905   \\smallCaps {
2906     Text in small caps
2907   }
2908 }
2909 @end lilypond"
2910   (define (char-list->markup chars lower)
2911     (let ((final-string (string-upcase (reverse-list->string chars))))
2912       (if lower
2913           (markup #:fontsize -2 final-string)
2914           final-string)))
2915   (define (make-small-caps rest-chars currents current-is-lower prev-result)
2916     (if (null? rest-chars)
2917         (make-concat-markup
2918          (reverse! (cons (char-list->markup currents current-is-lower)
2919                          prev-result)))
2920         (let* ((ch (car rest-chars))
2921                (is-lower (char-lower-case? ch)))
2922           (if (or (and current-is-lower is-lower)
2923                   (and (not current-is-lower) (not is-lower)))
2924               (make-small-caps (cdr rest-chars)
2925                                (cons ch currents)
2926                                is-lower
2927                                prev-result)
2928               (make-small-caps (cdr rest-chars)
2929                                (list ch)
2930                                is-lower
2931                                (if (null? currents)
2932                                    prev-result
2933                                    (cons (char-list->markup
2934                                           currents current-is-lower)
2935                                          prev-result)))))))
2936   (interpret-markup layout props
2937                     (if (string? arg)
2938                         (make-small-caps (string->list arg) (list) #f (list))
2939                         arg)))
2940
2941 (define-markup-command (caps layout props arg)
2942   (markup?)
2943   #:category font
2944   "Copy of the @code{\\smallCaps} command.
2945
2946 @lilypond[verbatim,quote]
2947 \\markup {
2948   default
2949   \\hspace #2
2950   \\caps {
2951     Text in small caps
2952   }
2953 }
2954 @end lilypond"
2955   (interpret-markup layout props (make-smallCaps-markup arg)))
2956
2957 (define-markup-command (dynamic layout props arg)
2958   (markup?)
2959   #:category font
2960   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
2961 @b{z}, @b{p}, and @b{r}.  When producing phrases, like
2962 @q{pi@`{u}@tie{}@b{f}}, the normal words (like @q{pi@`{u}}) should be
2963 done in a different font.  The recommended font for this is bold and italic.
2964 @lilypond[verbatim,quote]
2965 \\markup {
2966   \\dynamic {
2967     sfzp
2968   }
2969 }
2970 @end lilypond"
2971   (interpret-markup
2972    layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2973
2974 (define-markup-command (text layout props arg)
2975   (markup?)
2976   #:category font
2977   "Use a text font instead of music symbol or music alphabet font.
2978
2979 @lilypond[verbatim,quote]
2980 \\markup {
2981   \\number {
2982     1, 2,
2983     \\text {
2984       three, four,
2985     }
2986     5
2987   }
2988 }
2989 @end lilypond"
2990
2991   ;; ugh - latin1
2992   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
2993                     arg))
2994
2995 (define-markup-command (italic layout props arg)
2996   (markup?)
2997   #:category font
2998   "Use italic @code{font-shape} for @var{arg}.
2999
3000 @lilypond[verbatim,quote]
3001 \\markup {
3002   default
3003   \\hspace #2
3004   \\italic
3005   italic
3006 }
3007 @end lilypond"
3008   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
3009
3010 (define-markup-command (typewriter layout props arg)
3011   (markup?)
3012   #:category font
3013   "Use @code{font-family} typewriter for @var{arg}.
3014
3015 @lilypond[verbatim,quote]
3016 \\markup {
3017   default
3018   \\hspace #2
3019   \\typewriter
3020   typewriter
3021 }
3022 @end lilypond"
3023   (interpret-markup
3024    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
3025
3026 (define-markup-command (upright layout props arg)
3027   (markup?)
3028   #:category font
3029   "Set @code{font-shape} to @code{upright}.  This is the opposite
3030 of @code{italic}.
3031
3032 @lilypond[verbatim,quote]
3033 \\markup {
3034   \\italic {
3035     italic text
3036     \\hspace #2
3037     \\upright {
3038       upright text
3039     }
3040     \\hspace #2
3041     italic again
3042   }
3043 }
3044 @end lilypond"
3045   (interpret-markup
3046    layout (prepend-alist-chain 'font-shape 'upright props) arg))
3047
3048 (define-markup-command (medium layout props arg)
3049   (markup?)
3050   #:category font
3051   "Switch to medium font-series (in contrast to bold).
3052
3053 @lilypond[verbatim,quote]
3054 \\markup {
3055   \\bold {
3056     some bold text
3057     \\hspace #2
3058     \\medium {
3059       medium font series
3060     }
3061     \\hspace #2
3062     bold again
3063   }
3064 }
3065 @end lilypond"
3066   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
3067                     arg))
3068
3069 (define-markup-command (normal-text layout props arg)
3070   (markup?)
3071   #:category font
3072   "Set all font related properties (except the size) to get the default
3073 normal text font, no matter what font was used earlier.
3074
3075 @lilypond[verbatim,quote]
3076 \\markup {
3077   \\huge \\bold \\sans \\caps {
3078     huge bold sans caps
3079     \\hspace #2
3080     \\normal-text {
3081       huge normal
3082     }
3083     \\hspace #2
3084     as before
3085   }
3086 }
3087 @end lilypond"
3088   ;; ugh - latin1
3089   (interpret-markup layout
3090                     (cons '((font-family . roman) (font-shape . upright)
3091                             (font-series . medium) (font-encoding . latin1))
3092                           props)
3093                     arg))
3094
3095 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3096 ;; symbols.
3097 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3098
3099 (define-markup-command (musicglyph layout props glyph-name)
3100   (string?)
3101   #:category music
3102   "@var{glyph-name} is converted to a musical symbol; for example,
3103 @code{\\musicglyph #\"accidentals.natural\"} selects the natural sign from
3104 the music font.  See @ruser{The Emmentaler font} for a complete listing of
3105 the possible glyphs.
3106
3107 @lilypond[verbatim,quote]
3108 \\markup {
3109   \\musicglyph #\"f\"
3110   \\musicglyph #\"rests.2\"
3111   \\musicglyph #\"clefs.G_change\"
3112 }
3113 @end lilypond"
3114   (let* ((font (ly:paper-get-font layout
3115                                   (cons '((font-encoding . fetaMusic)
3116                                           (font-name . #f))
3117
3118                                         props)))
3119          (glyph (ly:font-get-glyph font glyph-name)))
3120     (if (null? (ly:stencil-expr glyph))
3121         (ly:warning (_ "Cannot find glyph ~a") glyph-name))
3122
3123     glyph))
3124
3125 (define-markup-command (doublesharp layout props)
3126   ()
3127   #:category music
3128   "Draw a double sharp symbol.
3129
3130 @lilypond[verbatim,quote]
3131 \\markup {
3132   \\doublesharp
3133 }
3134 @end lilypond"
3135   (interpret-markup layout props (markup #:musicglyph (assoc-get 1 standard-alteration-glyph-name-alist ""))))
3136
3137 (define-markup-command (sesquisharp layout props)
3138   ()
3139   #:category music
3140   "Draw a 3/2 sharp symbol.
3141
3142 @lilypond[verbatim,quote]
3143 \\markup {
3144   \\sesquisharp
3145 }
3146 @end lilypond"
3147   (interpret-markup layout props (markup #:musicglyph (assoc-get 3/4 standard-alteration-glyph-name-alist ""))))
3148
3149 (define-markup-command (sharp layout props)
3150   ()
3151   #:category music
3152   "Draw a sharp symbol.
3153
3154 @lilypond[verbatim,quote]
3155 \\markup {
3156   \\sharp
3157 }
3158 @end lilypond"
3159   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/2 standard-alteration-glyph-name-alist ""))))
3160
3161 (define-markup-command (semisharp layout props)
3162   ()
3163   #:category music
3164   "Draw a semisharp symbol.
3165
3166 @lilypond[verbatim,quote]
3167 \\markup {
3168   \\semisharp
3169 }
3170 @end lilypond"
3171   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/4 standard-alteration-glyph-name-alist ""))))
3172
3173 (define-markup-command (natural layout props)
3174   ()
3175   #:category music
3176   "Draw a natural symbol.
3177
3178 @lilypond[verbatim,quote]
3179 \\markup {
3180   \\natural
3181 }
3182 @end lilypond"
3183   (interpret-markup layout props (markup #:musicglyph (assoc-get 0 standard-alteration-glyph-name-alist ""))))
3184
3185 (define-markup-command (semiflat layout props)
3186   ()
3187   #:category music
3188   "Draw a semiflat symbol.
3189
3190 @lilypond[verbatim,quote]
3191 \\markup {
3192   \\semiflat
3193 }
3194 @end lilypond"
3195   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/4 standard-alteration-glyph-name-alist ""))))
3196
3197 (define-markup-command (flat layout props)
3198   ()
3199   #:category music
3200   "Draw a flat symbol.
3201
3202 @lilypond[verbatim,quote]
3203 \\markup {
3204   \\flat
3205 }
3206 @end lilypond"
3207   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/2 standard-alteration-glyph-name-alist ""))))
3208
3209 (define-markup-command (sesquiflat layout props)
3210   ()
3211   #:category music
3212   "Draw a 3/2 flat symbol.
3213
3214 @lilypond[verbatim,quote]
3215 \\markup {
3216   \\sesquiflat
3217 }
3218 @end lilypond"
3219   (interpret-markup layout props (markup #:musicglyph (assoc-get -3/4 standard-alteration-glyph-name-alist ""))))
3220
3221 (define-markup-command (doubleflat layout props)
3222   ()
3223   #:category music
3224   "Draw a double flat symbol.
3225
3226 @lilypond[verbatim,quote]
3227 \\markup {
3228   \\doubleflat
3229 }
3230 @end lilypond"
3231   (interpret-markup layout props (markup #:musicglyph (assoc-get -1 standard-alteration-glyph-name-alist ""))))
3232
3233 (define-markup-command (with-color layout props color arg)
3234   (color? markup?)
3235   #:category other
3236   "
3237 @cindex coloring text
3238
3239 Draw @var{arg} in color specified by @var{color}.
3240
3241 @lilypond[verbatim,quote]
3242 \\markup {
3243   \\with-color #red
3244   red
3245   \\hspace #2
3246   \\with-color #green
3247   green
3248   \\hspace #2
3249   \\with-color #blue
3250   blue
3251 }
3252 @end lilypond"
3253   (let ((stil (interpret-markup layout props arg)))
3254     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
3255                      (ly:stencil-extent stil X)
3256                      (ly:stencil-extent stil Y))))
3257
3258 (define-markup-command (tied-lyric layout props str)
3259   (string?)
3260   #:category music
3261   #:properties ((word-space))
3262   "
3263 @cindex simple text strings with tie characters
3264
3265 Like simple-markup, but use tie characters for @q{~} tilde symbols.
3266
3267 @lilypond[verbatim,quote]
3268 \\markup \\column {
3269   \\tied-lyric #\"Siam navi~all'onde~algenti Lasciate~in abbandono\"
3270   \\tied-lyric #\"Impetuosi venti I nostri~affetti sono\"
3271   \\tied-lyric #\"Ogni diletto~e scoglio Tutta la vita~e~un mar.\"
3272 }
3273 @end lilypond"
3274   (define (replace-ties tie str)
3275     (if (string-contains str "~")
3276         (let*
3277             ((half-space (/ word-space 2))
3278              (parts (string-split str #\~))
3279              (tie-str (markup #:hspace half-space
3280                               #:musicglyph tie
3281                               #:hspace half-space))
3282              (joined  (list-join parts tie-str)))
3283           (make-concat-markup joined))
3284         str))
3285
3286   (define short-tie-regexp (make-regexp "~[^.]~"))
3287   (define (match-short str) (regexp-exec short-tie-regexp str))
3288
3289   (define (replace-short str mkp)
3290     (let ((match (match-short str)))
3291       (if (not match)
3292           (make-concat-markup (list
3293                                mkp
3294                                (replace-ties "ties.lyric.default" str)))
3295           (let ((new-str (match:suffix match))
3296                 (new-mkp (make-concat-markup (list
3297                                               mkp
3298                                               (replace-ties "ties.lyric.default"
3299                                                             (match:prefix match))
3300                                               (replace-ties "ties.lyric.short"
3301                                                             (match:substring match))))))
3302             (replace-short new-str new-mkp)))))
3303
3304   (interpret-markup layout
3305                     props
3306                     (replace-short str (markup))))
3307
3308 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3309 ;; glyphs
3310 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3311
3312 (define-markup-command (arrow-head layout props axis dir filled)
3313   (integer? ly:dir? boolean?)
3314   #:category graphic
3315   "Produce an arrow head in specified direction and axis.
3316 Use the filled head if @var{filled} is specified.
3317 @lilypond[verbatim,quote]
3318 \\markup {
3319   \\fontsize #5 {
3320     \\general-align #Y #DOWN {
3321       \\arrow-head #Y #UP ##t
3322       \\arrow-head #Y #DOWN ##f
3323       \\hspace #2
3324       \\arrow-head #X #RIGHT ##f
3325       \\arrow-head #X #LEFT ##f
3326     }
3327   }
3328 }
3329 @end lilypond"
3330   (let*
3331       ((name (format #f "arrowheads.~a.~a~a"
3332                      (if filled
3333                          "close"
3334                          "open")
3335                      axis
3336                      dir)))
3337     (ly:font-get-glyph
3338      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
3339                                      props))
3340      name)))
3341
3342 (define-markup-command (lookup layout props glyph-name)
3343   (string?)
3344   #:category other
3345   "Lookup a glyph by name.
3346
3347 @lilypond[verbatim,quote]
3348 \\markup {
3349   \\override #'(font-encoding . fetaBraces) {
3350     \\lookup #\"brace200\"
3351     \\hspace #2
3352     \\rotate #180
3353     \\lookup #\"brace180\"
3354   }
3355 }
3356 @end lilypond"
3357   (ly:font-get-glyph (ly:paper-get-font layout props)
3358                      glyph-name))
3359
3360 (define-markup-command (char layout props num)
3361   (integer?)
3362   #:category other
3363   "Produce a single character.  Characters encoded in hexadecimal
3364 format require the prefix @code{#x}.
3365
3366 @lilypond[verbatim,quote]
3367 \\markup {
3368   \\char #65 \\char ##x00a9
3369 }
3370 @end lilypond"
3371   (ly:text-interface::interpret-markup layout props (ly:wide-char->utf-8 num)))
3372
3373 (define number->mark-letter-vector (make-vector 25 #\A))
3374
3375 (do ((i 0 (1+ i))
3376      (j 0 (1+ j)))
3377     ((>= i 26))
3378   (if (= i (- (char->integer #\I) (char->integer #\A)))
3379       (set! i (1+ i)))
3380   (vector-set! number->mark-letter-vector j
3381                (integer->char (+ i (char->integer #\A)))))
3382
3383 (define number->mark-alphabet-vector (list->vector
3384                                       (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
3385
3386 (define (number->markletter-string vec n)
3387   "Double letters for big marks."
3388   (let* ((lst (vector-length vec)))
3389
3390     (if (>= n lst)
3391         (string-append (number->markletter-string vec (1- (quotient n lst)))
3392                        (number->markletter-string vec (remainder n lst)))
3393         (make-string 1 (vector-ref vec n)))))
3394
3395 (define-markup-command (markletter layout props num)
3396   (integer?)
3397   #:category other
3398   "Make a markup letter for @var{num}.  The letters start with A
3399 to@tie{}Z (skipping letter@tie{}I), and continue with double letters.
3400
3401 @lilypond[verbatim,quote]
3402 \\markup {
3403   \\markletter #8
3404   \\hspace #2
3405   \\markletter #26
3406 }
3407 @end lilypond"
3408   (ly:text-interface::interpret-markup layout props
3409                                        (number->markletter-string number->mark-letter-vector num)))
3410
3411 (define-markup-command (markalphabet layout props num)
3412   (integer?)
3413   #:category other
3414   "Make a markup letter for @var{num}.  The letters start with A to@tie{}Z
3415 and continue with double letters.
3416
3417 @lilypond[verbatim,quote]
3418 \\markup {
3419   \\markalphabet #8
3420   \\hspace #2
3421   \\markalphabet #26
3422 }
3423 @end lilypond"
3424   (ly:text-interface::interpret-markup layout props
3425                                        (number->markletter-string number->mark-alphabet-vector num)))
3426
3427 (define-public (horizontal-slash-interval num forward number-interval mag)
3428   (if forward
3429       (cond ;; ((= num 6) (interval-widen number-interval (* mag 0.5)))
3430        ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
3431        (else (interval-widen number-interval (* mag 0.25))))
3432       (cond ((= num 6) (interval-widen number-interval (* mag 0.5)))
3433             ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
3434             (else (interval-widen number-interval (* mag 0.25))))
3435       ))
3436
3437 (define-public (adjust-slash-stencil num forward stencil mag)
3438   (if forward
3439       (cond ((= num 2)
3440              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
3441             ((= num 3)
3442              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
3443             ;; ((= num 5)
3444             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.07))))
3445             ;; ((= num 7)
3446             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
3447             (else stencil))
3448       (cond ((= num 6)
3449              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.15))))
3450             ;; ((= num 8)
3451             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
3452             (else stencil))
3453       )
3454   )
3455
3456 (define (slashed-digit-internal layout props num forward font-size thickness)
3457   (let* ((mag (magstep font-size))
3458          (thickness (* mag
3459                        (ly:output-def-lookup layout 'line-thickness)
3460                        thickness))
3461          ;; backward slashes might use slope and point in the other direction!
3462          (dy (* mag (if forward 0.4 -0.4)))
3463          (number-stencil (interpret-markup layout
3464                                            (prepend-alist-chain 'font-encoding 'fetaText props)
3465                                            (number->string num)))
3466          (num-x (horizontal-slash-interval num forward (ly:stencil-extent number-stencil X) mag))
3467          (center (interval-center (ly:stencil-extent number-stencil Y)))
3468          ;; Use the real extents of the slash, not the whole number,
3469          ;; because we might translate the slash later on!
3470          (num-y (interval-widen (cons center center) (abs dy)))
3471          (is-sane (and (interval-sane? num-x) (interval-sane? num-y)))
3472          (slash-stencil (if is-sane
3473                             (make-line-stencil thickness
3474                                                (car num-x) (- (interval-center num-y) dy)
3475                                                (cdr num-x) (+ (interval-center num-y) dy))
3476                             #f)))
3477     (if (ly:stencil? slash-stencil)
3478         (begin
3479           ;; for some numbers we need to shift the slash/backslash up or
3480           ;; down to make the slashed digit look better
3481           (set! slash-stencil (adjust-slash-stencil num forward slash-stencil mag))
3482           (set! number-stencil
3483                 (ly:stencil-add number-stencil slash-stencil)))
3484         (ly:warning "Unable to create slashed digit ~a" num))
3485     number-stencil))
3486
3487
3488 (define-markup-command (slashed-digit layout props num)
3489   (integer?)
3490   #:category other
3491   #:properties ((font-size 0)
3492                 (thickness 1.6))
3493   "
3494 @cindex slashed digits
3495
3496 A feta number, with slash.  This is for use in the context of
3497 figured bass notation.
3498 @lilypond[verbatim,quote]
3499 \\markup {
3500   \\slashed-digit #5
3501   \\hspace #2
3502   \\override #'(thickness . 3)
3503   \\slashed-digit #7
3504 }
3505 @end lilypond"
3506   (slashed-digit-internal layout props num #t font-size thickness))
3507
3508 (define-markup-command (backslashed-digit layout props num)
3509   (integer?)
3510   #:category other
3511   #:properties ((font-size 0)
3512                 (thickness 1.6))
3513   "
3514 @cindex backslashed digits
3515
3516 A feta number, with backslash.  This is for use in the context of
3517 figured bass notation.
3518 @lilypond[verbatim,quote]
3519 \\markup {
3520   \\backslashed-digit #5
3521   \\hspace #2
3522   \\override #'(thickness . 3)
3523   \\backslashed-digit #7
3524 }
3525 @end lilypond"
3526   (slashed-digit-internal layout props num #f font-size thickness))
3527
3528 ;; eyeglasses
3529 (define eyeglassespath
3530   '((moveto 0.42 0.77)
3531     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
3532     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
3533     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
3534     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
3535     (closepath)
3536     (moveto 2.07 0.77)
3537     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
3538     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
3539     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
3540     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
3541     (closepath)
3542     (moveto 1.025 0.935)
3543     (rcurveto 0 0.182 -0.148 0.33 -0.33 0.33)
3544     (rcurveto -0.182 0 -0.33 -0.148 -0.33 -0.33)
3545     (moveto -0.68 0.77)
3546     (rlineto 0.66 1.43)
3547     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)
3548     (moveto 2.07 0.77)
3549     (rlineto 0.66 1.43)
3550     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)))
3551
3552 (define-markup-command (eyeglasses layout props)
3553   ()
3554   #:category other
3555   "Prints out eyeglasses, indicating strongly to look at the conductor.
3556 @lilypond[verbatim,quote]
3557 \\markup { \\eyeglasses }
3558 @end lilypond"
3559   (interpret-markup layout props
3560                     (make-override-markup '(line-cap-style . butt)
3561                                           (make-path-markup 0.15 eyeglassespath))))
3562
3563 (define-markup-command (left-brace layout props size)
3564   (number?)
3565   #:category other
3566   "
3567 A feta brace in point size @var{size}.
3568
3569 @lilypond[verbatim,quote]
3570 \\markup {
3571   \\left-brace #35
3572   \\hspace #2
3573   \\left-brace #45
3574 }
3575 @end lilypond"
3576   (let* ((font (ly:paper-get-font layout
3577                                   (cons '((font-encoding . fetaBraces)
3578                                           (font-name . #f))
3579                                         props)))
3580          (glyph-count (1- (ly:otf-glyph-count font)))
3581          (scale (ly:output-def-lookup layout 'output-scale))
3582          (scaled-size (/ (ly:pt size) scale))
3583          (glyph (lambda (n)
3584                   (ly:font-get-glyph font (string-append "brace"
3585                                                          (number->string n)))))
3586          (get-y-from-brace (lambda (brace)
3587                              (interval-length
3588                               (ly:stencil-extent (glyph brace) Y))))
3589          (find-brace (binary-search 0 glyph-count get-y-from-brace scaled-size))
3590          (glyph-found (glyph find-brace)))
3591
3592     (if (or (null? (ly:stencil-expr glyph-found))
3593             (< scaled-size (interval-length (ly:stencil-extent (glyph 0) Y)))
3594             (> scaled-size (interval-length
3595                             (ly:stencil-extent (glyph glyph-count) Y))))
3596         (begin
3597           (ly:warning (_ "no brace found for point size ~S ") size)
3598           (ly:warning (_ "defaulting to ~S pt")
3599                       (/ (* scale (interval-length
3600                                    (ly:stencil-extent glyph-found Y)))
3601                          (ly:pt 1)))))
3602     glyph-found))
3603
3604 (define-markup-command (right-brace layout props size)
3605   (number?)
3606   #:category other
3607   "
3608 A feta brace in point size @var{size}, rotated 180 degrees.
3609
3610 @lilypond[verbatim,quote]
3611 \\markup {
3612   \\right-brace #45
3613   \\hspace #2
3614   \\right-brace #35
3615 }
3616 @end lilypond"
3617   (interpret-markup layout props (markup #:rotate 180 #:left-brace size)))
3618
3619 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3620 ;; the note command.
3621 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3622
3623 ;; TODO: better syntax.
3624
3625 (define-markup-command (note-by-number layout props log dot-count dir)
3626   (number? number? number?)
3627   #:category music
3628   #:properties ((font-size 0)
3629                 (flag-style '())
3630                 (style '()))
3631   "
3632 @cindex notes within text by log and dot-count
3633
3634 Construct a note symbol, with stem and flag.  By using fractional values for
3635 @var{dir}, longer or shorter stems can be obtained.
3636 Supports all note-head-styles.  Ancient note-head-styles will get
3637 mensural-style-flags.  @code{flag-style} may be overridden independently.
3638 Supported flag-styles are @code{default}, @code{old-straight-flag},
3639 @code{modern-straight-flag}, @code{flat-flag}, @code{mensural} and
3640 @code{neomensural}.  The latter two flag-styles will both result in
3641 mensural-flags.  Both are supplied for convenience.
3642
3643 @lilypond[verbatim,quote]
3644 \\markup {
3645   \\note-by-number #3 #0 #DOWN
3646   \\hspace #2
3647   \\note-by-number #1 #2 #0.8
3648 }
3649 @end lilypond"
3650   (define (get-glyph-name-candidates dir log style)
3651     (map (lambda (dir-name)
3652            (format #f "noteheads.~a~a"
3653                    dir-name
3654                    (if (and (symbol? style)
3655                             (not (equal? 'default style)))
3656                        (select-head-glyph style (min log 2))
3657                        (min log 2))))
3658          (list (if (= dir UP) "u" "d")
3659                "s")))
3660
3661   (define (get-glyph-name font cands)
3662     (if (null? cands)
3663         ""
3664         (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
3665             (get-glyph-name font (cdr cands))
3666             (car cands))))
3667
3668   (define (buildflags flag-stencil remain curr-stencil spacing)
3669     ;; Function to recursively create a stencil with @code{remain} flags
3670     ;; from the single-flag stencil @code{curr-stencil}, which is already
3671     ;; translated to the position of the previous flag position.
3672     ;;
3673     ;; Copy and paste from /scm/flag-styles.scm
3674     (if (> remain 0)
3675         (let* ((translated-stencil
3676                 (ly:stencil-translate-axis curr-stencil spacing Y))
3677                (new-stencil (ly:stencil-add flag-stencil translated-stencil)))
3678           (buildflags new-stencil (- remain 1) translated-stencil spacing))
3679         flag-stencil))
3680
3681   (define (straight-flag-mrkp flag-thickness flag-spacing
3682                               upflag-angle upflag-length
3683                               downflag-angle downflag-length
3684                               dir)
3685     ;; Create a stencil for a straight flag.  @var{flag-thickness} and
3686     ;; @var{flag-spacing} are given in staff spaces, @var{upflag-angle} and
3687     ;; @var{downflag-angle} are given in degrees, and @var{upflag-length} and
3688     ;; @var{downflag-length} are given in staff spaces.
3689     ;;
3690     ;; All lengths are scaled according to the font size of the note.
3691     ;;
3692     ;; From /scm/flag-styles.scm, modified to fit here.
3693
3694     (let* ((stem-up (> dir 0))
3695            ;; scale with the note size
3696            (factor (magstep font-size))
3697            (stem-thickness (* factor 0.1))
3698            (line-thickness (ly:output-def-lookup layout 'line-thickness))
3699            (half-stem-thickness (/ (* stem-thickness line-thickness) 2))
3700            (raw-length (if stem-up upflag-length downflag-length))
3701            (angle (if stem-up upflag-angle downflag-angle))
3702            (flag-length (+ (* raw-length factor) half-stem-thickness))
3703            (flag-end (polar->rectangular flag-length angle))
3704            (thickness (* flag-thickness factor))
3705            (thickness-offset (cons 0 (* -1 thickness dir)))
3706            (spacing (* -1 flag-spacing factor dir))
3707            (start (cons (- half-stem-thickness) (* half-stem-thickness dir)))
3708            (raw-points
3709              (list
3710                '(0 . 0)
3711                flag-end
3712                (offset-add flag-end thickness-offset)
3713                thickness-offset))
3714            (points (map (lambda (coord) (offset-add coord start)) raw-points))
3715            (stencil (ly:round-filled-polygon points half-stem-thickness))
3716            ;; Log for 1/8 is 3, so we need to subtract 3
3717            (flag-stencil (buildflags stencil (- log 3) stencil spacing)))
3718       flag-stencil))
3719
3720   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)
3721                                                  (font-name . #f))
3722                                                props)))
3723          ;; default for text-font-size is 11
3724          ;; hence we use (/ text-font-size 11) later, to ensure proper scaling
3725          ;; of stem-length and thickness
3726          (text-font-size (ly:output-def-lookup layout 'text-font-size 11))
3727          (size-factor (magstep font-size))
3728          (blot (ly:output-def-lookup layout 'blot-diameter))
3729          (head-glyph-name
3730           (let ((result (get-glyph-name font
3731                                         (get-glyph-name-candidates
3732                                          (sign dir) log style))))
3733             (if (string-null? result)
3734                 ;; If no glyph name can be found, select default heads.
3735                 ;; Though this usually means an unsupported style has been
3736                 ;; chosen, it also prevents unrelated 'style settings from
3737                 ;; other grobs (e.g., TextSpanner and TimeSignature) leaking
3738                 ;; into markup.
3739                 (get-glyph-name font
3740                                 (get-glyph-name-candidates
3741                                  (sign dir) log 'default))
3742                 result)))
3743          (head-glyph (ly:font-get-glyph font head-glyph-name))
3744          (ancient-flags?
3745            (member style
3746                    '(mensural neomensural petrucci semipetrucci blackpetrucci)))
3747          (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
3748          (stem-length (* size-factor (max 3 (- log 1))))
3749          ;; With ancient-flags we want a tighter stem
3750          (stem-thickness
3751            (* size-factor (/ text-font-size 11) (if ancient-flags? 0.1 0.13)))
3752          (stemy (* dir (/ text-font-size 11) stem-length))
3753          (attach-off (cons (interval-index
3754                             (ly:stencil-extent head-glyph X)
3755                             (* (sign dir) (car attach-indices)))
3756                            ;; fixme, this is inconsistent between X & Y.
3757                            (* (sign dir)
3758                               (interval-index
3759                                (ly:stencil-extent head-glyph Y)
3760                                (cdr attach-indices)))))
3761          ;; For a tighter stem (with ancient-flags) the stem-width has to be
3762          ;; adjusted.
3763          (stem-X-corr
3764            (if (or ancient-flags?
3765                    (member flag-style '(mensural neomensural)))
3766                    (* 0.5 dir stem-thickness) 0))
3767          (stem-glyph (and (> log 0)
3768                           (ly:round-filled-box
3769                            (ordered-cons (+ stem-X-corr (car attach-off))
3770                                          (+ stem-X-corr (car attach-off)
3771                                             (* (- (sign dir)) stem-thickness)))
3772                            (cons (min stemy (cdr attach-off))
3773                                  (max stemy (cdr attach-off)))
3774                            (/ stem-thickness 3))))
3775          (dot (ly:font-get-glyph font "dots.dot"))
3776          (dotwid (interval-length (ly:stencil-extent dot X)))
3777          (dots (and (> dot-count 0)
3778                     (apply ly:stencil-add
3779                            (map (lambda (x)
3780                                   (ly:stencil-translate-axis
3781                                    dot (* 2 x dotwid) X))
3782                                 (iota dot-count)))))
3783          ;; Straight-flags. Values taken from /scm/flag-style.scm
3784          (modern-straight-flag (straight-flag-mrkp 0.55 1 -18 1.1 22 1.2 dir))
3785          (old-straight-flag (straight-flag-mrkp 0.55 1 -45 1.2 45 1.4 dir))
3786          (flat-flag (straight-flag-mrkp 0.55 1.0 0 1.0 0 1.0 dir))
3787          ;; Calculate a corrective to avoid a gap between
3788          ;; straight-flags and the stem.
3789          (flag-style-Y-corr (if (or (eq? flag-style 'modern-straight-flag)
3790                                     (eq? flag-style 'old-straight-flag)
3791                                     (eq? flag-style 'flat-flag))
3792                                 (/ blot 10 (* -1 dir))
3793                                 0))
3794          (flaggl (and (> log 2)
3795                       (ly:stencil-translate
3796                        (cond ((eq? flag-style 'modern-straight-flag)
3797                               modern-straight-flag)
3798                              ((eq? flag-style 'old-straight-flag)
3799                               old-straight-flag)
3800                              ((eq? flag-style 'flat-flag)
3801                               flat-flag)
3802                              (else
3803                               (ly:font-get-glyph font
3804                                 (format #f
3805                                         (if (or (member flag-style
3806                                                         '(mensural neomensural))
3807                                                 (and ancient-flags?
3808                                                      (null? flag-style)))
3809                                             "flags.mensural~a2~a"
3810                                             "flags.~a~a")
3811                                         (if (> dir 0) "u" "d")
3812                                         log))))
3813                        (cons (+ (car attach-off)
3814                                 ;; For tighter stems (with ancient-flags) the
3815                                 ;; flag has to be adjusted different.
3816                                 (if (and (not ancient-flags?) (< dir 0))
3817                                     stem-thickness
3818                                     0))
3819                              (+ stemy flag-style-Y-corr))))))
3820     ;; If there is a flag on an upstem and the stem is short, move the dots
3821     ;; to avoid the flag.  16th notes get a special case because their flags
3822     ;; hang lower than any other flags.
3823     ;; Not with ancient flags or straight-flags.
3824     (if (and dots (> dir 0) (> log 2)
3825              (or (eq? flag-style 'default) (null? flag-style))
3826              (not ancient-flags?)
3827              (or (< dir 1.15) (and (= log 4) (< dir 1.3))))
3828         (set! dots (ly:stencil-translate-axis dots 0.5 X)))
3829     (if flaggl
3830         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
3831     (if (ly:stencil? stem-glyph)
3832         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
3833         (set! stem-glyph head-glyph))
3834     (if (ly:stencil? dots)
3835         (set! stem-glyph
3836               (ly:stencil-add
3837                (ly:stencil-translate-axis
3838                 dots
3839                 (+ (cdr (ly:stencil-extent head-glyph X)) dotwid)
3840                 X)
3841                stem-glyph)))
3842     stem-glyph))
3843
3844 (define-public log2
3845   (let ((divisor (log 2)))
3846     (lambda (z) (inexact->exact (/ (log z) divisor)))))
3847
3848 (define (parse-simple-duration duration-string)
3849   "Parse the `duration-string', e.g. ''4..'' or ''breve.'',
3850 and return a (log dots) list."
3851   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)")
3852                             duration-string)))
3853     (if (and match (string=? duration-string (match:substring match 0)))
3854         (let ((len (match:substring match 1))
3855               (dots (match:substring match 2)))
3856           (list (cond ((string=? len "breve") -1)
3857                       ((string=? len "longa") -2)
3858                       ((string=? len "maxima") -3)
3859                       (else (log2 (string->number len))))
3860                 (if dots (string-length dots) 0)))
3861         (ly:error (_ "not a valid duration string: ~a") duration-string))))
3862
3863 (define-markup-command (note layout props duration dir)
3864   (string? number?)
3865   #:category music
3866   #:properties (note-by-number-markup)
3867   "
3868 @cindex notes within text by string
3869
3870 This produces a note with a stem pointing in @var{dir} direction, with
3871 the @var{duration} for the note head type and augmentation dots.  For
3872 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
3873 a shortened down stem.
3874
3875 @lilypond[verbatim,quote]
3876 \\markup {
3877   \\override #'(style . cross) {
3878     \\note #\"4..\" #UP
3879   }
3880   \\hspace #2
3881   \\note #\"breve\" #0
3882 }
3883 @end lilypond"
3884   (let ((parsed (parse-simple-duration duration)))
3885     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
3886
3887 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3888 ;; the rest command.
3889 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3890
3891 (define-markup-command (rest-by-number layout props log dot-count)
3892   (number? number?)
3893   #:category music
3894   #:properties ((font-size 0)
3895                 (style '())
3896                 (multi-measure-rest #f))
3897   "
3898 @cindex rests or multi-measure-rests within text by log and dot-count
3899
3900 A rest or multi-measure-rest symbol.
3901
3902 @lilypond[verbatim,quote]
3903 \\markup {
3904   \\rest-by-number #3 #2
3905   \\hspace #2
3906   \\rest-by-number #0 #1
3907   \\hspace #2
3908   \\override #'(multi-measure-rest . #t)
3909   \\rest-by-number #0 #0
3910 }
3911 @end lilypond"
3912
3913   (define (get-glyph-name-candidates log style)
3914     (let* (;; Choose the style-string to be added.
3915            ;; If no glyph exists, select others for the specified styles
3916            ;; otherwise defaulting.
3917            (style-strg
3918             (cond (
3919                    ;; 'baroque needs to be special-cased, otherwise
3920                    ;; `select-head-glyphĀ“ would catch neomensural-glyphs for
3921                    ;; this style, if (< log 0).
3922                    (eq? style 'baroque)
3923                    (string-append (number->string log) ""))
3924                   ((eq? style 'petrucci)
3925                    (string-append (number->string log) "mensural"))
3926                   ;; In other cases `select-head-glyphĀ“ from output-lib.scm
3927                   ;; works for rest-glyphs, too.
3928                   ((and (symbol? style) (not (eq? style 'default)))
3929                    (select-head-glyph style log))
3930                   (else log)))
3931            ;; Choose ledgered glyphs for whole and half rest.
3932            ;; Except for the specified styles, logs and MultiMeasureRests.
3933            (ledger-style-rests
3934             (if (and (or (list? style)
3935                          (not (member style
3936                                       '(neomensural mensural petrucci))))
3937                      (not multi-measure-rest)
3938                      (or (= log 0) (= log 1)))
3939                 "o"
3940                 "")))
3941       (format #f "rests.~a~a" style-strg ledger-style-rests)))
3942
3943   (define (get-glyph-name font cands)
3944     (if (ly:stencil-empty? (ly:font-get-glyph font cands))
3945         ""
3946         cands))
3947
3948   (let* ((font
3949           (ly:paper-get-font layout
3950                              (cons '((font-encoding . fetaMusic)
3951                                      (font-name . #f))
3952                                    props)))
3953          (rest-glyph-name
3954           (let ((result
3955                  (get-glyph-name font
3956                                  (get-glyph-name-candidates log style))))
3957             (if (string-null? result)
3958                 ;; If no glyph name can be found, select default rests.  Though
3959                 ;; this usually means an unsupported style has been chosen, it
3960                 ;; also prevents unrelated 'style settings from other grobs
3961                 ;; (e.g., TextSpanner and TimeSignature) leaking into markup.
3962                 (get-glyph-name font (get-glyph-name-candidates log 'default))
3963                 result)))
3964          (rest-glyph (ly:font-get-glyph font rest-glyph-name))
3965          (dot (ly:font-get-glyph font "dots.dot"))
3966          (dot-width (interval-length (ly:stencil-extent dot X)))
3967          (dots (and (> dot-count 0)
3968                     (apply ly:stencil-add
3969                            (map (lambda (x)
3970                                   (ly:stencil-translate-axis
3971                                    dot (* 2 x dot-width) X))
3972                                 (iota dot-count))))))
3973
3974     ;; Apart from mensural-, neomensural- and petrucci-style ledgered
3975     ;; glyphs are taken for whole and half rests.
3976     ;; If they are dotted, move the dots in X-direction to avoid collision.
3977     (if (and dots
3978              (< log 2)
3979              (>= log 0)
3980              (not (member style '(neomensural mensural petrucci))))
3981         (set! dots (ly:stencil-translate-axis dots dot-width X)))
3982
3983     ;; Add dots to the rest-glyph.
3984     ;;
3985     ;; Not sure how to vertical align dots.
3986     ;; For now the dots are centered for half, whole or longer rests.
3987     ;; Otherwise placed near the top of the rest.
3988     ;;
3989     ;; Dots for rests with (< log 0) dots are allowed, but not
3990     ;; if multi-measure-rest is set #t.
3991     (if (and (not multi-measure-rest) dots)
3992         (set! rest-glyph
3993               (ly:stencil-add
3994                (ly:stencil-translate
3995                 dots
3996                 (cons
3997                  (+ (cdr (ly:stencil-extent rest-glyph X)) dot-width)
3998                  (if (< log 2)
3999                      (interval-center (ly:stencil-extent rest-glyph Y))
4000                      (- (interval-end (ly:stencil-extent rest-glyph Y))
4001                         (/ (* 2 dot-width) 3)))))
4002                rest-glyph)))
4003     rest-glyph))
4004
4005 (define-markup-command (rest layout props duration)
4006   (string?)
4007   #:category music
4008   #:properties ((style '())
4009                 (multi-measure-rest #f)
4010                 (multi-measure-rest-number #t)
4011                 (word-space 0.6))
4012   "
4013 @cindex rests or multi-measure-rests within text by string
4014
4015 This produces a rest, with the @var{duration} for the rest type and
4016 augmentation dots.
4017 @code{\"breve\"}, @code{\"longa\"} and @code{\"maxima\"} are valid
4018 input-strings.
4019
4020 Printing MultiMeasureRests could be enabled with
4021 @code{\\override #'(multi-measure-rest . #t)}
4022 If MultiMeasureRests are taken, the MultiMeasureRestNumber is printed above.
4023 This is enabled for all styles using default-glyphs.
4024 Could be disabled with @code{\\override #'(multi-measure-rest-number . #f)}
4025
4026 @lilypond[verbatim,quote]
4027 \\markup {
4028   \\rest #\"4..\"
4029   \\hspace #2
4030   \\rest #\"breve\"
4031   \\hspace #2
4032   \\override #'(multi-measure-rest . #t)
4033   {
4034   \\rest #\"7\"
4035   \\hspace #2
4036   \\override #'(multi-measure-rest-number . #f)
4037   \\rest #\"7\"
4038   }
4039 }
4040 @end lilypond"
4041   ;; Get the number of mmr-glyphs.
4042   ;; Store them in a list.
4043   ;; example: (mmr-numbers 25) -> '(3 0 0 1)
4044   (define (mmr-numbers nmbr)
4045     (let* ((8-bar-glyph (floor (/ nmbr 8)))
4046            (8-remainder (remainder nmbr 8))
4047            (4-bar-glyph (floor (/ 8-remainder 4)))
4048            (4-remainder (remainder nmbr 4))
4049            (2-bar-glyph (floor (/ 4-remainder 2)))
4050            (2-remainder (remainder 4-remainder 2))
4051            (1-bar-glyph (floor (/ 2-remainder 1))))
4052       (list 8-bar-glyph 4-bar-glyph 2-bar-glyph 1-bar-glyph)))
4053
4054   ;; Get the correct mmr-glyphs.
4055   ;; Store them in a list.
4056   ;; example:
4057   ;; (get-mmr-glyphs '(1 0 1 0) '("rests.M3" "rests.M2" "rests.M1" "rests.0"))
4058   ;; -> ("rests.M3" "rests.M1")
4059   (define (get-mmr-glyphs lst1 lst2)
4060     (define (helper l1 l2 l3)
4061       (if (null? l1)
4062           (reverse l3)
4063           (helper (cdr l1)
4064                   (cdr l2)
4065                   (append (make-list (car l1) (car l2)) l3))))
4066     (helper lst1 lst2 '()))
4067
4068   ;; If duration is not valid, print a warning and return empty-stencil
4069   (if (or (and (not (integer? (car (parse-simple-duration duration))))
4070                (not multi-measure-rest))
4071           (and (= (string-length (car (string-split duration #\. ))) 1)
4072                (= (string->number (car (string-split duration #\. ))) 0)))
4073       (begin
4074         (ly:warning (_ "not a valid duration string: ~a - ignoring") duration)
4075         empty-stencil)
4076       (let* (
4077              ;; For simple rests:
4078              ;; Get a (log dots) list.
4079              (parsed (parse-simple-duration duration))
4080              ;; Create the rest-stencil
4081              (stil
4082               (rest-by-number-markup layout props (car parsed) (cadr parsed)))
4083              ;; For MultiMeasureRests:
4084              ;; Get the duration-part of duration
4085              (dur-part-string (car (string-split duration #\. )))
4086              ;; Get the duration of MMR:
4087              ;; If not a number (eg. "maxima") calculate it.
4088              (mmr-duration
4089               (or (string->number dur-part-string) (expt 2 (abs (car parsed)))))
4090              ;; Get a list of the correct number of each mmr-glyph.
4091              (count-mmr-glyphs-list (mmr-numbers mmr-duration))
4092              ;; Create a list of mmr-stencils,
4093              ;; translating the glyph for a whole rest.
4094              (mmr-stils-list
4095               (map
4096                (lambda (x)
4097                  (let ((single-mmr-stil
4098                         (rest-by-number-markup layout props (* -1 x) 0)))
4099                    (if (= x 0)
4100                        (ly:stencil-translate-axis
4101                         single-mmr-stil
4102                         ;; Ugh, hard-coded, why 1?
4103                         1
4104                         Y)
4105                        single-mmr-stil)))
4106                (get-mmr-glyphs count-mmr-glyphs-list (reverse (iota 4)))))
4107              ;; Adjust the space between the mmr-glyphs,
4108              ;; if not default-glyphs are used.
4109              (word-space (if (member style
4110                                      '(neomensural mensural petrucci))
4111                              (/ (* word-space 2) 3)
4112                              word-space))
4113              ;; Create the final mmr-stencil
4114              ;; via `stack-stencil-lineĀ“ from /scm/markup.scm
4115              (mmr-stil (stack-stencil-line word-space mmr-stils-list)))
4116
4117         ;; Print the number above a multi-measure-rest
4118         ;; Depends on duration, style and multi-measure-rest-number set #t
4119         (if (and multi-measure-rest
4120                  multi-measure-rest-number
4121                  (> mmr-duration 1)
4122                  (not (member style '(neomensural mensural petrucci))))
4123             (let* ((mmr-stil-x-center
4124                     (interval-center (ly:stencil-extent mmr-stil X)))
4125                    (duration-markup
4126                     (markup
4127                      #:fontsize -2
4128                      #:override '(font-encoding . fetaText)
4129                      (number->string mmr-duration)))
4130                    (mmr-number-stil
4131                     (interpret-markup layout props duration-markup))
4132                    (mmr-number-stil-x-center
4133                     (interval-center (ly:stencil-extent mmr-number-stil X))))
4134
4135               (set! mmr-stil (ly:stencil-combine-at-edge
4136                               mmr-stil
4137                               Y UP
4138                               (ly:stencil-translate-axis
4139                                mmr-number-stil
4140                                (- mmr-stil-x-center mmr-number-stil-x-center)
4141                                X)
4142                               ;; Ugh, hardcoded
4143                               0.8))))
4144         (if multi-measure-rest
4145             mmr-stil
4146             stil))))
4147
4148 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4149 ;; fermata markup
4150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4151
4152 (define-markup-command (fermata layout props) ()
4153   #:category music
4154   #:properties ((direction UP))
4155   "Create a fermata glyph.  When @var{direction} is @code{DOWN}, use
4156 an inverted glyph.  Note that within music, one would usually use the
4157 @code{\\fermata} articulation instead of a markup.
4158
4159 @lilypond[verbatim,quote]
4160  { c''1^\\markup \\fermata d''1_\\markup \\fermata }
4161
4162 \\markup { \\fermata \\override #`(direction . ,DOWN) \\fermata }
4163 @end lilypond
4164 "
4165   (interpret-markup layout props
4166                     (if (eqv? direction DOWN)
4167                         (markup #:musicglyph "scripts.dfermata")
4168                         (markup #:musicglyph "scripts.ufermata"))))
4169
4170 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4171 ;; translating.
4172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4173
4174 (define-markup-command (lower layout props amount arg)
4175   (number? markup?)
4176   #:category align
4177   "
4178 @cindex lowering text
4179
4180 Lower @var{arg} by the distance @var{amount}.
4181 A negative @var{amount} indicates raising; see also @code{\\raise}.
4182
4183 @lilypond[verbatim,quote]
4184 \\markup {
4185   one
4186   \\lower #3
4187   two
4188   three
4189 }
4190 @end lilypond"
4191   (ly:stencil-translate-axis (interpret-markup layout props arg)
4192                              (- amount) Y))
4193
4194 (define-markup-command (translate-scaled layout props offset arg)
4195   (number-pair? markup?)
4196   #:category align
4197   #:properties ((font-size 0))
4198   "
4199 @cindex translating text
4200 @cindex scaling text
4201
4202 Translate @var{arg} by @var{offset}, scaling the offset by the
4203 @code{font-size}.
4204
4205 @lilypond[verbatim,quote]
4206 \\markup {
4207   \\fontsize #5 {
4208     * \\translate #'(2 . 3) translate
4209     \\hspace #2
4210     * \\translate-scaled #'(2 . 3) translate-scaled
4211   }
4212 }
4213 @end lilypond"
4214   (let* ((factor (magstep font-size))
4215          (scaled (cons (* factor (car offset))
4216                        (* factor (cdr offset)))))
4217     (ly:stencil-translate (interpret-markup layout props arg)
4218                           scaled)))
4219
4220 (define-markup-command (raise layout props amount arg)
4221   (number? markup?)
4222   #:category align
4223   "
4224 @cindex raising text
4225
4226 Raise @var{arg} by the distance @var{amount}.
4227 A negative @var{amount} indicates lowering, see also @code{\\lower}.
4228
4229 The argument to @code{\\raise} is the vertical displacement amount,
4230 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
4231 raise objects in relation to their surrounding markups.
4232
4233 If the text object itself is positioned above or below the staff, then
4234 @code{\\raise} cannot be used to move it, since the mechanism that
4235 positions it next to the staff cancels any shift made with
4236 @code{\\raise}.  For vertical positioning, use the @code{padding}
4237 and/or @code{extra-offset} properties.
4238
4239 @lilypond[verbatim,quote]
4240 \\markup {
4241   C
4242   \\small
4243   \\bold
4244   \\raise #1.0
4245   9/7+
4246 }
4247 @end lilypond"
4248   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
4249
4250 (define-markup-command (fraction layout props arg1 arg2)
4251   (markup? markup?)
4252   #:category other
4253   #:properties ((font-size 0))
4254   "
4255 @cindex creating text fractions
4256
4257 Make a fraction of two markups.
4258 @lilypond[verbatim,quote]
4259 \\markup {
4260   Ļ€ ā‰ˆ
4261   \\fraction 355 113
4262 }
4263 @end lilypond"
4264   (let* ((m1 (interpret-markup layout props arg1))
4265          (m2 (interpret-markup layout props arg2))
4266          (factor (magstep font-size))
4267          (boxdimen (cons (* factor -0.05) (* factor 0.05)))
4268          (padding (* factor 0.2))
4269          (baseline (* factor 0.6))
4270          (offset (* factor 0.75)))
4271     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
4272     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
4273     (let* ((x1 (ly:stencil-extent m1 X))
4274            (x2 (ly:stencil-extent m2 X))
4275            (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0))
4276            ;; should stack mols separately, to maintain LINE on baseline
4277            (stack (stack-lines DOWN padding baseline (list m1 line m2))))
4278       (set! stack
4279             (ly:stencil-aligned-to stack Y CENTER))
4280       (set! stack
4281             (ly:stencil-aligned-to stack X LEFT))
4282       ;; should have EX dimension
4283       ;; empirical anyway
4284       (ly:stencil-translate-axis stack offset Y))))
4285
4286 (define-markup-command (normal-size-super layout props arg)
4287   (markup?)
4288   #:category font
4289   #:properties ((font-size 0))
4290   "
4291 @cindex setting superscript in standard font size
4292
4293 Set @var{arg} in superscript with a normal font size.
4294
4295 @lilypond[verbatim,quote]
4296 \\markup {
4297   default
4298   \\normal-size-super {
4299     superscript in standard size
4300   }
4301 }
4302 @end lilypond"
4303   (ly:stencil-translate-axis
4304    (interpret-markup layout props arg)
4305    (* 1.0 (magstep font-size)) Y))
4306
4307 (define-markup-command (super layout props arg)
4308   (markup?)
4309   #:category font
4310   #:properties ((font-size 0))
4311   "
4312 @cindex superscript text
4313
4314 Set @var{arg} in superscript.
4315
4316 @lilypond[verbatim,quote]
4317 \\markup {
4318   E =
4319   \\concat {
4320     mc
4321     \\super
4322     2
4323   }
4324 }
4325 @end lilypond"
4326   (ly:stencil-translate-axis
4327    (interpret-markup
4328     layout
4329     (cons `((font-size . ,(- font-size 3))) props)
4330     arg)
4331    (* 1.0 (magstep font-size)) ; original font-size
4332    Y))
4333
4334 (define-markup-command (translate layout props offset arg)
4335   (number-pair? markup?)
4336   #:category align
4337   "
4338 @cindex translating text
4339
4340 Translate @var{arg} relative to its surroundings.  @var{offset}
4341 is a pair of numbers representing the displacement in the X and Y axis.
4342
4343 @lilypond[verbatim,quote]
4344 \\markup {
4345   *
4346   \\translate #'(2 . 3)
4347   \\line { translated two spaces right, three up }
4348 }
4349 @end lilypond"
4350   (ly:stencil-translate (interpret-markup layout props arg)
4351                         offset))
4352
4353 (define-markup-command (sub layout props arg)
4354   (markup?)
4355   #:category font
4356   #:properties ((font-size 0))
4357   "
4358 @cindex subscript text
4359
4360 Set @var{arg} in subscript.
4361
4362 @lilypond[verbatim,quote]
4363 \\markup {
4364   \\concat {
4365     H
4366     \\sub {
4367       2
4368     }
4369     O
4370   }
4371 }
4372 @end lilypond"
4373   (ly:stencil-translate-axis
4374    (interpret-markup
4375     layout
4376     (cons `((font-size . ,(- font-size 3))) props)
4377     arg)
4378    (* -0.75 (magstep font-size)) ; original font-size
4379    Y))
4380
4381 (define-markup-command (normal-size-sub layout props arg)
4382   (markup?)
4383   #:category font
4384   #:properties ((font-size 0))
4385   "
4386 @cindex setting subscript in standard font size
4387
4388 Set @var{arg} in subscript with a normal font size.
4389
4390 @lilypond[verbatim,quote]
4391 \\markup {
4392   default
4393   \\normal-size-sub {
4394     subscript in standard size
4395   }
4396 }
4397 @end lilypond"
4398   (ly:stencil-translate-axis
4399    (interpret-markup layout props arg)
4400    (* -0.75 (magstep font-size))
4401    Y))
4402
4403 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4404 ;; brackets.
4405 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4406
4407 (define-markup-command (hbracket layout props arg)
4408   (markup?)
4409   #:category graphic
4410   "
4411 @cindex placing horizontal brackets around text
4412
4413 Draw horizontal brackets around @var{arg}.
4414
4415 @lilypond[verbatim,quote]
4416 \\markup {
4417   \\hbracket {
4418     \\line {
4419       one two three
4420     }
4421   }
4422 }
4423 @end lilypond"
4424   (let ((th 0.1) ;; todo: take from GROB.
4425         (m (interpret-markup layout props arg)))
4426     (bracketify-stencil m X th (* 2.5 th) th)))
4427
4428 (define-markup-command (bracket layout props arg)
4429   (markup?)
4430   #:category graphic
4431   "
4432 @cindex placing vertical brackets around text
4433
4434 Draw vertical brackets around @var{arg}.
4435
4436 @lilypond[verbatim,quote]
4437 \\markup {
4438   \\bracket {
4439     \\note #\"2.\" #UP
4440   }
4441 }
4442 @end lilypond"
4443   (let ((th 0.1) ;; todo: take from GROB.
4444         (m (interpret-markup layout props arg)))
4445     (bracketify-stencil m Y th (* 2.5 th) th)))
4446
4447 (define-markup-command (parenthesize layout props arg)
4448   (markup?)
4449   #:category graphic
4450   #:properties ((angularity 0)
4451                 (padding)
4452                 (size 1)
4453                 (thickness 1)
4454                 (line-thickness 0.1)
4455                 (width 0.25))
4456   "
4457 @cindex placing parentheses around text
4458
4459 Draw parentheses around @var{arg}.  This is useful for parenthesizing
4460 a column containing several lines of text.
4461
4462 @lilypond[verbatim,quote]
4463 \\markup {
4464   \\line {
4465     \\parenthesize {
4466       \\column {
4467         foo
4468         bar
4469       }
4470     }
4471     \\override #'(angularity . 2) {
4472       \\parenthesize {
4473         \\column {
4474           bah
4475           baz
4476         }
4477       }
4478     }
4479   }
4480 }
4481 @end lilypond"
4482   (let* ((m (interpret-markup layout props arg))
4483          (scaled-width (* size width))
4484          (scaled-thickness
4485           (* line-thickness thickness))
4486          (half-thickness
4487           (min (* size 0.5 scaled-thickness)
4488                (* (/ 4 3.0) scaled-width)))
4489          (padding (or padding half-thickness)))
4490     (parenthesize-stencil
4491      m half-thickness scaled-width angularity padding)))
4492
4493
4494 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4495 ;; Delayed markup evaluation
4496 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4497
4498 (define-markup-command (page-ref layout props label gauge default)
4499   (symbol? markup? markup?)
4500   #:category other
4501   "
4502 @cindex referencing page numbers in text
4503
4504 Reference to a page number.  @var{label} is the label set on the referenced
4505 page (using the @code{\\label} command), @var{gauge} a markup used to estimate
4506 the maximum width of the page number, and @var{default} the value to display
4507 when @var{label} is not found.
4508
4509 (If the current book or bookpart is set to use roman numerals for page numbers,
4510 the reference will be formatted accordingly -- in which case the @var{gauge}'s
4511 width may require additional tweaking.)"
4512   (let* ((gauge-stencil (interpret-markup layout props gauge))
4513          (x-ext (ly:stencil-extent gauge-stencil X))
4514          (y-ext (ly:stencil-extent gauge-stencil Y)))
4515    (ly:stencil-add
4516     (make-transparent-box-stencil x-ext y-ext))
4517     (ly:make-stencil
4518      `(delay-stencil-evaluation
4519        ,(delay (ly:stencil-expr
4520                 (let* ((table (ly:output-def-lookup layout 'label-page-table))
4521                        (page-number (if (list? table)
4522                                         (assoc-get label table)
4523                                         #f))
4524                        (number-type (ly:output-def-lookup layout 'page-number-type))
4525                        (page-markup (if page-number
4526                                         (number-format number-type page-number)
4527                                         default))
4528                        (page-stencil (interpret-markup layout props page-markup))
4529                        (gap (- (interval-length x-ext)
4530                                (interval-length (ly:stencil-extent page-stencil X)))))
4531                   (interpret-markup layout props
4532                                     (markup #:hspace gap page-markup))))))
4533      x-ext
4534      y-ext)))
4535
4536 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4537 ;; scaling
4538 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4539
4540 (define-markup-command (scale layout props factor-pair arg)
4541   (number-pair? markup?)
4542   #:category graphic
4543   "
4544 @cindex scaling markup
4545 @cindex mirroring markup
4546
4547 Scale @var{arg}.  @var{factor-pair} is a pair of numbers
4548 representing the scaling-factor in the X and Y axes.
4549 Negative values may be used to produce mirror images.
4550
4551 @lilypond[verbatim,quote]
4552 \\markup {
4553   \\line {
4554     \\scale #'(2 . 1)
4555     stretched
4556     \\scale #'(1 . -1)
4557     mirrored
4558   }
4559 }
4560 @end lilypond"
4561   (let ((stil (interpret-markup layout props arg))
4562         (sx (car factor-pair))
4563         (sy (cdr factor-pair)))
4564     (ly:stencil-scale stil sx sy)))
4565
4566 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4567 ;; Repeating
4568 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4569
4570 (define-markup-command (pattern layout props count axis space pattern)
4571   (integer? integer? number? markup?)
4572   #:category other
4573   "
4574 Prints @var{count} times a @var{pattern} markup.
4575 Patterns are spaced apart by @var{space}.
4576 Patterns are distributed on @var{axis}.
4577
4578 @lilypond[verbatim, quote]
4579 \\markup \\column {
4580   \"Horizontally repeated :\"
4581   \\pattern #7 #X #2 \\flat
4582   \\null
4583   \"Vertically repeated :\"
4584   \\pattern #3 #Y #0.5 \\flat
4585 }
4586 @end lilypond"
4587   (let ((pattern-width (interval-length
4588                         (ly:stencil-extent (interpret-markup layout props pattern) X)))
4589         (new-props (prepend-alist-chain 'word-space 0 (prepend-alist-chain 'baseline-skip 0 props))))
4590     (let loop ((i (1- count)) (patterns (markup)))
4591       (if (zero? i)
4592           (interpret-markup
4593            layout
4594            new-props
4595            (if (= axis X)
4596                (markup patterns pattern)
4597                (markup #:column (patterns pattern))))
4598           (loop (1- i)
4599                 (if (= axis X)
4600                     (markup patterns pattern #:hspace space)
4601                     (markup #:column (patterns pattern #:vspace space))))))))
4602
4603 (define-markup-command (fill-with-pattern layout props space dir pattern left right)
4604   (number? ly:dir? markup? markup? markup?)
4605   #:category align
4606   #:properties ((word-space)
4607                 (line-width))
4608   "
4609 Put @var{left} and @var{right} in a horizontal line of width @code{line-width}
4610 with a line of markups @var{pattern} in between.
4611 Patterns are spaced apart by @var{space}.
4612 Patterns are aligned to the @var{dir} markup.
4613
4614 @lilypond[verbatim, quote]
4615 \\markup \\column {
4616   \"right-aligned :\"
4617   \\fill-with-pattern #1 #RIGHT . first right
4618   \\fill-with-pattern #1 #RIGHT . second right
4619   \\null
4620   \"center-aligned :\"
4621   \\fill-with-pattern #1.5 #CENTER - left right
4622   \\null
4623   \"left-aligned :\"
4624   \\override #'(line-width . 50)
4625   \\fill-with-pattern #2 #LEFT : left first
4626   \\override #'(line-width . 50)
4627   \\fill-with-pattern #2 #LEFT : left second
4628 }
4629 @end lilypond"
4630   (let* ((pattern-x-extent (ly:stencil-extent (interpret-markup layout props pattern) X))
4631          (pattern-width (interval-length pattern-x-extent))
4632          (left-width (interval-length (ly:stencil-extent (interpret-markup layout props left) X)))
4633          (right-width (interval-length (ly:stencil-extent (interpret-markup layout props right) X)))
4634          (middle-width (max 0 (- line-width (+ (+ left-width right-width) (* word-space 2)))))
4635          (period (+ space pattern-width))
4636          (count (truncate (/ (- middle-width pattern-width) period)))
4637          (x-offset (+ (* (- (- middle-width (* count period)) pattern-width) (/ (1+ dir) 2)) (abs (car pattern-x-extent)))))
4638     (interpret-markup layout props
4639                       (markup left
4640                               #:with-dimensions (cons 0 middle-width) '(0 . 0)
4641                               #:translate (cons x-offset 0)
4642                               #:pattern (1+ count) X space pattern
4643                               right))))
4644
4645 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4646 ;; Replacements
4647 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4648
4649 (define-markup-command (replace layout props replacements arg)
4650   (list? markup?)
4651   #:category font
4652   "
4653 Used to automatically replace a string by another in the markup @var{arg}.
4654 Each pair of the alist @var{replacements} specifies what should be replaced.
4655 The @code{key} is the string to be replaced by the @code{value} string.
4656
4657 @lilypond[verbatim, quote]
4658 \\markup \\replace #'((\"thx\" . \"Thanks!\")) thx
4659 @end lilypond"
4660   (interpret-markup
4661    layout
4662    (internal-add-text-replacements
4663     props
4664     replacements)
4665    (markup arg)))
4666
4667 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4668 ;; Markup list commands
4669 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4670
4671 (define-public (space-lines baseline stils)
4672   (let space-stil ((stils stils)
4673                    (result (list)))
4674     (if (null? stils)
4675         (reverse! result)
4676         (let* ((stil (car stils))
4677                (dy-top (max (- (/ baseline 1.5)
4678                                (interval-bound (ly:stencil-extent stil Y) UP))
4679                             0.0))
4680                (dy-bottom (max (+ (/ baseline 3.0)
4681                                   (interval-bound (ly:stencil-extent stil Y) DOWN))
4682                                0.0))
4683                (new-stil (ly:make-stencil
4684                           (ly:stencil-expr stil)
4685                           (ly:stencil-extent stil X)
4686                           (cons (- (interval-bound (ly:stencil-extent stil Y) DOWN)
4687                                    dy-bottom)
4688                                 (+ (interval-bound (ly:stencil-extent stil Y) UP)
4689                                    dy-top)))))
4690           (space-stil (cdr stils) (cons new-stil result))))))
4691
4692 (define-markup-list-command (justified-lines layout props args)
4693   (markup-list?)
4694   #:properties ((baseline-skip)
4695                 wordwrap-internal-markup-list)
4696   "
4697 @cindex justifying lines of text
4698
4699 Like @code{\\justify}, but return a list of lines instead of a single markup.
4700 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width;
4701 @var{X}@tie{}is the number of staff spaces."
4702   (space-lines baseline-skip
4703                (interpret-markup-list layout props
4704                                       (make-wordwrap-internal-markup-list #t args))))
4705
4706 (define-markup-list-command (wordwrap-lines layout props args)
4707   (markup-list?)
4708   #:properties ((baseline-skip)
4709                 wordwrap-internal-markup-list)
4710   "Like @code{\\wordwrap}, but return a list of lines instead of a single markup.
4711 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width,
4712 where @var{X} is the number of staff spaces."
4713   (space-lines baseline-skip
4714                (interpret-markup-list layout props
4715                                       (make-wordwrap-internal-markup-list #f args))))
4716
4717 (define-markup-list-command (column-lines layout props args)
4718   (markup-list?)
4719   #:properties ((baseline-skip))
4720   "Like @code{\\column}, but return a list of lines instead of a single markup.
4721 @code{baseline-skip} determines the space between each markup in @var{args}."
4722   (space-lines baseline-skip
4723                (interpret-markup-list layout props args)))
4724
4725 (define-markup-list-command (override-lines layout props new-prop args)
4726   (pair? markup-list?)
4727   "Like @code{\\override}, for markup lists."
4728   (interpret-markup-list layout (cons (list new-prop) props) args))
4729
4730 (define-markup-list-command (table layout props column-align lst)
4731   (number-list? markup-list?)
4732   #:properties ((padding 0)
4733                 (baseline-skip))
4734   "@cindex creating a table.
4735
4736 Returns a table.
4737
4738 @var{column-align} specifies how each column is aligned, possible values are
4739 -1, 0, 1.  The number of elements in @var{column-align} determines how many
4740 columns will be printed.
4741 The entries to print are given by @var{lst}, a markup-list.  If needed, the last
4742 row is filled up with @code{point-stencil}s.
4743 Overriding @code{padding} may be used to increase columns horizontal distance.
4744 Overriding @code{baseline-skip} to increase rows vertical distance.
4745 @lilypond[verbatim,quote]
4746 \\markuplist {
4747   \\override #'(padding . 2)
4748   \\table
4749     #'(0 1 0 -1)
4750     {
4751       \\underline { center-aligned right-aligned center-aligned left-aligned }
4752       one \\number 1 thousandth \\number 0.001
4753       eleven \\number 11 hundredth \\number 0.01
4754       twenty \\number 20 tenth \\number 0.1
4755       thousand \\number 1000 one \\number 1.0
4756     }
4757 }
4758 @end lilypond
4759 "
4760
4761   (define (split-lst initial-lst lngth result-lst)
4762     ;; split a list into a list of sublists of length lngth
4763     ;; eg. (split-lst '(1 2 3 4 5 6) 2 '())
4764     ;; -> ((1 2) (3 4) (5 6))
4765     (cond ((not (integer? (/ (length initial-lst) lngth)))
4766            (ly:warning
4767             "Can't split list of length ~a into ~a parts, returning empty list"
4768             (length initial-lst) lngth)
4769            '())
4770           ((null? initial-lst)
4771             (reverse result-lst))
4772           (else
4773             (split-lst
4774               (drop initial-lst lngth)
4775               lngth
4776               (cons (take initial-lst lngth) result-lst)))))
4777
4778   (define (dists-list init padding lst)
4779     ;; Returns a list, where each element of `lst' is
4780     ;; added to the sum of the previous elements of `lst' plus padding.
4781     ;; `init' will be the first element of the resulting list. The addition
4782     ;; starts with the values of `init', `padding' and `(car lst)'.
4783     ;; eg. (dists-list 0.01 0.1 '(1 2 3 4)))
4784     ;; -> (0.01 1.11 3.21 6.31 10.41)
4785     (if (or (not (number? init))
4786             (not (number? padding))
4787             (not (number-list? lst)))
4788         (begin
4789           (ly:warning
4790             "not fitting argument for `dists-list', return empty lst ")
4791           '())
4792         (reverse
4793           (fold (lambda (elem rl) (cons (+ elem padding (car rl)) rl))
4794                 (list init)
4795                 lst))))
4796
4797   (let* (;; get the number of columns
4798          (columns (length column-align))
4799          (init-stils (interpret-markup-list layout props lst))
4800          ;; If the given markup-list is the result of a markup-list call, their
4801          ;; length may not be easily predictable, thus we add point-stencils
4802          ;; to fill last row of the table.
4803          (rem (remainder (length init-stils) columns))
4804          (filled-stils
4805            (if (zero? rem)
4806                init-stils
4807                (append init-stils (make-list (- columns rem) point-stencil))))
4808          ;; get the stencils in sublists of length `columns'
4809          (stils
4810            (split-lst filled-stils columns '()))
4811          ;; procedure to return stencil-length
4812          ;; If it is nan, return 0
4813          (lengths-proc
4814            (lambda (m)
4815              (let ((lngth (interval-length (ly:stencil-extent m X))))
4816                (if (nan? lngth) 0 lngth))))
4817          ;; get the max width of each column in a list
4818          (columns-max-x-lengths
4819            (map
4820              (lambda (x)
4821                (apply max 0
4822                       (map
4823                         lengths-proc
4824                         (map (lambda (l) (list-ref l x)) stils))))
4825              (iota columns)))
4826          ;; create a list of (basic) distances, which each column should
4827          ;; moved, using `dists-list'. Some padding may be added.
4828          (dist-sequence
4829            (dists-list 0 padding columns-max-x-lengths))
4830          ;; Get all stencils of a row, moved accurately to build columns.
4831          ;; If the items of a column are aligned other than left, we need to
4832          ;; move them to avoid collisions:
4833          ;; center aligned: move all items half the width of the widest item
4834          ;; right aligned: move all items the full width of the widest item.
4835          ;; Added to the default-offset calculated in `dist-sequence'.
4836          ;; `stencils-for-row-proc' needs four arguments:
4837          ;;    stil    - a stencil
4838          ;;    dist    - a numerical value as basic offset in X direction
4839          ;;    column  - a numerical value for the column we're in
4840          ;;    x-align - a numerical value how current column should be
4841          ;;              aligned, where (-1, 0, 1) means (LEFT, CENTER, RIGHT)
4842          (stencils-for-row-proc
4843            (lambda (stil dist column x-align)
4844              (ly:stencil-translate-axis
4845                (ly:stencil-aligned-to stil X x-align)
4846                (cond ((member x-align '(0 1))
4847                       (let* (;; get the stuff for relevant column
4848                              (stuff-for-column
4849                                (map
4850                                  (lambda (s) (list-ref s column))
4851                                  stils))
4852                              ;; get length of every column-item
4853                              (lengths-for-column
4854                                (map lengths-proc stuff-for-column))
4855                              (widest
4856                                (apply max 0 lengths-for-column)))
4857                         (+ dist (/ widest (if (= x-align 0) 2 1)))))
4858                      (else dist))
4859                X)))
4860          ;; get a list of rows using `ly:stencil-add' on a list of stencils
4861          (rows
4862            (map
4863              (lambda (stil-list)
4864                (apply ly:stencil-add
4865                  (map
4866                    ;; the procedure creating the stencils:
4867                    stencils-for-row-proc
4868                    ;; the procedure's args:
4869                    stil-list
4870                    dist-sequence
4871                    (iota columns)
4872                    column-align)))
4873              stils)))
4874    (space-lines baseline-skip rows)))
4875
4876 (define-markup-list-command (map-markup-commands layout props compose args)
4877   (procedure? markup-list?)
4878   "This applies the function @var{compose} to every markup in
4879 @var{args} (including elements of markup list command calls) in order
4880 to produce a new markup list.  Since the return value from a markup
4881 list command call is not a markup list but rather a list of stencils,
4882 this requires passing those stencils off as the results of individual
4883 markup calls.  That way, the results should work out as long as no
4884 markups rely on side effects."
4885   (let ((key (make-symbol "key")))
4886     (catch
4887      key
4888      (lambda ()
4889        ;; if `compose' does not actually interpret its markup
4890        ;; argument, we still need to return a list of stencils,
4891        ;; created from the single returned stencil
4892        (list
4893         (interpret-markup layout props
4894                           (compose
4895                            (make-on-the-fly-markup
4896                             (lambda (layout props m)
4897                               ;; here all effects of `compose' on the
4898                               ;; properties should be visible, so we
4899                               ;; call interpret-markup-list at this
4900                               ;; point of time and harvest its
4901                               ;; stencils
4902                               (throw key
4903                                      (interpret-markup-list
4904                                       layout props args)))
4905                             (make-null-markup))))))
4906      (lambda (key stencils)
4907        (map
4908         (lambda (sten)
4909           (interpret-markup layout props
4910                             (compose (make-stencil-markup sten))))
4911         stencils)))))