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