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