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