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