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