]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
* lily/tie.cc (print): idem.
[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--2005  Han-Wen Nienhuys <hanwen@cs.uu.nl>
6 ;;;;                  Jan Nieuwenhuizen <janneke@gnu.org>
7
8 ;;; markup commands
9 ;;;  * each markup function should have a doc string with
10 ;;     syntax, description and example. 
11
12 (use-modules (ice-9 regex))
13
14 (define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1)))
15 (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
16
17 (def-markup-command (stencil layout props stil) (ly:stencil?)
18   "Stencil as markup"
19   stil)
20
21 (def-markup-command (draw-circle layout props radius thickness)
22   (number? number?)
23   "A circle of radius @var{radius} and thickness @var{thickness}"
24   (make-circle-stencil radius thickness))
25
26 (def-markup-command (circle layout props arg) (markup?)
27   "Draw a circle around @var{arg}.  Use @code{thickness},
28 @code{circle-padding} and @code{font-size} properties to determine line
29 thickness and padding around the markup."
30   (let* ((th (chain-assoc-get 'thickness props  0.1))
31          (size (chain-assoc-get 'font-size props 0))
32          (pad
33           (* (magstep size)
34              (chain-assoc-get 'circle-padding props 0.2)))
35          (m (interpret-markup layout props arg)))
36     (circle-stencil m th pad)))
37
38 (def-markup-command (with-url layout props url arg) (string? markup?)
39   "Add a link to URL @var{url} around @var{arg}. This only works in
40 the PDF backend."
41   (let* ((stil (interpret-markup layout props arg))
42          (xextent (ly:stencil-extent stil X))
43          (yextent (ly:stencil-extent stil Y))
44          (old-expr (ly:stencil-expr stil))
45          (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
46     (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
47
48 (def-markup-command (score layout props score) (ly:score?)
49   "Inline an image of music."
50   (let* ((systems (ly:score-embedded-format score layout)))
51
52     (if (= 0 (vector-length systems))
53         (begin
54           (ly:warn (_"No systems found in \\score markup.  Does it have a \\layout? block"))
55           empty-markup)
56         (let* ((stencil (ly:paper-system-stencil (vector-ref systems 0)))) 
57           
58           (ly:stencil-aligned-to stencil Y CENTER)))))
59
60 (def-markup-command (simple layout props str) (string?)
61   "A simple text string; @code{\\markup @{ foo @}} is equivalent with
62 @code{\\markup @{ \\simple #\"foo\" @}}."
63   (interpret-markup layout props str))
64
65 (def-markup-command (encoded-simple layout props sym str) (symbol? string?)
66   "A text string, encoded with encoding @var{sym}. See
67 @usermanref{Text encoding} for more information."
68   (Text_interface::interpret_string layout props sym str))
69
70
71 ;; TODO: use font recoding.
72 ;;                    (make-line-markup
73 ;;                     (map make-word-markup (string-tokenize str)))))
74
75 (define-public empty-markup
76   (make-simple-markup ""))
77
78
79 (def-markup-command (postscript layout props str) (string?)
80   "This inserts @var{str} directly into the output as a PostScript
81 command string.  Due to technicalities of the output backends,
82 different scales should be used for the @TeX{} and PostScript backend,
83 selected with @code{-f}. 
84
85
86 For the TeX backend, the following string prints a rotated text
87
88 @cindex rotated text
89
90 @verbatim
91 0 0 moveto /ecrm10 findfont 
92 1.75 scalefont setfont 90 rotate (hello) show
93 @end verbatim
94
95 @noindent
96 The magical constant 1.75 scales from LilyPond units (staff spaces) to
97 TeX dimensions.
98
99 For the postscript backend, use the following
100
101 @verbatim
102 gsave /ecrm10 findfont 
103  10.0 output-scale div 
104  scalefont setfont 90 rotate (hello) show grestore 
105 @end verbatim
106 "
107   ;; FIXME
108   (ly:make-stencil
109    (list 'embedded-ps str)
110    '(0 . 0) '(0 . 0)))
111
112 ;;(def-markup-command (fill-line layout props line-width markups)
113 ;;  (number? markup-list?)
114 ;; no parser tag -- should make number? markuk-list? thingy
115 (def-markup-command (fill-line layout props markups)
116   (markup-list?)
117   "Put @var{markups} in a horizontal line of width @var{line-width}.
118    The markups are spaced/flushed to fill the entire line.
119    If there are no arguments, return an empty stencil.
120 "
121   (let* ((orig-stencils
122           (map (lambda (x) (interpret-markup layout props x))
123                markups))
124          (stencils
125           (map (lambda (stc)
126                  (if (ly:stencil-empty? stc)
127                      point-stencil
128                      stc)) orig-stencils))
129      (text-widths
130         (map (lambda (stc)
131                 (if (ly:stencil-empty? stc)
132                         0.0
133                                 (interval-length (ly:stencil-extent stc X))))
134                         stencils))
135      (text-width (apply + text-widths))
136          (word-count (length stencils))
137          (word-space (chain-assoc-get 'word-space props))
138          (line-width (chain-assoc-get 'linewidth props))
139          (fill-space
140                 (cond
141                         ((= word-count 1) 
142                                 (list
143                                         (/ (- line-width text-width) 2)
144                                         (/ (- line-width text-width) 2)))
145                         ((= word-count 2)
146                                 (list
147                                         (- line-width text-width)))
148                         (else 
149                                 (get-fill-space word-count line-width text-widths))))
150      (fill-space-normal
151         (map (lambda (x)
152                 (if (< x word-space)
153                         word-space
154                                 x))
155                         fill-space))
156                                         
157          (line-stencils (if (= word-count 1)
158                             (list
159                              point-stencil
160                              (car stencils)
161                              point-stencil)
162                             stencils)))
163
164     (if (null? (remove ly:stencil-empty? orig-stencils))
165         empty-stencil
166         (stack-stencils-padding-list X RIGHT fill-space-normal line-stencils))))
167         
168 (define (get-fill-space word-count line-width text-widths)
169         "Calculate the necessary paddings between each two adjacent texts.
170         The lengths of all texts are stored in @var{text-widths}.
171         The normal formula for the padding between texts a and b is:
172         padding = line-width/(word-count - 1) - (length(a) + length(b))/2
173         The first and last padding have to be calculated specially using the
174         whole length of the first or last text.
175         Return a list of paddings.
176 "
177         (cond
178          ;; special case first padding
179          ((= (length text-widths) word-count)
180           (cons 
181            (- (- (/ line-width (1- word-count)) (car text-widths))
182               (/ (car (cdr text-widths)) 2))
183            (get-fill-space word-count line-width (cdr text-widths))))
184          ;; special case last padding
185          ((= (length text-widths) 2)
186           (list (- (/ line-width (1- word-count))
187                    (+ (/ (car text-widths) 2) (car (cdr text-widths)))) 0))
188          (else
189           (cons 
190            (- (/ line-width (1- word-count))
191               (/ (+ (car text-widths) (car (cdr text-widths))) 2))
192            (get-fill-space word-count line-width (cdr text-widths))))))
193
194 (define (font-markup qualifier value)
195   (lambda (layout props arg)
196     (interpret-markup layout (prepend-alist-chain qualifier value props) arg)))
197
198 (def-markup-command (line layout props args) (markup-list?)
199   "Put @var{args} in a horizontal line.  The property @code{word-space}
200 determines the space between each markup in @var{args}."
201   (stack-stencil-line
202    (chain-assoc-get 'word-space props)
203    (remove ly:stencil-empty?
204            (map (lambda (m) (interpret-markup layout props m)) args))))
205
206 (def-markup-command (fromproperty layout props symbol) (symbol?)
207   "Read the @var{symbol} from property settings, and produce a stencil
208   from the markup contained within. If @var{symbol} is not defined, it
209   returns an empty markup"
210   (let* ((m (chain-assoc-get symbol props)))
211     (if (markup? m)
212         (interpret-markup layout props m)
213         (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
214
215
216 (def-markup-command (on-the-fly layout props procedure arg) (symbol? markup?)
217   "Apply the @var{procedure} markup command to
218 @var{arg}. @var{procedure} should take a single argument."
219   (let* ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
220     (set-object-property! anonymous-with-signature
221                           'markup-signature
222                           (list markup?))
223     (interpret-markup layout props (list anonymous-with-signature arg))))
224
225
226 (def-markup-command (combine layout props m1 m2) (markup? markup?)
227   "Print two markups on top of each other."
228   (let* ((s1 (interpret-markup layout props m1))
229          (s2 (interpret-markup layout props m2)))
230     (ly:stencil-add s1 s2)))
231
232 (def-markup-command (finger layout props arg) (markup?)
233   "Set the argument as small numbers."
234   (interpret-markup layout
235                     (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
236                     arg))
237
238 (def-markup-command (fontsize layout props mag arg) (number? markup?)
239   "Set the relative font size, e.g.
240 @example
241 A \\fontsize #2 @{ B C @} D
242 @end example
243
244
245 This will enlarge the B and the C by two steps.
246 "
247   (interpret-markup
248    layout 
249    (prepend-alist-chain 'font-size mag props)
250    arg))
251
252 (def-markup-command (magnify layout props sz arg) (number? markup?)
253   "Set the font magnification for the its argument. In the following
254 example, the middle A will be 10% larger:
255 @example
256 A \\magnify #1.1 @{ A @} A
257 @end example
258
259 Note: magnification only works if a font-name is explicitly selected.
260 Use @code{\\fontsize} otherwise."
261   (interpret-markup
262    layout 
263    (prepend-alist-chain 'font-magnification sz props)
264    arg))
265
266 (def-markup-command (bold layout props arg) (markup?)
267   "Switch to bold font-series"
268   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
269
270 (def-markup-command (sans layout props arg) (markup?)
271   "Switch to the sans serif family"
272   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
273
274 (def-markup-command (number layout props arg) (markup?)
275   "Set font family to @code{number}, which yields the font used for
276 time signatures and fingerings.  This font only contains numbers and
277 some punctuation. It doesn't have any letters.  "
278   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
279
280 (def-markup-command (roman layout props arg) (markup?)
281   "Set font family to @code{roman}."
282   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
283
284 (def-markup-command (huge layout props arg) (markup?)
285   "Set font size to +2."
286   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
287
288 (def-markup-command (large layout props arg) (markup?)
289   "Set font size to +1."
290   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
291
292 (def-markup-command (normalsize layout props arg) (markup?)
293   "Set font size to default."
294   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
295
296 (def-markup-command (small layout props arg) (markup?)
297   "Set font size to -1."
298   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
299
300 (def-markup-command (tiny layout props arg) (markup?)
301   "Set font size to -2."
302   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
303
304 (def-markup-command (teeny layout props arg) (markup?)
305   "Set font size to -3."
306   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
307
308 (def-markup-command (caps layout props arg) (markup?)
309   "Set @code{font-shape} to @code{caps}."
310   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
311
312 ;(def-markup-command (latin-i layout props arg) (markup?)
313 ;  "TEST latin1 encoding."
314 ;  (interpret-markup layout (prepend-alist-chain 'font-shape 'latin1 props) arg))
315
316 (def-markup-command (dynamic layout props arg) (markup?)
317   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
318 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
319 normal words (like ``pi@`{u}'') should be done in a different font.  The
320 recommend font for this is bold and italic"
321   (interpret-markup
322    layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
323
324 (def-markup-command (italic layout props arg) (markup?)
325   "Use italic @code{font-shape} for @var{arg}. "
326   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
327
328 (def-markup-command (typewriter layout props arg) (markup?)
329   "Use @code{font-family} typewriter for @var{arg}."
330   (interpret-markup
331    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
332
333 (def-markup-command (upright layout props arg) (markup?)
334   "Set font shape to @code{upright}."
335   (interpret-markup
336    layout (prepend-alist-chain 'font-shape 'upright props) arg))
337
338 (def-markup-command (doublesharp layout props) ()
339   "Draw a double sharp symbol."
340
341   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
342 (def-markup-command (sesquisharp layout props) ()
343   "Draw a 3/2 sharp symbol."
344   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
345
346 (def-markup-command (sharp layout props) ()
347   "Draw a sharp symbol."
348   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
349
350 (def-markup-command (semisharp layout props) ()
351   "Draw a semi sharp symbol."
352   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
353
354 (def-markup-command (natural layout props) ()
355   "Draw a natural symbol."
356   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
357
358 (def-markup-command (semiflat layout props) ()
359   "Draw a semiflat."
360   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
361
362 (def-markup-command (flat layout props) ()
363   "Draw a flat symbol."
364   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
365
366 (def-markup-command (sesquiflat layout props) ()
367   "Draw a 3/2 flat symbol."
368   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
369
370 (def-markup-command (doubleflat layout props) ()
371   "Draw a double flat symbol."
372   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
373
374 (def-markup-command (with-color layout props color arg) (color? markup?)
375   "Draw @var{arg} in color specified by @var{color}"
376
377   (let* ((stil (interpret-markup layout props arg)))
378
379     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
380                      (ly:stencil-extent stil X)
381                      (ly:stencil-extent stil Y))))
382
383 ;;
384 ;; TODO: should extract baseline-skip from each argument somehow..
385 ;; 
386 (def-markup-command (column layout props args) (markup-list?)
387   "Stack the markups in @var{args} vertically.  The property
388 @code{baseline-skip} determines the space between each markup in @var{args}."
389   (stack-lines
390    -1 0.0 (chain-assoc-get 'baseline-skip props)
391    (remove ly:stencil-empty?
392            (map (lambda (m) (interpret-markup layout props m)) args))))
393
394 (def-markup-command (dir-column layout props args) (markup-list?)
395   "Make a column of args, going up or down, depending on the setting
396 of the @code{#'direction} layout property."
397   (let* ((dir (chain-assoc-get 'direction props)))
398     (stack-lines
399      (if (number? dir) dir -1)
400      0.0
401      (chain-assoc-get 'baseline-skip props)
402      (map (lambda (x) (interpret-markup layout props x)) args))))
403
404 (def-markup-command (center-align layout props args) (markup-list?)
405   "Put @code{args} in a centered column. "
406   (let* ((mols (map (lambda (x) (interpret-markup layout props x)) args))
407          (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
408     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) cmols)))
409
410 (def-markup-command (vcenter layout props arg) (markup?)
411   "Align @code{arg} to its Y center. "
412   (let* ((mol (interpret-markup layout props arg)))
413     (ly:stencil-aligned-to mol Y CENTER)))
414
415 (def-markup-command (hcenter layout props arg) (markup?)
416   "Align @code{arg} to its X center. "
417   (let* ((mol (interpret-markup layout props arg)))
418     (ly:stencil-aligned-to mol X CENTER)))
419
420 (def-markup-command (right-align layout props arg) (markup?)
421   "Align @var{arg} on its right edge. "
422   (let* ((m (interpret-markup layout props arg)))
423     (ly:stencil-aligned-to m X RIGHT)))
424
425 (def-markup-command (left-align layout props arg) (markup?)
426   "Align @var{arg} on its left edge. "
427   (let* ((m (interpret-markup layout props arg)))
428     (ly:stencil-aligned-to m X LEFT)))
429
430 (def-markup-command (general-align layout props axis dir arg)  (integer? number? markup?)
431   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
432   (let* ((m (interpret-markup layout props arg)))
433     (ly:stencil-aligned-to m axis dir)))
434
435 (def-markup-command (halign layout props dir arg) (number? markup?)
436   "Set horizontal alignment. If @var{dir} is @code{-1}, then it is
437 left-aligned, while @code{+1} is right. Values in between interpolate
438 alignment accordingly."
439   (let* ((m (interpret-markup layout props arg)))
440     (ly:stencil-aligned-to m X dir)))
441
442 (def-markup-command (musicglyph layout props glyph-name) (string?)
443   "This is converted to a musical symbol, e.g. @code{\\musicglyph
444 #\"accidentals.0\"} will select the natural sign from the music font.
445 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
446   (ly:font-get-glyph
447    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
448                                    props))
449    glyph-name))
450
451 (def-markup-command (lookup layout props glyph-name) (string?)
452   "Lookup a glyph by name."
453   (ly:font-get-glyph (ly:paper-get-font layout props)
454                      glyph-name))
455
456 (def-markup-command (char layout props num) (integer?)
457   "Produce a single character, e.g. @code{\\char #65} produces the 
458 letter 'A'."
459   (ly:get-glyph (ly:paper-get-font layout props) num))
460
461 (def-markup-command (raise layout props amount arg) (number? markup?)
462   "
463 This  raises  @var{arg}, by the distance @var{amount}.
464 A negative @var{amount} indicates lowering:
465 @c
466 @lilypond[verbatim,fragment,relative=1]
467  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
468 @end lilypond
469 The argument to @code{\\raise} is the vertical displacement amount,
470 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
471 raise objects in relation to their surrounding markups.
472
473 If the text object itself is positioned above or below the staff, then
474 @code{\\raise} cannot be used to move it, since the mechanism that
475 positions it next to the staff cancels any shift made with
476 @code{\\raise}. For vertical positioning, use the @code{padding}
477 and/or @code{extra-offset} properties. "
478   (ly:stencil-translate-axis (interpret-markup layout props arg)
479                              amount Y))
480
481 (def-markup-command (fraction layout props arg1 arg2) (markup? markup?)
482   "Make a fraction of two markups."
483   (let* ((m1 (interpret-markup layout props arg1))
484          (m2 (interpret-markup layout props arg2)))
485     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
486     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
487     (let* ((x1 (ly:stencil-extent m1 X))
488            (x2 (ly:stencil-extent m2 X))
489            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
490            ;; should stack mols separately, to maintain LINE on baseline
491            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
492       (set! stack
493             (ly:stencil-aligned-to stack Y CENTER))
494       (set! stack
495             (ly:stencil-aligned-to stack X LEFT))
496       ;; should have EX dimension
497       ;; empirical anyway
498       (ly:stencil-translate-axis stack 0.75 Y))))
499
500
501
502 (def-markup-command (filled-box layout props xext yext blot)
503   (number-pair? number-pair? number?)
504   "Draw a box with rounded corners of dimensions @var{xext} and @var{yext}."
505   (ly:round-filled-box
506    xext yext blot))
507
508 (def-markup-command (whiteout layout props arg) (markup?)
509   "Provide a white underground for @var{arg}"
510   (let* ((stil (interpret-markup layout props
511                                  (make-with-color-markup black arg)))
512          (white
513           (interpret-markup layout props
514                             (make-with-color-markup
515                              white
516                              (make-filled-box-markup
517                               (ly:stencil-extent stil X)
518                               (ly:stencil-extent stil Y)
519                               0.0)))))
520
521     (ly:stencil-add white stil)))
522
523 ;; TODO: better syntax.
524
525 (def-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
526   "Construct a note symbol, with stem.  By using fractional values for
527 @var{dir}, you can obtain longer or shorter stems."
528   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
529          (size (chain-assoc-get 'font-size props 0))
530          (stem-length (* (magstep size) (max 3 (- log 1))))
531          (head-glyph (ly:font-get-glyph
532                       font
533                       (string-append "noteheads.s" (number->string (min log 2)))))
534          (stem-thickness 0.13)
535          (stemy (* dir stem-length))
536          (attachx (if (> dir 0)
537                       (- (cdr (ly:stencil-extent head-glyph X)) stem-thickness)
538                       0))
539          (attachy (* dir 0.28))
540          (stem-glyph (and (> log 0)
541                           (ly:round-filled-box
542                            (cons attachx (+ attachx  stem-thickness))
543                            (cons (min stemy attachy)
544                                  (max stemy attachy))
545                            (/ stem-thickness 3))))
546          (dot (ly:font-get-glyph font "dots.dot"))
547          (dotwid (interval-length (ly:stencil-extent dot X)))
548          (dots (and (> dot-count 0)
549                     (apply ly:stencil-add
550                            (map (lambda (x)
551                                   (ly:stencil-translate-axis
552                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
553                                 (iota dot-count 1)))))
554          (flaggl (and (> log 2)
555                       (ly:stencil-translate
556                        (ly:font-get-glyph font
557                                           (string-append "flags."
558                                                          (if (> dir 0) "u" "d")
559                                                          (number->string log)))
560                        (cons (+ attachx (/ stem-thickness 2)) stemy)))))
561     (if flaggl
562         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
563     (if (ly:stencil? stem-glyph)
564         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
565         (set! stem-glyph head-glyph))
566     (if (ly:stencil? dots)
567         (set! stem-glyph
568               (ly:stencil-add
569                (ly:stencil-translate-axis
570                 dots
571                 (+ (if (and (> dir 0) (> log 2))
572                        (* 1.5 dotwid)
573                        0)
574                    ;; huh ? why not necessary?
575                    ;;(cdr (ly:stencil-extent head-glyph X))
576                    dotwid)
577                 X)
578                stem-glyph)))
579     stem-glyph))
580
581 (define-public log2 
582   (let ((divisor (log 2)))
583     (lambda (z) (inexact->exact (/ (log z) divisor)))))
584
585 (define (parse-simple-duration duration-string)
586   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
587   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
588     (if (and match (string=? duration-string (match:substring match 0)))
589         (let ((len  (match:substring match 1))
590               (dots (match:substring match 2)))
591           (list (cond ((string=? len "breve") -1)
592                       ((string=? len "longa") -2)
593                       ((string=? len "maxima") -3)
594                       (else (log2 (string->number len))))
595                 (if dots (string-length dots) 0)))
596         (error "This is not a valid duration string:" duration-string))))
597
598 (def-markup-command (note layout props duration dir) (string? number?)
599   "This produces a note with a stem pointing in @var{dir} direction, with
600 the @var{duration} for the note head type and augmentation dots. For
601 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
602 a shortened down stem."
603   (let ((parsed (parse-simple-duration duration)))
604     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
605
606 (def-markup-command (normal-size-super layout props arg) (markup?)
607   "Set @var{arg} in superscript with a normal font size."
608   (ly:stencil-translate-axis
609    (interpret-markup layout props arg)
610    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
611
612 (def-markup-command (super layout props arg) (markup?)
613   "
614 @cindex raising text
615 @cindex lowering text
616 @cindex moving text
617 @cindex translating text
618
619 @cindex @code{\\super}
620
621
622 Raising and lowering texts can be done with @code{\\super} and
623 @code{\\sub}:
624
625 @lilypond[verbatim,fragment,relative=1]
626  c1^\\markup { E \"=\" mc \\super \"2\" }
627 @end lilypond
628
629 "
630   (ly:stencil-translate-axis
631    (interpret-markup
632     layout
633     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
634     arg)
635    (* 0.5 (chain-assoc-get 'baseline-skip props))
636    Y))
637
638 (def-markup-command (translate layout props offset arg) (number-pair? markup?)
639   "This translates an object. Its first argument is a cons of numbers
640 @example
641 A \\translate #(cons 2 -3) @{ B C @} D
642 @end example
643 This moves `B C' 2 spaces to the right, and 3 down, relative to its
644 surroundings. This command cannot be used to move isolated scripts
645 vertically, for the same reason that @code{\\raise} cannot be used for
646 that.
647
648 "
649   (ly:stencil-translate (interpret-markup  layout props arg)
650                         offset))
651
652 (def-markup-command (sub layout props arg) (markup?)
653   "Set @var{arg} in subscript."
654   (ly:stencil-translate-axis
655    (interpret-markup
656     layout
657     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
658     arg)
659    (* -0.5 (chain-assoc-get 'baseline-skip props))
660    Y))
661
662 (def-markup-command (beam layout props width slope thickness)
663   (number? number? number?)
664   "Create a beam with the specified parameters."
665   (let* ((y (* slope width))
666          (yext (cons (min 0 y) (max 0 y)))
667          (half (/ thickness 2)))
668
669     (ly:make-stencil
670      (list 'beam width
671            slope
672            thickness
673            (ly:output-def-lookup layout 'blotdiameter))
674      (cons 0 width)
675      (cons (+ (- half) (car yext))
676            (+ half (cdr yext))))))
677
678 (def-markup-command (normal-size-sub layout props arg) (markup?)
679   "Set @var{arg} in subscript, in a normal font size."
680   (ly:stencil-translate-axis
681    (interpret-markup layout props arg)
682    (* -0.5 (chain-assoc-get 'baseline-skip props))
683    Y))
684
685 (def-markup-command (hbracket layout props arg) (markup?)
686   "Draw horizontal brackets around @var{arg}."  
687   (let ((th 0.1) ;; todo: take from GROB.
688         (m (interpret-markup layout props arg)))
689     (bracketify-stencil m X th (* 2.5 th) th)))
690
691 (def-markup-command (bracket layout props arg) (markup?)
692   "Draw vertical brackets around @var{arg}."  
693   (let ((th 0.1) ;; todo: take from GROB.
694         (m (interpret-markup layout props arg)))
695     (bracketify-stencil m Y th (* 2.5 th) th)))
696
697 ;; todo: fix negative space
698 (def-markup-command (hspace layout props amount) (number?)
699   "This produces a invisible object taking horizontal space.
700 @example 
701 \\markup @{ A \\hspace #2.0 B @} 
702 @end example
703 will put extra space between A and B, on top of the space that is
704 normally inserted before elements on a line.
705 "
706   (if (> amount 0)
707       (ly:make-stencil "" (cons 0 amount) '(-1 . 1))
708       (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
709
710 (def-markup-command (override layout props new-prop arg) (pair? markup?)
711   "Add the first argument in to the property list.  Properties may be
712 any sort of property supported by @internalsref{font-interface} and
713 @internalsref{text-interface}, for example
714
715 @verbatim
716 \\override #'(font-family . married) \"bla\"
717 @end verbatim
718
719 "
720   (interpret-markup layout (cons (list new-prop) props) arg))
721
722
723 (def-markup-command (fontsize layout props increment arg) (number? markup?)
724   "Add @var{increment} to the font-size. Adjust baseline skip accordingly."
725
726   (let* ((fs (chain-assoc-get 'font-size props 0))
727          (bs (chain-assoc-get 'baseline-skip props 2)) 
728          (entries (list
729                    (cons 'baseline-skip (* bs (magstep increment)))
730                    (cons 'font-size (+ fs increment )))))
731
732     (interpret-markup layout (cons entries props) arg)))
733   
734 (def-markup-command (bigger layout props arg) (markup?)
735   "Increase the font size relative to current setting"
736   (interpret-markup layout props
737    `(,fontsize-markup 1 ,arg)))
738
739 (def-markup-command (smaller layout props arg) (markup?)
740   "Decrease the font size relative to current setting"
741   (interpret-markup layout props
742    `(,fontsize-markup -1 ,arg)))
743
744 (def-markup-command larger (markup?) bigger-markup)
745
746 (def-markup-command (box layout props arg) (markup?)
747   "Draw a box round @var{arg}.  Looks at @code{thickness},
748 @code{box-padding} and @code{font-size} properties to determine line
749 thickness and padding around the markup."
750   (let* ((th (chain-assoc-get 'thickness props  0.1))
751          (size (chain-assoc-get 'font-size props 0))
752          (pad (* (magstep size)
753                  (chain-assoc-get 'box-padding props 0.2)))
754          (m (interpret-markup layout props arg)))
755     (box-stencil m th pad)))
756
757 ;;FIXME: is this working? 
758 (def-markup-command (strut layout props) ()
759   "Create a box of the same height as the space in the current font."
760   (let ((m (Text_interface::interpret_markup layout props " ")))
761     (ly:make-stencil (ly:stencil-expr m)
762                      (ly:stencil-extent m X)
763                      '(1000 . -1000))))
764
765 (define number->mark-letter-vector (make-vector 25 #\A))
766
767 (do ((i 0 (1+ i))
768      (j 0 (1+ j)))
769     ((>= i 26))
770   (if (= i (- (char->integer #\I) (char->integer #\A)))
771       (set! i (1+ i)))
772   (vector-set! number->mark-letter-vector j
773                (integer->char (+ i (char->integer #\A)))))
774
775 (define number->mark-alphabet-vector (list->vector
776   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
777
778 (define (number->markletter-string vec n)
779   "Double letters for big marks."
780   (let* ((lst (vector-length vec)))
781     
782     (if (>= n lst)
783         (string-append (number->markletter-string vec (1- (quotient n lst)))
784                        (number->markletter-string vec (remainder n lst)))
785         (make-string 1 (vector-ref vec n)))))
786
787 (def-markup-command (markletter layout props num) (integer?)
788   "Make a markup letter for @var{num}.  The letters start with A to Z
789  (skipping I), and continues with double letters."
790   (Text_interface::interpret_markup layout props
791     (number->markletter-string number->mark-letter-vector num)))
792
793 (def-markup-command (markalphabet layout props num) (integer?)
794    "Make a markup letter for @var{num}.  The letters start with A to Z
795  and continues with double letters."
796    (Text_interface::interpret_markup layout props
797      (number->markletter-string number->mark-alphabet-vector num)))
798
799 (def-markup-command (bracketed-y-column layout props indices args)
800   (list? markup-list?)
801   "Make a column of the markups in @var{args}, putting brackets around
802 the elements marked in @var{indices}, which is a list of numbers."
803   (define (sublist lst start stop)
804     (take (drop lst start) (- (1+ stop) start)))
805
806   (define (stencil-list-extent ss axis)
807     (cons
808      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
809      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
810   
811   (define (stack-stencils stencils bskip last-stencil)
812     (cond
813      ((null? stencils) '())
814      ((not (ly:stencil? last-stencil))
815       (cons (car stencils)
816             (stack-stencils (cdr stencils) bskip (car stencils))))
817      (else
818       (let* ((orig (car stencils))
819              (dir (chain-assoc-get 'direction  props DOWN))
820              (new (ly:stencil-moved-to-edge last-stencil Y dir
821                                             orig
822                                             0.1 bskip)))
823
824         (cons new (stack-stencils (cdr stencils) bskip new))))))
825
826   (define (make-brackets stencils indices acc)
827     (if (and stencils
828              (pair? indices)
829              (pair? (cdr indices)))
830         (let* ((encl (sublist stencils (car indices) (cadr indices)))
831                (x-ext (stencil-list-extent encl X))
832                (y-ext (stencil-list-extent encl Y))
833                (thick 0.10)
834                (pad 0.35)
835                (protusion (* 2.5 thick))
836                (lb
837                 (ly:stencil-translate-axis 
838                  (ly:bracket Y y-ext thick protusion)
839                  (- (car x-ext) pad) X))
840                (rb (ly:stencil-translate-axis
841                     (ly:bracket Y y-ext thick (- protusion))
842                     (+ (cdr x-ext) pad) X)))
843
844           (make-brackets
845            stencils (cddr indices)
846            (append
847             (list lb rb)
848             acc)))
849         acc))
850
851   (let* ((stencils
852           (map (lambda (x)
853                  (interpret-markup
854                   layout
855                   props
856                   x)) args))
857          (leading
858           (chain-assoc-get 'baseline-skip props))
859          (stacked (stack-stencils
860                    (remove ly:stencil-empty? stencils) 1.25 #f))
861          (brackets (make-brackets stacked indices '())))
862
863     (apply ly:stencil-add
864            (append stacked brackets))))