]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
Add \concat markup command.
[lilypond.git] / scm / define-markup-commands.scm
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@xs4all.nl>
6 ;;;;                  Jan Nieuwenhuizen <janneke@gnu.org>
7
8
9 ;;; markup commands
10 ;;;  * each markup function should have a doc string with
11 ;;     syntax, description and example. 
12
13 (use-modules (ice-9 regex))
14
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16 ;; utility functions
17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
18
19 (define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1)))
20 (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
21
22
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;; geometric shapes
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26
27 (define-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))
32
33 (define-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))))
41
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)
49
50      (cons 0 ex)
51      (cons 0 (* .86 ex))
52      )))
53
54 (define-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   
59   (let* ((th (chain-assoc-get 'thickness props  0.1))
60          (size (chain-assoc-get 'font-size props 0))
61          (pad
62           (* (magstep size)
63              (chain-assoc-get 'circle-padding props 0.2)))
64          (m (interpret-markup layout props arg)))
65     (circle-stencil m th pad)))
66
67 (define-markup-command (with-url layout props url arg) (string? markup?)
68   "Add a link to URL @var{url} around @var{arg}. This only works in
69 the PDF backend."
70   (let* ((stil (interpret-markup layout props arg))
71          (xextent (ly:stencil-extent stil X))
72          (yextent (ly:stencil-extent stil Y))
73          (old-expr (ly:stencil-expr stil))
74          (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
75     (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
76
77
78 (define-markup-command (beam layout props width slope thickness)
79   (number? number? number?)
80   "Create a beam with the specified parameters."
81   (let* ((y (* slope width))
82          (yext (cons (min 0 y) (max 0 y)))
83          (half (/ thickness 2)))
84
85     (ly:make-stencil
86      `(polygon ',(list 
87                   0 (/ thickness -2)
88                     width (+ (* width slope)  (/ thickness -2))
89                     width (+ (* width slope)  (/ thickness 2))
90                     0 (/ thickness 2))
91                ,(ly:output-def-lookup layout 'blot-diameter)
92                #t)
93      (cons 0 width)
94      (cons (+ (- half) (car yext))
95            (+ half (cdr yext))))))
96
97 (define-markup-command (box layout props arg) (markup?)
98   "Draw a box round @var{arg}.  Looks at @code{thickness},
99 @code{box-padding} and @code{font-size} properties to determine line
100 thickness and padding around the markup."
101   
102   (let* ((th (chain-assoc-get 'thickness props  0.1))
103          (size (chain-assoc-get 'font-size props 0))
104          (pad (* (magstep size)
105                  (chain-assoc-get 'box-padding props 0.2)))
106          (m (interpret-markup layout props arg)))
107     (box-stencil m th pad)))
108
109 (define-markup-command (filled-box layout props xext yext blot)
110   (number-pair? number-pair? number?)
111   "Draw a box with rounded corners of dimensions @var{xext} and
112 @var{yext}.  For example,
113 @verbatim
114 \\filled-box #'(-.3 . 1.8) #'(-.3 . 1.8) #0
115 @end verbatim
116 create a box extending horizontally from -0.3 to 1.8 and
117 vertically from -0.3 up to 1.8, with corners formed from a
118 circle of diameter 0 (ie sharp corners)."
119   (ly:round-filled-box
120    xext yext blot))
121
122 (define-markup-command (rotate layout props ang arg) (number? markup?)
123   "Rotate object with @var{ang} degrees around its center."
124   (let* ((stil (interpret-markup layout props arg)))
125     (ly:stencil-rotate stil ang 0 0)))
126
127
128 (define-markup-command (whiteout layout props arg) (markup?)
129   "Provide a white underground for @var{arg}"
130   (let* ((stil (interpret-markup layout props arg))
131          (white
132           (interpret-markup layout props
133                             (make-with-color-markup
134                              white
135                              (make-filled-box-markup
136                               (ly:stencil-extent stil X)
137                               (ly:stencil-extent stil Y)
138                               0.0)))))
139
140     (ly:stencil-add white stil)))
141
142 (define-markup-command (pad-markup layout props padding arg) (number? markup?)
143   "Add space around a markup object."
144
145   (let*
146       ((stil (interpret-markup layout props arg))
147        (xext (ly:stencil-extent stil X))
148        (yext (ly:stencil-extent stil Y)))
149
150     (ly:make-stencil
151      (ly:stencil-expr stil)
152      (interval-widen xext padding)
153      (interval-widen yext padding))))
154
155 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
156 ;; space
157 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
158
159 ;;FIXME: is this working? 
160 (define-markup-command (strut layout props) ()
161   "Create a box of the same height as the space in the current font."
162   (let ((m (ly:text-interface::interpret-markup layout props " ")))
163     (ly:make-stencil (ly:stencil-expr m)
164                      '(1000 . -1000)
165                      (ly:stencil-extent m X)
166                      )))
167
168
169 ;; todo: fix negative space
170 (define-markup-command (hspace layout props amount) (number?)
171   "This produces a invisible object taking horizontal space.
172 @example 
173 \\markup @{ A \\hspace #2.0 B @} 
174 @end example
175 will put extra space between A and B, on top of the space that is
176 normally inserted before elements on a line.
177 "
178   (if (> amount 0)
179       (ly:make-stencil "" (cons 0 amount) '(-1 . 1))
180       (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
181
182
183 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
184 ;; importing graphics.
185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
186
187 (define-markup-command (stencil layout props stil) (ly:stencil?)
188   "Stencil as markup"
189   stil)
190
191 (define bbox-regexp
192   (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)"))
193
194 (define (get-postscript-bbox string)
195   "Extract the bbox from STRING, or return #f if not present."
196   (let*
197       ((match (regexp-exec bbox-regexp string)))
198     
199     (if match
200         (map (lambda (x)
201                (string->number (match:substring match x)))
202              (cdr (iota 5)))
203              
204         #f)))
205
206 (define-markup-command (epsfile layout props axis size file-name) (number? number? string?)
207   "Inline an EPS image. The image is scaled along @var{axis} to
208 @var{size}."
209
210   (if (ly:get-option 'safe)
211       (interpret-markup layout props "not allowed in safe")
212       (eps-file->stencil axis size file-name)
213       ))
214
215 (define-markup-command (postscript layout props str) (string?)
216   "This inserts @var{str} directly into the output as a PostScript
217 command string.  Due to technicalities of the output backends,
218 different scales should be used for the @TeX{} and PostScript backend,
219 selected with @code{-f}. 
220
221
222 For the TeX backend, the following string prints a rotated text
223
224 @cindex rotated text
225
226 @verbatim
227 0 0 moveto /ecrm10 findfont 
228 1.75 scalefont setfont 90 rotate (hello) show
229 @end verbatim
230
231 @noindent
232 The magical constant 1.75 scales from LilyPond units (staff spaces) to
233 TeX dimensions.
234
235 For the postscript backend, use the following
236
237 @verbatim
238 gsave /ecrm10 findfont 
239  10.0 output-scale div 
240  scalefont setfont 90 rotate (hello) show grestore 
241 @end verbatim
242 "
243
244   ;; FIXME
245   (ly:make-stencil
246    (list 'embedded-ps
247          (format "
248 gsave currentpoint translate
249 0.1 setlinewidth
250  ~a
251 grestore
252 "
253                  str))
254    '(0 . 0) '(0 . 0)))
255
256
257 (define-markup-command (score layout props score) (ly:score?)
258   "Inline an image of music."
259   (let* ((output (ly:score-embedded-format score layout)))
260
261     (if (ly:music-output? output)
262         (paper-system-stencil
263          (vector-ref (ly:paper-score-paper-systems output) 0))
264         (begin
265           (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
266           empty-stencil))))
267
268 (define-markup-command (null layout props) ()
269   "An empty markup with extents of a single point"
270
271   point-stencil)
272
273 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
274 ;; basic formatting.
275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
276
277
278
279 (define-markup-command (simple layout props str) (string?)
280   "A simple text string; @code{\\markup @{ foo @}} is equivalent with
281 @code{\\markup @{ \\simple #\"foo\" @}}."
282   (interpret-markup layout props str))
283
284 (define-markup-command (tied-lyric layout props str) (string?)
285   
286   "Like simple-markup, but use tie characters for ~ tilde symbols."
287
288   (if (string-contains str "~")
289       (let*
290           ((parts (string-split str #\~))
291            (tie-str (ly:wide-char->utf-8 #x203f))
292            (joined  (list-join parts tie-str))
293            (join-stencil (interpret-markup layout props tie-str))
294            )
295
296         (interpret-markup layout 
297                           (prepend-alist-chain
298                            'word-space
299                            (/ (interval-length (ly:stencil-extent join-stencil X)) -3.5)
300                            props)
301                           (make-line-markup joined)))
302                            ;(map (lambda (s) (interpret-markup layout props s)) parts))
303       (interpret-markup layout props str)))
304
305
306 ;; TODO: use font recoding.
307 ;;                    (make-line-markup
308 ;;                     (map make-word-markup (string-tokenize str)))))
309
310 (define-public empty-markup
311   (make-simple-markup ""))
312
313 ;; helper for justifying lines.
314 (define (get-fill-space word-count line-width text-widths)
315   "Calculate the necessary paddings between each two adjacent texts.
316         The lengths of all texts are stored in @var{text-widths}.
317         The normal formula for the padding between texts a and b is:
318         padding = line-width/(word-count - 1) - (length(a) + length(b))/2
319         The first and last padding have to be calculated specially using the
320         whole length of the first or last text.
321         Return a list of paddings.
322 "
323   (cond
324    ((null? text-widths) '())
325    
326    ;; special case first padding
327    ((= (length text-widths) word-count)
328     (cons 
329      (- (- (/ line-width (1- word-count)) (car text-widths))
330         (/ (car (cdr text-widths)) 2))
331      (get-fill-space word-count line-width (cdr text-widths))))
332    ;; special case last padding
333    ((= (length text-widths) 2)
334     (list (- (/ line-width (1- word-count))
335              (+ (/ (car text-widths) 2) (car (cdr text-widths)))) 0))
336    (else
337     (cons 
338      (- (/ line-width (1- word-count))
339         (/ (+ (car text-widths) (car (cdr text-widths))) 2))
340      (get-fill-space word-count line-width (cdr text-widths))))))
341
342 (define-markup-command (fill-line layout props markups)
343   (markup-list?)
344   "Put @var{markups} in a horizontal line of width @var{line-width}.
345    The markups are spaced/flushed to fill the entire line.
346    If there are no arguments, return an empty stencil."
347  
348   (let* ((orig-stencils
349           (map (lambda (x) (interpret-markup layout props x))
350                markups))
351          (stencils
352           (map (lambda (stc)
353                  (if (ly:stencil-empty? stc)
354                      point-stencil
355                      stc)) orig-stencils))
356          (text-widths
357           (map (lambda (stc)
358                  (if (ly:stencil-empty? stc)
359                      0.0
360                      (interval-length (ly:stencil-extent stc X))))
361                stencils))
362          (text-width (apply + text-widths))
363          (text-dir (chain-assoc-get 'text-direction props RIGHT))
364          (word-count (length stencils))
365          (word-space (chain-assoc-get 'word-space props 1))
366          (prop-line-width (chain-assoc-get 'line-width props #f))
367          (line-width (if prop-line-width prop-line-width
368                          (ly:output-def-lookup layout 'line-width)))
369          (fill-space
370                 (cond
371                         ((= word-count 1) 
372                                 (list
373                                         (/ (- line-width text-width) 2)
374                                         (/ (- line-width text-width) 2)))
375                         ((= word-count 2)
376                                 (list
377                                         (- line-width text-width)))
378                         (else 
379                                 (get-fill-space word-count line-width text-widths))))
380          (fill-space-normal
381           (map (lambda (x)
382                  (if (< x word-space)
383                      word-space
384                      x))
385                fill-space))
386                                         
387          (line-stencils (if (= word-count 1)
388                             (list
389                              point-stencil
390                              (car stencils)
391                              point-stencil)
392                             stencils)))
393
394     (if (= text-dir LEFT)
395         (set! line-stencils (reverse line-stencils)))
396
397     (if (null? (remove ly:stencil-empty? orig-stencils))
398         empty-stencil
399         (stack-stencils-padding-list X
400                                      RIGHT fill-space-normal line-stencils))))
401         
402 (define-markup-command (line layout props args) (markup-list?)
403   "Put @var{args} in a horizontal line.  The property @code{word-space}
404 determines the space between each markup in @var{args}."
405   (let*
406       ((stencils (map (lambda (m) (interpret-markup layout props m)) args))
407        (space    (chain-assoc-get 'word-space props))
408        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
409        )
410
411     (if (= text-dir LEFT)
412         (set! stencils (reverse stencils)))
413     
414
415     (stack-stencil-line
416      space
417      (remove ly:stencil-empty? stencils))))
418
419 (define-markup-command (concat layout props args) (markup-list?)
420   "Concatenate @var{args} in a horizontal line, without spaces inbetween.
421 Strings and simple markups are concatenated on the input level, allowing
422 ligatures.  For example, @code{\\concat @{ \"f\" \\simple #\"i\" @}} is
423 equivalent to @code{\"fi\"}."
424
425   (define (concat-string-args arg-list)
426     (fold-right (lambda (arg result-list)
427                   (let ((result (if (pair? result-list)
428                                     (car result-list)
429                                   '())))
430                     (if (and (pair? arg) (eqv? (car arg) simple-markup))
431                       (set! arg (cadr arg)))
432                     (if (and (string? result) (string? arg))
433                         (cons (string-append arg result) (cdr result-list))
434                       (cons arg result-list))))
435                 '()
436                 arg-list))
437
438   (interpret-markup layout
439                     (prepend-alist-chain 'word-space 0 props)
440                     (make-line-markup (concat-string-args args))))
441
442 (define (wordwrap-stencils stencils
443                            justify base-space line-width text-dir)
444   
445   "Perform simple wordwrap, return stencil of each line."
446   
447   (define space (if justify
448                     
449                     ;; justify only stretches lines.
450                     (* 0.7 base-space)
451                     base-space))
452        
453   (define (take-list width space stencils
454                      accumulator accumulated-width)
455     "Return (head-list . tail) pair, with head-list fitting into width"
456     (if (null? stencils)
457         (cons accumulator stencils)
458         (let*
459             ((first (car stencils))
460              (first-wid (cdr (ly:stencil-extent (car stencils) X)))
461              (newwid (+ space first-wid accumulated-width))
462              )
463
464           (if
465            (or (null? accumulator)
466                (< newwid width))
467
468            (take-list width space
469                       (cdr stencils)
470                       (cons first accumulator)
471                       newwid)
472              (cons accumulator stencils))
473            )))
474
475     (let loop
476         ((lines '())
477          (todo stencils))
478
479       (let*
480           ((line-break (take-list line-width space todo
481                                  '() 0.0))
482            (line-stencils (car line-break))
483            (space-left (- line-width (apply + (map (lambda (x) (cdr (ly:stencil-extent x X)))
484                                               line-stencils))))
485
486            (line-word-space (cond
487                              ((not justify) space)
488
489                              ;; don't stretch last line of paragraph.
490                              ;; hmmm . bug - will overstretch the last line in some case. 
491                              ((null? (cdr line-break))
492                               base-space)
493                              ((null? line-stencils) 0.0)
494                              ((null? (cdr line-stencils)) 0.0)
495                              (else (/ space-left (1- (length line-stencils))))))
496
497            (line (stack-stencil-line
498                   line-word-space
499                   (if (= text-dir RIGHT)
500                       (reverse line-stencils)
501                       line-stencils))))
502
503         (if (pair? (cdr line-break))
504             (loop (cons line lines)
505                   (cdr line-break))
506
507             (begin
508               (if (= text-dir LEFT)
509                   (set! line
510                         (ly:stencil-translate-axis line
511                                                    (- line-width (interval-end (ly:stencil-extent line X)))
512                                                    X)))
513               (reverse (cons line lines))
514               
515             )))
516
517       ))
518
519
520 (define (wordwrap-markups layout props args justify)
521   (let*
522       ((baseline-skip (chain-assoc-get 'baseline-skip props))
523        (prop-line-width (chain-assoc-get 'line-width props #f))
524        (line-width (if prop-line-width prop-line-width
525                        (ly:output-def-lookup layout 'line-width)))
526        (word-space (chain-assoc-get 'word-space props))
527        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
528        (lines (wordwrap-stencils
529                (remove ly:stencil-empty?
530                        (map (lambda (m) (interpret-markup layout props m)) args))
531                justify word-space line-width
532                text-dir)
533                ))
534
535     (stack-lines DOWN 0.0 baseline-skip lines)))
536
537 (define-markup-command (justify layout props args) (markup-list?)
538   "Like wordwrap, but with lines stretched to justify the margins.
539 Use @code{\\override #'(line-width . X)} to set line-width, where X
540 is the number of staff spaces."
541
542   (wordwrap-markups layout props args #t))
543
544 (define-markup-command (wordwrap layout props args) (markup-list?)
545   "Simple wordwrap.  Use @code{\\override #'(line-width . X)} to set
546 line-width, where X is the number of staff spaces."
547
548   (wordwrap-markups layout props args #f))
549
550 (define (wordwrap-string layout props justify arg) 
551   (let*
552       ((baseline-skip (chain-assoc-get 'baseline-skip props))
553        (line-width (chain-assoc-get 'line-width props))
554        (word-space (chain-assoc-get 'word-space props))
555        
556        (para-strings (regexp-split
557                       (string-regexp-substitute "\r" "\n"
558                                                 (string-regexp-substitute "\r\n" "\n" arg))
559                       "\n[ \t\n]*\n[ \t\n]*"))
560        
561        (text-dir (chain-assoc-get 'text-direction props RIGHT)) 
562        (list-para-words (map (lambda (str)
563                                (regexp-split str "[ \t\n]+"))
564                              para-strings))
565        (para-lines (map (lambda (words)
566                           (let*
567                               ((stencils
568                                 (remove
569                                  ly:stencil-empty? (map 
570                                       (lambda (x)
571                                         (interpret-markup layout props x))
572                                       words)))
573                                (lines (wordwrap-stencils stencils
574                                                          justify word-space
575                                                          line-width text-dir
576                                                          )))
577
578                             lines))
579                         
580                         list-para-words)))
581
582     (stack-lines DOWN 0.0 baseline-skip (apply append para-lines))))
583
584
585 (define-markup-command (wordwrap-string layout props arg) (string?)
586   "Wordwrap a string. Paragraphs may be separated with double newlines"
587   (wordwrap-string layout props  #f arg))
588   
589 (define-markup-command (justify-string layout props arg) (string?)
590   "Justify a string. Paragraphs may be separated with double newlines"
591   (wordwrap-string layout props #t arg))
592
593
594 (define-markup-command (wordwrap-field layout props symbol) (symbol?)
595    (let* ((m (chain-assoc-get symbol props)))
596      (if (string? m)
597       (interpret-markup layout props
598        (list wordwrap-string-markup m))
599       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
600
601 (define-markup-command (justify-field layout props symbol) (symbol?)
602    (let* ((m (chain-assoc-get symbol props)))
603      (if (string? m)
604       (interpret-markup layout props
605        (list justify-string-markup m))
606       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
607
608
609
610 (define-markup-command (combine layout props m1 m2) (markup? markup?)
611   "Print two markups on top of each other."
612   (let* ((s1 (interpret-markup layout props m1))
613          (s2 (interpret-markup layout props m2)))
614     (ly:stencil-add s1 s2)))
615
616 ;;
617 ;; TODO: should extract baseline-skip from each argument somehow..
618 ;; 
619 (define-markup-command (column layout props args) (markup-list?)
620   "Stack the markups in @var{args} vertically.  The property
621 @code{baseline-skip} determines the space between each markup in @var{args}."
622
623   (let*
624       ((arg-stencils (map (lambda (m) (interpret-markup layout props m)) args))
625        (skip (chain-assoc-get 'baseline-skip props)))
626
627     
628     (stack-lines
629      -1 0.0 skip
630      (remove ly:stencil-empty? arg-stencils))))
631
632
633 (define-markup-command (dir-column layout props args) (markup-list?)
634   "Make a column of args, going up or down, depending on the setting
635 of the @code{#'direction} layout property."
636   (let* ((dir (chain-assoc-get 'direction props)))
637     (stack-lines
638      (if (number? dir) dir -1)
639      0.0
640      (chain-assoc-get 'baseline-skip props)
641      (map (lambda (x) (interpret-markup layout props x)) args))))
642
643 (define-markup-command (center-align layout props args) (markup-list?)
644   "Put @code{args} in a centered column. "
645   (let* ((mols (map (lambda (x) (interpret-markup layout props x)) args))
646          (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
647     
648     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) cmols)))
649
650 (define-markup-command (vcenter layout props arg) (markup?)
651   "Align @code{arg} to its Y center. "
652   (let* ((mol (interpret-markup layout props arg)))
653     (ly:stencil-aligned-to mol Y CENTER)))
654
655 (define-markup-command (hcenter layout props arg) (markup?)
656   "Align @code{arg} to its X center. "
657   (let* ((mol (interpret-markup layout props arg)))
658     (ly:stencil-aligned-to mol X CENTER)))
659
660 (define-markup-command (right-align layout props arg) (markup?)
661   "Align @var{arg} on its right edge. "
662   (let* ((m (interpret-markup layout props arg)))
663     (ly:stencil-aligned-to m X RIGHT)))
664
665 (define-markup-command (left-align layout props arg) (markup?)
666   "Align @var{arg} on its left edge. "
667   (let* ((m (interpret-markup layout props arg)))
668     (ly:stencil-aligned-to m X LEFT)))
669
670 (define-markup-command (general-align layout props axis dir arg)  (integer? number? markup?)
671   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
672   (let* ((m (interpret-markup layout props arg)))
673     (ly:stencil-aligned-to m axis dir)))
674
675 (define-markup-command (halign layout props dir arg) (number? markup?)
676   "Set horizontal alignment. If @var{dir} is @code{-1}, then it is
677 left-aligned, while @code{+1} is right. Values in between interpolate
678 alignment accordingly."
679   (let* ((m (interpret-markup layout props arg)))
680     (ly:stencil-aligned-to m X dir)))
681
682
683
684 (define-markup-command (with-dimensions layout props x y arg) (number-pair? number-pair? markup?)
685   "Set the dimensions of @var{arg} to @var{x} and @var{y}."
686   
687   (let* ((m (interpret-markup layout props arg)))
688     (ly:make-stencil (ly:stencil-expr m) x y)))
689
690
691 (define-markup-command (pad-around layout props amount arg) (number? markup?)
692
693   "Add padding @var{amount} all around @var{arg}. "
694   
695   (let*
696       ((m (interpret-markup layout props arg))
697        (x (ly:stencil-extent m X))
698        (y (ly:stencil-extent m Y)))
699     
700        
701     (ly:make-stencil (ly:stencil-expr m)
702                      (interval-widen x amount)
703                      (interval-widen y amount))
704    ))
705
706
707 (define-markup-command (pad-x layout props amount arg) (number? markup?)
708
709   "Add padding @var{amount} around @var{arg} in the X-direction. "
710   (let*
711       ((m (interpret-markup layout props arg))
712        (x (ly:stencil-extent m X))
713        (y (ly:stencil-extent m Y)))
714     
715        
716     (ly:make-stencil (ly:stencil-expr m)
717                      (interval-widen x amount)
718                      y)
719    ))
720
721
722 (define-markup-command (put-adjacent layout props arg1 axis dir arg2) (markup? integer? ly:dir?  markup?)
723
724   "Put @var{arg2} next to @var{arg1}, without moving @var{arg1}.  "
725   
726   (let* ((m1 (interpret-markup layout props arg1))
727          (m2 (interpret-markup layout props arg2)))
728
729     (ly:stencil-combine-at-edge m1 axis dir m2 0.0 0.0)
730   ))
731
732 (define-markup-command (transparent layout props arg) (markup?)
733   "Make the argument transparent"
734   (let*
735       ((m (interpret-markup layout props arg))
736        (x (ly:stencil-extent m X))
737        (y (ly:stencil-extent m Y)))
738     
739
740     
741     (ly:make-stencil ""
742                      x y)))
743
744
745 (define-markup-command (pad-to-box layout props x-ext y-ext arg)
746   (number-pair? number-pair? markup?)
747   "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space"
748
749   (let*
750       ((m (interpret-markup layout props arg))
751        (x (ly:stencil-extent m X))
752        (y (ly:stencil-extent m Y)))
753
754     (ly:make-stencil (ly:stencil-expr m)
755                      (interval-union x-ext x)
756                      (interval-union y-ext y))))
757
758
759 (define-markup-command (hcenter-in layout props length arg)
760   (number? markup?)
761   "Center @var{arg} horizontally within a box of extending
762 @var{length}/2 to the left and right."
763
764   (interpret-markup layout props
765                     (make-pad-to-box-markup
766                      (cons (/ length -2) (/ length 2))
767                      '(0 . 0)
768                      (make-hcenter-markup arg))))
769
770
771 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
772 ;; property
773 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
774
775 (define-markup-command (fromproperty layout props symbol) (symbol?)
776   "Read the @var{symbol} from property settings, and produce a stencil
777   from the markup contained within. If @var{symbol} is not defined, it
778   returns an empty markup"
779   (let* ((m (chain-assoc-get symbol props)))
780     (if (markup? m)
781         (interpret-markup layout props m)
782         (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
783
784
785 (define-markup-command (on-the-fly layout props procedure arg) (symbol? markup?)
786   "Apply the @var{procedure} markup command to
787 @var{arg}. @var{procedure} should take a single argument."
788   (let* ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
789     (set-object-property! anonymous-with-signature
790                           'markup-signature
791                           (list markup?))
792     (interpret-markup layout props (list anonymous-with-signature arg))))
793
794
795
796 (define-markup-command (override layout props new-prop arg) (pair? markup?)
797   "Add the first argument in to the property list.  Properties may be
798 any sort of property supported by @internalsref{font-interface} and
799 @internalsref{text-interface}, for example
800
801 @verbatim
802 \\override #'(font-family . married) \"bla\"
803 @end verbatim
804
805 "
806   (interpret-markup layout (cons (list new-prop) props) arg))
807
808 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
809 ;; files
810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
811
812 (define-markup-command (verbatim-file layout props name) (string?)
813   "Read the contents of a file, and include verbatimly"
814
815   (interpret-markup
816    layout props
817    (if  (ly:get-option 'safe)
818         "verbatim-file disabled in safe mode"
819         (let*
820             ((str (ly:gulp-file name))
821              (lines (string-split str #\nl)))
822
823           (make-typewriter-markup
824            (make-column-markup lines)))
825         )))
826
827 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
828 ;; fonts.
829 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
830
831
832 (define-markup-command (bigger layout props arg) (markup?)
833   "Increase the font size relative to current setting"
834   (interpret-markup layout props
835    `(,fontsize-markup 1 ,arg)))
836
837 (define-markup-command (smaller layout props arg) (markup?)
838   "Decrease the font size relative to current setting"
839   (interpret-markup layout props
840    `(,fontsize-markup -1 ,arg)))
841
842 (define-markup-command larger (markup?) bigger-markup)
843
844 (define-markup-command (finger layout props arg) (markup?)
845   "Set the argument as small numbers."
846   (interpret-markup layout
847                     (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
848                     arg))
849
850
851 (define-markup-command (fontsize layout props increment arg) (number? markup?)
852   "Add @var{increment} to the font-size. Adjust baseline skip accordingly."
853
854   (let* ((fs (chain-assoc-get 'font-size props 0))
855          (bs (chain-assoc-get 'baseline-skip props 2)) 
856          (entries (list
857                    (cons 'baseline-skip (* bs (magstep increment)))
858                    (cons 'font-size (+ fs increment )))))
859
860     (interpret-markup layout (cons entries props) arg)))
861   
862
863
864 ;; FIXME -> should convert to font-size.
865 (define-markup-command (magnify layout props sz arg) (number? markup?)
866   "Set the font magnification for the its argument. In the following
867 example, the middle A will be 10% larger:
868 @example
869 A \\magnify #1.1 @{ A @} A
870 @end example
871
872 Note: magnification only works if a font-name is explicitly selected.
873 Use @code{\\fontsize} otherwise."
874   (interpret-markup
875    layout 
876    (prepend-alist-chain 'font-magnification sz props)
877    arg))
878
879 (define-markup-command (bold layout props arg) (markup?)
880   "Switch to bold font-series"
881   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
882
883 (define-markup-command (sans layout props arg) (markup?)
884   "Switch to the sans serif family"
885   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
886
887 (define-markup-command (number layout props arg) (markup?)
888   "Set font family to @code{number}, which yields the font used for
889 time signatures and fingerings.  This font only contains numbers and
890 some punctuation. It doesn't have any letters.  "
891   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
892
893 (define-markup-command (roman layout props arg) (markup?)
894   "Set font family to @code{roman}."
895   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
896
897 (define-markup-command (huge layout props arg) (markup?)
898   "Set font size to +2."
899   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
900
901 (define-markup-command (large layout props arg) (markup?)
902   "Set font size to +1."
903   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
904
905 (define-markup-command (normalsize layout props arg) (markup?)
906   "Set font size to default."
907   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
908
909 (define-markup-command (small layout props arg) (markup?)
910   "Set font size to -1."
911   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
912
913 (define-markup-command (tiny layout props arg) (markup?)
914   "Set font size to -2."
915   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
916
917 (define-markup-command (teeny layout props arg) (markup?)
918   "Set font size to -3."
919   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
920
921 (define-markup-command (fontCaps layout props arg) (markup?)
922   "Set @code{font-shape} to @code{caps}."
923   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
924
925 ;; Poor man's caps
926 (define-markup-command (smallCaps layout props text) (markup?)
927   "Turn @code{text}, which should be a string, to small caps.
928 @example
929 \\markup \\smallCaps \"Text between double quotes\"
930 @end example
931 "
932   (define (make-small-caps-markup chars)
933     (cond ((null? chars)
934            (markup))
935           ((char-whitespace? (car chars))
936            (markup #:fontsize -2 #:simple (string-upcase (list->string (cdr chars)))))
937           (else
938            (markup #:hspace -1
939                    #:fontsize -2 #:simple (string-upcase (list->string chars))))))
940   (define (make-not-small-caps-markup chars)
941     (cond ((null? chars)
942            (markup))
943           ((char-whitespace? (car chars))
944            (markup #:simple (list->string (cdr chars))))
945           (else
946            (markup #:hspace -1
947                    #:simple (list->string chars)))))
948   (define (small-caps-aux done-markups current-chars rest-chars small? after-space?)
949     (cond ((null? rest-chars)
950            ;; the end of the string: build the markup
951            (make-line-markup (reverse! (cons ((if small?
952                                                   make-small-caps-markup
953                                                   make-not-small-caps-markup)
954                                               (reverse! current-chars))
955                                              done-markups))))
956           ((char-whitespace? (car rest-chars))
957            ;; a space char.
958            (small-caps-aux done-markups current-chars (cdr rest-chars) small? #t))
959           ((or (and small? (char-lower-case? (car rest-chars)))
960                (and (not small?) (not (char-lower-case? (car rest-chars)))))
961            ;; same case
962            ;; add the char to the current char list
963            (small-caps-aux done-markups
964                            (cons (car rest-chars)
965                                  (if after-space? 
966                                      (cons #\space current-chars)
967                                      current-chars))
968                            (cdr rest-chars) 
969                            small?
970                            #f))
971           (else
972            ;; case change
973            ;; make a markup with current chars, and start a new list with new char
974            (small-caps-aux (cons ((if small?
975                                       make-small-caps-markup
976                                       make-not-small-caps-markup)
977                                   (reverse! current-chars))
978                                  done-markups)
979                            (if after-space?
980                                (list (car rest-chars) #\space)
981                                (list (car rest-chars)))
982                            (cdr rest-chars)
983                            (not small?)
984                            #f))))
985   (interpret-markup layout props (small-caps-aux (list) 
986                                                  (list) 
987                                                  (cons #\space (string->list text))
988                                                  #f
989                                                  #f)))
990
991 (define-markup-command (caps layout props arg) (markup?)
992   (interpret-markup layout props (make-smallCaps-markup arg)))
993
994 (define-markup-command (dynamic layout props arg) (markup?)
995   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
996 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
997 normal words (like ``pi@`{u}'') should be done in a different font.  The
998 recommend font for this is bold and italic"
999   (interpret-markup
1000    layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
1001
1002 (define-markup-command (text layout props arg) (markup?)
1003   "Use a text font instead of music symbol or music alphabet font."  
1004
1005   ;; ugh - latin1
1006   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
1007                     arg))
1008
1009
1010 (define-markup-command (italic layout props arg) (markup?)
1011   "Use italic @code{font-shape} for @var{arg}. "
1012   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
1013
1014 (define-markup-command (typewriter layout props arg) (markup?)
1015   "Use @code{font-family} typewriter for @var{arg}."
1016   (interpret-markup
1017    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
1018
1019 (define-markup-command (upright layout props arg) (markup?)
1020   "Set font shape to @code{upright}.  This is the opposite of @code{italic}."
1021   (interpret-markup
1022    layout (prepend-alist-chain 'font-shape 'upright props) arg))
1023
1024 (define-markup-command (medium layout props arg) (markup?)
1025   "Switch to medium font-series (in contrast to bold)."
1026   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
1027                     arg))
1028
1029 (define-markup-command (normal-text layout props arg) (markup?)
1030   "Set all font related properties (except the size) to get the default normal text font, no matter what font was used earlier."
1031   ;; ugh - latin1
1032   (interpret-markup layout
1033                     (cons '((font-family . roman) (font-shape . upright)
1034                             (font-series . medium) (font-encoding . latin1))
1035                           props)
1036                     arg))
1037
1038 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1039 ;; symbols.
1040 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1041
1042 (define-markup-command (doublesharp layout props) ()
1043   "Draw a double sharp symbol."
1044
1045   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
1046
1047 (define-markup-command (sesquisharp layout props) ()
1048   "Draw a 3/2 sharp symbol."
1049   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
1050
1051 (define-markup-command (sharp layout props) ()
1052   "Draw a sharp symbol."
1053   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
1054
1055 (define-markup-command (semisharp layout props) ()
1056   "Draw a semi sharp symbol."
1057   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
1058
1059 (define-markup-command (natural layout props) ()
1060   "Draw a natural symbol."
1061   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
1062
1063 (define-markup-command (semiflat layout props) ()
1064   "Draw a semiflat."
1065   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
1066
1067 (define-markup-command (flat layout props) ()
1068   "Draw a flat symbol."
1069   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
1070
1071 (define-markup-command (sesquiflat layout props) ()
1072   "Draw a 3/2 flat symbol."
1073   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
1074
1075 (define-markup-command (doubleflat layout props) ()
1076   "Draw a double flat symbol."
1077   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
1078
1079 (define-markup-command (with-color layout props color arg) (color? markup?)
1080   "Draw @var{arg} in color specified by @var{color}"
1081
1082   (let* ((stil (interpret-markup layout props arg)))
1083
1084     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
1085                      (ly:stencil-extent stil X)
1086                      (ly:stencil-extent stil Y))))
1087
1088 \f
1089 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1090 ;; glyphs
1091 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1092
1093
1094 (define-markup-command (arrow-head layout props axis direction filled)
1095   (integer? ly:dir? boolean?)
1096   "produce an arrow head in specified direction and axis. Use the filled head if @var{filled} is  specified."
1097   (let*
1098       ((name (format "arrowheads.~a.~a~a"
1099                      (if filled
1100                          "close"
1101                          "open")
1102                      axis
1103                      direction)))
1104     (ly:font-get-glyph
1105      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
1106                                      props))
1107      name)))
1108
1109 (define-markup-command (musicglyph layout props glyph-name) (string?)
1110   "This is converted to a musical symbol, e.g. @code{\\musicglyph
1111 #\"accidentals.0\"} will select the natural sign from the music font.
1112 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
1113   (ly:font-get-glyph
1114    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
1115                                    props))
1116    glyph-name))
1117
1118 (define-markup-command (lookup layout props glyph-name) (string?)
1119   "Lookup a glyph by name."
1120   (ly:font-get-glyph (ly:paper-get-font layout props)
1121                      glyph-name))
1122
1123 (define-markup-command (char layout props num) (integer?)
1124   "Produce a single character, e.g. @code{\\char #65} produces the 
1125 letter 'A'."
1126
1127   (ly:text-interface::interpret-markup layout props (ly:wide-char->utf-8 num)))
1128
1129 (define number->mark-letter-vector (make-vector 25 #\A))
1130
1131 (do ((i 0 (1+ i))
1132      (j 0 (1+ j)))
1133     ((>= i 26))
1134   (if (= i (- (char->integer #\I) (char->integer #\A)))
1135       (set! i (1+ i)))
1136   (vector-set! number->mark-letter-vector j
1137                (integer->char (+ i (char->integer #\A)))))
1138
1139 (define number->mark-alphabet-vector (list->vector
1140   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
1141
1142 (define (number->markletter-string vec n)
1143   "Double letters for big marks."
1144   (let* ((lst (vector-length vec)))
1145     
1146     (if (>= n lst)
1147         (string-append (number->markletter-string vec (1- (quotient n lst)))
1148                        (number->markletter-string vec (remainder n lst)))
1149         (make-string 1 (vector-ref vec n)))))
1150
1151 (define-markup-command (markletter layout props num) (integer?)
1152   "Make a markup letter for @var{num}.  The letters start with A to Z
1153  (skipping I), and continues with double letters."
1154   (ly:text-interface::interpret-markup layout props
1155     (number->markletter-string number->mark-letter-vector num)))
1156
1157 (define-markup-command (markalphabet layout props num) (integer?)
1158    "Make a markup letter for @var{num}.  The letters start with A to Z
1159  and continues with double letters."
1160    (ly:text-interface::interpret-markup layout props
1161      (number->markletter-string number->mark-alphabet-vector num)))
1162
1163
1164
1165 (define-markup-command (slashed-digit layout props num) (integer?)
1166   "A feta number, with slash. This is for use in the context of
1167 figured bass notation"
1168   (let*
1169       ((mag (magstep (chain-assoc-get 'font-size props 0)))
1170        (thickness
1171         (* mag
1172            (chain-assoc-get 'thickness props 0.16)))
1173        (dy (* mag 0.15))
1174        (number-stencil (interpret-markup layout
1175                                          (prepend-alist-chain 'font-encoding 'fetaNumber props)
1176                                          (number->string num)))
1177        (num-x (interval-widen (ly:stencil-extent number-stencil X)
1178                               (* mag 0.2)))
1179        (num-y (ly:stencil-extent number-stencil Y))
1180        (slash-stencil 
1181         (ly:make-stencil
1182          `(draw-line
1183            ,thickness
1184            ,(car num-x) ,(- (interval-center num-y) dy)
1185            ,(cdr num-x) ,(+ (interval-center num-y) dy))
1186          num-x num-y
1187          )))
1188
1189     (ly:stencil-add number-stencil
1190                     (cond
1191                      ((= num 5) (ly:stencil-translate slash-stencil
1192                                                       ;;(cons (* mag -0.05) (* mag 0.42))
1193                                                       (cons (* mag -0.00) (* mag -0.07))
1194
1195                                                       ))
1196                      ((= num 7) (ly:stencil-translate slash-stencil
1197                                                       ;;(cons (* mag -0.05) (* mag 0.42))
1198                                                       (cons (* mag -0.00) (* mag -0.15))
1199
1200                                                       ))
1201                      
1202                      (else slash-stencil)))
1203     ))
1204 \f
1205 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1206 ;; the note command.
1207 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1208
1209
1210 ;; TODO: better syntax.
1211
1212 (define-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
1213   "Construct a note symbol, with stem.  By using fractional values for
1214 @var{dir}, you can obtain longer or shorter stems."
1215
1216   (define (get-glyph-name-candidates dir log style)
1217     (map (lambda (dir-name)
1218      (format "noteheads.~a~a~a" dir-name (min log 2)
1219              (if (and (symbol? style)
1220                       (not (equal? 'default style)))
1221                  (symbol->string style)
1222                  "")))
1223          (list (if (= dir UP) "u" "d")
1224                "s")))
1225                    
1226   (define (get-glyph-name font cands)
1227     (if (null? cands)
1228      ""
1229      (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
1230          (get-glyph-name font (cdr cands))
1231          (car cands))))
1232     
1233   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
1234          (size-factor (magstep (chain-assoc-get 'font-size props 0)))
1235          (style (chain-assoc-get 'style props '()))
1236          (stem-length (*  size-factor (max 3 (- log 1))))
1237          (head-glyph-name (get-glyph-name font (get-glyph-name-candidates (sign dir) log style)))
1238          (head-glyph (ly:font-get-glyph font head-glyph-name))
1239          (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
1240          (stem-thickness (* size-factor 0.13))
1241          (stemy (* dir stem-length))
1242          (attach-off (cons (interval-index
1243                             (ly:stencil-extent head-glyph X)
1244                             (* (sign dir) (car attach-indices)))
1245                            (* (sign dir)        ; fixme, this is inconsistent between X & Y.
1246                               (interval-index
1247                                (ly:stencil-extent head-glyph Y)
1248                                (cdr attach-indices)))))
1249          (stem-glyph (and (> log 0)
1250                           (ly:round-filled-box
1251                            (ordered-cons (car attach-off)
1252                                          (+ (car attach-off)  (* (- (sign dir)) stem-thickness)))
1253                            (cons (min stemy (cdr attach-off))
1254                                  (max stemy (cdr attach-off)))
1255                            (/ stem-thickness 3))))
1256          
1257          (dot (ly:font-get-glyph font "dots.dot"))
1258          (dotwid (interval-length (ly:stencil-extent dot X)))
1259          (dots (and (> dot-count 0)
1260                     (apply ly:stencil-add
1261                            (map (lambda (x)
1262                                   (ly:stencil-translate-axis
1263                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
1264                                 (iota dot-count 1)))))
1265          (flaggl (and (> log 2)
1266                       (ly:stencil-translate
1267                        (ly:font-get-glyph font
1268                                           (string-append "flags."
1269                                                          (if (> dir 0) "u" "d")
1270                                                          (number->string log)))
1271                        (cons (+ (car attach-off) (/ stem-thickness 2)) stemy)))))
1272     (if flaggl
1273         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
1274     (if (ly:stencil? stem-glyph)
1275         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
1276         (set! stem-glyph head-glyph))
1277     (if (ly:stencil? dots)
1278         (set! stem-glyph
1279               (ly:stencil-add
1280                (ly:stencil-translate-axis
1281                 dots
1282                 (+ (if (and (> dir 0) (> log 2))
1283                        (* 1.5 dotwid)
1284                        0)
1285                    ;; huh ? why not necessary?
1286                    ;;(cdr (ly:stencil-extent head-glyph X))
1287                    dotwid)
1288                 X)
1289                stem-glyph)))
1290     stem-glyph))
1291
1292 (define-public log2 
1293   (let ((divisor (log 2)))
1294     (lambda (z) (inexact->exact (/ (log z) divisor)))))
1295
1296 (define (parse-simple-duration duration-string)
1297   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
1298   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
1299     (if (and match (string=? duration-string (match:substring match 0)))
1300         (let ((len  (match:substring match 1))
1301               (dots (match:substring match 2)))
1302           (list (cond ((string=? len "breve") -1)
1303                       ((string=? len "longa") -2)
1304                       ((string=? len "maxima") -3)
1305                       (else (log2 (string->number len))))
1306                 (if dots (string-length dots) 0)))
1307         (ly:error (_ "not a valid duration string: ~a") duration-string))))
1308
1309 (define-markup-command (note layout props duration dir) (string? number?)
1310   "This produces a note with a stem pointing in @var{dir} direction, with
1311 the @var{duration} for the note head type and augmentation dots. For
1312 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
1313 a shortened down stem."
1314   (let ((parsed (parse-simple-duration duration)))
1315     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
1316
1317 \f
1318 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1319 ;; translating.
1320 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1321
1322 (define-markup-command (lower layout props amount arg) (number? markup?)
1323   "
1324 Lower @var{arg}, by the distance @var{amount}.
1325 A negative @var{amount} indicates raising, see also @code{\\raise}.
1326 "
1327   (ly:stencil-translate-axis (interpret-markup layout props arg)
1328                              (- amount) Y))
1329
1330
1331 (define-markup-command (translate-scaled layout props offset arg) (number-pair? markup?)
1332   "Translate @var{arg} by @var{offset}, scaling the offset by the @code{font-size}."
1333
1334   (let*
1335       ((factor (magstep (chain-assoc-get 'font-size props 0)))
1336        (scaled (cons (* factor (car offset))
1337                      (* factor (cdr offset)))))
1338     
1339   (ly:stencil-translate (interpret-markup layout props arg)
1340                         scaled)))
1341
1342 (define-markup-command (raise layout props amount arg) (number? markup?)
1343   "
1344 Raise @var{arg}, by the distance @var{amount}.
1345 A negative @var{amount} indicates lowering, see also @code{\\lower}.
1346 @c
1347 @lilypond[verbatim,fragment,relative=1]
1348  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
1349 @end lilypond
1350 The argument to @code{\\raise} is the vertical displacement amount,
1351 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
1352 raise objects in relation to their surrounding markups.
1353
1354 If the text object itself is positioned above or below the staff, then
1355 @code{\\raise} cannot be used to move it, since the mechanism that
1356 positions it next to the staff cancels any shift made with
1357 @code{\\raise}. For vertical positioning, use the @code{padding}
1358 and/or @code{extra-offset} properties. "
1359   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
1360
1361 (define-markup-command (fraction layout props arg1 arg2) (markup? markup?)
1362   "Make a fraction of two markups."
1363   (let* ((m1 (interpret-markup layout props arg1))
1364          (m2 (interpret-markup layout props arg2))
1365          (factor (magstep (chain-assoc-get 'font-size props 0)))
1366          (boxdimen (cons (* factor -0.05) (* factor 0.05)))
1367          (padding (* factor 0.2))
1368          (baseline (* factor 0.6))
1369          (offset (* factor 0.75)))
1370     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
1371     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
1372     (let* ((x1 (ly:stencil-extent m1 X))
1373            (x2 (ly:stencil-extent m2 X))
1374            (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0))
1375            ;; should stack mols separately, to maintain LINE on baseline
1376            (stack (stack-lines DOWN padding baseline (list m1 line m2))))
1377       (set! stack
1378             (ly:stencil-aligned-to stack Y CENTER))
1379       (set! stack
1380             (ly:stencil-aligned-to stack X LEFT))
1381       ;; should have EX dimension
1382       ;; empirical anyway
1383       (ly:stencil-translate-axis stack offset Y))))
1384
1385
1386
1387
1388
1389 (define-markup-command (normal-size-super layout props arg) (markup?)
1390   "Set @var{arg} in superscript with a normal font size."
1391   (ly:stencil-translate-axis
1392    (interpret-markup layout props arg)
1393    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
1394
1395 (define-markup-command (super layout props arg) (markup?)
1396   "
1397 @cindex raising text
1398 @cindex lowering text
1399 @cindex moving text
1400 @cindex translating text
1401
1402 @cindex @code{\\super}
1403
1404
1405 Raising and lowering texts can be done with @code{\\super} and
1406 @code{\\sub}:
1407
1408 @lilypond[verbatim,fragment,relative=1]
1409  c1^\\markup { E \"=\" mc \\super \"2\" }
1410 @end lilypond
1411
1412 "
1413   (ly:stencil-translate-axis
1414    (interpret-markup
1415     layout
1416     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1417     arg)
1418    (* 0.5 (chain-assoc-get 'baseline-skip props))
1419    Y))
1420
1421 (define-markup-command (translate layout props offset arg) (number-pair? markup?)
1422   "This translates an object. Its first argument is a cons of numbers
1423 @example
1424 A \\translate #(cons 2 -3) @{ B C @} D
1425 @end example
1426 This moves `B C' 2 spaces to the right, and 3 down, relative to its
1427 surroundings. This command cannot be used to move isolated scripts
1428 vertically, for the same reason that @code{\\raise} cannot be used for
1429 that.
1430
1431 "
1432   (ly:stencil-translate (interpret-markup  layout props arg)
1433                         offset))
1434
1435 (define-markup-command (sub layout props arg) (markup?)
1436   "Set @var{arg} in subscript."
1437   (ly:stencil-translate-axis
1438    (interpret-markup
1439     layout
1440     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1441     arg)
1442    (* -0.5 (chain-assoc-get 'baseline-skip props))
1443    Y))
1444
1445 (define-markup-command (normal-size-sub layout props arg) (markup?)
1446   "Set @var{arg} in subscript, in a normal font size."
1447   (ly:stencil-translate-axis
1448    (interpret-markup layout props arg)
1449    (* -0.5 (chain-assoc-get 'baseline-skip props))
1450    Y))
1451 \f
1452 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1453 ;; brackets.
1454 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1455
1456 (define-markup-command (hbracket layout props arg) (markup?)
1457   "Draw horizontal brackets around @var{arg}."  
1458   (let ((th 0.1) ;; todo: take from GROB.
1459         (m (interpret-markup layout props arg)))
1460     (bracketify-stencil m X th (* 2.5 th) th)))
1461
1462 (define-markup-command (bracket layout props arg) (markup?)
1463   "Draw vertical brackets around @var{arg}."  
1464   (let ((th 0.1) ;; todo: take from GROB.
1465         (m (interpret-markup layout props arg)))
1466     (bracketify-stencil m Y th (* 2.5 th) th)))
1467
1468 (define-markup-command (bracketed-y-column layout props indices args)
1469   (list? markup-list?)
1470   "Make a column of the markups in @var{args}, putting brackets around
1471 the elements marked in @var{indices}, which is a list of numbers.
1472
1473 "
1474 ;;
1475 ;; DROPME? This command is a relic from the old figured bass implementation.
1476 ;;
1477   
1478   (define (sublist lst start stop)
1479     (take (drop lst start) (- (1+ stop) start)))
1480
1481   (define (stencil-list-extent ss axis)
1482     (cons
1483      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
1484      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
1485   
1486
1487   (define (stack-stencils-vertically stencils bskip last-stencil)
1488     (cond
1489      ((null? stencils) '())
1490      ((not (ly:stencil? last-stencil))
1491       (cons (car stencils)
1492             (stack-stencils-vertically (cdr stencils) bskip (car stencils))))
1493      (else
1494       (let* ((orig (car stencils))
1495              (dir (chain-assoc-get 'direction  props DOWN))
1496              (new (ly:stencil-moved-to-edge last-stencil Y dir
1497                                             orig
1498                                             0.1 bskip)))
1499
1500         (cons new (stack-stencils-vertically (cdr stencils) bskip new))))))
1501
1502   (define (make-brackets stencils indices acc)
1503     (if (and stencils
1504              (pair? indices)
1505              (pair? (cdr indices)))
1506         (let* ((encl (sublist stencils (car indices) (cadr indices)))
1507                (x-ext (stencil-list-extent encl X))
1508                (y-ext (stencil-list-extent encl Y))
1509                (thick 0.10)
1510                (pad 0.35)
1511                (protusion (* 2.5 thick))
1512                (lb
1513                 (ly:stencil-translate-axis 
1514                  (ly:bracket Y y-ext thick protusion)
1515                  (- (car x-ext) pad) X))
1516                (rb (ly:stencil-translate-axis
1517                     (ly:bracket Y y-ext thick (- protusion))
1518                     (+ (cdr x-ext) pad) X)))
1519
1520           (make-brackets
1521            stencils (cddr indices)
1522            (append
1523             (list lb rb)
1524             acc)))
1525         acc))
1526
1527   (let* ((stencils
1528           (map (lambda (x)
1529                  (interpret-markup
1530                   layout
1531                   props
1532                   x)) args))
1533          (leading
1534           (chain-assoc-get 'baseline-skip props))
1535          (stacked (stack-stencils-vertically
1536                    (remove ly:stencil-empty? stencils) 1.25 #f))
1537          (brackets (make-brackets stacked indices '())))
1538
1539     (apply ly:stencil-add
1540            (append stacked brackets))))
1541 \f
1542
1543 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1544 ;; size indications arrow
1545 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1546