]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
Add independent control of thickness and offset for underline markup
[lilypond.git] / scm / define-markup-commands.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; Copyright (C) 2000--2010  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. There are restrictions on the
49 ;;;     possible arguments for a markup command.
50 ;;;     First, arguments are distinguished according to their type:
51 ;;;       1) a markup (or a string), corresponding to type predicate `markup?'
52 ;;;       2) a list of markups, corresponding to type predicate `markup-list?'
53 ;;;       3) any scheme object, corresponding to type predicates such as
54 ;;;       `list?', 'number?', 'boolean?', etc.
55 ;;;     The supported arrangements of arguments, according to their type, are:
56 ;;;       - no argument
57 ;;;       - markup
58 ;;;       - scheme
59 ;;;       - markup, markup
60 ;;;       - markup-list
61 ;;;       - scheme, scheme
62 ;;;       - scheme, markup
63 ;;;       - scheme, scheme, markup
64 ;;;       - scheme, scheme, markup, markup
65 ;;;       - scheme, markup, markup
66 ;;;       - scheme, scheme, scheme
67 ;;;     This combinations are hard-coded in the lexer and in the parser
68 ;;;     (lily/lexer.ll and lily/parser.yy)
69 ;;;
70 ;;;   args-signature
71 ;;;     the arguments signature, i.e. a list of type predicates which
72 ;;;     are used to type check the arguments, and also to define the general
73 ;;;     argument types (markup, markup-list, scheme) that the command is
74 ;;;     expecting.
75 ;;;     For instance, if a command expects a number, then a markup, the
76 ;;;     signature would be: (number? markup?)
77 ;;;
78 ;;;   category
79 ;;;     for documentation purpose, builtin markup commands are grouped by
80 ;;;     category. This can be any symbol. When documentation is generated,
81 ;;;     the symbol is converted to a capitalized string, where hyphens are
82 ;;;     replaced by spaces.
83 ;;;
84 ;;;   property-bindings
85 ;;;     this is used both for documentation generation, and to ease
86 ;;;     programming the command itself. It is list of
87 ;;;        (property-name default-value)
88 ;;;     or (property-name)
89 ;;;     elements. Each property is looked-up in the `props' argument, and
90 ;;;     the symbol naming the property is bound to its value.
91 ;;;     When the property is not found in `props', then the symbol is bound
92 ;;;     to the given default value. When no default value is given, #f is
93 ;;;     used instead.
94 ;;;     Thus, using the following property bindings:
95 ;;;       ((thickness 0.1)
96 ;;;        (font-size 0))
97 ;;;     is equivalent to writing:
98 ;;;       (let ((thickness (chain-assoc-get 'thickness props 0.1))
99 ;;;             (font-size (chain-assoc-get 'font-size props 0)))
100 ;;;         ..body..)
101 ;;;     When a command `B' internally calls an other command `A', it may
102 ;;;     desirable to see in `B' documentation all the properties and
103 ;;;     default values used by `A'. In that case, add `A-markup' to the
104 ;;;     property-bindings of B. (This is used when generating
105 ;;;     documentation, but won't create bindings.)
106 ;;;
107 ;;;   documentation-string
108 ;;;     the command documentation string (used to generate manuals)
109 ;;;
110 ;;;   body
111 ;;;     the command body. The function is supposed to return a stencil.
112 ;;;
113 ;;; Each markup command definition shall have a documentation string
114 ;;; with description, syntax and example.
115
116 (use-modules (ice-9 regex))
117
118 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119 ;; utility functions
120 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
121
122 (define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1)))
123 (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
124
125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
126 ;; geometric shapes
127 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
128
129 (define-markup-command (draw-line layout props dest)
130   (number-pair?)
131   #:category graphic
132   #:properties ((thickness 1))
133   "
134 @cindex drawing lines within text
135
136 A simple line.
137 @lilypond[verbatim,quote]
138 \\markup {
139   \\draw-line #'(4 . 4)
140   \\override #'(thickness . 5)
141   \\draw-line #'(-3 . 0)
142 }
143 @end lilypond"
144   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
145                thickness))
146         (x (car dest))
147         (y (cdr dest)))
148     (make-line-stencil th 0 0 x y)))
149
150 (define-markup-command (draw-circle layout props radius thickness filled)
151   (number? number? boolean?)
152   #:category graphic
153   "
154 @cindex drawing circles within text
155
156 A circle of radius @var{radius} and thickness @var{thickness},
157 optionally filled.
158
159 @lilypond[verbatim,quote]
160 \\markup {
161   \\draw-circle #2 #0.5 ##f
162   \\hspace #2
163   \\draw-circle #2 #0 ##t
164 }
165 @end lilypond"
166   (make-circle-stencil radius thickness filled))
167
168 (define-markup-command (triangle layout props filled)
169   (boolean?)
170   #:category graphic
171   #:properties ((thickness 0.1)
172                 (font-size 0)
173                 (baseline-skip 2))
174   "
175 @cindex drawing triangles within text
176
177 A triangle, either filled or empty.
178
179 @lilypond[verbatim,quote]
180 \\markup {
181   \\triangle ##t
182   \\hspace #2
183   \\triangle ##f
184 }
185 @end lilypond"
186   (let ((ex (* (magstep font-size) 0.8 baseline-skip)))
187     (ly:make-stencil
188      `(polygon '(0.0 0.0
189                      ,ex 0.0
190                      ,(* 0.5 ex)
191                      ,(* 0.86 ex))
192            ,thickness
193            ,filled)
194      (cons 0 ex)
195      (cons 0 (* .86 ex)))))
196
197 (define-markup-command (circle layout props arg)
198   (markup?)
199   #:category graphic
200   #:properties ((thickness 1)
201                 (font-size 0)
202                 (circle-padding 0.2))
203   "
204 @cindex circling text
205
206 Draw a circle around @var{arg}.  Use @code{thickness},
207 @code{circle-padding} and @code{font-size} properties to determine line
208 thickness and padding around the markup.
209
210 @lilypond[verbatim,quote]
211 \\markup {
212   \\circle {
213     Hi
214   }
215 }
216 @end lilypond"
217   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
218                thickness))
219          (pad (* (magstep font-size) circle-padding))
220          (m (interpret-markup layout props arg)))
221     (circle-stencil m th pad)))
222
223 (define-markup-command (with-url layout props url arg)
224   (string? markup?)
225   #:category graphic
226   "
227 @cindex inserting URL links into text
228
229 Add a link to URL @var{url} around @var{arg}.  This only works in
230 the PDF backend.
231
232 @lilypond[verbatim,quote]
233 \\markup {
234   \\with-url #\"http://lilypond.org/web/\" {
235     LilyPond ... \\italic {
236       music notation for everyone
237     }
238   }
239 }
240 @end lilypond"
241   (let* ((stil (interpret-markup layout props arg))
242          (xextent (ly:stencil-extent stil X))
243          (yextent (ly:stencil-extent stil Y))
244          (old-expr (ly:stencil-expr stil))
245          (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
246
247     (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
248
249 (define-markup-command (beam layout props width slope thickness)
250   (number? number? number?)
251   #:category graphic
252   "
253 @cindex drawing beams within text
254
255 Create a beam with the specified parameters.
256 @lilypond[verbatim,quote]
257 \\markup {
258   \\beam #5 #1 #2
259 }
260 @end lilypond"
261   (let* ((y (* slope width))
262          (yext (cons (min 0 y) (max 0 y)))
263          (half (/ thickness 2)))
264
265     (ly:make-stencil
266      `(polygon ',(list
267                   0 (/ thickness -2)
268                     width (+ (* width slope)  (/ thickness -2))
269                     width (+ (* width slope)  (/ thickness 2))
270                     0 (/ thickness 2))
271                ,(ly:output-def-lookup layout 'blot-diameter)
272                #t)
273      (cons 0 width)
274      (cons (+ (- half) (car yext))
275            (+ half (cdr yext))))))
276
277 (define-markup-command (underline layout props arg)
278   (markup?)
279   #:category font
280   #:properties ((thickness 1) (offset 2))
281   "
282 @cindex underlining text
283
284 Underline @var{arg}.  Looks at @code{thickness} to determine line
285 thickness, and @code{offset} to determine line y-offset.
286
287 @lilypond[verbatim,quote]
288 \\markup \\fill-line {
289   \\underline "underlined"
290   \\override #'(offset . 5) \\override #'(thickness . 1) \\underline
291 "underlined"
292   \\override #'(offset . 1) \\override #'(thickness . 5) \\underline
293 "underlined"
294 }
295 @end lilypond"
296   (let* ((thick (ly:output-def-lookup layout 'line-thickness))
297          (underline-thick (* thickness thick))
298          (markup (interpret-markup layout props arg))
299          (x1 (car (ly:stencil-extent markup X)))
300          (x2 (cdr (ly:stencil-extent markup X)))
301          (y (* thick (- offset)))
302          (line (make-line-stencil underline-thick x1 y x2 y)))
303     (ly:stencil-add markup line)))
304
305 (define-markup-command (box layout props arg)
306   (markup?)
307   #:category font
308   #:properties ((thickness 1)
309                 (font-size 0)
310                 (box-padding 0.2))
311   "
312 @cindex enclosing text within a box
313
314 Draw a box round @var{arg}.  Looks at @code{thickness},
315 @code{box-padding} and @code{font-size} properties to determine line
316 thickness and padding around the markup.
317
318 @lilypond[verbatim,quote]
319 \\markup {
320   \\override #'(box-padding . 0.5)
321   \\box
322   \\line { V. S. }
323 }
324 @end lilypond"
325   (let* ((th (* (ly:output-def-lookup layout 'line-thickness)
326                 thickness))
327          (pad (* (magstep font-size) box-padding))
328          (m (interpret-markup layout props arg)))
329     (box-stencil m th pad)))
330
331 (define-markup-command (filled-box layout props xext yext blot)
332   (number-pair? number-pair? number?)
333   #:category graphic
334   "
335 @cindex drawing solid boxes within text
336 @cindex drawing boxes with rounded corners
337
338 Draw a box with rounded corners of dimensions @var{xext} and
339 @var{yext}.  For example,
340 @verbatim
341 \\filled-box #'(-.3 . 1.8) #'(-.3 . 1.8) #0
342 @end verbatim
343 creates a box extending horizontally from -0.3 to 1.8 and
344 vertically from -0.3 up to 1.8, with corners formed from a
345 circle of diameter@tie{}0 (i.e., sharp corners).
346
347 @lilypond[verbatim,quote]
348 \\markup {
349   \\filled-box #'(0 . 4) #'(0 . 4) #0
350   \\filled-box #'(0 . 2) #'(-4 . 2) #0.4
351   \\filled-box #'(1 . 8) #'(0 . 7) #0.2
352   \\with-color #white
353   \\filled-box #'(-4.5 . -2.5) #'(3.5 . 5.5) #0.7
354 }
355 @end lilypond"
356   (ly:round-filled-box
357    xext yext blot))
358
359 (define-markup-command (rounded-box layout props arg)
360   (markup?)
361   #:category graphic
362   #:properties ((thickness 1)
363                 (corner-radius 1)
364                 (font-size 0)
365                 (box-padding 0.5))
366   "@cindex enclosing text in a box with rounded corners
367    @cindex drawing boxes with rounded corners around text
368 Draw a box with rounded corners around @var{arg}.  Looks at @code{thickness},
369 @code{box-padding} and @code{font-size} properties to determine line
370 thickness and padding around the markup; the @code{corner-radius} property
371 makes it possible to define another shape for the corners (default is 1).
372
373 @lilypond[quote,verbatim,relative=2]
374 c4^\\markup {
375   \\rounded-box {
376     Overtura
377   }
378 }
379 c,8. c16 c4 r
380 @end lilypond"
381   (let ((th (* (ly:output-def-lookup layout 'line-thickness)
382                thickness))
383         (pad (* (magstep font-size) box-padding))
384         (m (interpret-markup layout props arg)))
385     (ly:stencil-add (rounded-box-stencil m th pad corner-radius)
386                     m)))
387
388 (define-markup-command (rotate layout props ang arg)
389   (number? markup?)
390   #:category align
391   "
392 @cindex rotating text
393
394 Rotate object with @var{ang} degrees around its center.
395
396 @lilypond[verbatim,quote]
397 \\markup {
398   default
399   \\hspace #2
400   \\rotate #45
401   \\line {
402     rotated 45°
403   }
404 }
405 @end lilypond"
406   (let* ((stil (interpret-markup layout props arg)))
407     (ly:stencil-rotate stil ang 0 0)))
408
409 (define-markup-command (whiteout layout props arg)
410   (markup?)
411   #:category other
412   "
413 @cindex adding a white background to text
414
415 Provide a white background for @var{arg}.
416
417 @lilypond[verbatim,quote]
418 \\markup {
419   \\combine
420     \\filled-box #'(-1 . 10) #'(-3 . 4) #1
421     \\whiteout whiteout
422 }
423 @end lilypond"
424   (stencil-whiteout (interpret-markup layout props arg)))
425
426 (define-markup-command (pad-markup layout props amount arg)
427   (number? markup?)
428   #:category align
429   "
430 @cindex padding text
431 @cindex putting space around text
432
433 Add space around a markup object.
434
435 @lilypond[verbatim,quote]
436 \\markup {
437   \\box {
438     default
439   }
440   \\hspace #2
441   \\box {
442     \\pad-markup #1 {
443       padded
444     }
445   }
446 }
447 @end lilypond"
448   (let*
449       ((stil (interpret-markup layout props arg))
450        (xext (ly:stencil-extent stil X))
451        (yext (ly:stencil-extent stil Y)))
452
453     (ly:make-stencil
454      (ly:stencil-expr stil)
455      (interval-widen xext amount)
456      (interval-widen yext amount))))
457
458 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
459 ;; space
460 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
461
462 (define-markup-command (strut layout props)
463   ()
464   #:category other
465   "
466 @cindex creating vertical spaces in text
467
468 Create a box of the same height as the space in the current font."
469   (let ((m (ly:text-interface::interpret-markup layout props " ")))
470     (ly:make-stencil (ly:stencil-expr m)
471                      '(0 . 0)
472                      (ly:stencil-extent m X)
473                      )))
474
475 ;; todo: fix negative space
476 (define-markup-command (hspace layout props amount)
477   (number?)
478   #:category align
479   #:properties ((word-space))
480   "
481 @cindex creating horizontal spaces in text
482
483 Create an invisible object taking up horizontal space @var{amount}.
484
485 @lilypond[verbatim,quote]
486 \\markup {
487   one
488   \\hspace #2
489   two
490   \\hspace #8
491   three
492 }
493 @end lilypond"
494   (let ((corrected-space (- amount word-space)))
495     (if (> corrected-space 0)
496         (ly:make-stencil "" (cons 0 corrected-space) '(0 . 0))
497         (ly:make-stencil "" (cons corrected-space corrected-space) '(0 . 0)))))
498
499 ;; todo: fix negative space
500 (define-markup-command (vspace layout props amount)
501  (number?)
502  #:category align
503  "
504 @cindex creating vertical spaces in text
505
506 Create an invisible object taking up vertical space
507 of @var{amount} multiplied by 3.
508
509 @lilypond[verbatim,quote]
510 \\markup {
511     \\center-column {
512     one
513     \\vspace #2
514     two
515     \\vspace #5
516     three
517   }
518 }
519 @end lilypond"
520   (let ((amount (* amount 3.0)))
521     (if (> amount 0)
522         (ly:make-stencil "" (cons 0 0) (cons 0 amount))
523         (ly:make-stencil "" (cons 0 0) (cons amount amount)))))
524
525
526 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
527 ;; importing graphics.
528 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
529
530 (define-markup-command (stencil layout props stil)
531   (ly:stencil?)
532   #:category other
533   "
534 @cindex importing stencils into text
535
536 Use a stencil as markup.
537
538 @lilypond[verbatim,quote]
539 \\markup {
540   \\stencil #(make-circle-stencil 2 0 #t)
541 }
542 @end lilypond"
543   stil)
544
545 (define bbox-regexp
546   (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)"))
547
548 (define (get-postscript-bbox string)
549   "Extract the bbox from STRING, or return #f if not present."
550   (let*
551       ((match (regexp-exec bbox-regexp string)))
552
553     (if match
554         (map (lambda (x)
555                (string->number (match:substring match x)))
556              (cdr (iota 5)))
557
558         #f)))
559
560 (define-markup-command (epsfile layout props axis size file-name)
561   (number? number? string?)
562   #:category graphic
563   "
564 @cindex inlining an Encapsulated PostScript image
565
566 Inline an EPS image.  The image is scaled along @var{axis} to
567 @var{size}.
568
569 @lilypond[verbatim,quote]
570 \\markup {
571   \\general-align #Y #DOWN {
572     \\epsfile #X #20 #\"context-example.eps\"
573     \\epsfile #Y #20 #\"context-example.eps\"
574   }
575 }
576 @end lilypond"
577   (if (ly:get-option 'safe)
578       (interpret-markup layout props "not allowed in safe")
579       (eps-file->stencil axis size file-name)
580       ))
581
582 (define-markup-command (postscript layout props str)
583   (string?)
584   #:category graphic
585   "
586 @cindex inserting PostScript directly into text
587 This inserts @var{str} directly into the output as a PostScript
588 command string.
589
590 @lilypond[verbatim,quote]
591 ringsps = #\"
592   0.15 setlinewidth
593   0.9 0.6 moveto
594   0.4 0.6 0.5 0 361 arc
595   stroke
596   1.0 0.6 0.5 0 361 arc
597   stroke
598   \"
599
600 rings = \\markup {
601   \\with-dimensions #'(-0.2 . 1.6) #'(0 . 1.2)
602   \\postscript #ringsps
603 }
604
605 \\relative c'' {
606   c2^\\rings
607   a2_\\rings
608 }
609 @end lilypond"
610   ;; FIXME
611   (ly:make-stencil
612    (list 'embedded-ps
613          (format "
614 gsave currentpoint translate
615 0.1 setlinewidth
616  ~a
617 grestore
618 "
619                  str))
620    '(0 . 0) '(0 . 0)))
621
622 (define-markup-command (path layout props thickness commands) (number? list?)
623   #:category graphic
624   #:properties ((line-cap-style 'round)
625                 (line-join-style 'round)
626                 (filled #f))
627   "
628 @cindex paths, drawing
629 @cindex drawing paths
630 Draws a path with line thickness @var{thickness} according to the
631 directions given in @var{commands}.  @var{commands} is a list of
632 lists where the @code{car} of each sublist is a drawing command and
633 the @code{cdr} comprises the associated arguments for each command.
634
635 Line-cap styles and line-join styles may be customized by
636 overriding the @code{line-cap-style} and @code{line-join-style}
637 properties, respectively.  Available line-cap styles are
638 @code{'butt}, @code{'round}, and @code{'square}.  Available
639 line-join styles are @code{'miter}, @code{'round}, and
640 @code{'bevel}.
641
642 The property @code{filled} specifies whether or not the path is
643 filled with color.
644
645 There are seven commands available to use in the list
646 @code{commands}: @code{moveto}, @code{rmoveto}, @code{lineto},
647 @code{rlineto}, @code{curveto}, @code{rcurveto}, and
648 @code{closepath}.  Note that the commands that begin with @emph{r}
649 are the relative variants of the other three commands.
650
651 The commands @code{moveto}, @code{rmoveto}, @code{lineto}, and
652 @code{rlineto} take 2 arguments; they are the X and Y coordinates
653 for the destination point.
654
655 The commands @code{curveto} and @code{rcurveto} create cubic
656 Bézier curves, and take 6 arguments; the first two are the X and Y
657 coordinates for the first control point, the second two are the X
658 and Y coordinates for the second control point, and the last two
659 are the X and Y coordinates for the destination point.
660
661 The @code{closepath} command takes zero arguments and closes the
662 current subpath in the active path.
663
664 Note that a sequence of commands @emph{must} begin with a
665 @code{moveto} or @code{rmoveto} to work with the SVG output.
666
667 @lilypond[verbatim,quote]
668 samplePath =
669   #'((moveto 0 0)
670      (lineto -1 1)
671      (lineto 1 1)
672      (lineto 1 -1)
673      (curveto -5 -5 -5 5 -1 0)
674      (closepath))
675
676 \\markup {
677   \\path #0.25 #samplePath
678 }
679 @end lilypond"
680   (let* ((half-thickness (/ thickness 2))
681          (current-point '(0 . 0))
682          (set-point (lambda (lst) (set! current-point lst)))
683          (relative? (lambda (x)
684                       (string-prefix? "r" (symbol->string (car x)))))
685          ;; For calculating extents, we want to modify the command
686          ;; list so that all coordinates are absolute.
687          (new-commands (map (lambda (x)
688                               (cond
689                                 ;; for rmoveto, rlineto
690                                 ((and (relative? x) (eq? 3 (length x)))
691                                  (let ((cp (cons
692                                              (+ (car current-point)
693                                                 (second x))
694                                              (+ (cdr current-point)
695                                                 (third x)))))
696                                    (set-point cp)
697                                    (list (car cp)
698                                          (cdr cp))))
699                                 ;; for rcurveto
700                                 ((and (relative? x) (eq? 7 (length x)))
701                                  (let* ((old-cp current-point)
702                                         (cp (cons
703                                               (+ (car old-cp)
704                                                  (sixth x))
705                                               (+ (cdr old-cp)
706                                                  (seventh x)))))
707                                    (set-point cp)
708                                    (list (+ (car old-cp) (second x))
709                                          (+ (cdr old-cp) (third x))
710                                          (+ (car old-cp) (fourth x))
711                                          (+ (cdr old-cp) (fifth x))
712                                          (car cp)
713                                          (cdr cp))))
714                                 ;; for moveto, lineto
715                                 ((eq? 3 (length x))
716                                  (set-point (cons (second x)
717                                                   (third x)))
718                                  (drop x 1))
719                                 ;; for curveto
720                                 ((eq? 7 (length x))
721                                  (set-point (cons (sixth x)
722                                                   (seventh x)))
723                                  (drop x 1))
724                                 ;; keep closepath for filtering;
725                                 ;; see `without-closepath'.
726                                 (else x)))
727                             commands))
728          ;; path-min-max does not accept 0-arg lists,
729          ;; and since closepath does not affect extents, filter
730          ;; out those commands here.
731          (without-closepath (filter (lambda (x)
732                                       (not (equal? 'closepath (car x))))
733                                     new-commands))
734          (extents (path-min-max
735                     ;; set the origin to the first moveto
736                     (list (list-ref (car without-closepath) 0)
737                           (list-ref (car without-closepath) 1))
738                     without-closepath))
739          (X-extent (cons (list-ref extents 0) (list-ref extents 1)))
740          (Y-extent (cons (list-ref extents 2) (list-ref extents 3)))
741          (command-list (fold-right append '() commands)))
742
743     ;; account for line thickness
744     (set! X-extent (interval-widen X-extent half-thickness))
745     (set! Y-extent (interval-widen Y-extent half-thickness))
746
747     (ly:make-stencil
748       `(path ,thickness `(,@',command-list)
749              ',line-cap-style ',line-join-style ,filled)
750       X-extent
751       Y-extent)))
752
753 (define-markup-command (score layout props score)
754   (ly:score?)
755   #:category music
756   #:properties ((baseline-skip))
757   "
758 @cindex inserting music into text
759
760 Inline an image of music.
761
762 @lilypond[verbatim,quote]
763 \\markup {
764   \\score {
765     \\new PianoStaff <<
766       \\new Staff \\relative c' {
767         \\key f \\major
768         \\time 3/4
769         \\mark \\markup { Allegro }
770         f2\\p( a4)
771         c2( a4)
772         bes2( g'4)
773         f8( e) e4 r
774       }
775       \\new Staff \\relative c {
776         \\clef bass
777         \\key f \\major
778         \\time 3/4
779         f8( a c a c a
780         f c' es c es c)
781         f,( bes d bes d bes)
782         f( g bes g bes g)
783       }
784     >>
785     \\layout {
786       indent = 0.0\\cm
787       \\context {
788         \\Score
789         \\override RehearsalMark #'break-align-symbols =
790           #'(time-signature key-signature)
791         \\override RehearsalMark #'self-alignment-X = #LEFT
792       }
793       \\context {
794         \\Staff
795         \\override TimeSignature #'break-align-anchor-alignment = #LEFT
796       }
797     }
798   }
799 }
800 @end lilypond"
801   (let ((output (ly:score-embedded-format score layout)))
802
803     (if (ly:music-output? output)
804         (stack-stencils Y DOWN baseline-skip
805                         (map paper-system-stencil
806                              (vector->list
807                               (ly:paper-score-paper-systems output))))
808         (begin
809           (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
810           empty-stencil))))
811
812 (define-markup-command (null layout props)
813   ()
814   #:category other
815   "
816 @cindex creating empty text objects
817
818 An empty markup with extents of a single point.
819
820 @lilypond[verbatim,quote]
821 \\markup {
822   \\null
823 }
824 @end lilypond"
825   point-stencil)
826
827 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
828 ;; basic formatting.
829 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
830
831 (define-markup-command (simple layout props str)
832   (string?)
833   #:category font
834   "
835 @cindex simple text strings
836
837 A simple text string; @code{\\markup @{ foo @}} is equivalent with
838 @code{\\markup @{ \\simple #\"foo\" @}}.
839
840 Note: for creating standard text markup or defining new markup commands,
841 the use of @code{\\simple} is unnecessary.
842
843 @lilypond[verbatim,quote]
844 \\markup {
845   \\simple #\"simple\"
846   \\simple #\"text\"
847   \\simple #\"strings\"
848 }
849 @end lilypond"
850   (interpret-markup layout props str))
851
852 (define-markup-command (tied-lyric layout props str)
853   (string?)
854   #:category music
855   "
856 @cindex simple text strings with tie characters
857
858 Like simple-markup, but use tie characters for @q{~} tilde symbols.
859
860 @lilypond[verbatim,quote]
861 \\markup {
862   \\tied-lyric #\"Lasciate~i monti\"
863 }
864 @end lilypond"
865   (if (string-contains str "~")
866       (let*
867           ((parts (string-split str #\~))
868            (tie-str (ly:wide-char->utf-8 #x203f))
869            (joined  (list-join parts tie-str))
870            (join-stencil (interpret-markup layout props tie-str))
871            )
872
873         (interpret-markup layout
874                           (prepend-alist-chain
875                            'word-space
876                            (/ (interval-length (ly:stencil-extent join-stencil X)) -3.5)
877                            props)
878                           (make-line-markup joined)))
879                            ;(map (lambda (s) (interpret-markup layout props s)) parts))
880       (interpret-markup layout props str)))
881
882 (define-public empty-markup
883   (make-simple-markup ""))
884
885 ;; helper for justifying lines.
886 (define (get-fill-space word-count line-width text-widths)
887   "Calculate the necessary paddings between each two adjacent texts.
888         The lengths of all texts are stored in @var{text-widths}.
889         The normal formula for the padding between texts a and b is:
890         padding = line-width/(word-count - 1) - (length(a) + length(b))/2
891         The first and last padding have to be calculated specially using the
892         whole length of the first or last text.
893         Return a list of paddings."
894   (cond
895    ((null? text-widths) '())
896
897    ;; special case first padding
898    ((= (length text-widths) word-count)
899     (cons
900      (- (- (/ line-width (1- word-count)) (car text-widths))
901         (/ (car (cdr text-widths)) 2))
902      (get-fill-space word-count line-width (cdr text-widths))))
903    ;; special case last padding
904    ((= (length text-widths) 2)
905     (list (- (/ line-width (1- word-count))
906              (+ (/ (car text-widths) 2) (car (cdr text-widths)))) 0))
907    (else
908     (cons
909      (- (/ line-width (1- word-count))
910         (/ (+ (car text-widths) (car (cdr text-widths))) 2))
911      (get-fill-space word-count line-width (cdr text-widths))))))
912
913 (define-markup-command (fill-line layout props args)
914   (markup-list?)
915   #:category align
916   #:properties ((text-direction RIGHT)
917                 (word-space 1)
918                 (line-width #f))
919   "Put @var{markups} in a horizontal line of width @var{line-width}.
920 The markups are spaced or flushed to fill the entire line.
921 If there are no arguments, return an empty stencil.
922
923 @lilypond[verbatim,quote]
924 \\markup {
925   \\column {
926     \\fill-line {
927       Words evenly spaced across the page
928     }
929     \\null
930     \\fill-line {
931       \\line { Text markups }
932       \\line {
933         \\italic { evenly spaced }
934       }
935       \\line { across the page }
936     }
937   }
938 }
939 @end lilypond"
940   (let* ((orig-stencils (interpret-markup-list layout props args))
941          (stencils
942           (map (lambda (stc)
943                  (if (ly:stencil-empty? stc)
944                      point-stencil
945                      stc)) orig-stencils))
946          (text-widths
947           (map (lambda (stc)
948                  (if (ly:stencil-empty? stc)
949                      0.0
950                      (interval-length (ly:stencil-extent stc X))))
951                stencils))
952          (text-width (apply + text-widths))
953          (word-count (length stencils))
954          (line-width (or line-width (ly:output-def-lookup layout 'line-width)))
955          (fill-space
956                 (cond
957                         ((= word-count 1)
958                                 (list
959                                         (/ (- line-width text-width) 2)
960                                         (/ (- line-width text-width) 2)))
961                         ((= word-count 2)
962                                 (list
963                                         (- line-width text-width)))
964                         (else
965                                 (get-fill-space word-count line-width text-widths))))
966          (fill-space-normal
967           (map (lambda (x)
968                  (if (< x word-space)
969                      word-space
970                      x))
971                fill-space))
972
973          (line-stencils (if (= word-count 1)
974                             (list
975                              point-stencil
976                              (car stencils)
977                              point-stencil)
978                             stencils)))
979
980     (if (= text-direction LEFT)
981         (set! line-stencils (reverse line-stencils)))
982
983     (if (null? (remove ly:stencil-empty? orig-stencils))
984         empty-stencil
985         (ly:stencil-translate-axis
986           (stack-stencils-padding-list X
987                                        RIGHT fill-space-normal line-stencils)
988           (- (car (ly:stencil-extent (car stencils) X)))
989           X))))
990
991 (define-markup-command (line layout props args)
992   (markup-list?)
993   #:category align
994   #:properties ((word-space)
995                 (text-direction RIGHT))
996   "Put @var{args} in a horizontal line.  The property @code{word-space}
997 determines the space between markups in @var{args}.
998
999 @lilypond[verbatim,quote]
1000 \\markup {
1001   \\line {
1002     one two three
1003   }
1004 }
1005 @end lilypond"
1006   (let ((stencils (interpret-markup-list layout props args)))
1007     (if (= text-direction LEFT)
1008         (set! stencils (reverse stencils)))
1009     (stack-stencil-line
1010      word-space
1011      (remove ly:stencil-empty? stencils))))
1012
1013 (define-markup-command (concat layout props args)
1014   (markup-list?)
1015   #:category align
1016   "
1017 @cindex concatenating text
1018 @cindex ligatures in text
1019
1020 Concatenate @var{args} in a horizontal line, without spaces in between.
1021 Strings and simple markups are concatenated on the input level, allowing
1022 ligatures.  For example, @code{\\concat @{ \"f\" \\simple #\"i\" @}} is
1023 equivalent to @code{\"fi\"}.
1024
1025 @lilypond[verbatim,quote]
1026 \\markup {
1027   \\concat {
1028     one
1029     two
1030     three
1031   }
1032 }
1033 @end lilypond"
1034   (define (concat-string-args arg-list)
1035     (fold-right (lambda (arg result-list)
1036                   (let ((result (if (pair? result-list)
1037                                     (car result-list)
1038                                   '())))
1039                     (if (and (pair? arg) (eqv? (car arg) simple-markup))
1040                       (set! arg (cadr arg)))
1041                     (if (and (string? result) (string? arg))
1042                         (cons (string-append arg result) (cdr result-list))
1043                       (cons arg result-list))))
1044                 '()
1045                 arg-list))
1046
1047   (interpret-markup layout
1048                     (prepend-alist-chain 'word-space 0 props)
1049                     (make-line-markup (if (markup-command-list? args)
1050                                           args
1051                                           (concat-string-args args)))))
1052
1053 (define (wordwrap-stencils stencils
1054                            justify base-space line-width text-dir)
1055   "Perform simple wordwrap, return stencil of each line."
1056   (define space (if justify
1057                     ;; justify only stretches lines.
1058                     (* 0.7 base-space)
1059                     base-space))
1060   (define (take-list width space stencils
1061                      accumulator accumulated-width)
1062     "Return (head-list . tail) pair, with head-list fitting into width"
1063     (if (null? stencils)
1064         (cons accumulator stencils)
1065         (let* ((first (car stencils))
1066                (first-wid (cdr (ly:stencil-extent (car stencils) X)))
1067                (newwid (+ space first-wid accumulated-width)))
1068           (if (or (null? accumulator)
1069                   (< newwid width))
1070               (take-list width space
1071                          (cdr stencils)
1072                          (cons first accumulator)
1073                          newwid)
1074               (cons accumulator stencils)))))
1075   (let loop ((lines '())
1076              (todo stencils))
1077     (let* ((line-break (take-list line-width space todo
1078                                   '() 0.0))
1079            (line-stencils (car line-break))
1080            (space-left (- line-width
1081                           (apply + (map (lambda (x) (cdr (ly:stencil-extent x X)))
1082                                         line-stencils))))
1083            (line-word-space (cond ((not justify) space)
1084                                   ;; don't stretch last line of paragraph.
1085                                   ;; hmmm . bug - will overstretch the last line in some case.
1086                                   ((null? (cdr line-break))
1087                                    base-space)
1088                                   ((null? line-stencils) 0.0)
1089                                   ((null? (cdr line-stencils)) 0.0)
1090                                   (else (/ space-left (1- (length line-stencils))))))
1091            (line (stack-stencil-line line-word-space
1092                                      (if (= text-dir RIGHT)
1093                                          (reverse line-stencils)
1094                                          line-stencils))))
1095       (if (pair? (cdr line-break))
1096           (loop (cons line lines)
1097                 (cdr line-break))
1098           (begin
1099             (if (= text-dir LEFT)
1100                 (set! line
1101                       (ly:stencil-translate-axis
1102                        line
1103                        (- line-width (interval-end (ly:stencil-extent line X)))
1104                        X)))
1105             (reverse (cons line lines)))))))
1106
1107 (define-markup-list-command (wordwrap-internal layout props justify args)
1108   (boolean? markup-list?)
1109   #:properties ((line-width #f)
1110                 (word-space)
1111                 (text-direction RIGHT))
1112   "Internal markup list command used to define @code{\\justify} and @code{\\wordwrap}."
1113   (wordwrap-stencils (remove ly:stencil-empty?
1114                              (interpret-markup-list layout props args))
1115                      justify
1116                      word-space
1117                      (or line-width
1118                          (ly:output-def-lookup layout 'line-width))
1119                      text-direction))
1120
1121 (define-markup-command (justify layout props args)
1122   (markup-list?)
1123   #:category align
1124   #:properties ((baseline-skip)
1125                 wordwrap-internal-markup-list)
1126   "
1127 @cindex justifying text
1128
1129 Like @code{\\wordwrap}, but with lines stretched to justify the margins.
1130 Use @code{\\override #'(line-width . @var{X})} to set the line width;
1131 @var{X}@tie{}is the number of staff spaces.
1132
1133 @lilypond[verbatim,quote]
1134 \\markup {
1135   \\justify {
1136     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1137     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1138     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1139     laboris nisi ut aliquip ex ea commodo consequat.
1140   }
1141 }
1142 @end lilypond"
1143   (stack-lines DOWN 0.0 baseline-skip
1144                (wordwrap-internal-markup-list layout props #t args)))
1145
1146 (define-markup-command (wordwrap layout props args)
1147   (markup-list?)
1148   #:category align
1149   #:properties ((baseline-skip)
1150                 wordwrap-internal-markup-list)
1151   "Simple wordwrap.  Use @code{\\override #'(line-width . @var{X})} to set
1152 the line width, where @var{X} is the number of staff spaces.
1153
1154 @lilypond[verbatim,quote]
1155 \\markup {
1156   \\wordwrap {
1157     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
1158     do eiusmod tempor incididunt ut labore et dolore magna aliqua.
1159     Ut enim ad minim veniam, quis nostrud exercitation ullamco
1160     laboris nisi ut aliquip ex ea commodo consequat.
1161   }
1162 }
1163 @end lilypond"
1164   (stack-lines DOWN 0.0 baseline-skip
1165                (wordwrap-internal-markup-list layout props #f args)))
1166
1167 (define-markup-list-command (wordwrap-string-internal layout props justify arg)
1168   (boolean? string?)
1169   #:properties ((line-width)
1170                 (word-space)
1171                 (text-direction RIGHT))
1172   "Internal markup list command used to define @code{\\justify-string} and
1173 @code{\\wordwrap-string}."
1174   (let* ((para-strings (regexp-split
1175                         (string-regexp-substitute
1176                          "\r" "\n"
1177                          (string-regexp-substitute "\r\n" "\n" arg))
1178                         "\n[ \t\n]*\n[ \t\n]*"))
1179          (list-para-words (map (lambda (str)
1180                                  (regexp-split str "[ \t\n]+"))
1181                                para-strings))
1182          (para-lines (map (lambda (words)
1183                             (let* ((stencils
1184                                     (remove ly:stencil-empty?
1185                                             (map (lambda (x)
1186                                                    (interpret-markup layout props x))
1187                                                  words))))
1188                               (wordwrap-stencils stencils
1189                                                  justify word-space
1190                                                  line-width text-direction)))
1191                           list-para-words)))
1192     (apply append para-lines)))
1193
1194 (define-markup-command (wordwrap-string layout props arg)
1195   (string?)
1196   #:category align
1197   #:properties ((baseline-skip)
1198                 wordwrap-string-internal-markup-list)
1199   "Wordwrap a string.  Paragraphs may be separated with double newlines.
1200
1201 @lilypond[verbatim,quote]
1202 \\markup {
1203   \\override #'(line-width . 40)
1204   \\wordwrap-string #\"Lorem ipsum dolor sit amet, consectetur
1205       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1206       et dolore magna aliqua.
1207
1208
1209       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1210       laboris nisi ut aliquip ex ea commodo consequat.
1211
1212
1213       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1214       qui officia deserunt mollit anim id est laborum\"
1215 }
1216 @end lilypond"
1217   (stack-lines DOWN 0.0 baseline-skip
1218                (wordwrap-string-internal-markup-list layout props #f arg)))
1219
1220 (define-markup-command (justify-string layout props arg)
1221   (string?)
1222   #:category align
1223   #:properties ((baseline-skip)
1224                 wordwrap-string-internal-markup-list)
1225   "Justify a string.  Paragraphs may be separated with double newlines
1226
1227 @lilypond[verbatim,quote]
1228 \\markup {
1229   \\override #'(line-width . 40)
1230   \\justify-string #\"Lorem ipsum dolor sit amet, consectetur
1231       adipisicing elit, sed do eiusmod tempor incididunt ut labore
1232       et dolore magna aliqua.
1233
1234
1235       Ut enim ad minim veniam, quis nostrud exercitation ullamco
1236       laboris nisi ut aliquip ex ea commodo consequat.
1237
1238
1239       Excepteur sint occaecat cupidatat non proident, sunt in culpa
1240       qui officia deserunt mollit anim id est laborum\"
1241 }
1242 @end lilypond"
1243   (stack-lines DOWN 0.0 baseline-skip
1244                (wordwrap-string-internal-markup-list layout props #t arg)))
1245
1246 (define-markup-command (wordwrap-field layout props symbol)
1247   (symbol?)
1248   #:category align
1249   "Wordwrap the data which has been assigned to @var{symbol}.
1250
1251 @lilypond[verbatim,quote]
1252 \\header {
1253   title = \"My title\"
1254   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1255     elit, sed do eiusmod tempor incididunt ut labore et dolore magna
1256     aliqua.  Ut enim ad minim veniam, quis nostrud exercitation ullamco
1257     laboris nisi ut aliquip ex ea commodo consequat.\"
1258 }
1259
1260 \\paper {
1261   bookTitleMarkup = \\markup {
1262     \\column {
1263       \\fill-line { \\fromproperty #'header:title }
1264       \\null
1265       \\wordwrap-field #'header:myText
1266     }
1267   }
1268 }
1269
1270 \\markup {
1271   \\null
1272 }
1273 @end lilypond"
1274   (let* ((m (chain-assoc-get symbol props)))
1275     (if (string? m)
1276         (wordwrap-string-markup layout props m)
1277         empty-stencil)))
1278
1279 (define-markup-command (justify-field layout props symbol)
1280   (symbol?)
1281   #:category align
1282   "Justify the data which has been assigned to @var{symbol}.
1283
1284 @lilypond[verbatim,quote]
1285 \\header {
1286   title = \"My title\"
1287   myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
1288     elit, sed do eiusmod tempor incididunt ut labore et dolore magna
1289     aliqua.  Ut enim ad minim veniam, quis nostrud exercitation ullamco
1290     laboris nisi ut aliquip ex ea commodo consequat.\"
1291 }
1292
1293 \\paper {
1294   bookTitleMarkup = \\markup {
1295     \\column {
1296       \\fill-line { \\fromproperty #'header:title }
1297       \\null
1298       \\justify-field #'header:myText
1299     }
1300   }
1301 }
1302
1303 \\markup {
1304   \\null
1305 }
1306 @end lilypond"
1307   (let* ((m (chain-assoc-get symbol props)))
1308     (if (string? m)
1309         (justify-string-markup layout props m)
1310         empty-stencil)))
1311
1312 (define-markup-command (combine layout props arg1 arg2)
1313   (markup? markup?)
1314   #:category align
1315   "
1316 @cindex merging text
1317
1318 Print two markups on top of each other.
1319
1320 Note: @code{\\combine} cannot take a list of markups enclosed in
1321 curly braces as an argument; the follow example will not compile:
1322
1323 @example
1324 \\combine @{ a list @}
1325 @end example
1326
1327 @lilypond[verbatim,quote]
1328 \\markup {
1329   \\fontsize #5
1330   \\override #'(thickness . 2)
1331   \\combine
1332     \\draw-line #'(0 . 4)
1333     \\arrow-head #Y #DOWN ##f
1334 }
1335 @end lilypond"
1336   (let* ((s1 (interpret-markup layout props arg1))
1337          (s2 (interpret-markup layout props arg2)))
1338     (ly:stencil-add s1 s2)))
1339
1340 ;;
1341 ;; TODO: should extract baseline-skip from each argument somehow..
1342 ;;
1343 (define-markup-command (column layout props args)
1344   (markup-list?)
1345   #:category align
1346   #:properties ((baseline-skip))
1347   "
1348 @cindex stacking text in a column
1349
1350 Stack the markups in @var{args} vertically.  The property
1351 @code{baseline-skip} determines the space between markups
1352 in @var{args}.
1353
1354 @lilypond[verbatim,quote]
1355 \\markup {
1356   \\column {
1357     one
1358     two
1359     three
1360   }
1361 }
1362 @end lilypond"
1363   (let ((arg-stencils (interpret-markup-list layout props args)))
1364     (stack-lines -1 0.0 baseline-skip
1365                  (remove ly:stencil-empty? arg-stencils))))
1366
1367 (define-markup-command (dir-column layout props args)
1368   (markup-list?)
1369   #:category align
1370   #:properties ((direction)
1371                 (baseline-skip))
1372   "
1373 @cindex changing direction of text columns
1374
1375 Make a column of @var{args}, going up or down, depending on the
1376 setting of the @code{direction} layout property.
1377
1378 @lilypond[verbatim,quote]
1379 \\markup {
1380   \\override #`(direction . ,UP) {
1381     \\dir-column {
1382       going up
1383     }
1384   }
1385   \\hspace #1
1386   \\dir-column {
1387     going down
1388   }
1389   \\hspace #1
1390   \\override #'(direction . 1) {
1391     \\dir-column {
1392       going up
1393     }
1394   }
1395 }
1396 @end lilypond"
1397   (stack-lines (if (number? direction) direction -1)
1398                0.0
1399                baseline-skip
1400                (interpret-markup-list layout props args)))
1401
1402 (define (general-column align-dir baseline mols)
1403   "Stack @var{mols} vertically, aligned to  @var{align-dir} horizontally."
1404
1405   (let* ((aligned-mols (map (lambda (x) (ly:stencil-aligned-to x X align-dir)) mols)))
1406     (stack-lines -1 0.0 baseline aligned-mols)))
1407
1408 (define-markup-command (center-column layout props args)
1409   (markup-list?)
1410   #:category align
1411   #:properties ((baseline-skip))
1412   "
1413 @cindex centering a column of text
1414
1415 Put @code{args} in a centered column.
1416
1417 @lilypond[verbatim,quote]
1418 \\markup {
1419   \\center-column {
1420     one
1421     two
1422     three
1423   }
1424 }
1425 @end lilypond"
1426   (general-column CENTER baseline-skip (interpret-markup-list layout props args)))
1427
1428 (define-markup-command (left-column layout props args)
1429   (markup-list?)
1430   #:category align
1431   #:properties ((baseline-skip))
1432  "
1433 @cindex text columns, left-aligned
1434
1435 Put @code{args} in a left-aligned column.
1436
1437 @lilypond[verbatim,quote]
1438 \\markup {
1439   \\left-column {
1440     one
1441     two
1442     three
1443   }
1444 }
1445 @end lilypond"
1446   (general-column LEFT baseline-skip (interpret-markup-list layout props args)))
1447
1448 (define-markup-command (right-column layout props args)
1449   (markup-list?)
1450   #:category align
1451   #:properties ((baseline-skip))
1452  "
1453 @cindex text columns, right-aligned
1454
1455 Put @code{args} in a right-aligned column.
1456
1457 @lilypond[verbatim,quote]
1458 \\markup {
1459   \\right-column {
1460     one
1461     two
1462     three
1463   }
1464 }
1465 @end lilypond"
1466   (general-column RIGHT baseline-skip (interpret-markup-list layout props args)))
1467
1468 (define-markup-command (vcenter layout props arg)
1469   (markup?)
1470   #:category align
1471   "
1472 @cindex vertically centering text
1473
1474 Align @code{arg} to its Y@tie{}center.
1475
1476 @lilypond[verbatim,quote]
1477 \\markup {
1478   one
1479   \\vcenter
1480   two
1481   three
1482 }
1483 @end lilypond"
1484   (let* ((mol (interpret-markup layout props arg)))
1485     (ly:stencil-aligned-to mol Y CENTER)))
1486
1487 (define-markup-command (center-align layout props arg)
1488   (markup?)
1489   #:category align
1490   "
1491 @cindex horizontally centering text
1492
1493 Align @code{arg} to its X@tie{}center.
1494
1495 @lilypond[verbatim,quote]
1496 \\markup {
1497   \\column {
1498     one
1499     \\center-align
1500     two
1501     three
1502   }
1503 }
1504 @end lilypond"
1505   (let* ((mol (interpret-markup layout props arg)))
1506     (ly:stencil-aligned-to mol X CENTER)))
1507
1508 (define-markup-command (right-align layout props arg)
1509   (markup?)
1510   #:category align
1511   "
1512 @cindex right aligning text
1513
1514 Align @var{arg} on its right edge.
1515
1516 @lilypond[verbatim,quote]
1517 \\markup {
1518   \\column {
1519     one
1520     \\right-align
1521     two
1522     three
1523   }
1524 }
1525 @end lilypond"
1526   (let* ((m (interpret-markup layout props arg)))
1527     (ly:stencil-aligned-to m X RIGHT)))
1528
1529 (define-markup-command (left-align layout props arg)
1530   (markup?)
1531   #:category align
1532   "
1533 @cindex left aligning text
1534
1535 Align @var{arg} on its left edge.
1536
1537 @lilypond[verbatim,quote]
1538 \\markup {
1539   \\column {
1540     one
1541     \\left-align
1542     two
1543     three
1544   }
1545 }
1546 @end lilypond"
1547   (let* ((m (interpret-markup layout props arg)))
1548     (ly:stencil-aligned-to m X LEFT)))
1549
1550 (define-markup-command (general-align layout props axis dir arg)
1551   (integer? number? markup?)
1552   #:category align
1553   "
1554 @cindex controlling general text alignment
1555
1556 Align @var{arg} in @var{axis} direction to the @var{dir} side.
1557
1558 @lilypond[verbatim,quote]
1559 \\markup {
1560   \\column {
1561     one
1562     \\general-align #X #LEFT
1563     two
1564     three
1565     \\null
1566     one
1567     \\general-align #X #CENTER
1568     two
1569     three
1570     \\null
1571     \\line {
1572       one
1573       \\general-align #Y #UP
1574       two
1575       three
1576     }
1577     \\null
1578     \\line {
1579       one
1580       \\general-align #Y #3.2
1581       two
1582       three
1583     }
1584   }
1585 }
1586 @end lilypond"
1587   (let* ((m (interpret-markup layout props arg)))
1588     (ly:stencil-aligned-to m axis dir)))
1589
1590 (define-markup-command (halign layout props dir arg)
1591   (number? markup?)
1592   #:category align
1593   "
1594 @cindex setting horizontal text alignment
1595
1596 Set horizontal alignment.  If @var{dir} is @code{-1}, then it is
1597 left-aligned, while @code{+1} is right.  Values in between interpolate
1598 alignment accordingly.
1599
1600 @lilypond[verbatim,quote]
1601 \\markup {
1602   \\column {
1603     one
1604     \\halign #LEFT
1605     two
1606     three
1607     \\null
1608     one
1609     \\halign #CENTER
1610     two
1611     three
1612     \\null
1613     one
1614     \\halign #RIGHT
1615     two
1616     three
1617     \\null
1618     one
1619     \\halign #-5
1620     two
1621     three
1622   }
1623 }
1624 @end lilypond"
1625   (let* ((m (interpret-markup layout props arg)))
1626     (ly:stencil-aligned-to m X dir)))
1627
1628 (define-markup-command (with-dimensions layout props x y arg)
1629   (number-pair? number-pair? markup?)
1630   #:category other
1631   "
1632 @cindex setting extent of text objects
1633
1634 Set the dimensions of @var{arg} to @var{x} and@tie{}@var{y}."
1635   (let* ((m (interpret-markup layout props arg)))
1636     (ly:make-stencil (ly:stencil-expr m) x y)))
1637
1638 (define-markup-command (pad-around layout props amount arg)
1639   (number? markup?)
1640   #:category align
1641   "Add padding @var{amount} all around @var{arg}.
1642
1643 @lilypond[verbatim,quote]
1644 \\markup {
1645   \\box {
1646     default
1647   }
1648   \\hspace #2
1649   \\box {
1650     \\pad-around #0.5 {
1651       padded
1652     }
1653   }
1654 }
1655 @end lilypond"
1656   (let* ((m (interpret-markup layout props arg))
1657          (x (ly:stencil-extent m X))
1658          (y (ly:stencil-extent m Y)))
1659     (ly:make-stencil (ly:stencil-expr m)
1660                      (interval-widen x amount)
1661                      (interval-widen y amount))))
1662
1663 (define-markup-command (pad-x layout props amount arg)
1664   (number? markup?)
1665   #:category align
1666   "
1667 @cindex padding text horizontally
1668
1669 Add padding @var{amount} around @var{arg} in the X@tie{}direction.
1670
1671 @lilypond[verbatim,quote]
1672 \\markup {
1673   \\box {
1674     default
1675   }
1676   \\hspace #4
1677   \\box {
1678     \\pad-x #2 {
1679       padded
1680     }
1681   }
1682 }
1683 @end lilypond"
1684   (let* ((m (interpret-markup layout props arg))
1685          (x (ly:stencil-extent m X))
1686          (y (ly:stencil-extent m Y)))
1687     (ly:make-stencil (ly:stencil-expr m)
1688                      (interval-widen x amount)
1689                      y)))
1690
1691 (define-markup-command (put-adjacent layout props axis dir arg1 arg2)
1692   (integer? ly:dir? markup? markup?)
1693   #:category align
1694   "Put @var{arg2} next to @var{arg1}, without moving @var{arg1}."
1695   (let ((m1 (interpret-markup layout props arg1))
1696         (m2 (interpret-markup layout props arg2)))
1697     (ly:stencil-combine-at-edge m1 axis dir m2 0.0)))
1698
1699 (define-markup-command (transparent layout props arg)
1700   (markup?)
1701   #:category other
1702   "Make @var{arg} transparent.
1703
1704 @lilypond[verbatim,quote]
1705 \\markup {
1706   \\transparent {
1707     invisible text
1708   }
1709 }
1710 @end lilypond"
1711   (let* ((m (interpret-markup layout props arg))
1712          (x (ly:stencil-extent m X))
1713          (y (ly:stencil-extent m Y)))
1714     (ly:make-stencil "" x y)))
1715
1716 (define-markup-command (pad-to-box layout props x-ext y-ext arg)
1717   (number-pair? number-pair? markup?)
1718   #:category align
1719   "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space.
1720
1721 @lilypond[verbatim,quote]
1722 \\markup {
1723   \\box {
1724     default
1725   }
1726   \\hspace #4
1727   \\box {
1728     \\pad-to-box #'(0 . 10) #'(0 . 3) {
1729       padded
1730     }
1731   }
1732 }
1733 @end lilypond"
1734   (let* ((m (interpret-markup layout props arg))
1735          (x (ly:stencil-extent m X))
1736          (y (ly:stencil-extent m Y)))
1737     (ly:make-stencil (ly:stencil-expr m)
1738                      (interval-union x-ext x)
1739                      (interval-union y-ext y))))
1740
1741 (define-markup-command (hcenter-in layout props length arg)
1742   (number? markup?)
1743   #:category align
1744   "Center @var{arg} horizontally within a box of extending
1745 @var{length}/2 to the left and right.
1746
1747 @lilypond[quote,verbatim]
1748 \\new StaffGroup <<
1749   \\new Staff {
1750     \\set Staff.instrumentName = \\markup {
1751       \\hcenter-in #12
1752       Oboe
1753     }
1754     c''1
1755   }
1756   \\new Staff {
1757     \\set Staff.instrumentName = \\markup {
1758       \\hcenter-in #12
1759       Bassoon
1760     }
1761     \\clef tenor
1762     c'1
1763   }
1764 >>
1765 @end lilypond"
1766   (interpret-markup layout props
1767                     (make-pad-to-box-markup
1768                      (cons (/ length -2) (/ length 2))
1769                      '(0 . 0)
1770                      (make-center-align-markup arg))))
1771
1772 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1773 ;; property
1774 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1775
1776 (define-markup-command (fromproperty layout props symbol)
1777   (symbol?)
1778   #:category other
1779   "Read the @var{symbol} from property settings, and produce a stencil
1780 from the markup contained within.  If @var{symbol} is not defined, it
1781 returns an empty markup.
1782
1783 @lilypond[verbatim,quote]
1784 \\header {
1785   myTitle = \"myTitle\"
1786   title = \\markup {
1787     from
1788     \\italic
1789     \\fromproperty #'header:myTitle
1790   }
1791 }
1792 \\markup {
1793   \\null
1794 }
1795 @end lilypond"
1796   (let ((m (chain-assoc-get symbol props)))
1797     (if (markup? m)
1798         (interpret-markup layout props m)
1799         empty-stencil)))
1800
1801 (define-markup-command (on-the-fly layout props procedure arg)
1802   (symbol? markup?)
1803   #:category other
1804   "Apply the @var{procedure} markup command to @var{arg}.
1805 @var{procedure} should take a single argument."
1806   (let ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
1807     (set-object-property! anonymous-with-signature
1808                           'markup-signature
1809                           (list markup?))
1810     (interpret-markup layout props (list anonymous-with-signature arg))))
1811
1812 (define-markup-command (override layout props new-prop arg)
1813   (pair? markup?)
1814   #:category other
1815   "
1816 @cindex overriding properties within text markup
1817
1818 Add the argument @var{new-prop} to the property list.  Properties
1819 may be any property supported by @rinternals{font-interface},
1820 @rinternals{text-interface} and
1821 @rinternals{instrument-specific-markup-interface}.
1822
1823 @lilypond[verbatim,quote]
1824 \\markup {
1825   \\line {
1826     \\column {
1827       default
1828       baseline-skip
1829     }
1830     \\hspace #2
1831     \\override #'(baseline-skip . 4) {
1832       \\column {
1833         increased
1834         baseline-skip
1835       }
1836     }
1837   }
1838 }
1839 @end lilypond"
1840   (interpret-markup layout (cons (list new-prop) props) arg))
1841
1842 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1843 ;; files
1844 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1845
1846 (define-markup-command (verbatim-file layout props name)
1847   (string?)
1848   #:category other
1849   "Read the contents of file @var{name}, and include it verbatim.
1850
1851 @lilypond[verbatim,quote]
1852 \\markup {
1853   \\verbatim-file #\"simple.ly\"
1854 }
1855 @end lilypond"
1856   (interpret-markup layout props
1857                     (if  (ly:get-option 'safe)
1858                          "verbatim-file disabled in safe mode"
1859                          (let* ((str (ly:gulp-file name))
1860                                 (lines (string-split str #\nl)))
1861                            (make-typewriter-markup
1862                             (make-column-markup lines))))))
1863
1864 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1865 ;; fonts.
1866 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1867
1868
1869 (define-markup-command (smaller layout props arg)
1870   (markup?)
1871   #:category font
1872   "Decrease the font size relative to the current setting.
1873
1874 @lilypond[verbatim,quote]
1875 \\markup {
1876   \\fontsize #3.5 {
1877     some large text
1878     \\hspace #2
1879     \\smaller {
1880       a bit smaller
1881     }
1882     \\hspace #2
1883     more large text
1884   }
1885 }
1886 @end lilypond"
1887   (interpret-markup layout props
1888    `(,fontsize-markup -1 ,arg)))
1889
1890 (define-markup-command (larger layout props arg)
1891   (markup?)
1892   #:category font
1893   "Increase the font size relative to the current setting.
1894
1895 @lilypond[verbatim,quote]
1896 \\markup {
1897   default
1898   \\hspace #2
1899   \\larger
1900   larger
1901 }
1902 @end lilypond"
1903   (interpret-markup layout props
1904    `(,fontsize-markup 1 ,arg)))
1905
1906 (define-markup-command (finger layout props arg)
1907   (markup?)
1908   #:category font
1909   "Set @var{arg} as small numbers.
1910
1911 @lilypond[verbatim,quote]
1912 \\markup {
1913   \\finger {
1914     1 2 3 4 5
1915   }
1916 }
1917 @end lilypond"
1918   (interpret-markup layout
1919                     (cons '((font-size . -5) (font-encoding . fetaText)) props)
1920                     arg))
1921
1922 (define-markup-command (abs-fontsize layout props size arg)
1923   (number? markup?)
1924   #:category font
1925   "Use @var{size} as the absolute font size to display @var{arg}.
1926 Adjusts @code{baseline-skip} and @code{word-space} accordingly.
1927
1928 @lilypond[verbatim,quote]
1929 \\markup {
1930   default text font size
1931   \\hspace #2
1932   \\abs-fontsize #16 { text font size 16 }
1933   \\hspace #2
1934   \\abs-fontsize #12 { text font size 12 }
1935 }
1936 @end lilypond"
1937   (let* ((ref-size (ly:output-def-lookup layout 'text-font-size 12))
1938          (text-props (list (ly:output-def-lookup layout 'text-font-defaults)))
1939          (ref-word-space (chain-assoc-get 'word-space text-props 0.6))
1940          (ref-baseline (chain-assoc-get 'baseline-skip text-props 3))
1941          (magnification (/ size ref-size)))
1942     (interpret-markup layout
1943                       (cons `((baseline-skip . ,(* magnification ref-baseline))
1944                               (word-space . ,(* magnification ref-word-space))
1945                               (font-size . ,(magnification->font-size magnification)))
1946                             props)
1947                       arg)))
1948
1949 (define-markup-command (fontsize layout props increment arg)
1950   (number? markup?)
1951   #:category font
1952   #:properties ((font-size 0)
1953                 (word-space 1)
1954                 (baseline-skip 2))
1955   "Add @var{increment} to the font-size.  Adjusts @code{baseline-skip}
1956 accordingly.
1957
1958 @lilypond[verbatim,quote]
1959 \\markup {
1960   default
1961   \\hspace #2
1962   \\fontsize #-1.5
1963   smaller
1964 }
1965 @end lilypond"
1966   (let ((entries (list
1967                   (cons 'baseline-skip (* baseline-skip (magstep increment)))
1968                   (cons 'word-space (* word-space (magstep increment)))
1969                   (cons 'font-size (+ font-size increment)))))
1970     (interpret-markup layout (cons entries props) arg)))
1971
1972 (define-markup-command (magnify layout props sz arg)
1973   (number? markup?)
1974   #:category font
1975   "
1976 @cindex magnifying text
1977
1978 Set the font magnification for its argument.  In the following
1979 example, the middle@tie{}A is 10% larger:
1980
1981 @example
1982 A \\magnify #1.1 @{ A @} A
1983 @end example
1984
1985 Note: Magnification only works if a font name is explicitly selected.
1986 Use @code{\\fontsize} otherwise.
1987
1988 @lilypond[verbatim,quote]
1989 \\markup {
1990   default
1991   \\hspace #2
1992   \\magnify #1.5 {
1993     50% larger
1994   }
1995 }
1996 @end lilypond"
1997   (interpret-markup
1998    layout
1999    (prepend-alist-chain 'font-size (magnification->font-size sz) props)
2000    arg))
2001
2002 (define-markup-command (bold layout props arg)
2003   (markup?)
2004   #:category font
2005   "Switch to bold font-series.
2006
2007 @lilypond[verbatim,quote]
2008 \\markup {
2009   default
2010   \\hspace #2
2011   \\bold
2012   bold
2013 }
2014 @end lilypond"
2015   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
2016
2017 (define-markup-command (sans layout props arg)
2018   (markup?)
2019   #:category font
2020   "Switch to the sans serif font family.
2021
2022 @lilypond[verbatim,quote]
2023 \\markup {
2024   default
2025   \\hspace #2
2026   \\sans {
2027     sans serif
2028   }
2029 }
2030 @end lilypond"
2031   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
2032
2033 (define-markup-command (number layout props arg)
2034   (markup?)
2035   #:category font
2036   "Set font family to @code{number}, which yields the font used for
2037 time signatures and fingerings.  This font contains numbers and
2038 some punctuation; it has no letters.
2039
2040 @lilypond[verbatim,quote]
2041 \\markup {
2042   \\number {
2043     0 1 2 3 4 5 6 7 8 9 . ,
2044   }
2045 }
2046 @end lilypond"
2047   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2048
2049 (define-markup-command (roman layout props arg)
2050   (markup?)
2051   #:category font
2052   "Set font family to @code{roman}.
2053
2054 @lilypond[verbatim,quote]
2055 \\markup {
2056   \\sans \\bold {
2057     sans serif, bold
2058     \\hspace #2
2059     \\roman {
2060       text in roman font family
2061     }
2062     \\hspace #2
2063     return to sans
2064   }
2065 }
2066 @end lilypond"
2067   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
2068
2069 (define-markup-command (huge layout props arg)
2070   (markup?)
2071   #:category font
2072   "Set font size to +2.
2073
2074 @lilypond[verbatim,quote]
2075 \\markup {
2076   default
2077   \\hspace #2
2078   \\huge
2079   huge
2080 }
2081 @end lilypond"
2082   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
2083
2084 (define-markup-command (large layout props arg)
2085   (markup?)
2086   #:category font
2087   "Set font size to +1.
2088
2089 @lilypond[verbatim,quote]
2090 \\markup {
2091   default
2092   \\hspace #2
2093   \\large
2094   large
2095 }
2096 @end lilypond"
2097   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
2098
2099 (define-markup-command (normalsize layout props arg)
2100   (markup?)
2101   #:category font
2102   "Set font size to default.
2103
2104 @lilypond[verbatim,quote]
2105 \\markup {
2106   \\teeny {
2107     this is very small
2108     \\hspace #2
2109     \\normalsize {
2110       normal size
2111     }
2112     \\hspace #2
2113     teeny again
2114   }
2115 }
2116 @end lilypond"
2117   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
2118
2119 (define-markup-command (small layout props arg)
2120   (markup?)
2121   #:category font
2122   "Set font size to -1.
2123
2124 @lilypond[verbatim,quote]
2125 \\markup {
2126   default
2127   \\hspace #2
2128   \\small
2129   small
2130 }
2131 @end lilypond"
2132   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
2133
2134 (define-markup-command (tiny layout props arg)
2135   (markup?)
2136   #:category font
2137   "Set font size to -2.
2138
2139 @lilypond[verbatim,quote]
2140 \\markup {
2141   default
2142   \\hspace #2
2143   \\tiny
2144   tiny
2145 }
2146 @end lilypond"
2147   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
2148
2149 (define-markup-command (teeny layout props arg)
2150   (markup?)
2151   #:category font
2152   "Set font size to -3.
2153
2154 @lilypond[verbatim,quote]
2155 \\markup {
2156   default
2157   \\hspace #2
2158   \\teeny
2159   teeny
2160 }
2161 @end lilypond"
2162   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
2163
2164 (define-markup-command (fontCaps layout props arg)
2165   (markup?)
2166   #:category font
2167   "Set @code{font-shape} to @code{caps}
2168
2169 Note: @code{\\fontCaps} requires the installation and selection of
2170 fonts which support the @code{caps} font shape."
2171   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
2172
2173 ;; Poor man's caps
2174 (define-markup-command (smallCaps layout props arg)
2175   (markup?)
2176   #:category font
2177   "Emit @var{arg} as small caps.
2178
2179 Note: @code{\\smallCaps} does not support accented characters.
2180
2181 @lilypond[verbatim,quote]
2182 \\markup {
2183   default
2184   \\hspace #2
2185   \\smallCaps {
2186     Text in small caps
2187   }
2188 }
2189 @end lilypond"
2190   (define (char-list->markup chars lower)
2191     (let ((final-string (string-upcase (reverse-list->string chars))))
2192       (if lower
2193           (markup #:fontsize -2 final-string)
2194           final-string)))
2195   (define (make-small-caps rest-chars currents current-is-lower prev-result)
2196     (if (null? rest-chars)
2197         (make-concat-markup
2198           (reverse! (cons (char-list->markup currents current-is-lower)
2199                           prev-result)))
2200         (let* ((ch (car rest-chars))
2201                (is-lower (char-lower-case? ch)))
2202           (if (or (and current-is-lower is-lower)
2203                   (and (not current-is-lower) (not is-lower)))
2204               (make-small-caps (cdr rest-chars)
2205                                (cons ch currents)
2206                                is-lower
2207                                prev-result)
2208               (make-small-caps (cdr rest-chars)
2209                                (list ch)
2210                                is-lower
2211                                (if (null? currents)
2212                                    prev-result
2213                                    (cons (char-list->markup
2214                                             currents current-is-lower)
2215                                          prev-result)))))))
2216   (interpret-markup layout props
2217     (if (string? arg)
2218         (make-small-caps (string->list arg) (list) #f (list))
2219         arg)))
2220
2221 (define-markup-command (caps layout props arg)
2222   (markup?)
2223   #:category font
2224   "Copy of the @code{\\smallCaps} command.
2225
2226 @lilypond[verbatim,quote]
2227 \\markup {
2228   default
2229   \\hspace #2
2230   \\caps {
2231     Text in small caps
2232   }
2233 }
2234 @end lilypond"
2235   (interpret-markup layout props (make-smallCaps-markup arg)))
2236
2237 (define-markup-command (dynamic layout props arg)
2238   (markup?)
2239   #:category font
2240   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
2241 @b{z}, @b{p}, and @b{r}.  When producing phrases, like
2242 @q{pi@`{u}@tie{}@b{f}}, the normal words (like @q{pi@`{u}}) should be
2243 done in a different font.  The recommended font for this is bold and italic.
2244 @lilypond[verbatim,quote]
2245 \\markup {
2246   \\dynamic {
2247     sfzp
2248   }
2249 }
2250 @end lilypond"
2251   (interpret-markup
2252    layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
2253
2254 (define-markup-command (text layout props arg)
2255   (markup?)
2256   #:category font
2257   "Use a text font instead of music symbol or music alphabet font.
2258
2259 @lilypond[verbatim,quote]
2260 \\markup {
2261   \\number {
2262     1, 2,
2263     \\text {
2264       three, four,
2265     }
2266     5
2267   }
2268 }
2269 @end lilypond"
2270
2271   ;; ugh - latin1
2272   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
2273                     arg))
2274
2275 (define-markup-command (italic layout props arg)
2276   (markup?)
2277   #:category font
2278   "Use italic @code{font-shape} for @var{arg}.
2279
2280 @lilypond[verbatim,quote]
2281 \\markup {
2282   default
2283   \\hspace #2
2284   \\italic
2285   italic
2286 }
2287 @end lilypond"
2288   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
2289
2290 (define-markup-command (typewriter layout props arg)
2291   (markup?)
2292   #:category font
2293   "Use @code{font-family} typewriter for @var{arg}.
2294
2295 @lilypond[verbatim,quote]
2296 \\markup {
2297   default
2298   \\hspace #2
2299   \\typewriter
2300   typewriter
2301 }
2302 @end lilypond"
2303   (interpret-markup
2304    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
2305
2306 (define-markup-command (upright layout props arg)
2307   (markup?)
2308   #:category font
2309   "Set @code{font-shape} to @code{upright}.  This is the opposite
2310 of @code{italic}.
2311
2312 @lilypond[verbatim,quote]
2313 \\markup {
2314   \\italic {
2315     italic text
2316     \\hspace #2
2317     \\upright {
2318       upright text
2319     }
2320     \\hspace #2
2321     italic again
2322   }
2323 }
2324 @end lilypond"
2325   (interpret-markup
2326    layout (prepend-alist-chain 'font-shape 'upright props) arg))
2327
2328 (define-markup-command (medium layout props arg)
2329   (markup?)
2330   #:category font
2331   "Switch to medium font-series (in contrast to bold).
2332
2333 @lilypond[verbatim,quote]
2334 \\markup {
2335   \\bold {
2336     some bold text
2337     \\hspace #2
2338     \\medium {
2339       medium font series
2340     }
2341     \\hspace #2
2342     bold again
2343   }
2344 }
2345 @end lilypond"
2346   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
2347                     arg))
2348
2349 (define-markup-command (normal-text layout props arg)
2350   (markup?)
2351   #:category font
2352   "Set all font related properties (except the size) to get the default
2353 normal text font, no matter what font was used earlier.
2354
2355 @lilypond[verbatim,quote]
2356 \\markup {
2357   \\huge \\bold \\sans \\caps {
2358     Some text with font overrides
2359     \\hspace #2
2360     \\normal-text {
2361       Default text, same font-size
2362     }
2363     \\hspace #2
2364     More text as before
2365   }
2366 }
2367 @end lilypond"
2368   ;; ugh - latin1
2369   (interpret-markup layout
2370                     (cons '((font-family . roman) (font-shape . upright)
2371                             (font-series . medium) (font-encoding . latin1))
2372                           props)
2373                     arg))
2374
2375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2376 ;; symbols.
2377 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2378
2379 (define-markup-command (doublesharp layout props)
2380   ()
2381   #:category music
2382   "Draw a double sharp symbol.
2383
2384 @lilypond[verbatim,quote]
2385 \\markup {
2386   \\doublesharp
2387 }
2388 @end lilypond"
2389   (interpret-markup layout props (markup #:musicglyph (assoc-get 1 standard-alteration-glyph-name-alist ""))))
2390
2391 (define-markup-command (sesquisharp layout props)
2392   ()
2393   #:category music
2394   "Draw a 3/2 sharp symbol.
2395
2396 @lilypond[verbatim,quote]
2397 \\markup {
2398   \\sesquisharp
2399 }
2400 @end lilypond"
2401   (interpret-markup layout props (markup #:musicglyph (assoc-get 3/4 standard-alteration-glyph-name-alist ""))))
2402
2403 (define-markup-command (sharp layout props)
2404   ()
2405   #:category music
2406   "Draw a sharp symbol.
2407
2408 @lilypond[verbatim,quote]
2409 \\markup {
2410   \\sharp
2411 }
2412 @end lilypond"
2413   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/2 standard-alteration-glyph-name-alist ""))))
2414
2415 (define-markup-command (semisharp layout props)
2416   ()
2417   #:category music
2418   "Draw a semisharp symbol.
2419
2420 @lilypond[verbatim,quote]
2421 \\markup {
2422   \\semisharp
2423 }
2424 @end lilypond"
2425   (interpret-markup layout props (markup #:musicglyph (assoc-get 1/4 standard-alteration-glyph-name-alist ""))))
2426
2427 (define-markup-command (natural layout props)
2428   ()
2429   #:category music
2430   "Draw a natural symbol.
2431
2432 @lilypond[verbatim,quote]
2433 \\markup {
2434   \\natural
2435 }
2436 @end lilypond"
2437   (interpret-markup layout props (markup #:musicglyph (assoc-get 0 standard-alteration-glyph-name-alist ""))))
2438
2439 (define-markup-command (semiflat layout props)
2440   ()
2441   #:category music
2442   "Draw a semiflat symbol.
2443
2444 @lilypond[verbatim,quote]
2445 \\markup {
2446   \\semiflat
2447 }
2448 @end lilypond"
2449   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/4 standard-alteration-glyph-name-alist ""))))
2450
2451 (define-markup-command (flat layout props)
2452   ()
2453   #:category music
2454   "Draw a flat symbol.
2455
2456 @lilypond[verbatim,quote]
2457 \\markup {
2458   \\flat
2459 }
2460 @end lilypond"
2461   (interpret-markup layout props (markup #:musicglyph (assoc-get -1/2 standard-alteration-glyph-name-alist ""))))
2462
2463 (define-markup-command (sesquiflat layout props)
2464   ()
2465   #:category music
2466   "Draw a 3/2 flat symbol.
2467
2468 @lilypond[verbatim,quote]
2469 \\markup {
2470   \\sesquiflat
2471 }
2472 @end lilypond"
2473   (interpret-markup layout props (markup #:musicglyph (assoc-get -3/4 standard-alteration-glyph-name-alist ""))))
2474
2475 (define-markup-command (doubleflat layout props)
2476   ()
2477   #:category music
2478   "Draw a double flat symbol.
2479
2480 @lilypond[verbatim,quote]
2481 \\markup {
2482   \\doubleflat
2483 }
2484 @end lilypond"
2485   (interpret-markup layout props (markup #:musicglyph (assoc-get -1 standard-alteration-glyph-name-alist ""))))
2486
2487 (define-markup-command (with-color layout props color arg)
2488   (color? markup?)
2489   #:category other
2490   "
2491 @cindex coloring text
2492
2493 Draw @var{arg} in color specified by @var{color}.
2494
2495 @lilypond[verbatim,quote]
2496 \\markup {
2497   \\with-color #red
2498   red
2499   \\hspace #2
2500   \\with-color #green
2501   green
2502   \\hspace #2
2503   \\with-color #blue
2504   blue
2505 }
2506 @end lilypond"
2507   (let ((stil (interpret-markup layout props arg)))
2508     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
2509                      (ly:stencil-extent stil X)
2510                      (ly:stencil-extent stil Y))))
2511
2512 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2513 ;; glyphs
2514 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2515
2516 (define-markup-command (arrow-head layout props axis dir filled)
2517   (integer? ly:dir? boolean?)
2518   #:category graphic
2519   "Produce an arrow head in specified direction and axis.
2520 Use the filled head if @var{filled} is specified.
2521 @lilypond[verbatim,quote]
2522 \\markup {
2523   \\fontsize #5 {
2524     \\general-align #Y #DOWN {
2525       \\arrow-head #Y #UP ##t
2526       \\arrow-head #Y #DOWN ##f
2527       \\hspace #2
2528       \\arrow-head #X #RIGHT ##f
2529       \\arrow-head #X #LEFT ##f
2530     }
2531   }
2532 }
2533 @end lilypond"
2534   (let*
2535       ((name (format "arrowheads.~a.~a~a"
2536                      (if filled
2537                          "close"
2538                          "open")
2539                      axis
2540                      dir)))
2541     (ly:font-get-glyph
2542      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
2543                                      props))
2544      name)))
2545
2546 (define-markup-command (musicglyph layout props glyph-name)
2547   (string?)
2548   #:category music
2549   "@var{glyph-name} is converted to a musical symbol; for example,
2550 @code{\\musicglyph #\"accidentals.natural\"} selects the natural sign from
2551 the music font.  See @ruser{The Feta font} for a complete listing of
2552 the possible glyphs.
2553
2554 @lilypond[verbatim,quote]
2555 \\markup {
2556   \\musicglyph #\"f\"
2557   \\musicglyph #\"rests.2\"
2558   \\musicglyph #\"clefs.G_change\"
2559 }
2560 @end lilypond"
2561   (let* ((font (ly:paper-get-font layout
2562                                   (cons '((font-encoding . fetaMusic)
2563                                           (font-name . #f))
2564
2565                                                  props)))
2566          (glyph (ly:font-get-glyph font glyph-name)))
2567     (if (null? (ly:stencil-expr glyph))
2568         (ly:warning (_ "Cannot find glyph ~a") glyph-name))
2569
2570     glyph))
2571
2572
2573 (define-markup-command (lookup layout props glyph-name)
2574   (string?)
2575   #:category other
2576   "Lookup a glyph by name.
2577
2578 @lilypond[verbatim,quote]
2579 \\markup {
2580   \\override #'(font-encoding . fetaBraces) {
2581     \\lookup #\"brace200\"
2582     \\hspace #2
2583     \\rotate #180
2584     \\lookup #\"brace180\"
2585   }
2586 }
2587 @end lilypond"
2588   (ly:font-get-glyph (ly:paper-get-font layout props)
2589                      glyph-name))
2590
2591 (define-markup-command (char layout props num)
2592   (integer?)
2593   #:category other
2594   "Produce a single character.  Characters encoded in hexadecimal
2595 format require the prefix @code{#x}.
2596
2597 @lilypond[verbatim,quote]
2598 \\markup {
2599   \\char #65 \\char ##x00a9
2600 }
2601 @end lilypond"
2602   (ly:text-interface::interpret-markup layout props (ly:wide-char->utf-8 num)))
2603
2604 (define number->mark-letter-vector (make-vector 25 #\A))
2605
2606 (do ((i 0 (1+ i))
2607      (j 0 (1+ j)))
2608     ((>= i 26))
2609   (if (= i (- (char->integer #\I) (char->integer #\A)))
2610       (set! i (1+ i)))
2611   (vector-set! number->mark-letter-vector j
2612                (integer->char (+ i (char->integer #\A)))))
2613
2614 (define number->mark-alphabet-vector (list->vector
2615   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
2616
2617 (define (number->markletter-string vec n)
2618   "Double letters for big marks."
2619   (let* ((lst (vector-length vec)))
2620
2621     (if (>= n lst)
2622         (string-append (number->markletter-string vec (1- (quotient n lst)))
2623                        (number->markletter-string vec (remainder n lst)))
2624         (make-string 1 (vector-ref vec n)))))
2625
2626 (define-markup-command (markletter layout props num)
2627   (integer?)
2628   #:category other
2629   "Make a markup letter for @var{num}.  The letters start with A to@tie{}Z
2630 (skipping letter@tie{}I), and continue with double letters.
2631
2632 @lilypond[verbatim,quote]
2633 \\markup {
2634   \\markletter #8
2635   \\hspace #2
2636   \\markletter #26
2637 }
2638 @end lilypond"
2639   (ly:text-interface::interpret-markup layout props
2640     (number->markletter-string number->mark-letter-vector num)))
2641
2642 (define-markup-command (markalphabet layout props num)
2643   (integer?)
2644   #:category other
2645    "Make a markup letter for @var{num}.  The letters start with A to@tie{}Z
2646 and continue with double letters.
2647
2648 @lilypond[verbatim,quote]
2649 \\markup {
2650   \\markalphabet #8
2651   \\hspace #2
2652   \\markalphabet #26
2653 }
2654 @end lilypond"
2655    (ly:text-interface::interpret-markup layout props
2656      (number->markletter-string number->mark-alphabet-vector num)))
2657
2658 (define-public (horizontal-slash-interval num forward number-interval mag)
2659   (if forward
2660     (cond ;((= num 6) (interval-widen number-interval (* mag 0.5)))
2661           ;((= num 5) (interval-widen number-interval (* mag 0.5)))
2662           (else (interval-widen number-interval (* mag 0.25))))
2663     (cond ((= num 6) (interval-widen number-interval (* mag 0.5)))
2664           ;((= num 5) (interval-widen number-interval (* mag 0.5)))
2665           (else (interval-widen number-interval (* mag 0.25))))
2666   ))
2667
2668 (define-public (adjust-slash-stencil num forward stencil mag)
2669   (if forward
2670     (cond ((= num 2)
2671               (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
2672           ((= num 3)
2673               (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
2674           ;((= num 5)
2675               ;(ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.07))))
2676           ;((= num 7)
2677           ;    (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
2678           (else stencil))
2679     (cond ((= num 6)
2680               (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.15))))
2681           ;((= num 8)
2682           ;    (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
2683           (else stencil))
2684   )
2685 )
2686
2687 (define (slashed-digit-internal layout props num forward font-size thickness)
2688   (let* ((mag (magstep font-size))
2689          (thickness (* mag
2690                        (ly:output-def-lookup layout 'line-thickness)
2691                        thickness))
2692          ; backward slashes might use slope and point in the other direction!
2693          (dy (* mag (if forward 0.4 -0.4)))
2694          (number-stencil (interpret-markup layout
2695                                            (prepend-alist-chain 'font-encoding 'fetaText props)
2696                                            (number->string num)))
2697          (num-x (horizontal-slash-interval num forward (ly:stencil-extent number-stencil X) mag))
2698          (center (interval-center (ly:stencil-extent number-stencil Y)))
2699          ; Use the real extents of the slash, not the whole number, because we
2700          ; might translate the slash later on!
2701          (num-y (interval-widen (cons center center) (abs dy)))
2702          (is-sane (and (interval-sane? num-x) (interval-sane? num-y)))
2703          (slash-stencil (if is-sane
2704                             (make-line-stencil thickness
2705                                          (car num-x) (- (interval-center num-y) dy)
2706                                          (cdr num-x) (+ (interval-center num-y) dy))
2707                             #f)))
2708     (if (ly:stencil? slash-stencil)
2709       (begin
2710         ; for some numbers we need to shift the slash/backslash up or down to make
2711         ; the slashed digit look better
2712         (set! slash-stencil (adjust-slash-stencil num forward slash-stencil mag))
2713         (set! number-stencil
2714           (ly:stencil-add number-stencil slash-stencil)))
2715       (ly:warning "Unable to create slashed digit ~a" num))
2716     number-stencil))
2717
2718
2719 (define-markup-command (slashed-digit layout props num)
2720   (integer?)
2721   #:category other
2722   #:properties ((font-size 0)
2723                 (thickness 1.6))
2724   "
2725 @cindex slashed digits
2726
2727 A feta number, with slash.  This is for use in the context of
2728 figured bass notation.
2729 @lilypond[verbatim,quote]
2730 \\markup {
2731   \\slashed-digit #5
2732   \\hspace #2
2733   \\override #'(thickness . 3)
2734   \\slashed-digit #7
2735 }
2736 @end lilypond"
2737   (slashed-digit-internal layout props num #t font-size thickness))
2738
2739 (define-markup-command (backslashed-digit layout props num)
2740   (integer?)
2741   #:category other
2742   #:properties ((font-size 0)
2743                 (thickness 1.6))
2744   "
2745 @cindex backslashed digits
2746
2747 A feta number, with backslash.  This is for use in the context of
2748 figured bass notation.
2749 @lilypond[verbatim,quote]
2750 \\markup {
2751   \\backslashed-digit #5
2752   \\hspace #2
2753   \\override #'(thickness . 3)
2754   \\backslashed-digit #7
2755 }
2756 @end lilypond"
2757   (slashed-digit-internal layout props num #f font-size thickness))
2758
2759 ;; eyeglasses
2760 (define eyeglassespath
2761   '((moveto 0.42 0.77)
2762     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
2763     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
2764     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
2765     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
2766     (closepath)
2767     (moveto 2.07 0.77)
2768     (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
2769     (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
2770     (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
2771     (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
2772     (closepath)
2773     (moveto 1.025 0.935)
2774     (rcurveto 0 0.182 -0.148 0.33 -0.33 0.33)
2775     (rcurveto -0.182 0 -0.33 -0.148 -0.33 -0.33)
2776     (moveto -0.68 0.77)
2777     (rlineto 0.66 1.43)
2778     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)
2779     (moveto 2.07 0.77)
2780     (rlineto 0.66 1.43)
2781     (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)))
2782
2783 (define-markup-command (eyeglasses layout props)
2784   ()
2785   #:category other
2786   "Prints out eyeglasses, indicating strongly to look at the conductor.
2787 @lilypond[verbatim,quote]
2788 \\markup { \\eyeglasses }
2789 @end lilypond"
2790   (interpret-markup layout props
2791     (make-override-markup '(line-cap-style . butt)
2792       (make-path-markup 0.15 eyeglassespath))))
2793
2794 (define-markup-command (left-brace layout props size)
2795   (number?)
2796   #:category other
2797   "
2798 A feta brace in point size @var{size}.
2799
2800 @lilypond[verbatim,quote]
2801 \\markup {
2802   \\left-brace #35
2803   \\hspace #2
2804   \\left-brace #45
2805 }
2806 @end lilypond"
2807   (let* ((font (ly:paper-get-font layout
2808                                   (cons '((font-encoding . fetaBraces)
2809                                           (font-name . #f))
2810                                         props)))
2811          (glyph-count (1- (ly:otf-glyph-count font)))
2812          (scale (ly:output-def-lookup layout 'output-scale))
2813          (scaled-size (/ (ly:pt size) scale))
2814          (glyph (lambda (n)
2815                   (ly:font-get-glyph font (string-append "brace"
2816                                                          (number->string n)))))
2817          (get-y-from-brace (lambda (brace)
2818                              (interval-length
2819                               (ly:stencil-extent (glyph brace) Y))))
2820          (find-brace (binary-search 0 glyph-count get-y-from-brace scaled-size))
2821          (glyph-found (glyph find-brace)))
2822
2823     (if (or (null? (ly:stencil-expr glyph-found))
2824             (< scaled-size (interval-length (ly:stencil-extent (glyph 0) Y)))
2825             (> scaled-size (interval-length
2826                             (ly:stencil-extent (glyph glyph-count) Y))))
2827         (begin
2828           (ly:warning (_ "no brace found for point size ~S ") size)
2829           (ly:warning (_ "defaulting to ~S pt")
2830                       (/ (* scale (interval-length
2831                                    (ly:stencil-extent glyph-found Y)))
2832                          (ly:pt 1)))))
2833     glyph-found))
2834
2835 (define-markup-command (right-brace layout props size)
2836   (number?)
2837   #:category other
2838   "
2839 A feta brace in point size @var{size}, rotated 180 degrees.
2840
2841 @lilypond[verbatim,quote]
2842 \\markup {
2843   \\right-brace #45
2844   \\hspace #2
2845   \\right-brace #35
2846 }
2847 @end lilypond"
2848   (interpret-markup layout props (markup #:rotate 180 #:left-brace size)))
2849
2850 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2851 ;; the note command.
2852 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2853
2854 ;; TODO: better syntax.
2855
2856 (define-markup-command (note-by-number layout props log dot-count dir)
2857   (number? number? number?)
2858   #:category music
2859   #:properties ((font-size 0)
2860                 (style '()))
2861   "
2862 @cindex notes within text by log and dot-count
2863
2864 Construct a note symbol, with stem.  By using fractional values for
2865 @var{dir}, longer or shorter stems can be obtained.
2866
2867 @lilypond[verbatim,quote]
2868 \\markup {
2869   \\note-by-number #3 #0 #DOWN
2870   \\hspace #2
2871   \\note-by-number #1 #2 #0.8
2872 }
2873 @end lilypond"
2874   (define (get-glyph-name-candidates dir log style)
2875     (map (lambda (dir-name)
2876            (format "noteheads.~a~a" dir-name
2877                    (if (and (symbol? style)
2878                             (not (equal? 'default style)))
2879                        (select-head-glyph style (min log 2))
2880                        (min log 2))))
2881          (list (if (= dir UP) "u" "d")
2882                "s")))
2883
2884   (define (get-glyph-name font cands)
2885     (if (null? cands)
2886         ""
2887         (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
2888             (get-glyph-name font (cdr cands))
2889             (car cands))))
2890
2891   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
2892                                                props)))
2893          (size-factor (magstep font-size))
2894          (stem-length (* size-factor (max 3 (- log 1))))
2895          (head-glyph-name
2896           (let ((result (get-glyph-name font (get-glyph-name-candidates
2897                                               (sign dir) log style))))
2898             (if (string-null? result)
2899                 ;; If no glyph name can be found, select default heads.  Though
2900                 ;; this usually means an unsupported style has been chosen, it
2901                 ;; also prevents unrelated 'style settings from other grobs
2902                 ;; (e.g., TextSpanner and TimeSignature) leaking into markup.
2903                 (get-glyph-name font (get-glyph-name-candidates
2904                                       (sign dir) log 'default))
2905                 result)))
2906          (head-glyph (ly:font-get-glyph font head-glyph-name))
2907          (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
2908          (stem-thickness (* size-factor 0.13))
2909          (stemy (* dir stem-length))
2910          (attach-off (cons (interval-index
2911                             (ly:stencil-extent head-glyph X)
2912                             (* (sign dir) (car attach-indices)))
2913                            (* (sign dir) ; fixme, this is inconsistent between X & Y.
2914                               (interval-index
2915                                (ly:stencil-extent head-glyph Y)
2916                                (cdr attach-indices)))))
2917          (stem-glyph (and (> log 0)
2918                           (ly:round-filled-box
2919                            (ordered-cons (car attach-off)
2920                                          (+ (car attach-off)
2921                                             (* (- (sign dir)) stem-thickness)))
2922                            (cons (min stemy (cdr attach-off))
2923                                  (max stemy (cdr attach-off)))
2924                            (/ stem-thickness 3))))
2925
2926          (dot (ly:font-get-glyph font "dots.dot"))
2927          (dotwid (interval-length (ly:stencil-extent dot X)))
2928          (dots (and (> dot-count 0)
2929                     (apply ly:stencil-add
2930                            (map (lambda (x)
2931                                   (ly:stencil-translate-axis
2932                                    dot (* 2 x dotwid) X))
2933                                 (iota dot-count)))))
2934          (flaggl (and (> log 2)
2935                       (ly:stencil-translate
2936                        (ly:font-get-glyph font
2937                                           (string-append "flags."
2938                                                          (if (> dir 0) "u" "d")
2939                                                          (number->string log)))
2940                        (cons (+ (car attach-off) (if (< dir 0)
2941                                                      stem-thickness 0))
2942                              stemy)))))
2943
2944     ;; If there is a flag on an upstem and the stem is short, move the dots
2945     ;; to avoid the flag.  16th notes get a special case because their flags
2946     ;; hang lower than any other flags.
2947     (if (and dots (> dir 0) (> log 2)
2948              (or (< dir 1.15) (and (= log 4) (< dir 1.3))))
2949         (set! dots (ly:stencil-translate-axis dots 0.5 X)))
2950     (if flaggl
2951         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
2952     (if (ly:stencil? stem-glyph)
2953         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
2954         (set! stem-glyph head-glyph))
2955     (if (ly:stencil? dots)
2956         (set! stem-glyph
2957               (ly:stencil-add
2958                (ly:stencil-translate-axis
2959                 dots
2960                 (+ (cdr (ly:stencil-extent head-glyph X)) dotwid)
2961                 X)
2962                stem-glyph)))
2963     stem-glyph))
2964
2965 (define-public log2
2966   (let ((divisor (log 2)))
2967     (lambda (z) (inexact->exact (/ (log z) divisor)))))
2968
2969 (define (parse-simple-duration duration-string)
2970   "Parse the `duration-string', e.g. ''4..'' or ''breve.'',
2971 and return a (log dots) list."
2972   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)")
2973                             duration-string)))
2974     (if (and match (string=? duration-string (match:substring match 0)))
2975         (let ((len (match:substring match 1))
2976               (dots (match:substring match 2)))
2977           (list (cond ((string=? len "breve") -1)
2978                       ((string=? len "longa") -2)
2979                       ((string=? len "maxima") -3)
2980                       (else (log2 (string->number len))))
2981                 (if dots (string-length dots) 0)))
2982         (ly:error (_ "not a valid duration string: ~a") duration-string))))
2983
2984 (define-markup-command (note layout props duration dir)
2985   (string? number?)
2986   #:category music
2987   #:properties (note-by-number-markup)
2988   "
2989 @cindex notes within text by string
2990
2991 This produces a note with a stem pointing in @var{dir} direction, with
2992 the @var{duration} for the note head type and augmentation dots.  For
2993 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
2994 a shortened down stem.
2995
2996 @lilypond[verbatim,quote]
2997 \\markup {
2998   \\override #'(style . cross) {
2999     \\note #\"4..\" #UP
3000   }
3001   \\hspace #2
3002   \\note #\"breve\" #0
3003 }
3004 @end lilypond"
3005   (let ((parsed (parse-simple-duration duration)))
3006     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
3007
3008 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3009 ;; translating.
3010 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3011
3012 (define-markup-command (lower layout props amount arg)
3013   (number? markup?)
3014   #:category align
3015   "
3016 @cindex lowering text
3017
3018 Lower @var{arg} by the distance @var{amount}.
3019 A negative @var{amount} indicates raising; see also @code{\\raise}.
3020
3021 @lilypond[verbatim,quote]
3022 \\markup {
3023   one
3024   \\lower #3
3025   two
3026   three
3027 }
3028 @end lilypond"
3029   (ly:stencil-translate-axis (interpret-markup layout props arg)
3030                              (- amount) Y))
3031
3032 (define-markup-command (translate-scaled layout props offset arg)
3033   (number-pair? markup?)
3034   #:category align
3035   #:properties ((font-size 0))
3036   "
3037 @cindex translating text
3038 @cindex scaling text
3039
3040 Translate @var{arg} by @var{offset}, scaling the offset by the
3041 @code{font-size}.
3042
3043 @lilypond[verbatim,quote]
3044 \\markup {
3045   \\fontsize #5 {
3046     * \\translate #'(2 . 3) translate
3047     \\hspace #2
3048     * \\translate-scaled #'(2 . 3) translate-scaled
3049   }
3050 }
3051 @end lilypond"
3052   (let* ((factor (magstep font-size))
3053          (scaled (cons (* factor (car offset))
3054                        (* factor (cdr offset)))))
3055     (ly:stencil-translate (interpret-markup layout props arg)
3056                           scaled)))
3057
3058 (define-markup-command (raise layout props amount arg)
3059   (number? markup?)
3060   #:category align
3061   "
3062 @cindex raising text
3063
3064 Raise @var{arg} by the distance @var{amount}.
3065 A negative @var{amount} indicates lowering, see also @code{\\lower}.
3066
3067 The argument to @code{\\raise} is the vertical displacement amount,
3068 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
3069 raise objects in relation to their surrounding markups.
3070
3071 If the text object itself is positioned above or below the staff, then
3072 @code{\\raise} cannot be used to move it, since the mechanism that
3073 positions it next to the staff cancels any shift made with
3074 @code{\\raise}.  For vertical positioning, use the @code{padding}
3075 and/or @code{extra-offset} properties.
3076
3077 @lilypond[verbatim,quote]
3078 \\markup {
3079   C
3080   \\small
3081   \\bold
3082   \\raise #1.0
3083   9/7+
3084 }
3085 @end lilypond"
3086   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
3087
3088 (define-markup-command (fraction layout props arg1 arg2)
3089   (markup? markup?)
3090   #:category other
3091   #:properties ((font-size 0))
3092   "
3093 @cindex creating text fractions
3094
3095 Make a fraction of two markups.
3096 @lilypond[verbatim,quote]
3097 \\markup {
3098   Ï€ â‰ˆ
3099   \\fraction 355 113
3100 }
3101 @end lilypond"
3102   (let* ((m1 (interpret-markup layout props arg1))
3103          (m2 (interpret-markup layout props arg2))
3104          (factor (magstep font-size))
3105          (boxdimen (cons (* factor -0.05) (* factor 0.05)))
3106          (padding (* factor 0.2))
3107          (baseline (* factor 0.6))
3108          (offset (* factor 0.75)))
3109     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
3110     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
3111     (let* ((x1 (ly:stencil-extent m1 X))
3112            (x2 (ly:stencil-extent m2 X))
3113            (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0))
3114            ;; should stack mols separately, to maintain LINE on baseline
3115            (stack (stack-lines DOWN padding baseline (list m1 line m2))))
3116       (set! stack
3117             (ly:stencil-aligned-to stack Y CENTER))
3118       (set! stack
3119             (ly:stencil-aligned-to stack X LEFT))
3120       ;; should have EX dimension
3121       ;; empirical anyway
3122       (ly:stencil-translate-axis stack offset Y))))
3123
3124 (define-markup-command (normal-size-super layout props arg)
3125   (markup?)
3126   #:category font
3127   #:properties ((baseline-skip))
3128   "
3129 @cindex setting superscript in standard font size
3130
3131 Set @var{arg} in superscript with a normal font size.
3132
3133 @lilypond[verbatim,quote]
3134 \\markup {
3135   default
3136   \\normal-size-super {
3137     superscript in standard size
3138   }
3139 }
3140 @end lilypond"
3141   (ly:stencil-translate-axis
3142    (interpret-markup layout props arg)
3143    (* 0.5 baseline-skip) Y))
3144
3145 (define-markup-command (super layout props arg)
3146   (markup?)
3147   #:category font
3148   #:properties ((font-size 0)
3149                 (baseline-skip))
3150   "
3151 @cindex superscript text
3152
3153 Set @var{arg} in superscript.
3154
3155 @lilypond[verbatim,quote]
3156 \\markup {
3157   E =
3158   \\concat {
3159     mc
3160     \\super
3161     2
3162   }
3163 }
3164 @end lilypond"
3165   (ly:stencil-translate-axis
3166    (interpret-markup
3167     layout
3168     (cons `((font-size . ,(- font-size 3))) props)
3169     arg)
3170    (* 0.5 baseline-skip)
3171    Y))
3172
3173 (define-markup-command (translate layout props offset arg)
3174   (number-pair? markup?)
3175   #:category align
3176   "
3177 @cindex translating text
3178
3179 Translate @var{arg} relative to its surroundings.  @var{offset}
3180 is a pair of numbers representing the displacement in the X and Y axis.
3181
3182 @lilypond[verbatim,quote]
3183 \\markup {
3184   *
3185   \\translate #'(2 . 3)
3186   \\line { translated two spaces right, three up }
3187 }
3188 @end lilypond"
3189   (ly:stencil-translate (interpret-markup layout props arg)
3190                         offset))
3191
3192 (define-markup-command (sub layout props arg)
3193   (markup?)
3194   #:category font
3195   #:properties ((font-size 0)
3196                 (baseline-skip))
3197   "
3198 @cindex subscript text
3199
3200 Set @var{arg} in subscript.
3201
3202 @lilypond[verbatim,quote]
3203 \\markup {
3204   \\concat {
3205     H
3206     \\sub {
3207       2
3208     }
3209     O
3210   }
3211 }
3212 @end lilypond"
3213   (ly:stencil-translate-axis
3214    (interpret-markup
3215     layout
3216     (cons `((font-size . ,(- font-size 3))) props)
3217     arg)
3218    (* -0.5 baseline-skip)
3219    Y))
3220
3221 (define-markup-command (normal-size-sub layout props arg)
3222   (markup?)
3223   #:category font
3224   #:properties ((baseline-skip))
3225   "
3226 @cindex setting subscript in standard font size
3227
3228 Set @var{arg} in subscript with a normal font size.
3229
3230 @lilypond[verbatim,quote]
3231 \\markup {
3232   default
3233   \\normal-size-sub {
3234     subscript in standard size
3235   }
3236 }
3237 @end lilypond"
3238   (ly:stencil-translate-axis
3239    (interpret-markup layout props arg)
3240    (* -0.5 baseline-skip)
3241    Y))
3242
3243 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3244 ;; brackets.
3245 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3246
3247 (define-markup-command (hbracket layout props arg)
3248   (markup?)
3249   #:category graphic
3250   "
3251 @cindex placing horizontal brackets around text
3252
3253 Draw horizontal brackets around @var{arg}.
3254
3255 @lilypond[verbatim,quote]
3256 \\markup {
3257   \\hbracket {
3258     \\line {
3259       one two three
3260     }
3261   }
3262 }
3263 @end lilypond"
3264   (let ((th 0.1) ;; todo: take from GROB.
3265         (m (interpret-markup layout props arg)))
3266     (bracketify-stencil m X th (* 2.5 th) th)))
3267
3268 (define-markup-command (bracket layout props arg)
3269   (markup?)
3270   #:category graphic
3271   "
3272 @cindex placing vertical brackets around text
3273
3274 Draw vertical brackets around @var{arg}.
3275
3276 @lilypond[verbatim,quote]
3277 \\markup {
3278   \\bracket {
3279     \\note #\"2.\" #UP
3280   }
3281 }
3282 @end lilypond"
3283   (let ((th 0.1) ;; todo: take from GROB.
3284         (m (interpret-markup layout props arg)))
3285     (bracketify-stencil m Y th (* 2.5 th) th)))
3286
3287 (define-markup-command (parenthesize layout props arg)
3288   (markup?)
3289   #:category graphic
3290   #:properties ((angularity 0)
3291                 (padding)
3292                 (size 1)
3293                 (thickness 1)
3294                 (width 0.25))
3295   "
3296 @cindex placing parentheses around text
3297
3298 Draw parentheses around @var{arg}.  This is useful for parenthesizing
3299 a column containing several lines of text.
3300
3301 @lilypond[verbatim,quote]
3302 \\markup {
3303   \\line {
3304     \\parenthesize {
3305       \\column {
3306         foo
3307         bar
3308       }
3309     }
3310     \\override #'(angularity . 2) {
3311       \\parenthesize {
3312         \\column {
3313           bah
3314           baz
3315         }
3316       }
3317     }
3318   }
3319 }
3320 @end lilypond"
3321   (let* ((markup (interpret-markup layout props arg))
3322          (scaled-width (* size width))
3323          (scaled-thickness
3324           (* (chain-assoc-get 'line-thickness props 0.1)
3325              thickness))
3326          (half-thickness
3327           (min (* size 0.5 scaled-thickness)
3328                (* (/ 4 3.0) scaled-width)))
3329          (padding (chain-assoc-get 'padding props half-thickness)))
3330     (parenthesize-stencil
3331      markup half-thickness scaled-width angularity padding)))
3332
3333 \f
3334 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3335 ;; Delayed markup evaluation
3336 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3337
3338 (define-markup-command (page-ref layout props label gauge default)
3339   (symbol? markup? markup?)
3340   #:category other
3341   "
3342 @cindex referencing page numbers in text
3343
3344 Reference to a page number. @var{label} is the label set on the referenced
3345 page (using the @code{\\label} command), @var{gauge} a markup used to estimate
3346 the maximum width of the page number, and @var{default} the value to display
3347 when @var{label} is not found."
3348   (let* ((gauge-stencil (interpret-markup layout props gauge))
3349          (x-ext (ly:stencil-extent gauge-stencil X))
3350          (y-ext (ly:stencil-extent gauge-stencil Y)))
3351     (ly:make-stencil
3352      `(delay-stencil-evaluation
3353        ,(delay (ly:stencil-expr
3354                 (let* ((table (ly:output-def-lookup layout 'label-page-table))
3355                        (page-number (if (list? table)
3356                                         (assoc-get label table)
3357                                         #f))
3358                        (page-markup (if page-number (format "~a" page-number) default))
3359                        (page-stencil (interpret-markup layout props page-markup))
3360                        (gap (- (interval-length x-ext)
3361                                (interval-length (ly:stencil-extent page-stencil X)))))
3362                   (interpret-markup layout props
3363                                     (markup #:concat (#:hspace gap page-markup)))))))
3364      x-ext
3365      y-ext)))
3366
3367 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3368 ;; Markup list commands
3369 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3370
3371 (define-public (space-lines baseline stils)
3372   (let space-stil ((stils stils)
3373                    (result (list)))
3374     (if (null? stils)
3375         (reverse! result)
3376         (let* ((stil (car stils))
3377                (dy-top (max (- (/ baseline 1.5)
3378                                (interval-bound (ly:stencil-extent stil Y) UP))
3379                             0.0))
3380                (dy-bottom (max (+ (/ baseline 3.0)
3381                                   (interval-bound (ly:stencil-extent stil Y) DOWN))
3382                                0.0))
3383                (new-stil (ly:make-stencil
3384                           (ly:stencil-expr stil)
3385                           (ly:stencil-extent stil X)
3386                           (cons (- (interval-bound (ly:stencil-extent stil Y) DOWN)
3387                                    dy-bottom)
3388                                 (+ (interval-bound (ly:stencil-extent stil Y) UP)
3389                                    dy-top)))))
3390           (space-stil (cdr stils) (cons new-stil result))))))
3391
3392 (define-markup-list-command (justified-lines layout props args)
3393   (markup-list?)
3394   #:properties ((baseline-skip)
3395                 wordwrap-internal-markup-list)
3396   "
3397 @cindex justifying lines of text
3398
3399 Like @code{\\justify}, but return a list of lines instead of a single markup.
3400 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width;
3401 @var{X}@tie{}is the number of staff spaces."
3402   (space-lines baseline-skip
3403                (interpret-markup-list layout props
3404                                       (make-wordwrap-internal-markup-list #t args))))
3405
3406 (define-markup-list-command (wordwrap-lines layout props args)
3407   (markup-list?)
3408   #:properties ((baseline-skip)
3409                 wordwrap-internal-markup-list)
3410   "Like @code{\\wordwrap}, but return a list of lines instead of a single markup.
3411 Use @code{\\override-lines #'(line-width . @var{X})} to set the line width,
3412 where @var{X} is the number of staff spaces."
3413   (space-lines baseline-skip
3414                (interpret-markup-list layout props
3415                                       (make-wordwrap-internal-markup-list #f args))))
3416
3417 (define-markup-list-command (column-lines layout props args)
3418   (markup-list?)
3419   #:properties ((baseline-skip))
3420   "Like @code{\\column}, but return a list of lines instead of a single markup.
3421 @code{baseline-skip} determines the space between each markup in @var{args}."
3422   (space-lines baseline-skip
3423                (interpret-markup-list layout props args)))
3424
3425 (define-markup-list-command (override-lines layout props new-prop args)
3426   (pair? markup-list?)
3427   "Like @code{\\override}, for markup lists."
3428   (interpret-markup-list layout (cons (list new-prop) props) args))