]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
Issue 4663: Deobfuscate on-the-fly-markup definition
[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 (and (pair? result-list)
1409                                      (car result-list))))
1410                     (cond ((not (pair? arg)))
1411                           ((eq? (car arg) simple-markup)
1412                            (set! arg (cadr arg)))
1413                           ((eq? (car arg) char-markup)
1414                            (set! arg (ly:wide-char->utf-8 (cadr arg)))))
1415                     (if (and (string? result) (string? arg))
1416                         (cons (string-append arg result) (cdr result-list))
1417                         (cons arg result-list))))
1418                 '()
1419                 arg-list))
1420
1421   (interpret-markup layout
1422                     (prepend-alist-chain 'word-space 0 props)
1423                     (make-line-markup
1424                      (make-override-lines-markup-list
1425                       (cons 'word-space
1426                             (chain-assoc-get 'word-space props))
1427                       (if (markup-command-list? args)
1428                           args
1429                           (concat-string-args args))))))
1430
1431 (define (wordwrap-stencils stencils
1432                            justify base-space line-width text-dir)
1433   "Perform simple wordwrap, return stencil of each line."
1434   (define space (if justify
1435                     ;; justify only stretches lines.
1436                     (* 0.7 base-space)
1437                     base-space))
1438   (define (stencil-len s)
1439     (interval-end (ly:stencil-extent s X)))
1440   (define (maybe-shift line)
1441     (if (= text-dir LEFT)
1442         (ly:stencil-translate-axis
1443          line
1444          (- line-width (stencil-len line))
1445          X)
1446         line))
1447   (if (null? stencils)
1448       '()
1449       (let loop ((lines '())
1450                  (todo stencils))
1451         (let word-loop
1452             ((line (first todo))
1453              (todo (cdr todo))
1454              (word-list (list (first todo))))
1455           (cond
1456            ((pair? todo)
1457             (let ((new (if (= text-dir LEFT)
1458                            (ly:stencil-stack (car todo) X RIGHT line space)
1459                            (ly:stencil-stack line X RIGHT (car todo) space))))
1460               (cond
1461                ((<= (stencil-len new) line-width)
1462                 (word-loop new (cdr todo)
1463                            (cons (car todo) word-list)))
1464                (justify
1465                 (let* ((word-list
1466                         ;; This depends on stencil stacking being
1467                         ;; associative so that stacking
1468                         ;; left-to-right and right-to-left leads to
1469                         ;; the same result
1470                         (if (= text-dir LEFT)
1471                             word-list
1472                             (reverse! word-list)))
1473                        (len (stencil-len line))
1474                        (stretch (- line-width len))
1475                        (spaces
1476                         (- (stencil-len
1477                             (stack-stencils X RIGHT (1+ space) word-list))
1478                            len)))
1479                   (if (zero? spaces)
1480                       ;; Uh oh, nothing to fill.
1481                       (loop (cons (maybe-shift line) lines) todo)
1482                       (loop (cons
1483                              (stack-stencils X RIGHT
1484                                              (+ space (/ stretch spaces))
1485                                              word-list)
1486                              lines)
1487                             todo))))
1488                (else ;; not justify
1489                 (loop (cons (maybe-shift line) lines) todo)))))
1490            ;; todo is null
1491            (justify
1492             ;; Now we have the last line assembled with space
1493             ;; which is compressed.  We want to use the
1494             ;; uncompressed version instead if it fits, and the
1495             ;; justified version if it doesn't.
1496             (let* ((word-list
1497                     ;; This depends on stencil stacking being
1498                     ;; associative so that stacking
1499                     ;; left-to-right and right-to-left leads to
1500                     ;; the same result
1501                     (if (= text-dir LEFT)
1502                         word-list
1503                         (reverse! word-list)))
1504                    (big-line (stack-stencils X RIGHT base-space word-list))
1505                    (big-len (stencil-len big-line))
1506                    (len (stencil-len line)))
1507               (reverse! lines
1508                         (list
1509                          (if (> big-len line-width)
1510                              (stack-stencils X RIGHT
1511                                              (/
1512                                               (+
1513                                                (* (- big-len line-width)
1514                                                   space)
1515                                                (* (- line-width len)
1516                                                   base-space))
1517                                               (- big-len len))
1518                                              word-list)
1519                              (maybe-shift big-line))))))
1520            (else ;; not justify
1521             (reverse! lines (list (maybe-shift line)))))))))
1522
1523
1524 (define-markup-list-command (wordwrap-internal layout props justify args)
1525   (boolean? markup-list?)
1526   #:properties ((line-width #f)
1527                 (word-space)
1528                 (text-direction RIGHT))
1529   "Internal markup list command used to define @code{\\justify} and @code{\\wordwrap}."
1530   (wordwrap-stencils (interpret-markup-list layout props args)
1531                      justify
1532                      word-space
1533                      (or line-width
1534                          (ly:output-def-lookup layout 'line-width))
1535                      text-direction))
1536
1537 (define-markup-command (justify layout props args)
1538   (markup-list?)
1539   #:category align
1540   #:properties ((baseline-skip)
1541                 wordwrap-internal-markup-list)
1542   "
1543 @cindex justifying text
1544
1545 Like @code{\\wordwrap}, but with lines stretched to justify the margins.
1546 Use @code{\\override #'(line-width . @var{X})} to set the line width;
1547 @var{X}@tie{}is the number of staff spaces.
1548
1549 @lilypond[verbatim,quote]
1550 \\markup {
1551   \\justify {
1552     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1553     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1554     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1555     laboris nisi ut aliquip ex ea commodo consequat.
1556   }
1557 }
1558 @end lilypond"
1559   (stack-lines DOWN 0.0 baseline-skip
1560                (wordwrap-internal-markup-list layout props #t args)))
1561
1562 (define-markup-command (wordwrap layout props args)
1563   (markup-list?)
1564   #:category align
1565   #:properties ((baseline-skip)
1566                 wordwrap-internal-markup-list)
1567   "Simple wordwrap.  Use @code{\\override #'(line-width . @var{X})} to set
1568 the line width, where @var{X} is the number of staff spaces.
1569
1570 @lilypond[verbatim,quote]
1571 \\markup {
1572   \\wordwrap {
1573     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1574     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1575     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1576     laboris nisi ut aliquip ex ea commodo consequat.
1577   }
1578 }
1579 @end lilypond"
1580   (stack-lines DOWN 0.0 baseline-skip
1581                (wordwrap-internal-markup-list layout props #f args)))
1582
1583 (define-markup-list-command (wordwrap-string-internal layout props justify arg)
1584   (boolean? string?)
1585   #:properties ((line-width)
1586                 (word-space)
1587                 (text-direction RIGHT))
1588   "Internal markup list command used to define @code{\\justify-string} and
1589 @code{\\wordwrap-string}."
1590   (let* ((para-strings (regexp-split
1591                         (string-regexp-substitute
1592                          "\r" "\n"
1593                          (string-regexp-substitute "\r\n" "\n" arg))
1594                         "\n[ \t\n]*\n[ \t\n]*"))
1595          (list-para-words (map (lambda (str)
1596                                  (regexp-split str "[ \t\n]+"))
1597                                para-strings))
1598          (para-lines (map (lambda (words)
1599                             (let* ((stencils
1600                                     (map (lambda (x)
1601                                            (interpret-markup layout props x))
1602                                          words)))
1603                               (wordwrap-stencils stencils
1604                                                  justify word-space
1605                                                  line-width text-direction)))
1606                           list-para-words)))
1607     (concatenate para-lines)))
1608
1609 (define-markup-command (wordwrap-string layout props arg)
1610   (string?)
1611   #:category align
1612   #:properties ((baseline-skip)
1613                 wordwrap-string-internal-markup-list)
1614   "Wordwrap a string.  Paragraphs may be separated with double newlines.
1615
1616 @lilypond[verbatim,quote]
1617 \\markup {
1618   \\override #'(line-width . 40)
1619   \\wordwrap-string #\"Lorem ipsum dolor sit amet, consectetur
1620       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1621       et dolore magna aliqua.
1622
1623
1624       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1625       laboris nisi ut aliquip ex ea commodo consequat.
1626
1627
1628       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1629       qui officia deserunt mollit anim id est laborum\"
1630 }
1631 @end lilypond"
1632   (stack-lines DOWN 0.0 baseline-skip
1633                (wordwrap-string-internal-markup-list layout props #f arg)))
1634
1635 (define-markup-command (justify-string layout props arg)
1636   (string?)
1637   #:category align
1638   #:properties ((baseline-skip)
1639                 wordwrap-string-internal-markup-list)
1640   "Justify a string.  Paragraphs may be separated with double newlines
1641
1642 @lilypond[verbatim,quote]
1643 \\markup {
1644   \\override #'(line-width . 40)
1645   \\justify-string #\"Lorem ipsum dolor sit amet, consectetur
1646       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1647       et dolore magna aliqua.
1648
1649
1650       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1651       laboris nisi ut aliquip ex ea commodo consequat.
1652
1653
1654       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1655       qui officia deserunt mollit anim id est laborum\"
1656 }
1657 @end lilypond"
1658   (stack-lines DOWN 0.0 baseline-skip
1659                (wordwrap-string-internal-markup-list layout props #t arg)))
1660
1661 (define-markup-command (wordwrap-field layout props symbol)
1662   (symbol?)
1663   #:category align
1664   "Wordwrap the data which has been assigned to @var{symbol}.
1665
1666 @lilypond[verbatim,quote]
1667 \\header {
1668   title = \"My title\"
1669   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1670     elit, sed do eiusmod tempor incididunt ut labore et dolore
1671     magna aliqua.  Ut enim ad minim veniam, quis nostrud
1672     exercitation ullamco laboris nisi ut aliquip ex ea commodo
1673     consequat.\"
1674 }
1675
1676 \\paper {
1677   bookTitleMarkup = \\markup {
1678     \\column {
1679       \\fill-line { \\fromproperty #'header:title }
1680       \\null
1681       \\wordwrap-field #'header:myText
1682     }
1683   }
1684 }
1685
1686 \\markup {
1687   \\null
1688 }
1689 @end lilypond"
1690   (let* ((m (chain-assoc-get symbol props)))
1691     (if (string? m)
1692         (wordwrap-string-markup layout props m)
1693         empty-stencil)))
1694
1695 (define-markup-command (justify-field layout props symbol)
1696   (symbol?)
1697   #:category align
1698   "Justify the data which has been assigned to @var{symbol}.
1699
1700 @lilypond[verbatim,quote]
1701 \\header {
1702   title = \"My title\"
1703   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1704     elit, sed do eiusmod tempor incididunt ut labore et dolore magna
1705     aliqua.  Ut enim ad minim veniam, quis nostrud exercitation ullamco
1706     laboris nisi ut aliquip ex ea commodo consequat.\"
1707 }
1708
1709 \\paper {
1710   bookTitleMarkup = \\markup {
1711     \\column {
1712       \\fill-line { \\fromproperty #'header:title }
1713       \\null
1714       \\justify-field #'header:myText
1715     }
1716   }
1717 }
1718
1719 \\markup {
1720   \\null
1721 }
1722 @end lilypond"
1723   (let* ((m (chain-assoc-get symbol props)))
1724     (if (string? m)
1725         (justify-string-markup layout props m)
1726         empty-stencil)))
1727
1728 (define-markup-command (combine layout props arg1 arg2)
1729   (markup? markup?)
1730   #:category align
1731   "
1732 @cindex merging text
1733
1734 Print two markups on top of each other.
1735
1736 Note: @code{\\combine} cannot take a list of markups enclosed in
1737 curly braces as an argument; for this purpose use @code{\\overlay} instead.
1738
1739 @lilypond[verbatim,quote]
1740 \\markup {
1741   \\fontsize #5
1742   \\override #'(thickness . 2)
1743   \\combine
1744     \\draw-line #'(0 . 4)
1745     \\arrow-head #Y #DOWN ##f
1746 }
1747 @end lilypond"
1748   (let* ((s1 (interpret-markup layout props arg1))
1749          (s2 (interpret-markup layout props arg2)))
1750     (ly:stencil-add s1 s2)))
1751
1752 (define-markup-command (overlay layout props args)
1753   (markup-list?)
1754   #:category align
1755   "
1756 @cindex merging text
1757
1758 Takes a list of markups combining them.
1759
1760 @lilypond[verbatim,quote]
1761 \\markup {
1762   \\fontsize #5
1763   \\override #'(thickness . 2)
1764   \\overlay {
1765     \\draw-line #'(0 . 4)
1766     \\arrow-head #Y #DOWN ##f
1767     \\translate #'(0 . 4)\\arrow-head #Y #UP ##f
1768   }
1769 }
1770 @end lilypond"
1771   (apply ly:stencil-add (interpret-markup-list layout props args)))
1772
1773 ;;
1774 ;; TODO: should extract baseline-skip from each argument somehow..
1775 ;;
1776 (define-markup-command (column layout props args)
1777   (markup-list?)
1778   #:category align
1779   #:properties ((baseline-skip))
1780   "
1781 @cindex stacking text in a column
1782
1783 Stack the markups in @var{args} vertically.  The property
1784 @code{baseline-skip} determines the space between markups
1785 in @var{args}.
1786
1787 @lilypond[verbatim,quote]
1788 \\markup {
1789   \\column {
1790     one
1791     two
1792     three
1793   }
1794 }
1795 @end lilypond"
1796   (let ((arg-stencils (interpret-markup-list layout props args)))
1797     (stack-lines -1 0.0 baseline-skip arg-stencils)))
1798
1799 (define-markup-command (dir-column layout props args)
1800   (markup-list?)
1801   #:category align
1802   #:properties ((direction)
1803                 (baseline-skip))
1804   "
1805 @cindex changing direction of text columns
1806
1807 Make a column of @var{args}, going up or down, depending on the
1808 setting of the @code{direction} layout property.
1809
1810 @lilypond[verbatim,quote]
1811 \\markup {
1812   \\override #`(direction . ,UP) {
1813     \\dir-column {
1814       going up
1815     }
1816   }
1817   \\hspace #1
1818   \\dir-column {
1819     going down
1820   }
1821   \\hspace #1
1822   \\override #'(direction . 1) {
1823     \\dir-column {
1824       going up
1825     }
1826   }
1827 }
1828 @end lilypond"
1829   (stack-lines (if (number? direction) direction -1)
1830                0.0
1831                baseline-skip
1832                (interpret-markup-list layout props args)))
1833
1834 (define (general-column align-dir baseline mols)
1835   "Stack @var{mols} vertically, aligned to  @var{align-dir} horizontally."
1836
1837   (let* ((aligned-mols (map (lambda (x) (ly:stencil-aligned-to x X align-dir)) mols))
1838          (stacked-stencil (stack-lines -1 0.0 baseline aligned-mols))
1839          (stacked-extent (ly:stencil-extent stacked-stencil X)))
1840     (ly:stencil-translate-axis stacked-stencil (- (car stacked-extent)) X )))
1841
1842 (define-markup-command (center-column layout props args)
1843   (markup-list?)
1844   #:category align
1845   #:properties ((baseline-skip))
1846   "
1847 @cindex centering a column of text
1848
1849 Put @code{args} in a centered column.
1850
1851 @lilypond[verbatim,quote]
1852 \\markup {
1853   \\center-column {
1854     one
1855     two
1856     three
1857   }
1858 }
1859 @end lilypond"
1860   (general-column CENTER baseline-skip (interpret-markup-list layout props args)))
1861
1862 (define-markup-command (left-column layout props args)
1863   (markup-list?)
1864   #:category align
1865   #:properties ((baseline-skip))
1866   "
1867 @cindex text columns, left-aligned
1868
1869 Put @code{args} in a left-aligned column.
1870
1871 @lilypond[verbatim,quote]
1872 \\markup {
1873   \\left-column {
1874     one
1875     two
1876     three
1877   }
1878 }
1879 @end lilypond"
1880   (general-column LEFT baseline-skip (interpret-markup-list layout props args)))
1881
1882 (define-markup-command (right-column layout props args)
1883   (markup-list?)
1884   #:category align
1885   #:properties ((baseline-skip))
1886   "
1887 @cindex text columns, right-aligned
1888
1889 Put @code{args} in a right-aligned column.
1890
1891 @lilypond[verbatim,quote]
1892 \\markup {
1893   \\right-column {
1894     one
1895     two
1896     three
1897   }
1898 }
1899 @end lilypond"
1900   (general-column RIGHT baseline-skip (interpret-markup-list layout props args)))
1901
1902 (define-markup-command (vcenter layout props arg)
1903   (markup?)
1904   #:category align
1905   "
1906 @cindex vertically centering text
1907
1908 Align @code{arg} to its Y@tie{}center.
1909
1910 @lilypond[verbatim,quote]
1911 \\markup {
1912   one
1913   \\vcenter
1914   two
1915   three
1916 }
1917 @end lilypond"
1918   (let* ((mol (interpret-markup layout props arg)))
1919     (ly:stencil-aligned-to mol Y CENTER)))
1920
1921 (define-markup-command (center-align layout props arg)
1922   (markup?)
1923   #:category align
1924   "
1925 @cindex horizontally centering text
1926
1927 Align @code{arg} to its X@tie{}center.
1928
1929 @lilypond[verbatim,quote]
1930 \\markup {
1931   \\column {
1932     one
1933     \\center-align
1934     two
1935     three
1936   }
1937 }
1938 @end lilypond"
1939   (let* ((mol (interpret-markup layout props arg)))
1940     (ly:stencil-aligned-to mol X CENTER)))
1941
1942 (define-markup-command (right-align layout props arg)
1943   (markup?)
1944   #:category align
1945   "
1946 @cindex right aligning text
1947
1948 Align @var{arg} on its right edge.
1949
1950 @lilypond[verbatim,quote]
1951 \\markup {
1952   \\column {
1953     one
1954     \\right-align
1955     two
1956     three
1957   }
1958 }
1959 @end lilypond"
1960   (let* ((m (interpret-markup layout props arg)))
1961     (ly:stencil-aligned-to m X RIGHT)))
1962
1963 (define-markup-command (left-align layout props arg)
1964   (markup?)
1965   #:category align
1966   "
1967 @cindex left aligning text
1968
1969 Align @var{arg} on its left edge.
1970
1971 @lilypond[verbatim,quote]
1972 \\markup {
1973   \\column {
1974     one
1975     \\left-align
1976     two
1977     three
1978   }
1979 }
1980 @end lilypond"
1981   (let* ((m (interpret-markup layout props arg)))
1982     (ly:stencil-aligned-to m X LEFT)))
1983
1984 (define-markup-command (general-align layout props axis dir arg)
1985   (integer? number? markup?)
1986   #:category align
1987   "
1988 @cindex controlling general text alignment
1989
1990 Align @var{arg} in @var{axis} direction to the @var{dir} side.
1991
1992 @lilypond[verbatim,quote]
1993 \\markup {
1994   \\column {
1995     one
1996     \\general-align #X #LEFT
1997     two
1998     three
1999     \\null
2000     one
2001     \\general-align #X #CENTER
2002     two
2003     three
2004     \\null
2005     \\line {
2006       one
2007       \\general-align #Y #UP
2008       two
2009       three
2010     }
2011     \\null
2012     \\line {
2013       one
2014       \\general-align #Y #3.2
2015       two
2016       three
2017     }
2018   }
2019 }
2020 @end lilypond"
2021   (let* ((m (interpret-markup layout props arg)))
2022     (ly:stencil-aligned-to m axis dir)))
2023
2024 (define-markup-command (halign layout props dir arg)
2025   (number? markup?)
2026   #:category align
2027   "
2028 @cindex setting horizontal text alignment
2029
2030 Set horizontal alignment.  If @var{dir} is @w{@code{-1}}, then it is
2031 left-aligned, while @code{+1} is right.  Values in between interpolate
2032 alignment accordingly.
2033
2034 @lilypond[verbatim,quote]
2035 \\markup {
2036   \\column {
2037     one
2038     \\halign #LEFT
2039     two
2040     three
2041     \\null
2042     one
2043     \\halign #CENTER
2044     two
2045     three
2046     \\null
2047     one
2048     \\halign #RIGHT
2049     two
2050     three
2051     \\null
2052     one
2053     \\halign #-5
2054     two
2055     three
2056   }
2057 }
2058 @end lilypond"
2059   (let* ((m (interpret-markup layout props arg)))
2060     (ly:stencil-aligned-to m X dir)))
2061
2062 (define-markup-command (with-dimensions layout props x y arg)
2063   (number-pair? number-pair? markup?)
2064   #:category other
2065   "
2066 @cindex setting extent of text objects
2067
2068 Set the dimensions of @var{arg} to @var{x} and@tie{}@var{y}."
2069   (let* ((expr (ly:stencil-expr (interpret-markup layout props arg))))
2070     (ly:stencil-add
2071      (make-transparent-box-stencil x y)
2072      (ly:make-stencil
2073       `(delay-stencil-evaluation ,(delay expr))
2074       x y))))
2075
2076 (define-markup-command (pad-around layout props amount arg)
2077   (number? markup?)
2078   #:category align
2079   "Add padding @var{amount} all around @var{arg}.
2080
2081 @lilypond[verbatim,quote]
2082 \\markup {
2083   \\box {
2084     default
2085   }
2086   \\hspace #2
2087   \\box {
2088     \\pad-around #0.5 {
2089       padded
2090     }
2091   }
2092 }
2093 @end lilypond"
2094   (let* ((m (interpret-markup layout props arg))
2095          (x (interval-widen (ly:stencil-extent m X) amount))
2096          (y (interval-widen (ly:stencil-extent m Y) amount)))
2097     (ly:stencil-add (make-transparent-box-stencil x y)
2098                     m)))
2099
2100 (define-markup-command (pad-x layout props amount arg)
2101   (number? markup?)
2102   #:category align
2103   "
2104 @cindex padding text horizontally
2105
2106 Add padding @var{amount} around @var{arg} in the X@tie{}direction.
2107
2108 @lilypond[verbatim,quote]
2109 \\markup {
2110   \\box {
2111     default
2112   }
2113   \\hspace #4
2114   \\box {
2115     \\pad-x #2 {
2116       padded
2117     }
2118   }
2119 }
2120 @end lilypond"
2121   (let* ((m (interpret-markup layout props arg))
2122          (x (ly:stencil-extent m X))
2123          (y (ly:stencil-extent m Y)))
2124     (ly:make-stencil (ly:stencil-expr m)
2125                      (interval-widen x amount)
2126                      y)))
2127
2128 (define-markup-command (put-adjacent layout props axis dir arg1 arg2)
2129   (integer? ly:dir? markup? markup?)
2130   #:category align
2131   "Put @var{arg2} next to @var{arg1}, without moving @var{arg1}."
2132   (let ((m1 (interpret-markup layout props arg1))
2133         (m2 (interpret-markup layout props arg2)))
2134     (ly:stencil-combine-at-edge m1 axis dir m2 0.0)))
2135
2136 (define-markup-command (transparent layout props arg)
2137   (markup?)
2138   #:category other
2139   "Make @var{arg} transparent.
2140
2141 @lilypond[verbatim,quote]
2142 \\markup {
2143   \\transparent {
2144     invisible text
2145   }
2146 }
2147 @end lilypond"
2148   (let* ((m (interpret-markup layout props arg))
2149          (x (ly:stencil-extent m X))
2150          (y (ly:stencil-extent m Y)))
2151     (ly:make-stencil (list 'transparent-stencil (ly:stencil-expr m)) x y)))
2152
2153 (define-markup-command (pad-to-box layout props x-ext y-ext arg)
2154   (number-pair? number-pair? markup?)
2155   #:category align
2156   "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space.
2157
2158 @lilypond[verbatim,quote]
2159 \\markup {
2160   \\box {
2161     default
2162   }
2163   \\hspace #4
2164   \\box {
2165     \\pad-to-box #'(0 . 10) #'(0 . 3) {
2166       padded
2167     }
2168   }
2169 }
2170 @end lilypond"
2171   (ly:stencil-add (make-transparent-box-stencil x-ext y-ext)
2172                   (interpret-markup layout props arg)))
2173
2174 (define-markup-command (hcenter-in layout props length arg)
2175   (number? markup?)
2176   #:category align
2177   "Center @var{arg} horizontally within a box of extending
2178 @var{length}/2 to the left and right.
2179
2180 @lilypond[quote,verbatim]
2181 \\new StaffGroup <<
2182   \\new Staff {
2183     \\set Staff.instrumentName = \\markup {
2184       \\hcenter-in #12
2185       Oboe
2186     }
2187     c''1
2188   }
2189   \\new Staff {
2190     \\set Staff.instrumentName = \\markup {
2191       \\hcenter-in #12
2192       Bassoon
2193     }
2194     \\clef tenor
2195     c'1
2196   }
2197 >>
2198 @end lilypond"
2199   (interpret-markup layout props
2200                     (make-pad-to-box-markup
2201                      (cons (/ length -2) (/ length 2))
2202                      '(0 . 0)
2203                      (make-center-align-markup arg))))
2204
2205 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2206 ;; property
2207 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2208
2209 (define-markup-command (property-recursive layout props symbol)
2210   (symbol?)
2211   #:category other
2212   "Print out a warning when a header field markup contains some recursive
2213 markup definition."
2214   (ly:warning "Recursive definition of property ~a detected!" symbol)
2215   empty-stencil)
2216
2217 (define-markup-command (fromproperty layout props symbol)
2218   (symbol?)
2219   #:category other
2220   "Read the @var{symbol} from property settings, and produce a stencil
2221 from the markup contained within.  If @var{symbol} is not defined, it
2222 returns an empty markup.
2223
2224 @lilypond[verbatim,quote]
2225 \\header {
2226   myTitle = \"myTitle\"
2227   title = \\markup {
2228     from
2229     \\italic
2230     \\fromproperty #'header:myTitle
2231   }
2232 }
2233 \\markup {
2234   \\null
2235 }
2236 @end lilypond"
2237   (let ((m (chain-assoc-get symbol props)))
2238     (if (markup? m)
2239         ;; prevent infinite loops by clearing the interpreted property:
2240         (interpret-markup layout (cons (list (cons symbol `(,property-recursive-markup ,symbol))) props) m)
2241         empty-stencil)))
2242
2243 (define-markup-command (on-the-fly layout props procedure arg)
2244   (procedure? markup?)
2245   #:category other
2246   "Apply the @var{procedure} markup command to @var{arg}.
2247 @var{procedure} takes the same arguments as @code{interpret-markup}
2248 and returns a stencil."
2249   (procedure layout props arg))
2250
2251 (define-markup-command (footnote layout props mkup note)
2252   (markup? markup?)
2253   #:category other
2254   "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
2255
2256 @lilypond[verbatim,quote]
2257 \\markup {
2258   \\auto-footnote a b
2259   \\override #'(padding . 0.2)
2260   \\auto-footnote c d
2261 }
2262 @end lilypond
2263 The footnote will not be annotated automatically."
2264   (ly:stencil-combine-at-edge
2265    (interpret-markup layout props mkup)
2266    X
2267    RIGHT
2268    (ly:make-stencil
2269     `(footnote (gensym "footnote") #f ,(interpret-markup layout props note))
2270     '(0 . 0)
2271     '(0 . 0))
2272    0.0))
2273
2274 (define-markup-command (auto-footnote layout props mkup note)
2275   (markup? markup?)
2276   #:category other
2277   #:properties ((raise 0.5)
2278                 (padding 0.0))
2279   "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
2280
2281 @lilypond[verbatim,quote]
2282 \\markup {
2283   \\auto-footnote a b
2284   \\override #'(padding . 0.2)
2285   \\auto-footnote c d
2286 }
2287 @end lilypond
2288 The footnote will be annotated automatically."
2289   (let* ((markup-stencil (interpret-markup layout props mkup))
2290          (footnote-hash (gensym "footnote"))
2291          (stencil-seed 0)
2292          (gauge-stencil (interpret-markup
2293                          layout
2294                          props
2295                          ((ly:output-def-lookup
2296                            layout
2297                            'footnote-numbering-function)
2298                           stencil-seed)))
2299          (x-ext (ly:stencil-extent gauge-stencil X))
2300          (y-ext (ly:stencil-extent gauge-stencil Y))
2301          (footnote-number
2302           `(delay-stencil-evaluation
2303             ,(delay
2304                (ly:stencil-expr
2305                 (let* ((table
2306                         (ly:output-def-lookup layout
2307                                               'number-footnote-table))
2308                        (footnote-stencil (if (list? table)
2309                                              (assoc-get footnote-hash
2310                                                         table)
2311                                              empty-stencil))
2312                        (footnote-stencil (if (ly:stencil? footnote-stencil)
2313                                              footnote-stencil
2314                                              (begin
2315                                                (ly:programming-error
2316                                                 "Cannot find correct footnote for a markup object.")
2317                                                empty-stencil)))
2318                        (gap (- (interval-length x-ext)
2319                                (interval-length
2320                                 (ly:stencil-extent footnote-stencil X))))
2321                        (y-trans (- (+ (cdr y-ext)
2322                                       raise)
2323                                    (cdr (ly:stencil-extent footnote-stencil
2324                                                            Y)))))
2325                   (ly:stencil-translate footnote-stencil
2326                                         (cons gap y-trans)))))))
2327          (main-stencil (ly:stencil-combine-at-edge
2328                         markup-stencil
2329                         X
2330                         RIGHT
2331                         (ly:make-stencil footnote-number x-ext y-ext)
2332                         padding)))
2333     (ly:stencil-add
2334      main-stencil
2335      (ly:make-stencil
2336       `(footnote ,footnote-hash #t ,(interpret-markup layout props note))
2337       '(0 . 0)
2338       '(0 . 0)))))
2339
2340 (define-markup-command (override layout props new-prop arg)
2341   (pair? markup?)
2342   #:category other
2343   "
2344 @cindex overriding properties within text markup
2345
2346 Add the argument @var{new-prop} to the property list.  Properties
2347 may be any property supported by @rinternals{font-interface},
2348 @rinternals{text-interface} and
2349 @rinternals{instrument-specific-markup-interface}.
2350
2351 @lilypond[verbatim,quote]
2352 \\markup {
2353   \\line {
2354     \\column {
2355       default
2356       baseline-skip
2357     }
2358     \\hspace #2
2359     \\override #'(baseline-skip . 4) {
2360       \\column {
2361         increased
2362         baseline-skip
2363       }
2364     }
2365   }
2366 }
2367 @end lilypond"
2368   (interpret-markup layout (cons (list new-prop) props) arg))
2369
2370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2371 ;; files
2372 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2373
2374 (define-markup-command (verbatim-file layout props name)
2375   (string?)
2376   #:category other
2377   "Read the contents of file @var{name}, and include it verbatim.
2378
2379 @lilypond[verbatim,quote]
2380 \\markup {
2381   \\verbatim-file #\"simple.ly\"
2382 }
2383 @end lilypond"
2384   (interpret-markup layout props
2385                     (if  (ly:get-option 'safe)
2386                          "verbatim-file disabled in safe mode"
2387                          (let* ((str (ly:gulp-file name))
2388                                 (lines (string-split str #\nl)))
2389                            (make-typewriter-markup
2390                             (make-column-markup lines))))))
2391
2392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2393 ;; fonts.
2394 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2395
2396
2397 (define-markup-command (smaller layout props arg)
2398   (markup?)
2399   #:category font
2400   "Decrease the font size relative to the current setting.
2401
2402 @lilypond[verbatim,quote]
2403 \\markup {
2404   \\fontsize #3.5 {
2405     some large text
2406     \\hspace #2
2407     \\smaller {
2408       a bit smaller
2409     }
2410     \\hspace #2
2411     more large text
2412   }
2413 }
2414 @end lilypond"
2415   (interpret-markup layout props
2416                     `(,fontsize-markup -1 ,arg)))
2417
2418 (define-markup-command (larger layout props arg)
2419   (markup?)
2420   #:category font
2421   "Increase the font size relative to the current setting.
2422
2423 @lilypond[verbatim,quote]
2424 \\markup {
2425   default
2426   \\hspace #2
2427   \\larger
2428   larger
2429 }
2430 @end lilypond"
2431   (interpret-markup layout props
2432                     `(,fontsize-markup 1 ,arg)))
2433
2434 (define-markup-command (finger layout props arg)
2435   (markup?)
2436   #:category font
2437   "Set @var{arg} as small numbers.
2438
2439 @lilypond[verbatim,quote]
2440 \\markup {
2441   \\finger {
2442     1 2 3 4 5
2443   }
2444 }
2445 @end lilypond"
2446   (interpret-markup layout
2447                     (cons '((font-size . -5) (font-encoding . fetaText)) props)
2448                     arg))
2449
2450 (define-markup-command (abs-fontsize layout props size arg)
2451   (number? markup?)
2452   #:category font
2453   "Use @var{size} as the absolute font size (in points) to display @var{arg}.
2454 Adjusts @code{baseline-skip} and @code{word-space} accordingly.
2455
2456 @lilypond[verbatim,quote]
2457 \\markup {
2458   default text font size
2459   \\hspace #2
2460   \\abs-fontsize #16 { text font size 16 }
2461   \\hspace #2
2462   \\abs-fontsize #12 { text font size 12 }
2463 }
2464 @end lilypond"
2465   (let* ((ref-size (ly:output-def-lookup layout 'text-font-size 12))
2466          (text-props (list (ly:output-def-lookup layout 'text-font-defaults)))
2467          (ref-word-space (chain-assoc-get 'word-space text-props 0.6))
2468          (ref-baseline (chain-assoc-get 'baseline-skip text-props 3))
2469          (magnification (/ size ref-size)))
2470     (interpret-markup
2471      layout
2472      (cons
2473       `((baseline-skip . ,(* magnification ref-baseline))
2474         (word-space . ,(* magnification ref-word-space))
2475         (font-size . ,(magnification->font-size magnification)))
2476       props)
2477      arg)))
2478
2479 (define-markup-command (fontsize layout props increment arg)
2480   (number? markup?)
2481   #:category font
2482   #:properties ((font-size 0)
2483                 (word-space 1)
2484                 (baseline-skip 2))
2485   "Add @var{increment} to the font-size.  Adjusts @code{baseline-skip}
2486 accordingly.
2487
2488 @lilypond[verbatim,quote]
2489 \\markup {
2490   default
2491   \\hspace #2
2492   \\fontsize #-1.5
2493   smaller
2494 }
2495 @end lilypond"
2496   (interpret-markup
2497    layout
2498    (cons
2499     `((baseline-skip . ,(* baseline-skip (magstep increment)))
2500       (word-space . ,(* word-space (magstep increment)))
2501       (font-size . ,(+ font-size increment)))
2502     props)
2503    arg))
2504
2505 (define-markup-command (magnify layout props sz arg)
2506   (number? markup?)
2507   #:category font
2508   "
2509 @cindex magnifying text
2510
2511 Set the font magnification for its argument.  In the following
2512 example, the middle@tie{}A is 10% larger:
2513
2514 @example
2515 A \\magnify #1.1 @{ A @} A
2516 @end example
2517
2518 Note: Magnification only works if a font name is explicitly selected.
2519 Use @code{\\fontsize} otherwise.
2520
2521 @lilypond[verbatim,quote]
2522 \\markup {
2523   default
2524   \\hspace #2
2525   \\magnify #1.5 {
2526     50% larger
2527   }
2528 }
2529 @end lilypond"
2530   (interpret-markup
2531    layout
2532    (prepend-alist-chain 'font-size (magnification->font-size sz) props)
2533    arg))
2534
2535 (define-markup-command (bold layout props arg)
2536   (markup?)
2537   #:category font
2538   "Switch to bold font-series.
2539
2540 @lilypond[verbatim,quote]
2541 \\markup {
2542   default
2543   \\hspace #2
2544   \\bold
2545   bold
2546 }
2547 @end lilypond"
2548   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
2549
2550 (define-markup-command (sans layout props arg)
2551   (markup?)
2552   #:category font
2553   "Switch to the sans serif font family.
2554
2555 @lilypond[verbatim,quote]
2556 \\markup {
2557   default
2558   \\hspace #2
2559   \\sans {
2560     sans serif
2561   }
2562 }
2563 @end lilypond"
2564   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
2565
2566 (define-markup-command (number layout props arg)
2567   (markup?)
2568   #:category font
2569   "Set font family to @code{number}, which yields the font used for
2570 time signatures and fingerings.  This font contains numbers and
2571 some punctuation; it has no letters.
2572
2573 @lilypond[verbatim,quote]
2574 \\markup {
2575   \\number {
2576     0 1 2 3 4 5 6 7 8 9 . ,
2577   }
2578 }
2579 @end lilypond"
2580   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2581
2582 (define-markup-command (roman layout props arg)
2583   (markup?)
2584   #:category font
2585   "Set font family to @code{roman}.
2586
2587 @lilypond[verbatim,quote]
2588 \\markup {
2589   \\sans \\bold {
2590     sans serif, bold
2591     \\hspace #2
2592     \\roman {
2593       text in roman font family
2594     }
2595     \\hspace #2
2596     return to sans
2597   }
2598 }
2599 @end lilypond"
2600   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
2601
2602 (define-markup-command (huge layout props arg)
2603   (markup?)
2604   #:category font
2605   "Set font size to +2.
2606
2607 @lilypond[verbatim,quote]
2608 \\markup {
2609   default
2610   \\hspace #2
2611   \\huge
2612   huge
2613 }
2614 @end lilypond"
2615   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
2616
2617 (define-markup-command (large layout props arg)
2618   (markup?)
2619   #:category font
2620   "Set font size to +1.
2621
2622 @lilypond[verbatim,quote]
2623 \\markup {
2624   default
2625   \\hspace #2
2626   \\large
2627   large
2628 }
2629 @end lilypond"
2630   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
2631
2632 (define-markup-command (normalsize layout props arg)
2633   (markup?)
2634   #:category font
2635   "Set font size to default.
2636
2637 @lilypond[verbatim,quote]
2638 \\markup {
2639   \\teeny {
2640     this is very small
2641     \\hspace #2
2642     \\normalsize {
2643       normal size
2644     }
2645     \\hspace #2
2646     teeny again
2647   }
2648 }
2649 @end lilypond"
2650   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
2651
2652 (define-markup-command (small layout props arg)
2653   (markup?)
2654   #:category font
2655   "Set font size to -1.
2656
2657 @lilypond[verbatim,quote]
2658 \\markup {
2659   default
2660   \\hspace #2
2661   \\small
2662   small
2663 }
2664 @end lilypond"
2665   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
2666
2667 (define-markup-command (tiny layout props arg)
2668   (markup?)
2669   #:category font
2670   "Set font size to -2.
2671
2672 @lilypond[verbatim,quote]
2673 \\markup {
2674   default
2675   \\hspace #2
2676   \\tiny
2677   tiny
2678 }
2679 @end lilypond"
2680   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
2681
2682 (define-markup-command (teeny layout props arg)
2683   (markup?)
2684   #:category font
2685   "Set font size to -3.
2686
2687 @lilypond[verbatim,quote]
2688 \\markup {
2689   default
2690   \\hspace #2
2691   \\teeny
2692   teeny
2693 }
2694 @end lilypond"
2695   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
2696
2697 (define-markup-command (fontCaps layout props arg)
2698   (markup?)
2699   #:category font
2700   "Set @code{font-shape} to @code{caps}
2701
2702 Note: @code{\\fontCaps} requires the installation and selection of
2703 fonts which support the @code{caps} font shape."
2704   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
2705
2706 ;; Poor man's caps
2707 (define-markup-command (smallCaps layout props arg)
2708   (markup?)
2709   #:category font
2710   "Emit @var{arg} as small caps.
2711
2712 Note: @code{\\smallCaps} does not support accented characters.
2713
2714 @lilypond[verbatim,quote]
2715 \\markup {
2716   default
2717   \\hspace #2
2718   \\smallCaps {
2719     Text in small caps
2720   }
2721 }
2722 @end lilypond"
2723   (define (char-list->markup chars lower)
2724     (let ((final-string (string-upcase (reverse-list->string chars))))
2725       (if lower
2726           (markup #:fontsize -2 final-string)
2727           final-string)))
2728   (define (make-small-caps rest-chars currents current-is-lower prev-result)
2729     (if (null? rest-chars)
2730         (make-concat-markup
2731          (reverse! (cons (char-list->markup currents current-is-lower)
2732                          prev-result)))
2733         (let* ((ch (car rest-chars))
2734                (is-lower (char-lower-case? ch)))
2735           (if (or (and current-is-lower is-lower)
2736                   (and (not current-is-lower) (not is-lower)))
2737               (make-small-caps (cdr rest-chars)
2738                                (cons ch currents)
2739                                is-lower
2740                                prev-result)
2741               (make-small-caps (cdr rest-chars)
2742                                (list ch)
2743                                is-lower
2744                                (if (null? currents)
2745                                    prev-result
2746                                    (cons (char-list->markup
2747                                           currents current-is-lower)
2748                                          prev-result)))))))
2749   (interpret-markup layout props
2750                     (if (string? arg)
2751                         (make-small-caps (string->list arg) (list) #f (list))
2752                         arg)))
2753
2754 (define-markup-command (caps layout props arg)
2755   (markup?)
2756   #:category font
2757   "Copy of the @code{\\smallCaps} command.
2758
2759 @lilypond[verbatim,quote]
2760 \\markup {
2761   default
2762   \\hspace #2
2763   \\caps {
2764     Text in small caps
2765   }
2766 }
2767 @end lilypond"
2768   (interpret-markup layout props (make-smallCaps-markup arg)))
2769
2770 (define-markup-command (dynamic layout props arg)
2771   (markup?)
2772   #:category font
2773   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
2774 @b{z}, @b{p}, and @b{r}.  When producing phrases, like
2775 @q{pi@`{u}@tie{}@b{f}}, the normal words (like @q{pi@`{u}}) should be
2776 done in a different font.  The recommended font for this is bold and italic.
2777 @lilypond[verbatim,quote]
2778 \\markup {
2779   \\dynamic {
2780     sfzp
2781   }
2782 }
2783 @end lilypond"
2784   (interpret-markup
2785    layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2786
2787 (define-markup-command (text layout props arg)
2788   (markup?)
2789   #:category font
2790   "Use a text font instead of music symbol or music alphabet font.
2791
2792 @lilypond[verbatim,quote]
2793 \\markup {
2794   \\number {
2795     1, 2,
2796     \\text {
2797       three, four,
2798     }
2799     5
2800   }
2801 }
2802 @end lilypond"
2803
2804   ;; ugh - latin1
2805   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
2806                     arg))
2807
2808 (define-markup-command (italic layout props arg)
2809   (markup?)
2810   #:category font
2811   "Use italic @code{font-shape} for @var{arg}.
2812
2813 @lilypond[verbatim,quote]
2814 \\markup {
2815   default
2816   \\hspace #2
2817   \\italic
2818   italic
2819 }
2820 @end lilypond"
2821   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
2822
2823 (define-markup-command (typewriter layout props arg)
2824   (markup?)
2825   #:category font
2826   "Use @code{font-family} typewriter for @var{arg}.
2827
2828 @lilypond[verbatim,quote]
2829 \\markup {
2830   default
2831   \\hspace #2
2832   \\typewriter
2833   typewriter
2834 }
2835 @end lilypond"
2836   (interpret-markup
2837    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
2838
2839 (define-markup-command (upright layout props arg)
2840   (markup?)
2841   #:category font
2842   "Set @code{font-shape} to @code{upright}.  This is the opposite
2843 of @code{italic}.
2844
2845 @lilypond[verbatim,quote]
2846 \\markup {
2847   \\italic {
2848     italic text
2849     \\hspace #2
2850     \\upright {
2851       upright text
2852     }
2853     \\hspace #2
2854     italic again
2855   }
2856 }
2857 @end lilypond"
2858   (interpret-markup
2859    layout (prepend-alist-chain 'font-shape 'upright props) arg))
2860
2861 (define-markup-command (medium layout props arg)
2862   (markup?)
2863   #:category font
2864   "Switch to medium font-series (in contrast to bold).
2865
2866 @lilypond[verbatim,quote]
2867 \\markup {
2868   \\bold {
2869     some bold text
2870     \\hspace #2
2871     \\medium {
2872       medium font series
2873     }
2874     \\hspace #2
2875     bold again
2876   }
2877 }
2878 @end lilypond"
2879   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
2880                     arg))
2881
2882 (define-markup-command (normal-text layout props arg)
2883   (markup?)
2884   #:category font
2885   "Set all font related properties (except the size) to get the default
2886 normal text font, no matter what font was used earlier.
2887
2888 @lilypond[verbatim,quote]
2889 \\markup {
2890   \\huge \\bold \\sans \\caps {
2891     huge bold sans caps
2892     \\hspace #2
2893     \\normal-text {
2894       huge normal
2895     }
2896     \\hspace #2
2897     as before
2898   }
2899 }
2900 @end lilypond"
2901   ;; ugh - latin1
2902   (interpret-markup layout
2903                     (cons '((font-family . roman) (font-shape . upright)
2904                             (font-series . medium) (font-encoding . latin1))
2905                           props)
2906                     arg))
2907
2908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2909 ;; symbols.
2910 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2911
2912 (define-markup-command (musicglyph layout props glyph-name)
2913   (string?)
2914   #:category music
2915   "@var{glyph-name} is converted to a musical symbol; for example,
2916 @code{\\musicglyph #\"accidentals.natural\"} selects the natural sign from
2917 the music font.  See @ruser{The Feta font} for a complete listing of
2918 the possible glyphs.
2919
2920 @lilypond[verbatim,quote]
2921 \\markup {
2922   \\musicglyph #\"f\"
2923   \\musicglyph #\"rests.2\"
2924   \\musicglyph #\"clefs.G_change\"
2925 }
2926 @end lilypond"
2927   (let* ((font (ly:paper-get-font layout
2928                                   (cons '((font-encoding . fetaMusic)
2929                                           (font-name . #f))
2930
2931                                         props)))
2932          (glyph (ly:font-get-glyph font glyph-name)))
2933     (if (null? (ly:stencil-expr glyph))
2934         (ly:warning (_ "Cannot find glyph ~a") glyph-name))
2935
2936     glyph))
2937
2938 (define-markup-command (doublesharp layout props)
2939   ()
2940   #:category music
2941   "Draw a double sharp symbol.
2942
2943 @lilypond[verbatim,quote]
2944 \\markup {
2945   \\doublesharp
2946 }
2947 @end lilypond"
2948   (interpret-markup layout props (markup #:musicglyph (assoc-get 1 standard-alteration-glyph-name-alist ""))))
2949
2950 (define-markup-command (sesquisharp layout props)
2951   ()
2952   #:category music
2953   "Draw a 3/2 sharp symbol.
2954
2955 @lilypond[verbatim,quote]
2956 \\markup {
2957   \\sesquisharp
2958 }
2959 @end lilypond"
2960   (interpret-markup layout props (markup #:musicglyph (assoc-get 3/4 standard-alteration-glyph-name-alist ""))))
2961
2962 (define-markup-command (sharp layout props)
2963   ()
2964   #:category music
2965   "Draw a sharp symbol.
2966
2967 @lilypond[verbatim,quote]
2968 \\markup {
2969   \\sharp
2970 }
2971 @end lilypond"
2972   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/2 standard-alteration-glyph-name-alist ""))))
2973
2974 (define-markup-command (semisharp layout props)
2975   ()
2976   #:category music
2977   "Draw a semisharp symbol.
2978
2979 @lilypond[verbatim,quote]
2980 \\markup {
2981   \\semisharp
2982 }
2983 @end lilypond"
2984   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/4 standard-alteration-glyph-name-alist ""))))
2985
2986 (define-markup-command (natural layout props)
2987   ()
2988   #:category music
2989   "Draw a natural symbol.
2990
2991 @lilypond[verbatim,quote]
2992 \\markup {
2993   \\natural
2994 }
2995 @end lilypond"
2996   (interpret-markup layout props (markup #:musicglyph (assoc-get 0 standard-alteration-glyph-name-alist ""))))
2997
2998 (define-markup-command (semiflat layout props)
2999   ()
3000   #:category music
3001   "Draw a semiflat symbol.
3002
3003 @lilypond[verbatim,quote]
3004 \\markup {
3005   \\semiflat
3006 }
3007 @end lilypond"
3008   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/4 standard-alteration-glyph-name-alist ""))))
3009
3010 (define-markup-command (flat layout props)
3011   ()
3012   #:category music
3013   "Draw a flat symbol.
3014
3015 @lilypond[verbatim,quote]
3016 \\markup {
3017   \\flat
3018 }
3019 @end lilypond"
3020   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/2 standard-alteration-glyph-name-alist ""))))
3021
3022 (define-markup-command (sesquiflat layout props)
3023   ()
3024   #:category music
3025   "Draw a 3/2 flat symbol.
3026
3027 @lilypond[verbatim,quote]
3028 \\markup {
3029   \\sesquiflat
3030 }
3031 @end lilypond"
3032   (interpret-markup layout props (markup #:musicglyph (assoc-get -3/4 standard-alteration-glyph-name-alist ""))))
3033
3034 (define-markup-command (doubleflat layout props)
3035   ()
3036   #:category music
3037   "Draw a double flat symbol.
3038
3039 @lilypond[verbatim,quote]
3040 \\markup {
3041   \\doubleflat
3042 }
3043 @end lilypond"
3044   (interpret-markup layout props (markup #:musicglyph (assoc-get -1 standard-alteration-glyph-name-alist ""))))
3045
3046 (define-markup-command (with-color layout props color arg)
3047   (color? markup?)
3048   #:category other
3049   "
3050 @cindex coloring text
3051
3052 Draw @var{arg} in color specified by @var{color}.
3053
3054 @lilypond[verbatim,quote]
3055 \\markup {
3056   \\with-color #red
3057   red
3058   \\hspace #2
3059   \\with-color #green
3060   green
3061   \\hspace #2
3062   \\with-color #blue
3063   blue
3064 }
3065 @end lilypond"
3066   (let ((stil (interpret-markup layout props arg)))
3067     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
3068                      (ly:stencil-extent stil X)
3069                      (ly:stencil-extent stil Y))))
3070
3071 (define-markup-command (tied-lyric layout props str)
3072   (string?)
3073   #:category music
3074   #:properties ((word-space))
3075   "
3076 @cindex simple text strings with tie characters
3077
3078 Like simple-markup, but use tie characters for @q{~} tilde symbols.
3079
3080 @lilypond[verbatim,quote]
3081 \\markup \\column {
3082   \\tied-lyric #\"Siam navi~all'onde~algenti Lasciate~in abbandono\"
3083   \\tied-lyric #\"Impetuosi venti I nostri~affetti sono\"
3084   \\tied-lyric #\"Ogni diletto~e scoglio Tutta la vita~e~un mar.\"
3085 }
3086 @end lilypond"
3087   (define (replace-ties tie str)
3088     (if (string-contains str "~")
3089         (let*
3090             ((half-space (/ word-space 2))
3091              (parts (string-split str #\~))
3092              (tie-str (markup #:hspace half-space
3093                               #:musicglyph tie
3094                               #:hspace half-space))
3095              (joined  (list-join parts tie-str)))
3096           (make-concat-markup joined))
3097         str))
3098
3099   (define short-tie-regexp (make-regexp "~[^.]~"))
3100   (define (match-short str) (regexp-exec short-tie-regexp str))
3101
3102   (define (replace-short str mkp)
3103     (let ((match (match-short str)))
3104       (if (not match)
3105           (make-concat-markup (list
3106                                mkp
3107                                (replace-ties "ties.lyric.default" str)))
3108           (let ((new-str (match:suffix match))
3109                 (new-mkp (make-concat-markup (list
3110                                               mkp
3111                                               (replace-ties "ties.lyric.default"
3112                                                             (match:prefix match))
3113                                               (replace-ties "ties.lyric.short"
3114                                                             (match:substring match))))))
3115             (replace-short new-str new-mkp)))))
3116
3117   (interpret-markup layout
3118                     props
3119                     (replace-short str (markup))))
3120
3121 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3122 ;; glyphs
3123 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3124
3125 (define-markup-command (arrow-head layout props axis dir filled)
3126   (integer? ly:dir? boolean?)
3127   #:category graphic
3128   "Produce an arrow head in specified direction and axis.
3129 Use the filled head if @var{filled} is specified.
3130 @lilypond[verbatim,quote]
3131 \\markup {
3132   \\fontsize #5 {
3133     \\general-align #Y #DOWN {
3134       \\arrow-head #Y #UP ##t
3135       \\arrow-head #Y #DOWN ##f
3136       \\hspace #2
3137       \\arrow-head #X #RIGHT ##f
3138       \\arrow-head #X #LEFT ##f
3139     }
3140   }
3141 }
3142 @end lilypond"
3143   (let*
3144       ((name (format #f "arrowheads.~a.~a~a"
3145                      (if filled
3146                          "close"
3147                          "open")
3148                      axis
3149                      dir)))
3150     (ly:font-get-glyph
3151      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
3152                                      props))
3153      name)))
3154
3155 (define-markup-command (lookup layout props glyph-name)
3156   (string?)
3157   #:category other
3158   "Lookup a glyph by name.
3159
3160 @lilypond[verbatim,quote]
3161 \\markup {
3162   \\override #'(font-encoding . fetaBraces) {
3163     \\lookup #\"brace200\"
3164     \\hspace #2
3165     \\rotate #180
3166     \\lookup #\"brace180\"
3167   }
3168 }
3169 @end lilypond"
3170   (ly:font-get-glyph (ly:paper-get-font layout props)
3171                      glyph-name))
3172
3173 (define-markup-command (char layout props num)
3174   (integer?)
3175   #:category other
3176   "Produce a single character.  Characters encoded in hexadecimal
3177 format require the prefix @code{#x}.
3178
3179 @lilypond[verbatim,quote]
3180 \\markup {
3181   \\char #65 \\char ##x00a9
3182 }
3183 @end lilypond"
3184   (ly:text-interface::interpret-markup layout props (ly:wide-char->utf-8 num)))
3185
3186 (define number->mark-letter-vector (make-vector 25 #\A))
3187
3188 (do ((i 0 (1+ i))
3189      (j 0 (1+ j)))
3190     ((>= i 26))
3191   (if (= i (- (char->integer #\I) (char->integer #\A)))
3192       (set! i (1+ i)))
3193   (vector-set! number->mark-letter-vector j
3194                (integer->char (+ i (char->integer #\A)))))
3195
3196 (define number->mark-alphabet-vector (list->vector
3197                                       (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
3198
3199 (define (number->markletter-string vec n)
3200   "Double letters for big marks."
3201   (let* ((lst (vector-length vec)))
3202
3203     (if (>= n lst)
3204         (string-append (number->markletter-string vec (1- (quotient n lst)))
3205                        (number->markletter-string vec (remainder n lst)))
3206         (make-string 1 (vector-ref vec n)))))
3207
3208 (define-markup-command (markletter layout props num)
3209   (integer?)
3210   #:category other
3211   "Make a markup letter for @var{num}.  The letters start with A
3212 to@tie{}Z (skipping letter@tie{}I), and continue with double letters.
3213
3214 @lilypond[verbatim,quote]
3215 \\markup {
3216   \\markletter #8
3217   \\hspace #2
3218   \\markletter #26
3219 }
3220 @end lilypond"
3221   (ly:text-interface::interpret-markup layout props
3222                                        (number->markletter-string number->mark-letter-vector num)))
3223
3224 (define-markup-command (markalphabet layout props num)
3225   (integer?)
3226   #:category other
3227   "Make a markup letter for @var{num}.  The letters start with A to@tie{}Z
3228 and continue with double letters.
3229
3230 @lilypond[verbatim,quote]
3231 \\markup {
3232   \\markalphabet #8
3233   \\hspace #2
3234   \\markalphabet #26
3235 }
3236 @end lilypond"
3237   (ly:text-interface::interpret-markup layout props
3238                                        (number->markletter-string number->mark-alphabet-vector num)))
3239
3240 (define-public (horizontal-slash-interval num forward number-interval mag)
3241   (if forward
3242       (cond ;; ((= num 6) (interval-widen number-interval (* mag 0.5)))
3243        ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
3244        (else (interval-widen number-interval (* mag 0.25))))
3245       (cond ((= num 6) (interval-widen number-interval (* mag 0.5)))
3246             ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
3247             (else (interval-widen number-interval (* mag 0.25))))
3248       ))
3249
3250 (define-public (adjust-slash-stencil num forward stencil mag)
3251   (if forward
3252       (cond ((= num 2)
3253              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
3254             ((= num 3)
3255              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
3256             ;; ((= num 5)
3257             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.07))))
3258             ;; ((= num 7)
3259             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
3260             (else stencil))
3261       (cond ((= num 6)
3262              (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.15))))
3263             ;; ((= num 8)
3264             ;;     (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
3265             (else stencil))
3266       )
3267   )
3268
3269 (define (slashed-digit-internal layout props num forward font-size thickness)
3270   (let* ((mag (magstep font-size))
3271          (thickness (* mag
3272                        (ly:output-def-lookup layout 'line-thickness)
3273                        thickness))
3274          ;; backward slashes might use slope and point in the other direction!
3275          (dy (* mag (if forward 0.4 -0.4)))
3276          (number-stencil (interpret-markup layout
3277                                            (prepend-alist-chain 'font-encoding 'fetaText props)
3278                                            (number->string num)))
3279          (num-x (horizontal-slash-interval num forward (ly:stencil-extent number-stencil X) mag))
3280          (center (interval-center (ly:stencil-extent number-stencil Y)))
3281          ;; Use the real extents of the slash, not the whole number,
3282          ;; because we might translate the slash later on!
3283          (num-y (interval-widen (cons center center) (abs dy)))
3284          (is-sane (and (interval-sane? num-x) (interval-sane? num-y)))
3285          (slash-stencil (if is-sane
3286                             (make-line-stencil thickness
3287                                                (car num-x) (- (interval-center num-y) dy)
3288                                                (cdr num-x) (+ (interval-center num-y) dy))
3289                             #f)))
3290     (if (ly:stencil? slash-stencil)
3291         (begin
3292           ;; for some numbers we need to shift the slash/backslash up or
3293           ;; down to make the slashed digit look better
3294           (set! slash-stencil (adjust-slash-stencil num forward slash-stencil mag))
3295           (set! number-stencil
3296                 (ly:stencil-add number-stencil slash-stencil)))
3297         (ly:warning "Unable to create slashed digit ~a" num))
3298     number-stencil))
3299
3300
3301 (define-markup-command (slashed-digit layout props num)
3302   (integer?)
3303   #:category other
3304   #:properties ((font-size 0)
3305                 (thickness 1.6))
3306   "
3307 @cindex slashed digits
3308
3309 A feta number, with slash.  This is for use in the context of
3310 figured bass notation.
3311 @lilypond[verbatim,quote]
3312 \\markup {
3313   \\slashed-digit #5
3314   \\hspace #2
3315   \\override #'(thickness . 3)
3316   \\slashed-digit #7
3317 }
3318 @end lilypond"
3319   (slashed-digit-internal layout props num #t font-size thickness))
3320
3321 (define-markup-command (backslashed-digit layout props num)
3322   (integer?)
3323   #:category other
3324   #:properties ((font-size 0)
3325                 (thickness 1.6))
3326   "
3327 @cindex backslashed digits
3328
3329 A feta number, with backslash.  This is for use in the context of
3330 figured bass notation.
3331 @lilypond[verbatim,quote]
3332 \\markup {
3333   \\backslashed-digit #5
3334   \\hspace #2
3335   \\override #'(thickness . 3)
3336   \\backslashed-digit #7
3337 }
3338 @end lilypond"
3339   (slashed-digit-internal layout props num #f font-size thickness))
3340
3341 ;; eyeglasses
3342 (define eyeglassespath
3343   '((moveto 0.42 0.77)
3344     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
3345     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
3346     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
3347     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
3348     (closepath)
3349     (moveto 2.07 0.77)
3350     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
3351     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
3352     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
3353     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
3354     (closepath)
3355     (moveto 1.025 0.935)
3356     (rcurveto 0 0.182 -0.148 0.33 -0.33 0.33)
3357     (rcurveto -0.182 0 -0.33 -0.148 -0.33 -0.33)
3358     (moveto -0.68 0.77)
3359     (rlineto 0.66 1.43)
3360     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)
3361     (moveto 2.07 0.77)
3362     (rlineto 0.66 1.43)
3363     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)))
3364
3365 (define-markup-command (eyeglasses layout props)
3366   ()
3367   #:category other
3368   "Prints out eyeglasses, indicating strongly to look at the conductor.
3369 @lilypond[verbatim,quote]
3370 \\markup { \\eyeglasses }
3371 @end lilypond"
3372   (interpret-markup layout props
3373                     (make-override-markup '(line-cap-style . butt)
3374                                           (make-path-markup 0.15 eyeglassespath))))
3375
3376 (define-markup-command (left-brace layout props size)
3377   (number?)
3378   #:category other
3379   "
3380 A feta brace in point size @var{size}.
3381
3382 @lilypond[verbatim,quote]
3383 \\markup {
3384   \\left-brace #35
3385   \\hspace #2
3386   \\left-brace #45
3387 }
3388 @end lilypond"
3389   (let* ((font (ly:paper-get-font layout
3390                                   (cons '((font-encoding . fetaBraces)
3391                                           (font-name . #f))
3392                                         props)))
3393          (glyph-count (1- (ly:otf-glyph-count font)))
3394          (scale (ly:output-def-lookup layout 'output-scale))
3395          (scaled-size (/ (ly:pt size) scale))
3396          (glyph (lambda (n)
3397                   (ly:font-get-glyph font (string-append "brace"
3398                                                          (number->string n)))))
3399          (get-y-from-brace (lambda (brace)
3400                              (interval-length
3401                               (ly:stencil-extent (glyph brace) Y))))
3402          (find-brace (binary-search 0 glyph-count get-y-from-brace scaled-size))
3403          (glyph-found (glyph find-brace)))
3404
3405     (if (or (null? (ly:stencil-expr glyph-found))
3406             (< scaled-size (interval-length (ly:stencil-extent (glyph 0) Y)))
3407             (> scaled-size (interval-length
3408                             (ly:stencil-extent (glyph glyph-count) Y))))
3409         (begin
3410           (ly:warning (_ "no brace found for point size ~S ") size)
3411           (ly:warning (_ "defaulting to ~S pt")
3412                       (/ (* scale (interval-length
3413                                    (ly:stencil-extent glyph-found Y)))
3414                          (ly:pt 1)))))
3415     glyph-found))
3416
3417 (define-markup-command (right-brace layout props size)
3418   (number?)
3419   #:category other
3420   "
3421 A feta brace in point size @var{size}, rotated 180 degrees.
3422
3423 @lilypond[verbatim,quote]
3424 \\markup {
3425   \\right-brace #45
3426   \\hspace #2
3427   \\right-brace #35
3428 }
3429 @end lilypond"
3430   (interpret-markup layout props (markup #:rotate 180 #:left-brace size)))
3431
3432 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3433 ;; the note command.
3434 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3435
3436 ;; TODO: better syntax.
3437
3438 (define-markup-command (note-by-number layout props log dot-count dir)
3439   (number? number? number?)
3440   #:category music
3441   #:properties ((font-size 0)
3442                 (flag-style '())
3443                 (style '()))
3444   "
3445 @cindex notes within text by log and dot-count
3446
3447 Construct a note symbol, with stem and flag.  By using fractional values for
3448 @var{dir}, longer or shorter stems can be obtained.
3449 Supports all note-head-styles.
3450 Supported flag-styles are @code{default}, @code{old-straight-flag},
3451 @code{modern-straight-flag} and @code{flat-flag}.
3452
3453 @lilypond[verbatim,quote]
3454 \\markup {
3455   \\note-by-number #3 #0 #DOWN
3456   \\hspace #2
3457   \\note-by-number #1 #2 #0.8
3458 }
3459 @end lilypond"
3460   (define (get-glyph-name-candidates dir log style)
3461     (map (lambda (dir-name)
3462            (format #f "noteheads.~a~a" dir-name
3463                    (if (and (symbol? style)
3464                             (not (equal? 'default style)))
3465                        (select-head-glyph style (min log 2))
3466                        (min log 2))))
3467          (list (if (= dir UP) "u" "d")
3468                "s")))
3469
3470   (define (get-glyph-name font cands)
3471     (if (null? cands)
3472         ""
3473         (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
3474             (get-glyph-name font (cdr cands))
3475             (car cands))))
3476
3477   (define (buildflags flag-stencil remain curr-stencil spacing)
3478     ;; Function to recursively create a stencil with @code{remain} flags
3479     ;; from the single-flag stencil @code{curr-stencil}, which is already
3480     ;; translated to the position of the previous flag position.
3481     ;;
3482     ;; Copy and paste from /scm/flag-styles.scm
3483     (if (> remain 0)
3484         (let* ((translated-stencil
3485                 (ly:stencil-translate-axis curr-stencil spacing Y))
3486                (new-stencil (ly:stencil-add flag-stencil translated-stencil)))
3487           (buildflags new-stencil (- remain 1) translated-stencil spacing))
3488         flag-stencil))
3489
3490   (define (straight-flag-mrkp flag-thickness flag-spacing
3491                               upflag-angle upflag-length
3492                               downflag-angle downflag-length
3493                               dir)
3494     ;; Create a stencil for a straight flag.  @var{flag-thickness} and
3495     ;; @var{flag-spacing} are given in staff spaces, @var{upflag-angle} and
3496     ;; @var{downflag-angle} are given in degrees, and @var{upflag-length} and
3497     ;; @var{downflag-length} are given in staff spaces.
3498     ;;
3499     ;; All lengths are scaled according to the font size of the note.
3500     ;;
3501     ;; From /scm/flag-styles.scm, modified to fit here.
3502
3503     (let* ((stem-up (> dir 0))
3504            ;; scale with the note size
3505            (factor (magstep font-size))
3506            (stem-thickness (* factor 0.1))
3507            (line-thickness (ly:output-def-lookup layout 'line-thickness))
3508            (half-stem-thickness (/ (* stem-thickness line-thickness) 2))
3509            (raw-length (if stem-up upflag-length downflag-length))
3510            (angle (if stem-up upflag-angle downflag-angle))
3511            (flag-length (+ (* raw-length factor) half-stem-thickness))
3512            (flag-end (if (= angle 0)
3513                          (cons flag-length (* half-stem-thickness dir))
3514                          (polar->rectangular flag-length angle)))
3515            (thickness (* flag-thickness factor))
3516            (thickness-offset (cons 0 (* -1 thickness dir)))
3517            (spacing (* -1 flag-spacing factor dir))
3518            (start (cons (- half-stem-thickness) (* half-stem-thickness dir)))
3519            (points (list start
3520                          flag-end
3521                          (offset-add flag-end thickness-offset)
3522                          (offset-add start thickness-offset)))
3523            (stencil (ly:round-filled-polygon points half-stem-thickness))
3524            ;; Log for 1/8 is 3, so we need to subtract 3
3525            (flag-stencil (buildflags stencil (- log 3) stencil spacing)))
3526       flag-stencil))
3527
3528   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)
3529                                                  (font-name . #f))
3530                                                props)))
3531          (size-factor (magstep font-size))
3532          (blot (ly:output-def-lookup layout 'blot-diameter))
3533          (head-glyph-name
3534           (let ((result (get-glyph-name font
3535                                         (get-glyph-name-candidates
3536                                          (sign dir) log style))))
3537             (if (string-null? result)
3538                 ;; If no glyph name can be found, select default heads.
3539                 ;; Though this usually means an unsupported style has been
3540                 ;; chosen, it also prevents unrelated 'style settings from
3541                 ;; other grobs (e.g., TextSpanner and TimeSignature) leaking
3542                 ;; into markup.
3543                 (get-glyph-name font
3544                                 (get-glyph-name-candidates
3545                                  (sign dir) log 'default))
3546                 result)))
3547          (head-glyph (ly:font-get-glyph font head-glyph-name))
3548          (ancient-flags? (or (eq? style 'mensural) (eq? style 'neomensural)))
3549          (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
3550          (stem-length (* size-factor (max 3 (- log 1))))
3551          ;; With ancient-flags we want a tighter stem
3552          (stem-thickness (* size-factor (if ancient-flags? 0.1 0.13)))
3553          (stemy (* dir stem-length))
3554          (attach-off (cons (interval-index
3555                             (ly:stencil-extent head-glyph X)
3556                             (* (sign dir) (car attach-indices)))
3557                            ;; fixme, this is inconsistent between X & Y.
3558                            (* (sign dir)
3559                               (interval-index
3560                                (ly:stencil-extent head-glyph Y)
3561                                (cdr attach-indices)))))
3562          ;; For a tighter stem (with ancient-flags) the stem-width has to be
3563          ;; adjusted.
3564          (stem-X-corr (if ancient-flags? (* 0.5 dir stem-thickness) 0))
3565          (stem-glyph (and (> log 0)
3566                           (ly:round-filled-box
3567                            (ordered-cons (+ stem-X-corr (car attach-off))
3568                                          (+ stem-X-corr (car attach-off)
3569                                             (* (- (sign dir)) stem-thickness)))
3570                            (cons (min stemy (cdr attach-off))
3571                                  (max stemy (cdr attach-off)))
3572                            (/ stem-thickness 3))))
3573          (dot (ly:font-get-glyph font "dots.dot"))
3574          (dotwid (interval-length (ly:stencil-extent dot X)))
3575          (dots (and (> dot-count 0)
3576                     (apply ly:stencil-add
3577                            (map (lambda (x)
3578                                   (ly:stencil-translate-axis
3579                                    dot (* 2 x dotwid) X))
3580                                 (iota dot-count)))))
3581          ;; Straight-flags. Values taken from /scm/flag-style.scm
3582          (modern-straight-flag (straight-flag-mrkp 0.55 1 -18 1.1 22 1.2 dir))
3583          (old-straight-flag (straight-flag-mrkp 0.55 1 -45 1.2 45 1.4 dir))
3584          (flat-flag (straight-flag-mrkp 0.55 1.0 0 1.0 0 1.0 dir))
3585          ;; Calculate a corrective to avoid a gap between
3586          ;; straight-flags and the stem.
3587          (flag-style-Y-corr (if (or (eq? flag-style 'modern-straight-flag)
3588                                     (eq? flag-style 'old-straight-flag)
3589                                     (eq? flag-style 'flat-flag))
3590                                 (/ blot 10 (* -1 dir))
3591                                 0))
3592          (flaggl (and (> log 2)
3593                       (ly:stencil-translate
3594                        (cond ((eq? flag-style 'modern-straight-flag)
3595                               modern-straight-flag)
3596                              ((eq? flag-style 'old-straight-flag)
3597                               old-straight-flag)
3598                              ((eq? flag-style 'flat-flag)
3599                               flat-flag)
3600                              (else
3601                               (ly:font-get-glyph font
3602                                                  (format #f (if ancient-flags?
3603                                                                 "flags.mensural~a2~a"
3604                                                                 "flags.~a~a")
3605                                                          (if (> dir 0) "u" "d")
3606                                                          log))))
3607                        (cons (+ (car attach-off)
3608                                 ;; For tighter stems (with ancient-flags) the
3609                                 ;; flag has to be adjusted different.
3610                                 (if (and (not ancient-flags?) (< dir 0))
3611                                     stem-thickness
3612                                     0))
3613                              (+ stemy flag-style-Y-corr))))))
3614
3615     ;; If there is a flag on an upstem and the stem is short, move the dots
3616     ;; to avoid the flag.  16th notes get a special case because their flags
3617     ;; hang lower than any other flags.
3618     ;; Not with ancient flags or straight-flags.
3619     (if (and dots (> dir 0) (> log 2)
3620              (or (eq? flag-style 'default) (null? flag-style))
3621              (not ancient-flags?)
3622              (or (< dir 1.15) (and (= log 4) (< dir 1.3))))
3623         (set! dots (ly:stencil-translate-axis dots 0.5 X)))
3624     (if flaggl
3625         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
3626     (if (ly:stencil? stem-glyph)
3627         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
3628         (set! stem-glyph head-glyph))
3629     (if (ly:stencil? dots)
3630         (set! stem-glyph
3631               (ly:stencil-add
3632                (ly:stencil-translate-axis
3633                 dots
3634                 (+ (cdr (ly:stencil-extent head-glyph X)) dotwid)
3635                 X)
3636                stem-glyph)))
3637     stem-glyph))
3638
3639 (define-public log2
3640   (let ((divisor (log 2)))
3641     (lambda (z) (inexact->exact (/ (log z) divisor)))))
3642
3643 (define (parse-simple-duration duration-string)
3644   "Parse the `duration-string', e.g. ''4..'' or ''breve.'',
3645 and return a (log dots) list."
3646   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)")
3647                             duration-string)))
3648     (if (and match (string=? duration-string (match:substring match 0)))
3649         (let ((len (match:substring match 1))
3650               (dots (match:substring match 2)))
3651           (list (cond ((string=? len "breve") -1)
3652                       ((string=? len "longa") -2)
3653                       ((string=? len "maxima") -3)
3654                       (else (log2 (string->number len))))
3655                 (if dots (string-length dots) 0)))
3656         (ly:error (_ "not a valid duration string: ~a") duration-string))))
3657
3658 (define-markup-command (note layout props duration dir)
3659   (string? number?)
3660   #:category music
3661   #:properties (note-by-number-markup)
3662   "
3663 @cindex notes within text by string
3664
3665 This produces a note with a stem pointing in @var{dir} direction, with
3666 the @var{duration} for the note head type and augmentation dots.  For
3667 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
3668 a shortened down stem.
3669
3670 @lilypond[verbatim,quote]
3671 \\markup {
3672   \\override #'(style . cross) {
3673     \\note #\"4..\" #UP
3674   }
3675   \\hspace #2
3676   \\note #\"breve\" #0
3677 }
3678 @end lilypond"
3679   (let ((parsed (parse-simple-duration duration)))
3680     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
3681
3682 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3683 ;; the rest command.
3684 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3685
3686 (define-markup-command (rest-by-number layout props log dot-count)
3687   (number? number?)
3688   #:category music
3689   #:properties ((font-size 0)
3690                 (style '())
3691                 (multi-measure-rest #f))
3692   "
3693 @cindex rests or multi-measure-rests within text by log and dot-count
3694
3695 A rest or multi-measure-rest symbol.
3696
3697 @lilypond[verbatim,quote]
3698 \\markup {
3699   \\rest-by-number #3 #2
3700   \\hspace #2
3701   \\rest-by-number #0 #1
3702   \\hspace #2
3703   \\override #'(multi-measure-rest . #t)
3704   \\rest-by-number #0 #0
3705 }
3706 @end lilypond"
3707
3708   (define (get-glyph-name-candidates log style)
3709     (let* (;; Choose the style-string to be added.
3710            ;; If no glyph exists, select others for the specified styles
3711            ;; otherwise defaulting.
3712            (style-strg
3713             (cond (
3714                    ;; 'baroque needs to be special-cased, otherwise
3715                    ;; `select-head-glyph´ would catch neomensural-glyphs for
3716                    ;; this style, if (< log 0).
3717                    (eq? style 'baroque)
3718                    (string-append (number->string log) ""))
3719                   ((eq? style 'petrucci)
3720                    (string-append (number->string log) "mensural"))
3721                   ;; In other cases `select-head-glyph´ from output-lib.scm
3722                   ;; works for rest-glyphs, too.
3723                   ((and (symbol? style) (not (eq? style 'default)))
3724                    (select-head-glyph style log))
3725                   (else log)))
3726            ;; Choose ledgered glyphs for whole and half rest.
3727            ;; Except for the specified styles, logs and MultiMeasureRests.
3728            (ledger-style-rests
3729             (if (and (or (list? style)
3730                          (not (member style
3731                                       '(neomensural mensural petrucci))))
3732                      (not multi-measure-rest)
3733                      (or (= log 0) (= log 1)))
3734                 "o"
3735                 "")))
3736       (format #f "rests.~a~a" style-strg ledger-style-rests)))
3737
3738   (define (get-glyph-name font cands)
3739     (if (ly:stencil-empty? (ly:font-get-glyph font cands))
3740         ""
3741         cands))
3742
3743   (let* ((font
3744           (ly:paper-get-font layout
3745                              (cons '((font-encoding . fetaMusic)
3746                                      (font-name . #f))
3747                                    props)))
3748          (rest-glyph-name
3749           (let ((result
3750                  (get-glyph-name font
3751                                  (get-glyph-name-candidates log style))))
3752             (if (string-null? result)
3753                 ;; If no glyph name can be found, select default rests.  Though
3754                 ;; this usually means an unsupported style has been chosen, it
3755                 ;; also prevents unrelated 'style settings from other grobs
3756                 ;; (e.g., TextSpanner and TimeSignature) leaking into markup.
3757                 (get-glyph-name font (get-glyph-name-candidates log 'default))
3758                 result)))
3759          (rest-glyph (ly:font-get-glyph font rest-glyph-name))
3760          (dot (ly:font-get-glyph font "dots.dot"))
3761          (dot-width (interval-length (ly:stencil-extent dot X)))
3762          (dots (and (> dot-count 0)
3763                     (apply ly:stencil-add
3764                            (map (lambda (x)
3765                                   (ly:stencil-translate-axis
3766                                    dot (* 2 x dot-width) X))
3767                                 (iota dot-count))))))
3768
3769     ;; Apart from mensural-, neomensural- and petrucci-style ledgered
3770     ;; glyphs are taken for whole and half rests.
3771     ;; If they are dotted, move the dots in X-direction to avoid collision.
3772     (if (and dots
3773              (< log 2)
3774              (>= log 0)
3775              (not (member style '(neomensural mensural petrucci))))
3776         (set! dots (ly:stencil-translate-axis dots dot-width X)))
3777
3778     ;; Add dots to the rest-glyph.
3779     ;;
3780     ;; Not sure how to vertical align dots.
3781     ;; For now the dots are centered for half, whole or longer rests.
3782     ;; Otherwise placed near the top of the rest.
3783     ;;
3784     ;; Dots for rests with (< log 0) dots are allowed, but not
3785     ;; if multi-measure-rest is set #t.
3786     (if (and (not multi-measure-rest) dots)
3787         (set! rest-glyph
3788               (ly:stencil-add
3789                (ly:stencil-translate
3790                 dots
3791                 (cons
3792                  (+ (cdr (ly:stencil-extent rest-glyph X)) dot-width)
3793                  (if (< log 2)
3794                      (interval-center (ly:stencil-extent rest-glyph Y))
3795                      (- (interval-end (ly:stencil-extent rest-glyph Y))
3796                         (/ (* 2 dot-width) 3)))))
3797                rest-glyph)))
3798     rest-glyph))
3799
3800 (define-markup-command (rest layout props duration)
3801   (string?)
3802   #:category music
3803   #:properties ((style '())
3804                 (multi-measure-rest #f)
3805                 (multi-measure-rest-number #t)
3806                 (word-space 0.6))
3807   "
3808 @cindex rests or multi-measure-rests within text by string
3809
3810 This produces a rest, with the @var{duration} for the rest type and
3811 augmentation dots.
3812 @code{\"breve\"}, @code{\"longa\"} and @code{\"maxima\"} are valid
3813 input-strings.
3814
3815 Printing MultiMeasureRests could be enabled with
3816 @code{\\override #'(multi-measure-rest . #t)}
3817 If MultiMeasureRests are taken, the MultiMeasureRestNumber is printed above.
3818 This is enabled for all styles using default-glyphs.
3819 Could be disabled with @code{\\override #'(multi-measure-rest-number . #f)}
3820
3821 @lilypond[verbatim,quote]
3822 \\markup {
3823   \\rest #\"4..\"
3824   \\hspace #2
3825   \\rest #\"breve\"
3826   \\hspace #2
3827   \\override #'(multi-measure-rest . #t)
3828   {
3829   \\rest #\"7\"
3830   \\hspace #2
3831   \\override #'(multi-measure-rest-number . #f)
3832   \\rest #\"7\"
3833   }
3834 }
3835 @end lilypond"
3836   ;; Get the number of mmr-glyphs.
3837   ;; Store them in a list.
3838   ;; example: (mmr-numbers 25) -> '(3 0 0 1)
3839   (define (mmr-numbers nmbr)
3840     (let* ((8-bar-glyph (floor (/ nmbr 8)))
3841            (8-remainder (remainder nmbr 8))
3842            (4-bar-glyph (floor (/ 8-remainder 4)))
3843            (4-remainder (remainder nmbr 4))
3844            (2-bar-glyph (floor (/ 4-remainder 2)))
3845            (2-remainder (remainder 4-remainder 2))
3846            (1-bar-glyph (floor (/ 2-remainder 1))))
3847       (list 8-bar-glyph 4-bar-glyph 2-bar-glyph 1-bar-glyph)))
3848
3849   ;; Get the correct mmr-glyphs.
3850   ;; Store them in a list.
3851   ;; example:
3852   ;; (get-mmr-glyphs '(1 0 1 0) '("rests.M3" "rests.M2" "rests.M1" "rests.0"))
3853   ;; -> ("rests.M3" "rests.M1")
3854   (define (get-mmr-glyphs lst1 lst2)
3855     (define (helper l1 l2 l3)
3856       (if (null? l1)
3857           (reverse l3)
3858           (helper (cdr l1)
3859                   (cdr l2)
3860                   (append (make-list (car l1) (car l2)) l3))))
3861     (helper lst1 lst2 '()))
3862
3863   ;; If duration is not valid, print a warning and return empty-stencil
3864   (if (or (and (not (integer? (car (parse-simple-duration duration))))
3865                (not multi-measure-rest))
3866           (and (= (string-length (car (string-split duration #\. ))) 1)
3867                (= (string->number (car (string-split duration #\. ))) 0)))
3868       (begin
3869         (ly:warning (_ "not a valid duration string: ~a - ignoring") duration)
3870         empty-stencil)
3871       (let* (
3872              ;; For simple rests:
3873              ;; Get a (log dots) list.
3874              (parsed (parse-simple-duration duration))
3875              ;; Create the rest-stencil
3876              (stil
3877               (rest-by-number-markup layout props (car parsed) (cadr parsed)))
3878              ;; For MultiMeasureRests:
3879              ;; Get the duration-part of duration
3880              (dur-part-string (car (string-split duration #\. )))
3881              ;; Get the duration of MMR:
3882              ;; If not a number (eg. "maxima") calculate it.
3883              (mmr-duration
3884               (or (string->number dur-part-string) (expt 2 (abs (car parsed)))))
3885              ;; Get a list of the correct number of each mmr-glyph.
3886              (count-mmr-glyphs-list (mmr-numbers mmr-duration))
3887              ;; Create a list of mmr-stencils,
3888              ;; translating the glyph for a whole rest.
3889              (mmr-stils-list
3890               (map
3891                (lambda (x)
3892                  (let ((single-mmr-stil
3893                         (rest-by-number-markup layout props (* -1 x) 0)))
3894                    (if (= x 0)
3895                        (ly:stencil-translate-axis
3896                         single-mmr-stil
3897                         ;; Ugh, hard-coded, why 1?
3898                         1
3899                         Y)
3900                        single-mmr-stil)))
3901                (get-mmr-glyphs count-mmr-glyphs-list (reverse (iota 4)))))
3902              ;; Adjust the space between the mmr-glyphs,
3903              ;; if not default-glyphs are used.
3904              (word-space (if (member style
3905                                      '(neomensural mensural petrucci))
3906                              (/ (* word-space 2) 3)
3907                              word-space))
3908              ;; Create the final mmr-stencil
3909              ;; via `stack-stencil-line´ from /scm/markup.scm
3910              (mmr-stil (stack-stencil-line word-space mmr-stils-list)))
3911
3912         ;; Print the number above a multi-measure-rest
3913         ;; Depends on duration, style and multi-measure-rest-number set #t
3914         (if (and multi-measure-rest
3915                  multi-measure-rest-number
3916                  (> mmr-duration 1)
3917                  (not (member style '(neomensural mensural petrucci))))
3918             (let* ((mmr-stil-x-center
3919                     (interval-center (ly:stencil-extent mmr-stil X)))
3920                    (duration-markup
3921                     (markup
3922                      #:fontsize -2
3923                      #:override '(font-encoding . fetaText)
3924                      (number->string mmr-duration)))
3925                    (mmr-number-stil
3926                     (interpret-markup layout props duration-markup))
3927                    (mmr-number-stil-x-center
3928                     (interval-center (ly:stencil-extent mmr-number-stil X))))
3929
3930               (set! mmr-stil (ly:stencil-combine-at-edge
3931                               mmr-stil
3932                               Y UP
3933                               (ly:stencil-translate-axis
3934                                mmr-number-stil
3935                                (- mmr-stil-x-center mmr-number-stil-x-center)
3936                                X)
3937                               ;; Ugh, hardcoded
3938                               0.8))))
3939         (if multi-measure-rest
3940             mmr-stil
3941             stil))))
3942
3943 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3944 ;; fermata markup
3945 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3946
3947 (define-markup-command (fermata layout props) ()
3948   #:category music
3949   #:properties ((direction UP))
3950   "Create a fermata glyph.  When @var{direction} is @code{DOWN}, use
3951 an inverted glyph.  Note that within music, one would usually use the
3952 @code{\\fermata} articulation instead of a markup.
3953
3954 @lilypond[verbatim,quote]
3955  { c''1^\\markup \\fermata d''1_\\markup \\fermata }
3956
3957 \\markup { \\fermata \\override #`(direction . ,DOWN) \\fermata }
3958 @end lilypond
3959 "
3960   (interpret-markup layout props
3961                     (if (eqv? direction DOWN)
3962                         (markup #:musicglyph "scripts.dfermata")
3963                         (markup #:musicglyph "scripts.ufermata"))))
3964
3965 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3966 ;; translating.
3967 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3968
3969 (define-markup-command (lower layout props amount arg)
3970   (number? markup?)
3971   #:category align
3972   "
3973 @cindex lowering text
3974
3975 Lower @var{arg} by the distance @var{amount}.
3976 A negative @var{amount} indicates raising; see also @code{\\raise}.
3977
3978 @lilypond[verbatim,quote]
3979 \\markup {
3980   one
3981   \\lower #3
3982   two
3983   three
3984 }
3985 @end lilypond"
3986   (ly:stencil-translate-axis (interpret-markup layout props arg)
3987                              (- amount) Y))
3988
3989 (define-markup-command (translate-scaled layout props offset arg)
3990   (number-pair? markup?)
3991   #:category align
3992   #:properties ((font-size 0))
3993   "
3994 @cindex translating text
3995 @cindex scaling text
3996
3997 Translate @var{arg} by @var{offset}, scaling the offset by the
3998 @code{font-size}.
3999
4000 @lilypond[verbatim,quote]
4001 \\markup {
4002   \\fontsize #5 {
4003     * \\translate #'(2 . 3) translate
4004     \\hspace #2
4005     * \\translate-scaled #'(2 . 3) translate-scaled
4006   }
4007 }
4008 @end lilypond"
4009   (let* ((factor (magstep font-size))
4010          (scaled (cons (* factor (car offset))
4011                        (* factor (cdr offset)))))
4012     (ly:stencil-translate (interpret-markup layout props arg)
4013                           scaled)))
4014
4015 (define-markup-command (raise layout props amount arg)
4016   (number? markup?)
4017   #:category align
4018   "
4019 @cindex raising text
4020
4021 Raise @var{arg} by the distance @var{amount}.
4022 A negative @var{amount} indicates lowering, see also @code{\\lower}.
4023
4024 The argument to @code{\\raise} is the vertical displacement amount,
4025 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
4026 raise objects in relation to their surrounding markups.
4027
4028 If the text object itself is positioned above or below the staff, then
4029 @code{\\raise} cannot be used to move it, since the mechanism that
4030 positions it next to the staff cancels any shift made with
4031 @code{\\raise}.  For vertical positioning, use the @code{padding}
4032 and/or @code{extra-offset} properties.
4033
4034 @lilypond[verbatim,quote]
4035 \\markup {
4036   C
4037   \\small
4038   \\bold
4039   \\raise #1.0
4040   9/7+
4041 }
4042 @end lilypond"
4043   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
4044
4045 (define-markup-command (fraction layout props arg1 arg2)
4046   (markup? markup?)
4047   #:category other
4048   #:properties ((font-size 0))
4049   "
4050 @cindex creating text fractions
4051
4052 Make a fraction of two markups.
4053 @lilypond[verbatim,quote]
4054 \\markup {
4055   π ≈
4056   \\fraction 355 113
4057 }
4058 @end lilypond"
4059   (let* ((m1 (interpret-markup layout props arg1))
4060          (m2 (interpret-markup layout props arg2))
4061          (factor (magstep font-size))
4062          (boxdimen (cons (* factor -0.05) (* factor 0.05)))
4063          (padding (* factor 0.2))
4064          (baseline (* factor 0.6))
4065          (offset (* factor 0.75)))
4066     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
4067     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
4068     (let* ((x1 (ly:stencil-extent m1 X))
4069            (x2 (ly:stencil-extent m2 X))
4070            (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0))
4071            ;; should stack mols separately, to maintain LINE on baseline
4072            (stack (stack-lines DOWN padding baseline (list m1 line m2))))
4073       (set! stack
4074             (ly:stencil-aligned-to stack Y CENTER))
4075       (set! stack
4076             (ly:stencil-aligned-to stack X LEFT))
4077       ;; should have EX dimension
4078       ;; empirical anyway
4079       (ly:stencil-translate-axis stack offset Y))))
4080
4081 (define-markup-command (normal-size-super layout props arg)
4082   (markup?)
4083   #:category font
4084   #:properties ((font-size 0))
4085   "
4086 @cindex setting superscript in standard font size
4087
4088 Set @var{arg} in superscript with a normal font size.
4089
4090 @lilypond[verbatim,quote]
4091 \\markup {
4092   default
4093   \\normal-size-super {
4094     superscript in standard size
4095   }
4096 }
4097 @end lilypond"
4098   (ly:stencil-translate-axis
4099    (interpret-markup layout props arg)
4100    (* 1.0 (magstep font-size)) Y))
4101
4102 (define-markup-command (super layout props arg)
4103   (markup?)
4104   #:category font
4105   #:properties ((font-size 0))
4106   "
4107 @cindex superscript text
4108
4109 Set @var{arg} in superscript.
4110
4111 @lilypond[verbatim,quote]
4112 \\markup {
4113   E =
4114   \\concat {
4115     mc
4116     \\super
4117     2
4118   }
4119 }
4120 @end lilypond"
4121   (ly:stencil-translate-axis
4122    (interpret-markup
4123     layout
4124     (cons `((font-size . ,(- font-size 3))) props)
4125     arg)
4126    (* 1.0 (magstep font-size)) ; original font-size
4127    Y))
4128
4129 (define-markup-command (translate layout props offset arg)
4130   (number-pair? markup?)
4131   #:category align
4132   "
4133 @cindex translating text
4134
4135 Translate @var{arg} relative to its surroundings.  @var{offset}
4136 is a pair of numbers representing the displacement in the X and Y axis.
4137
4138 @lilypond[verbatim,quote]
4139 \\markup {
4140   *
4141   \\translate #'(2 . 3)
4142   \\line { translated two spaces right, three up }
4143 }
4144 @end lilypond"
4145   (ly:stencil-translate (interpret-markup layout props arg)
4146                         offset))
4147
4148 (define-markup-command (sub layout props arg)
4149   (markup?)
4150   #:category font
4151   #:properties ((font-size 0))
4152   "
4153 @cindex subscript text
4154
4155 Set @var{arg} in subscript.
4156
4157 @lilypond[verbatim,quote]
4158 \\markup {
4159   \\concat {
4160     H
4161     \\sub {
4162       2
4163     }
4164     O
4165   }
4166 }
4167 @end lilypond"
4168   (ly:stencil-translate-axis
4169    (interpret-markup
4170     layout
4171     (cons `((font-size . ,(- font-size 3))) props)
4172     arg)
4173    (* -0.75 (magstep font-size)) ; original font-size
4174    Y))
4175
4176 (define-markup-command (normal-size-sub layout props arg)
4177   (markup?)
4178   #:category font
4179   #:properties ((font-size 0))
4180   "
4181 @cindex setting subscript in standard font size
4182
4183 Set @var{arg} in subscript with a normal font size.
4184
4185 @lilypond[verbatim,quote]
4186 \\markup {
4187   default
4188   \\normal-size-sub {
4189     subscript in standard size
4190   }
4191 }
4192 @end lilypond"
4193   (ly:stencil-translate-axis
4194    (interpret-markup layout props arg)
4195    (* -0.75 (magstep font-size))
4196    Y))
4197
4198 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4199 ;; brackets.
4200 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4201
4202 (define-markup-command (hbracket layout props arg)
4203   (markup?)
4204   #:category graphic
4205   "
4206 @cindex placing horizontal brackets around text
4207
4208 Draw horizontal brackets around @var{arg}.
4209
4210 @lilypond[verbatim,quote]
4211 \\markup {
4212   \\hbracket {
4213     \\line {
4214       one two three
4215     }
4216   }
4217 }
4218 @end lilypond"
4219   (let ((th 0.1) ;; todo: take from GROB.
4220         (m (interpret-markup layout props arg)))
4221     (bracketify-stencil m X th (* 2.5 th) th)))
4222
4223 (define-markup-command (bracket layout props arg)
4224   (markup?)
4225   #:category graphic
4226   "
4227 @cindex placing vertical brackets around text
4228
4229 Draw vertical brackets around @var{arg}.
4230
4231 @lilypond[verbatim,quote]
4232 \\markup {
4233   \\bracket {
4234     \\note #\"2.\" #UP
4235   }
4236 }
4237 @end lilypond"
4238   (let ((th 0.1) ;; todo: take from GROB.
4239         (m (interpret-markup layout props arg)))
4240     (bracketify-stencil m Y th (* 2.5 th) th)))
4241
4242 (define-markup-command (parenthesize layout props arg)
4243   (markup?)
4244   #:category graphic
4245   #:properties ((angularity 0)
4246                 (padding)
4247                 (size 1)
4248                 (thickness 1)
4249                 (width 0.25))
4250   "
4251 @cindex placing parentheses around text
4252
4253 Draw parentheses around @var{arg}.  This is useful for parenthesizing
4254 a column containing several lines of text.
4255
4256 @lilypond[verbatim,quote]
4257 \\markup {
4258   \\line {
4259     \\parenthesize {
4260       \\column {
4261         foo
4262         bar
4263       }
4264     }
4265     \\override #'(angularity . 2) {
4266       \\parenthesize {
4267         \\column {
4268           bah
4269           baz
4270         }
4271       }
4272     }
4273   }
4274 }
4275 @end lilypond"
4276   (let* ((m (interpret-markup layout props arg))
4277          (scaled-width (* size width))
4278          (scaled-thickness
4279           (* (chain-assoc-get 'line-thickness props 0.1)
4280              thickness))
4281          (half-thickness
4282           (min (* size 0.5 scaled-thickness)
4283                (* (/ 4 3.0) scaled-width)))
4284          (padding (chain-assoc-get 'padding props half-thickness)))
4285     (parenthesize-stencil
4286      m half-thickness scaled-width angularity padding)))
4287
4288
4289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4290 ;; Delayed markup evaluation
4291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4292
4293 (define-markup-command (page-ref layout props label gauge default)
4294   (symbol? markup? markup?)
4295   #:category other
4296   "
4297 @cindex referencing page numbers in text
4298
4299 Reference to a page number.  @var{label} is the label set on the referenced
4300 page (using the @code{\\label} command), @var{gauge} a markup used to estimate
4301 the maximum width of the page number, and @var{default} the value to display
4302 when @var{label} is not found.
4303
4304 (If the current book or bookpart is set to use roman numerals for page numbers,
4305 the reference will be formatted accordingly -- in which case the @var{gauge}'s
4306 width may require additional tweaking.)"
4307   (let* ((gauge-stencil (interpret-markup layout props gauge))
4308          (x-ext (ly:stencil-extent gauge-stencil X))
4309          (y-ext (ly:stencil-extent gauge-stencil Y)))
4310    (ly:stencil-add
4311     (make-transparent-box-stencil x-ext y-ext))
4312     (ly:make-stencil
4313      `(delay-stencil-evaluation
4314        ,(delay (ly:stencil-expr
4315                 (let* ((table (ly:output-def-lookup layout 'label-page-table))
4316                        (page-number (if (list? table)
4317                                         (assoc-get label table)
4318                                         #f))
4319                        (number-type (ly:output-def-lookup layout 'page-number-type))
4320                        (page-markup (if page-number
4321                                         (number-format number-type page-number)
4322                                         default))
4323                        (page-stencil (interpret-markup layout props page-markup))
4324                        (gap (- (interval-length x-ext)
4325                                (interval-length (ly:stencil-extent page-stencil X)))))
4326                   (interpret-markup layout props
4327                                     (markup #:hspace gap page-markup))))))
4328      x-ext
4329      y-ext)))
4330
4331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4332 ;; scaling
4333 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4334
4335 (define-markup-command (scale layout props factor-pair arg)
4336   (number-pair? markup?)
4337   #:category graphic
4338   "
4339 @cindex scaling markup
4340 @cindex mirroring markup
4341
4342 Scale @var{arg}.  @var{factor-pair} is a pair of numbers
4343 representing the scaling-factor in the X and Y axes.
4344 Negative values may be used to produce mirror images.
4345
4346 @lilypond[verbatim,quote]
4347 \\markup {
4348   \\line {
4349     \\scale #'(2 . 1)
4350     stretched
4351     \\scale #'(1 . -1)
4352     mirrored
4353   }
4354 }
4355 @end lilypond"
4356   (let ((stil (interpret-markup layout props arg))
4357         (sx (car factor-pair))
4358         (sy (cdr factor-pair)))
4359     (ly:stencil-scale stil sx sy)))
4360
4361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4362 ;; Repeating
4363 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4364
4365 (define-markup-command (pattern layout props count axis space pattern)
4366   (integer? integer? number? markup?)
4367   #:category other
4368   "
4369 Prints @var{count} times a @var{pattern} markup.
4370 Patterns are spaced apart by @var{space}.
4371 Patterns are distributed on @var{axis}.
4372
4373 @lilypond[verbatim, quote]
4374 \\markup \\column {
4375   \"Horizontally repeated :\"
4376   \\pattern #7 #X #2 \\flat
4377   \\null
4378   \"Vertically repeated :\"
4379   \\pattern #3 #Y #0.5 \\flat
4380 }
4381 @end lilypond"
4382   (let ((pattern-width (interval-length
4383                         (ly:stencil-extent (interpret-markup layout props pattern) X)))
4384         (new-props (prepend-alist-chain 'word-space 0 (prepend-alist-chain 'baseline-skip 0 props))))
4385     (let loop ((i (1- count)) (patterns (markup)))
4386       (if (zero? i)
4387           (interpret-markup
4388            layout
4389            new-props
4390            (if (= axis X)
4391                (markup patterns pattern)
4392                (markup #:column (patterns pattern))))
4393           (loop (1- i)
4394                 (if (= axis X)
4395                     (markup patterns pattern #:hspace space)
4396                     (markup #:column (patterns pattern #:vspace space))))))))
4397
4398 (define-markup-command (fill-with-pattern layout props space dir pattern left right)
4399   (number? ly:dir? markup? markup? markup?)
4400   #:category align
4401   #:properties ((word-space)
4402                 (line-width))
4403   "
4404 Put @var{left} and @var{right} in a horizontal line of width @code{line-width}
4405 with a line of markups @var{pattern} in between.
4406 Patterns are spaced apart by @var{space}.
4407 Patterns are aligned to the @var{dir} markup.
4408
4409 @lilypond[verbatim, quote]
4410 \\markup \\column {
4411   \"right-aligned :\"
4412   \\fill-with-pattern #1 #RIGHT . first right
4413   \\fill-with-pattern #1 #RIGHT . second right
4414   \\null
4415   \"center-aligned :\"
4416   \\fill-with-pattern #1.5 #CENTER - left right
4417   \\null
4418   \"left-aligned :\"
4419   \\override #'(line-width . 50)
4420   \\fill-with-pattern #2 #LEFT : left first
4421   \\override #'(line-width . 50)
4422   \\fill-with-pattern #2 #LEFT : left second
4423 }
4424 @end lilypond"
4425   (let* ((pattern-x-extent (ly:stencil-extent (interpret-markup layout props pattern) X))
4426          (pattern-width (interval-length pattern-x-extent))
4427          (left-width (interval-length (ly:stencil-extent (interpret-markup layout props left) X)))
4428          (right-width (interval-length (ly:stencil-extent (interpret-markup layout props right) X)))
4429          (middle-width (max 0 (- line-width (+ (+ left-width right-width) (* word-space 2)))))
4430          (period (+ space pattern-width))
4431          (count (truncate (/ (- middle-width pattern-width) period)))
4432          (x-offset (+ (* (- (- middle-width (* count period)) pattern-width) (/ (1+ dir) 2)) (abs (car pattern-x-extent)))))
4433     (interpret-markup layout props
4434                       (markup left
4435                               #:with-dimensions (cons 0 middle-width) '(0 . 0)
4436                               #:translate (cons x-offset 0)
4437                               #:pattern (1+ count) X space pattern
4438                               right))))
4439
4440 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4441 ;; Replacements
4442 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4443
4444 (define-markup-command (replace layout props replacements arg)
4445   (list? markup?)
4446   #:category font
4447   "
4448 Used to automatically replace a string by another in the markup @var{arg}.
4449 Each pair of the alist @var{replacements} specifies what should be replaced.
4450 The @code{key} is the string to be replaced by the @code{value} string.
4451
4452 @lilypond[verbatim, quote]
4453 \\markup \\replace #'((\"thx\" . \"Thanks!\")) thx
4454 @end lilypond"
4455   (interpret-markup
4456    layout
4457    (internal-add-text-replacements
4458     props
4459     replacements)
4460    (markup arg)))
4461
4462 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4463 ;; Markup list commands
4464 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4465
4466 (define-public (space-lines baseline stils)
4467   (let space-stil ((stils stils)
4468                    (result (list)))
4469     (if (null? stils)
4470         (reverse! result)
4471         (let* ((stil (car stils))
4472                (dy-top (max (- (/ baseline 1.5)
4473                                (interval-bound (ly:stencil-extent stil Y) UP))
4474                             0.0))
4475                (dy-bottom (max (+ (/ baseline 3.0)
4476                                   (interval-bound (ly:stencil-extent stil Y) DOWN))
4477                                0.0))
4478                (new-stil (ly:make-stencil
4479                           (ly:stencil-expr stil)
4480                           (ly:stencil-extent stil X)
4481                           (cons (- (interval-bound (ly:stencil-extent stil Y) DOWN)
4482                                    dy-bottom)
4483                                 (+ (interval-bound (ly:stencil-extent stil Y) UP)
4484                                    dy-top)))))
4485           (space-stil (cdr stils) (cons new-stil result))))))
4486
4487 (define-markup-list-command (justified-lines layout props args)
4488   (markup-list?)
4489   #:properties ((baseline-skip)
4490                 wordwrap-internal-markup-list)
4491   "
4492 @cindex justifying lines of text
4493
4494 Like @code{\\justify}, but return a list of lines instead of a single markup.
4495 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width;
4496 @var{X}@tie{}is the number of staff spaces."
4497   (space-lines baseline-skip
4498                (interpret-markup-list layout props
4499                                       (make-wordwrap-internal-markup-list #t args))))
4500
4501 (define-markup-list-command (wordwrap-lines layout props args)
4502   (markup-list?)
4503   #:properties ((baseline-skip)
4504                 wordwrap-internal-markup-list)
4505   "Like @code{\\wordwrap}, but return a list of lines instead of a single markup.
4506 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width,
4507 where @var{X} is the number of staff spaces."
4508   (space-lines baseline-skip
4509                (interpret-markup-list layout props
4510                                       (make-wordwrap-internal-markup-list #f args))))
4511
4512 (define-markup-list-command (column-lines layout props args)
4513   (markup-list?)
4514   #:properties ((baseline-skip))
4515   "Like @code{\\column}, but return a list of lines instead of a single markup.
4516 @code{baseline-skip} determines the space between each markup in @var{args}."
4517   (space-lines baseline-skip
4518                (interpret-markup-list layout props args)))
4519
4520 (define-markup-list-command (override-lines layout props new-prop args)
4521   (pair? markup-list?)
4522   "Like @code{\\override}, for markup lists."
4523   (interpret-markup-list layout (cons (list new-prop) props) args))
4524
4525 (define-markup-list-command (map-markup-commands layout props compose args)
4526   (procedure? markup-list?)
4527   "This applies the function @var{compose} to every markup in
4528 @var{args} (including elements of markup list command calls) in order
4529 to produce a new markup list.  Since the return value from a markup
4530 list command call is not a markup list but rather a list of stencils,
4531 this requires passing those stencils off as the results of individual
4532 markup calls.  That way, the results should work out as long as no
4533 markups rely on side effects."
4534   (let ((key (make-symbol "key")))
4535     (catch
4536      key
4537      (lambda ()
4538        ;; if `compose' does not actually interpret its markup
4539        ;; argument, we still need to return a list of stencils,
4540        ;; created from the single returned stencil
4541        (list
4542         (interpret-markup layout props
4543                           (compose
4544                            (make-on-the-fly-markup
4545                             (lambda (layout props m)
4546                               ;; here all effects of `compose' on the
4547                               ;; properties should be visible, so we
4548                               ;; call interpret-markup-list at this
4549                               ;; point of time and harvest its
4550                               ;; stencils
4551                               (throw key
4552                                      (interpret-markup-list
4553                                       layout props args)))
4554                             (make-null-markup))))))
4555      (lambda (key stencils)
4556        (map
4557         (lambda (sten)
4558           (interpret-markup layout props
4559                             (compose (make-stencil-markup sten))))
4560         stencils)))))