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