(with-dimensions):
[lilypond/patrick.git] / scm / define-markup-commands.scm
blob7e405f0f19ccf13313777427c8ded5ae92a51561
1 ;;;; define-markup-commands.scm -- markup commands
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;; 
5 ;;;; (c) 2000--2006  Han-Wen Nienhuys <hanwen@cs.uu.nl>
6 ;;;;                  Jan Nieuwenhuizen <janneke@gnu.org>
9 ;;; markup commands
10 ;;;  * each markup function should have a doc string with
11 ;;     syntax, description and example. 
13 (use-modules (ice-9 regex))
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16 ;; utility functions
17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19 (define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1)))
20 (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;; geometric shapes
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 (def-markup-command (draw-circle layout props radius thickness fill)
28   (number? number? boolean?)
29   "A circle of radius @var{radius}, thickness @var{thickness} and
30 optionally filled."
31   (make-circle-stencil radius thickness fill))
33 (def-markup-command (triangle layout props filled) (boolean?)
34   "A triangle, filled or not"
35   (let*
36       ((th (chain-assoc-get 'thickness props  0.1))
37        (size (chain-assoc-get 'font-size props 0))
38        (ex (* (magstep size)
39               0.8
40               (chain-assoc-get 'baseline-skip props 2))))
42     (ly:make-stencil
43      `(polygon '(0.0 0.0
44                      ,ex 0.0
45                      ,(* 0.5 ex)
46                      ,(* 0.86 ex))
47            ,th
48            ,filled)
50      (cons 0 ex)
51      (cons 0 (* .86 ex))
52      )))
54 (def-markup-command (circle layout props arg) (markup?)
55   "Draw a circle around @var{arg}.  Use @code{thickness},
56 @code{circle-padding} and @code{font-size} properties to determine line
57 thickness and padding around the markup."
58   (let* ((th (chain-assoc-get 'thickness props  0.1))
59          (size (chain-assoc-get 'font-size props 0))
60          (pad
61           (* (magstep size)
62              (chain-assoc-get 'circle-padding props 0.2)))
63          (m (interpret-markup layout props arg)))
64     (circle-stencil m th pad)))
66 (def-markup-command (with-url layout props url arg) (string? markup?)
67   "Add a link to URL @var{url} around @var{arg}. This only works in
68 the PDF backend."
69   (let* ((stil (interpret-markup layout props arg))
70          (xextent (ly:stencil-extent stil X))
71          (yextent (ly:stencil-extent stil Y))
72          (old-expr (ly:stencil-expr stil))
73          (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
74     (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
77 (def-markup-command (beam layout props width slope thickness)
78   (number? number? number?)
79   "Create a beam with the specified parameters."
80   (let* ((y (* slope width))
81          (yext (cons (min 0 y) (max 0 y)))
82          (half (/ thickness 2)))
84     (ly:make-stencil
85      `(polygon ',(list 
86                   0 (/ thickness -2)
87                     width (+ (* width slope)  (/ thickness -2))
88                     width (+ (* width slope)  (/ thickness 2))
89                     0 (/ thickness 2))
90                ,(ly:output-def-lookup layout 'blotdiameter)
91                #t)
92      (cons 0 width)
93      (cons (+ (- half) (car yext))
94            (+ half (cdr yext))))))
96 (def-markup-command (box layout props arg) (markup?)
97   "Draw a box round @var{arg}.  Looks at @code{thickness},
98 @code{box-padding} and @code{font-size} properties to determine line
99 thickness and padding around the markup."
100   
101   (let* ((th (chain-assoc-get 'thickness props  0.1))
102          (size (chain-assoc-get 'font-size props 0))
103          (pad (* (magstep size)
104                  (chain-assoc-get 'box-padding props 0.2)))
105          (m (interpret-markup layout props arg)))
106     (box-stencil m th pad)))
108 (def-markup-command (filled-box layout props xext yext blot)
109   (number-pair? number-pair? number?)
110   "Draw a box with rounded corners of dimensions @var{xext} and @var{yext}."
111   (ly:round-filled-box
112    xext yext blot))
114 (def-markup-command (whiteout layout props arg) (markup?)
115   "Provide a white underground for @var{arg}"
116   (let* ((stil (interpret-markup layout props
117                                  (make-with-color-markup black arg)))
118          (white
119           (interpret-markup layout props
120                             (make-with-color-markup
121                              white
122                              (make-filled-box-markup
123                               (ly:stencil-extent stil X)
124                               (ly:stencil-extent stil Y)
125                               0.0)))))
127     (ly:stencil-add white stil)))
129 (def-markup-command (pad-markup layout props padding arg) (number? markup?)
130   "Add space around a markup object."
132   (let*
133       ((stil (interpret-markup layout props arg))
134        (xext (ly:stencil-extent stil X))
135        (yext (ly:stencil-extent stil Y)))
137     (ly:make-stencil
138      (ly:stencil-expr stil)
139      (interval-widen xext padding)
140      (interval-widen yext padding))))
142 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
143 ;; space
144 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
146 ;;FIXME: is this working? 
147 (def-markup-command (strut layout props) ()
148   "Create a box of the same height as the space in the current font."
149   (let ((m (Text_interface::interpret_markup layout props " ")))
150     (ly:make-stencil (ly:stencil-expr m)
151                      '(1000 . -1000)
152                      (ly:stencil-extent m X)
153                      )))
156 ;; todo: fix negative space
157 (def-markup-command (hspace layout props amount) (number?)
158   "This produces a invisible object taking horizontal space.
159 @example 
160 \\markup @{ A \\hspace #2.0 B @} 
161 @end example
162 will put extra space between A and B, on top of the space that is
163 normally inserted before elements on a line.
165   (if (> amount 0)
166       (ly:make-stencil "" (cons 0 amount) '(-1 . 1))
167       (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
170 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
171 ;; importing graphics.
172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174 (def-markup-command (stencil layout props stil) (ly:stencil?)
175   "Stencil as markup"
176   stil)
178 (define bbox-regexp
179   (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)"))
181 (define (get-postscript-bbox string)
182   "Extract the bbox from STRING, or return #f if not present."
183   (let*
184       ((match (regexp-exec bbox-regexp string)))
185     
186     (if match
187         (map (lambda (x)
188                (string->number (match:substring match x)))
189              (cdr (iota 5)))
190              
191         #f)))
193 (def-markup-command (epsfile layout props file-name) (string?)
194   "Inline an EPS image. The image is scaled such that 10 PS units is
195 one staff-space."
197   (if (ly:get-option 'safe)
198       (interpret-markup layout props "not allowed in safe") 
199       (let*
200           ((contents (ly:gulp-file file-name))
201            (bbox (get-postscript-bbox contents))
202            (scaled-bbox
203             (if bbox
204                 (map (lambda (x) (/ x 10)) bbox)
205                 (begin
206                   (ly:warning (_ "can't find bounding box of `~a'")
207                            file-name)
208                   '()))))
209         
211         (if bbox
212             
213             (ly:make-stencil
214              (list
215               'embedded-ps
216               (string-append
218                ; adobe 5002.
219                "BeginEPSF "
220                "0.1 0.1 scale "
221                (format "\n%%BeginDocument: ~a\n" file-name)
222                contents
223                "%%EndDocument\n"
224                "EndEPSF\n"
225                ))
226              (cons (list-ref scaled-bbox 0) (list-ref scaled-bbox 2))
227              (cons (list-ref scaled-bbox 1) (list-ref scaled-bbox 3)))
228             
229             (ly:make-stencil "" '(0 . 0) '(0 . 0))))))  
232 (def-markup-command (postscript layout props str) (string?)
233   "This inserts @var{str} directly into the output as a PostScript
234 command string.  Due to technicalities of the output backends,
235 different scales should be used for the @TeX{} and PostScript backend,
236 selected with @code{-f}. 
239 For the TeX backend, the following string prints a rotated text
241 @cindex rotated text
243 @verbatim
244 0 0 moveto /ecrm10 findfont 
245 1.75 scalefont setfont 90 rotate (hello) show
246 @end verbatim
248 @noindent
249 The magical constant 1.75 scales from LilyPond units (staff spaces) to
250 TeX dimensions.
252 For the postscript backend, use the following
254 @verbatim
255 gsave /ecrm10 findfont 
256  10.0 output-scale div 
257  scalefont setfont 90 rotate (hello) show grestore 
258 @end verbatim
260   ;; FIXME
261   (ly:make-stencil
262    (list 'embedded-ps str)
263    '(0 . 0) '(0 . 0)))
266 (def-markup-command (score layout props score) (ly:score?)
267   "Inline an image of music."
268   (let* ((output (ly:score-embedded-format score layout)))
270     (if (ly:music-output? output)
271         (paper-system-stencil
272          (vector-ref (ly:paper-score-paper-systems output) 0))
273         (begin
274           (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
275           empty-stencil))))
277 (def-markup-command (null layout props) ()
278   "An empty markup with extents of a single point"
280   point-stencil)
282 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
283 ;; basic formatting.
284 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
286 (def-markup-command (simple layout props str) (string?)
287   "A simple text string; @code{\\markup @{ foo @}} is equivalent with
288 @code{\\markup @{ \\simple #\"foo\" @}}."
289   (interpret-markup layout props str))
292 ;; TODO: use font recoding.
293 ;;                    (make-line-markup
294 ;;                     (map make-word-markup (string-tokenize str)))))
296 (define-public empty-markup
297   (make-simple-markup ""))
299 ;; helper for justifying lines.
300 (define (get-fill-space word-count line-width text-widths)
301   "Calculate the necessary paddings between each two adjacent texts.
302         The lengths of all texts are stored in @var{text-widths}.
303         The normal formula for the padding between texts a and b is:
304         padding = line-width/(word-count - 1) - (length(a) + length(b))/2
305         The first and last padding have to be calculated specially using the
306         whole length of the first or last text.
307         Return a list of paddings.
309   (cond
310    ((null? text-widths) '())
311    
312    ;; special case first padding
313    ((= (length text-widths) word-count)
314     (cons 
315      (- (- (/ line-width (1- word-count)) (car text-widths))
316         (/ (car (cdr text-widths)) 2))
317      (get-fill-space word-count line-width (cdr text-widths))))
318    ;; special case last padding
319    ((= (length text-widths) 2)
320     (list (- (/ line-width (1- word-count))
321              (+ (/ (car text-widths) 2) (car (cdr text-widths)))) 0))
322    (else
323     (cons 
324      (- (/ line-width (1- word-count))
325         (/ (+ (car text-widths) (car (cdr text-widths))) 2))
326      (get-fill-space word-count line-width (cdr text-widths))))))
328 (def-markup-command (fill-line layout props markups)
329   (markup-list?)
330   "Put @var{markups} in a horizontal line of width @var{line-width}.
331    The markups are spaced/flushed to fill the entire line.
332    If there are no arguments, return an empty stencil."
334   (let* ((orig-stencils
335           (map (lambda (x) (interpret-markup layout props x))
336                markups))
337          (stencils
338           (map (lambda (stc)
339                  (if (ly:stencil-empty? stc)
340                      point-stencil
341                      stc)) orig-stencils))
342          (text-widths
343           (map (lambda (stc)
344                  (if (ly:stencil-empty? stc)
345                      0.0
346                      (interval-length (ly:stencil-extent stc X))))
347                stencils))
348          (text-width (apply + text-widths))
349          (text-dir (chain-assoc-get 'text-direction props RIGHT))
350          (word-count (length stencils))
351          (word-space (chain-assoc-get 'word-space props))
352          (line-width (chain-assoc-get 'linewidth props))
353          (fill-space
354                 (cond
355                         ((= word-count 1) 
356                                 (list
357                                         (/ (- line-width text-width) 2)
358                                         (/ (- line-width text-width) 2)))
359                         ((= word-count 2)
360                                 (list
361                                         (- line-width text-width)))
362                         (else 
363                                 (get-fill-space word-count line-width text-widths))))
364          (fill-space-normal
365           (map (lambda (x)
366                  (if (< x word-space)
367                      word-space
368                      x))
369                fill-space))
370                                         
371          (line-stencils (if (= word-count 1)
372                             (list
373                              point-stencil
374                              (car stencils)
375                              point-stencil)
376                             stencils)))
378     (if (= text-dir LEFT)
379         (set! line-stencils (reverse line-stencils)))
381     (if (null? (remove ly:stencil-empty? orig-stencils))
382         empty-stencil
383         (stack-stencils-padding-list X
384                                      RIGHT fill-space-normal line-stencils))))
385         
386 (def-markup-command (line layout props args) (markup-list?)
387   "Put @var{args} in a horizontal line.  The property @code{word-space}
388 determines the space between each markup in @var{args}."
389   (let*
390       ((stencils (map (lambda (m) (interpret-markup layout props m)) args))
391        (space    (chain-assoc-get 'word-space props))
392        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
393        )
395     (if (= text-dir LEFT)
396         (set! stencils (reverse stencils)))
397     
399     (stack-stencil-line
400      space
401      (remove ly:stencil-empty? stencils))))
404 (define (wordwrap-stencils stencils
405                            justify base-space line-width text-dir)
406   
407   "Perform simple wordwrap, return stencil of each line."
408   
409   (define space (if justify
410                     
411                     ;; justify only stretches lines.
412                     (* 0.7 base-space)
413                     base-space))
414        
415   (define (take-list width space stencils
416                      accumulator accumulated-width)
417     "Return (head-list . tail) pair, with head-list fitting into width"
418     (if (null? stencils)
419         (cons accumulator stencils)
420         (let*
421             ((first (car stencils))
422              (first-wid (cdr (ly:stencil-extent (car stencils) X)))
423              (newwid (+ space first-wid accumulated-width))
424              )
426           (if
427            (or (null? accumulator)
428                (< newwid width))
430            (take-list width space
431                       (cdr stencils)
432                       (cons first accumulator)
433                       newwid)
434              (cons accumulator stencils))
435            )))
437     (let loop
438         ((lines '())
439          (todo stencils))
441       (let*
442           ((line-break (take-list line-width space todo
443                                  '() 0.0))
444            (line-stencils (car line-break))
445            (space-left (- line-width (apply + (map (lambda (x) (cdr (ly:stencil-extent x X)))
446                                               line-stencils))))
448            (line-word-space (cond
449                              ((not justify) space)
451                              ;; don't stretch last line of paragraph.
452                              ;; hmmm . bug - will overstretch the last line in some case. 
453                              ((null? (cdr line-break))
454                               base-space)
455                              ((null? line-stencils) 0.0)
456                              ((null? (cdr line-stencils)) 0.0)
457                              (else (/ space-left (1- (length line-stencils))))))
459            (line (stack-stencil-line
460                   line-word-space
461                   (if (= text-dir RIGHT)
462                       (reverse line-stencils)
463                       line-stencils))))
465         (if (pair? (cdr line-break))
466             (loop (cons line lines)
467                   (cdr line-break))
469             (begin
470               (if (= text-dir LEFT)
471                   (set! line
472                         (ly:stencil-translate-axis line
473                                                    (- line-width (interval-end (ly:stencil-extent line X)))
474                                                    X)))
475               (reverse (cons line lines))
476               
477             )))
479       ))
482 (define (wordwrap-markups layout props args justify)
483   (let*
484       ((baseline-skip (chain-assoc-get 'baseline-skip props))
485        (line-width (chain-assoc-get 'linewidth props))
486        (word-space (chain-assoc-get 'word-space props))
487        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
488        (lines (wordwrap-stencils
489                (remove ly:stencil-empty?
490                        (map (lambda (m) (interpret-markup layout props m)) args))
491                justify word-space line-width
492                text-dir)
493                ))
495     (stack-lines DOWN 0.0 baseline-skip lines)))
497 (def-markup-command (justify layout props args) (markup-list?)
498   "Like wordwrap, but with lines stretched to justify the margins.
499 Use @code{\\override #'(linewidth . X)} to set linewidth, where X
500 is the number of staff spaces."
502   (wordwrap-markups layout props args #t))
504 (def-markup-command (wordwrap layout props args) (markup-list?)
505   "Simple wordwrap.  Use @code{\\override #'(linewidth . X)} to set
506 linewidth, where X is the number of staff spaces."
508   (wordwrap-markups layout props args #f))
510 (define (wordwrap-string layout props justify arg) 
511   (let*
512       ((baseline-skip (chain-assoc-get 'baseline-skip props))
513        (line-width (chain-assoc-get 'linewidth props))
514        (word-space (chain-assoc-get 'word-space props))
515        
516        (para-strings (regexp-split
517                       (string-regexp-substitute "\r" "\n"
518                                                 (string-regexp-substitute "\r\n" "\n" arg))
519                       "\n[ \t\n]*\n[ \t\n]*"))
520        
521        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
522        (list-para-words (map (lambda (str)
523                                (regexp-split str "[ \t\n]+"))
524                              para-strings))
525        (para-lines (map (lambda (words)
526                           (let*
527                               ((stencils
528                                 (remove
529                                  ly:stencil-empty? (map 
530                                       (lambda (x)
531                                         (interpret-markup layout props x))
532                                       words)))
533                                (lines (wordwrap-stencils stencils
534                                                          justify word-space
535                                                          line-width text-dir
536                                                          )))
538                             lines))
539                         
540                         list-para-words)))
542     (stack-lines DOWN 0.0 baseline-skip (apply append para-lines))))
545 (def-markup-command (wordwrap-string layout props arg) (string?)
546   "Wordwrap a string. Paragraphs may be separated with double newlines"
547   (wordwrap-string layout props  #f arg))
548   
549 (def-markup-command (justify-string layout props arg) (string?)
550   "Justify a string. Paragraphs may be separated with double newlines"
551   (wordwrap-string layout props #t arg))
554 (def-markup-command (wordwrap-field layout props symbol) (symbol?)
555    (let* ((m (chain-assoc-get symbol props)))
556      (if (string? m)
557       (interpret-markup layout props
558        (list wordwrap-string-markup m))
559       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
561 (def-markup-command (justify-field layout props symbol) (symbol?)
562 -   (let* ((m (chain-assoc-get symbol props)))
563      (if (string? m)
564       (interpret-markup layout props
565        (list justify-string-markup m))
566       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
570 (def-markup-command (combine layout props m1 m2) (markup? markup?)
571   "Print two markups on top of each other."
572   (let* ((s1 (interpret-markup layout props m1))
573          (s2 (interpret-markup layout props m2)))
574     (ly:stencil-add s1 s2)))
577 ;; TODO: should extract baseline-skip from each argument somehow..
578 ;; 
579 (def-markup-command (column layout props args) (markup-list?)
580   "Stack the markups in @var{args} vertically.  The property
581 @code{baseline-skip} determines the space between each markup in @var{args}."
582   (stack-lines
583    -1 0.0 (chain-assoc-get 'baseline-skip props)
584    (remove ly:stencil-empty?
585            (map (lambda (m) (interpret-markup layout props m)) args))))
587 (def-markup-command (dir-column layout props args) (markup-list?)
588   "Make a column of args, going up or down, depending on the setting
589 of the @code{#'direction} layout property."
590   (let* ((dir (chain-assoc-get 'direction props)))
591     (stack-lines
592      (if (number? dir) dir -1)
593      0.0
594      (chain-assoc-get 'baseline-skip props)
595      (map (lambda (x) (interpret-markup layout props x)) args))))
597 (def-markup-command (center-align layout props args) (markup-list?)
598   "Put @code{args} in a centered column. "
599   (let* ((mols (map (lambda (x) (interpret-markup layout props x)) args))
600          (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
601     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) cmols)))
603 (def-markup-command (vcenter layout props arg) (markup?)
604   "Align @code{arg} to its Y center. "
605   (let* ((mol (interpret-markup layout props arg)))
606     (ly:stencil-aligned-to mol Y CENTER)))
608 (def-markup-command (hcenter layout props arg) (markup?)
609   "Align @code{arg} to its X center. "
610   (let* ((mol (interpret-markup layout props arg)))
611     (ly:stencil-aligned-to mol X CENTER)))
613 (def-markup-command (right-align layout props arg) (markup?)
614   "Align @var{arg} on its right edge. "
615   (let* ((m (interpret-markup layout props arg)))
616     (ly:stencil-aligned-to m X RIGHT)))
618 (def-markup-command (left-align layout props arg) (markup?)
619   "Align @var{arg} on its left edge. "
620   (let* ((m (interpret-markup layout props arg)))
621     (ly:stencil-aligned-to m X LEFT)))
623 (def-markup-command (general-align layout props axis dir arg)  (integer? number? markup?)
624   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
625   (let* ((m (interpret-markup layout props arg)))
626     (ly:stencil-aligned-to m axis dir)))
628 (def-markup-command (halign layout props dir arg) (number? markup?)
629   "Set horizontal alignment. If @var{dir} is @code{-1}, then it is
630 left-aligned, while @code{+1} is right. Values in between interpolate
631 alignment accordingly."
632   (let* ((m (interpret-markup layout props arg)))
633     (ly:stencil-aligned-to m X dir)))
637 (def-markup-command (with-dimensions layout props x y arg) (number-pair? number-pair? markup?)
638   "Set the dimensions of @var{arg} to @var{x} and @var{y}."
639   
640   (let* ((m (interpret-markup layout props arg)))
641     (ly:make-stencil (ly:stencil-expr m) x y)))
644 (def-markup-command (pad-around layout props amount arg) (number? markup?)
646   "Add padding @var{amount} all around @var{arg}. "
647   
648   (let*
649       ((m (interpret-markup layout props arg))
650        (x (ly:stencil-extent m X))
651        (y (ly:stencil-extent m Y)))
652     
653        
654     (ly:make-stencil (ly:stencil-expr m)
655                      (interval-widen x amount)
656                      (interval-widen y amount))
657    ))
660 (def-markup-command (pad-x layout props amount arg) (number? markup?)
662   "Add padding @var{amount} around @var{arg} in the X-direction. "
663   (let*
664       ((m (interpret-markup layout props arg))
665        (x (ly:stencil-extent m X))
666        (y (ly:stencil-extent m Y)))
667     
668        
669     (ly:make-stencil (ly:stencil-expr m)
670                      (interval-widen x amount)
671                      y)
672    ))
675 (def-markup-command (put-adjacent layout props arg1 axis dir arg2) (markup? integer? ly:dir?  markup?)
677   "Put @var{arg2} next to @var{arg1}, without moving @var{arg1}.  "
678   
679   (let* ((m1 (interpret-markup layout props arg1))
680          (m2 (interpret-markup layout props arg2)))
682     (ly:stencil-combine-at-edge m1 axis dir m2 0.0 0.0)
683   ))
685 (def-markup-command (transparent layout props arg) (markup?)
686   "Make the argument transparent"
687   (let*
688       ((m (interpret-markup layout props arg))
689        (x (ly:stencil-extent m X))
690        (y (ly:stencil-extent m Y)))
691     
693     
694     (ly:make-stencil ""
695                      x y)))
698 (def-markup-command (pad-to-box layout props x-ext y-ext arg) (number-pair? number-pair? markup?)
699   "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space"
701   (let*
702       ((m (interpret-markup layout props arg))
703        (x (ly:stencil-extent m X))
704        (y (ly:stencil-extent m Y)))
706     (ly:make-stencil (ly:stencil-expr m)
707                      (interval-union x-ext x)
708                      (interval-union y-ext y))))
712 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
713 ;; property
714 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
716 (def-markup-command (fromproperty layout props symbol) (symbol?)
717   "Read the @var{symbol} from property settings, and produce a stencil
718   from the markup contained within. If @var{symbol} is not defined, it
719   returns an empty markup"
720   (let* ((m (chain-assoc-get symbol props)))
721     (if (markup? m)
722         (interpret-markup layout props m)
723         (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
726 (def-markup-command (on-the-fly layout props procedure arg) (symbol? markup?)
727   "Apply the @var{procedure} markup command to
728 @var{arg}. @var{procedure} should take a single argument."
729   (let* ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
730     (set-object-property! anonymous-with-signature
731                           'markup-signature
732                           (list markup?))
733     (interpret-markup layout props (list anonymous-with-signature arg))))
737 (def-markup-command (override layout props new-prop arg) (pair? markup?)
738   "Add the first argument in to the property list.  Properties may be
739 any sort of property supported by @internalsref{font-interface} and
740 @internalsref{text-interface}, for example
742 @verbatim
743 \\override #'(font-family . married) \"bla\"
744 @end verbatim
747   (interpret-markup layout (cons (list new-prop) props) arg))
749 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
750 ;; fonts.
751 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
754 (def-markup-command (bigger layout props arg) (markup?)
755   "Increase the font size relative to current setting"
756   (interpret-markup layout props
757    `(,fontsize-markup 1 ,arg)))
759 (def-markup-command (smaller layout props arg) (markup?)
760   "Decrease the font size relative to current setting"
761   (interpret-markup layout props
762    `(,fontsize-markup -1 ,arg)))
764 (def-markup-command larger (markup?) bigger-markup)
766 (def-markup-command (finger layout props arg) (markup?)
767   "Set the argument as small numbers."
768   (interpret-markup layout
769                     (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
770                     arg))
773 (def-markup-command (fontsize layout props increment arg) (number? markup?)
774   "Add @var{increment} to the font-size. Adjust baseline skip accordingly."
776   (let* ((fs (chain-assoc-get 'font-size props 0))
777          (bs (chain-assoc-get 'baseline-skip props 2)) 
778          (entries (list
779                    (cons 'baseline-skip (* bs (magstep increment)))
780                    (cons 'font-size (+ fs increment )))))
782     (interpret-markup layout (cons entries props) arg)))
783   
786 ;; FIXME -> should convert to font-size.
787 (def-markup-command (magnify layout props sz arg) (number? markup?)
788   "Set the font magnification for the its argument. In the following
789 example, the middle A will be 10% larger:
790 @example
791 A \\magnify #1.1 @{ A @} A
792 @end example
794 Note: magnification only works if a font-name is explicitly selected.
795 Use @code{\\fontsize} otherwise."
796   (interpret-markup
797    layout 
798    (prepend-alist-chain 'font-magnification sz props)
799    arg))
801 (def-markup-command (bold layout props arg) (markup?)
802   "Switch to bold font-series"
803   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
805 (def-markup-command (sans layout props arg) (markup?)
806   "Switch to the sans serif family"
807   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
809 (def-markup-command (number layout props arg) (markup?)
810   "Set font family to @code{number}, which yields the font used for
811 time signatures and fingerings.  This font only contains numbers and
812 some punctuation. It doesn't have any letters.  "
813   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
815 (def-markup-command (roman layout props arg) (markup?)
816   "Set font family to @code{roman}."
817   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
819 (def-markup-command (huge layout props arg) (markup?)
820   "Set font size to +2."
821   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
823 (def-markup-command (large layout props arg) (markup?)
824   "Set font size to +1."
825   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
827 (def-markup-command (normalsize layout props arg) (markup?)
828   "Set font size to default."
829   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
831 (def-markup-command (small layout props arg) (markup?)
832   "Set font size to -1."
833   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
835 (def-markup-command (tiny layout props arg) (markup?)
836   "Set font size to -2."
837   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
839 (def-markup-command (teeny layout props arg) (markup?)
840   "Set font size to -3."
841   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
843 (def-markup-command (caps layout props arg) (markup?)
844   "Set @code{font-shape} to @code{caps}."
845   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
847 (def-markup-command (dynamic layout props arg) (markup?)
848   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
849 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
850 normal words (like ``pi@`{u}'') should be done in a different font.  The
851 recommend font for this is bold and italic"
852   (interpret-markup
853    layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
855 (def-markup-command (text layout props arg) (markup?)
856   "Use a text font instead of music symbol or music alphabet font."  
858   ;; ugh - latin1
859   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
860                     arg))
863 (def-markup-command (italic layout props arg) (markup?)
864   "Use italic @code{font-shape} for @var{arg}. "
865   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
867 (def-markup-command (typewriter layout props arg) (markup?)
868   "Use @code{font-family} typewriter for @var{arg}."
869   (interpret-markup
870    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
872 (def-markup-command (upright layout props arg) (markup?)
873   "Set font shape to @code{upright}.  This is the opposite of @code{italic}."
874   (interpret-markup
875    layout (prepend-alist-chain 'font-shape 'upright props) arg))
877 (def-markup-command (medium layout props arg) (markup?)
878   "Switch to medium font-series (in contrast to bold)."
879   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
880                     arg))
882 (def-markup-command (normal-text layout props arg) (markup?)
883   "Set all font related properties (except the size) to get the default normal text font, no matter what font was used earlier."
884   ;; ugh - latin1
885   (interpret-markup layout
886                     (cons '((font-family . roman) (font-shape . upright)
887                             (font-series . medium) (font-encoding . latin1))
888                           props)
889                     arg))
891 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
892 ;; symbols.
893 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
895 (def-markup-command (doublesharp layout props) ()
896   "Draw a double sharp symbol."
898   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
900 (def-markup-command (sesquisharp layout props) ()
901   "Draw a 3/2 sharp symbol."
902   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
904 (def-markup-command (sharp layout props) ()
905   "Draw a sharp symbol."
906   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
908 (def-markup-command (semisharp layout props) ()
909   "Draw a semi sharp symbol."
910   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
912 (def-markup-command (natural layout props) ()
913   "Draw a natural symbol."
914   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
916 (def-markup-command (semiflat layout props) ()
917   "Draw a semiflat."
918   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
920 (def-markup-command (flat layout props) ()
921   "Draw a flat symbol."
922   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
924 (def-markup-command (sesquiflat layout props) ()
925   "Draw a 3/2 flat symbol."
926   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
928 (def-markup-command (doubleflat layout props) ()
929   "Draw a double flat symbol."
930   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
932 (def-markup-command (with-color layout props color arg) (color? markup?)
933   "Draw @var{arg} in color specified by @var{color}"
935   (let* ((stil (interpret-markup layout props arg)))
937     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
938                      (ly:stencil-extent stil X)
939                      (ly:stencil-extent stil Y))))
942 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
943 ;; glyphs
944 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
947 (def-markup-command (arrow-head layout props axis direction filled)
948   (integer? ly:dir? boolean?)
949   "produce an arrow head in specified direction and axis. Use the filled head if @var{filled} is  specified."
950   (let*
951       ((name (format "arrowheads.~a.~a~a"
952                      (if filled
953                          "close"
954                          "open")
955                      axis
956                      direction)))
957     (ly:font-get-glyph
958      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
959                                      props))
960      name)))
962 (def-markup-command (musicglyph layout props glyph-name) (string?)
963   "This is converted to a musical symbol, e.g. @code{\\musicglyph
964 #\"accidentals.0\"} will select the natural sign from the music font.
965 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
966   (ly:font-get-glyph
967    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
968                                    props))
969    glyph-name))
971 (def-markup-command (lookup layout props glyph-name) (string?)
972   "Lookup a glyph by name."
973   (ly:font-get-glyph (ly:paper-get-font layout props)
974                      glyph-name))
976 (def-markup-command (char layout props num) (integer?)
977   "Produce a single character, e.g. @code{\\char #65} produces the 
978 letter 'A'."
979   (ly:get-glyph (ly:paper-get-font layout props) num))
982 (define number->mark-letter-vector (make-vector 25 #\A))
984 (do ((i 0 (1+ i))
985      (j 0 (1+ j)))
986     ((>= i 26))
987   (if (= i (- (char->integer #\I) (char->integer #\A)))
988       (set! i (1+ i)))
989   (vector-set! number->mark-letter-vector j
990                (integer->char (+ i (char->integer #\A)))))
992 (define number->mark-alphabet-vector (list->vector
993   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
995 (define (number->markletter-string vec n)
996   "Double letters for big marks."
997   (let* ((lst (vector-length vec)))
998     
999     (if (>= n lst)
1000         (string-append (number->markletter-string vec (1- (quotient n lst)))
1001                        (number->markletter-string vec (remainder n lst)))
1002         (make-string 1 (vector-ref vec n)))))
1004 (def-markup-command (markletter layout props num) (integer?)
1005   "Make a markup letter for @var{num}.  The letters start with A to Z
1006  (skipping I), and continues with double letters."
1007   (Text_interface::interpret_markup layout props
1008     (number->markletter-string number->mark-letter-vector num)))
1010 (def-markup-command (markalphabet layout props num) (integer?)
1011    "Make a markup letter for @var{num}.  The letters start with A to Z
1012  and continues with double letters."
1013    (Text_interface::interpret_markup layout props
1014      (number->markletter-string number->mark-alphabet-vector num)))
1018 (def-markup-command (slashed-digit layout props num) (integer?)
1019   "A feta number, with slash. This is for use in the context of
1020 figured bass notation"
1021   (let*
1022       ((mag (magstep (chain-assoc-get 'font-size props 0)))
1023        (thickness
1024         (* mag
1025            (chain-assoc-get 'thickness props 0.16)))
1026        (dy (* mag 0.15))
1027        (number-stencil (interpret-markup layout
1028                                          (prepend-alist-chain 'font-encoding 'fetaNumber props)
1029                                          (number->string num)))
1030        (num-x (interval-widen (ly:stencil-extent number-stencil X)
1031                               (* mag 0.2)))
1032        (num-y (ly:stencil-extent number-stencil Y))
1033        (slash-stencil 
1034         (ly:make-stencil
1035          `(draw-line
1036            ,thickness
1037            ,(car num-x) ,(- (interval-center num-y) dy)
1038            ,(cdr num-x) ,(+ (interval-center num-y) dy))
1039          num-x num-y
1040          )))
1042     (ly:stencil-add number-stencil
1043                     (cond
1044                      ((= num 5) (ly:stencil-translate slash-stencil
1045                                                       ;;(cons (* mag -0.05) (* mag 0.42))
1046                                                       (cons (* mag -0.00) (* mag -0.07))
1048                                                       ))
1049                      ((= num 7) (ly:stencil-translate slash-stencil
1050                                                       ;;(cons (* mag -0.05) (* mag 0.42))
1051                                                       (cons (* mag -0.00) (* mag -0.15))
1053                                                       ))
1054                      
1055                      (else slash-stencil)))
1056     ))
1058 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1059 ;; the note command.
1060 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1063 ;; TODO: better syntax.
1065 (def-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
1066   "Construct a note symbol, with stem.  By using fractional values for
1067 @var{dir}, you can obtain longer or shorter stems."
1068   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
1069          (size (chain-assoc-get 'font-size props 0))
1070          (stem-length (* (magstep size) (max 3 (- log 1))))
1071          (head-glyph (ly:font-get-glyph
1072                       font
1073                       (string-append "noteheads.s" (number->string (min log 2)))))
1074          (stem-thickness 0.13)
1075          (stemy (* dir stem-length))
1076          (attachx (if (> dir 0)
1077                       (- (cdr (ly:stencil-extent head-glyph X)) stem-thickness)
1078                       0))
1079          (attachy (* dir 0.28))
1080          (stem-glyph (and (> log 0)
1081                           (ly:round-filled-box
1082                            (cons attachx (+ attachx  stem-thickness))
1083                            (cons (min stemy attachy)
1084                                  (max stemy attachy))
1085                            (/ stem-thickness 3))))
1086          (dot (ly:font-get-glyph font "dots.dot"))
1087          (dotwid (interval-length (ly:stencil-extent dot X)))
1088          (dots (and (> dot-count 0)
1089                     (apply ly:stencil-add
1090                            (map (lambda (x)
1091                                   (ly:stencil-translate-axis
1092                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
1093                                 (iota dot-count 1)))))
1094          (flaggl (and (> log 2)
1095                       (ly:stencil-translate
1096                        (ly:font-get-glyph font
1097                                           (string-append "flags."
1098                                                          (if (> dir 0) "u" "d")
1099                                                          (number->string log)))
1100                        (cons (+ attachx (/ stem-thickness 2)) stemy)))))
1101     (if flaggl
1102         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
1103     (if (ly:stencil? stem-glyph)
1104         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
1105         (set! stem-glyph head-glyph))
1106     (if (ly:stencil? dots)
1107         (set! stem-glyph
1108               (ly:stencil-add
1109                (ly:stencil-translate-axis
1110                 dots
1111                 (+ (if (and (> dir 0) (> log 2))
1112                        (* 1.5 dotwid)
1113                        0)
1114                    ;; huh ? why not necessary?
1115                    ;;(cdr (ly:stencil-extent head-glyph X))
1116                    dotwid)
1117                 X)
1118                stem-glyph)))
1119     stem-glyph))
1121 (define-public log2 
1122   (let ((divisor (log 2)))
1123     (lambda (z) (inexact->exact (/ (log z) divisor)))))
1125 (define (parse-simple-duration duration-string)
1126   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
1127   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
1128     (if (and match (string=? duration-string (match:substring match 0)))
1129         (let ((len  (match:substring match 1))
1130               (dots (match:substring match 2)))
1131           (list (cond ((string=? len "breve") -1)
1132                       ((string=? len "longa") -2)
1133                       ((string=? len "maxima") -3)
1134                       (else (log2 (string->number len))))
1135                 (if dots (string-length dots) 0)))
1136         (ly:error (_ "not a valid duration string: ~a") duration-string))))
1138 (def-markup-command (note layout props duration dir) (string? number?)
1139   "This produces a note with a stem pointing in @var{dir} direction, with
1140 the @var{duration} for the note head type and augmentation dots. For
1141 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
1142 a shortened down stem."
1143   (let ((parsed (parse-simple-duration duration)))
1144     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
1147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1148 ;; translating.
1149 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1151 (def-markup-command (lower layout props amount arg) (number? markup?)
1152   "
1153 Lower @var{arg}, by the distance @var{amount}.
1154 A negative @var{amount} indicates raising, see also @code{\\raise}.
1156   (ly:stencil-translate-axis (interpret-markup layout props arg)
1157                              (- amount) Y))
1160 (def-markup-command (raise layout props amount arg) (number? markup?)
1161   "
1162 Raise @var{arg}, by the distance @var{amount}.
1163 A negative @var{amount} indicates lowering, see also @code{\\lower}.
1165 @lilypond[verbatim,fragment,relative=1]
1166  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
1167 @end lilypond
1168 The argument to @code{\\raise} is the vertical displacement amount,
1169 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
1170 raise objects in relation to their surrounding markups.
1172 If the text object itself is positioned above or below the staff, then
1173 @code{\\raise} cannot be used to move it, since the mechanism that
1174 positions it next to the staff cancels any shift made with
1175 @code{\\raise}. For vertical positioning, use the @code{padding}
1176 and/or @code{extra-offset} properties. "
1177   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
1179 (def-markup-command (fraction layout props arg1 arg2) (markup? markup?)
1180   "Make a fraction of two markups."
1181   (let* ((m1 (interpret-markup layout props arg1))
1182          (m2 (interpret-markup layout props arg2)))
1183     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
1184     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
1185     (let* ((x1 (ly:stencil-extent m1 X))
1186            (x2 (ly:stencil-extent m2 X))
1187            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
1188            ;; should stack mols separately, to maintain LINE on baseline
1189            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
1190       (set! stack
1191             (ly:stencil-aligned-to stack Y CENTER))
1192       (set! stack
1193             (ly:stencil-aligned-to stack X LEFT))
1194       ;; should have EX dimension
1195       ;; empirical anyway
1196       (ly:stencil-translate-axis stack 0.75 Y))))
1202 (def-markup-command (normal-size-super layout props arg) (markup?)
1203   "Set @var{arg} in superscript with a normal font size."
1204   (ly:stencil-translate-axis
1205    (interpret-markup layout props arg)
1206    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
1208 (def-markup-command (super layout props arg) (markup?)
1209   "
1210 @cindex raising text
1211 @cindex lowering text
1212 @cindex moving text
1213 @cindex translating text
1215 @cindex @code{\\super}
1218 Raising and lowering texts can be done with @code{\\super} and
1219 @code{\\sub}:
1221 @lilypond[verbatim,fragment,relative=1]
1222  c1^\\markup { E \"=\" mc \\super \"2\" }
1223 @end lilypond
1226   (ly:stencil-translate-axis
1227    (interpret-markup
1228     layout
1229     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1230     arg)
1231    (* 0.5 (chain-assoc-get 'baseline-skip props))
1232    Y))
1234 (def-markup-command (translate layout props offset arg) (number-pair? markup?)
1235   "This translates an object. Its first argument is a cons of numbers
1236 @example
1237 A \\translate #(cons 2 -3) @{ B C @} D
1238 @end example
1239 This moves `B C' 2 spaces to the right, and 3 down, relative to its
1240 surroundings. This command cannot be used to move isolated scripts
1241 vertically, for the same reason that @code{\\raise} cannot be used for
1242 that.
1245   (ly:stencil-translate (interpret-markup  layout props arg)
1246                         offset))
1248 (def-markup-command (sub layout props arg) (markup?)
1249   "Set @var{arg} in subscript."
1250   (ly:stencil-translate-axis
1251    (interpret-markup
1252     layout
1253     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1254     arg)
1255    (* -0.5 (chain-assoc-get 'baseline-skip props))
1256    Y))
1258 (def-markup-command (normal-size-sub layout props arg) (markup?)
1259   "Set @var{arg} in subscript, in a normal font size."
1260   (ly:stencil-translate-axis
1261    (interpret-markup layout props arg)
1262    (* -0.5 (chain-assoc-get 'baseline-skip props))
1263    Y))
1265 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1266 ;; brackets.
1267 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1269 (def-markup-command (hbracket layout props arg) (markup?)
1270   "Draw horizontal brackets around @var{arg}."  
1271   (let ((th 0.1) ;; todo: take from GROB.
1272         (m (interpret-markup layout props arg)))
1273     (bracketify-stencil m X th (* 2.5 th) th)))
1275 (def-markup-command (bracket layout props arg) (markup?)
1276   "Draw vertical brackets around @var{arg}."  
1277   (let ((th 0.1) ;; todo: take from GROB.
1278         (m (interpret-markup layout props arg)))
1279     (bracketify-stencil m Y th (* 2.5 th) th)))
1281 (def-markup-command (bracketed-y-column layout props indices args)
1282   (list? markup-list?)
1283   "Make a column of the markups in @var{args}, putting brackets around
1284 the elements marked in @var{indices}, which is a list of numbers.
1288 ;; DROPME? This command is a relic from the old figured bass implementation.
1290   
1291   (define (sublist lst start stop)
1292     (take (drop lst start) (- (1+ stop) start)))
1294   (define (stencil-list-extent ss axis)
1295     (cons
1296      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
1297      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
1298   
1300   (define (stack-stencils-vertically stencils bskip last-stencil)
1301     (cond
1302      ((null? stencils) '())
1303      ((not (ly:stencil? last-stencil))
1304       (cons (car stencils)
1305             (stack-stencils-vertically (cdr stencils) bskip (car stencils))))
1306      (else
1307       (let* ((orig (car stencils))
1308              (dir (chain-assoc-get 'direction  props DOWN))
1309              (new (ly:stencil-moved-to-edge last-stencil Y dir
1310                                             orig
1311                                             0.1 bskip)))
1313         (cons new (stack-stencils-vertically (cdr stencils) bskip new))))))
1315   (define (make-brackets stencils indices acc)
1316     (if (and stencils
1317              (pair? indices)
1318              (pair? (cdr indices)))
1319         (let* ((encl (sublist stencils (car indices) (cadr indices)))
1320                (x-ext (stencil-list-extent encl X))
1321                (y-ext (stencil-list-extent encl Y))
1322                (thick 0.10)
1323                (pad 0.35)
1324                (protusion (* 2.5 thick))
1325                (lb
1326                 (ly:stencil-translate-axis 
1327                  (ly:bracket Y y-ext thick protusion)
1328                  (- (car x-ext) pad) X))
1329                (rb (ly:stencil-translate-axis
1330                     (ly:bracket Y y-ext thick (- protusion))
1331                     (+ (cdr x-ext) pad) X)))
1333           (make-brackets
1334            stencils (cddr indices)
1335            (append
1336             (list lb rb)
1337             acc)))
1338         acc))
1340   (let* ((stencils
1341           (map (lambda (x)
1342                  (interpret-markup
1343                   layout
1344                   props
1345                   x)) args))
1346          (leading
1347           (chain-assoc-get 'baseline-skip props))
1348          (stacked (stack-stencils-vertically
1349                    (remove ly:stencil-empty? stencils) 1.25 #f))
1350          (brackets (make-brackets stacked indices '())))
1352     (apply ly:stencil-add
1353            (append stacked brackets))))
1356 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1357 ;; size indications arrow
1358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;