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