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