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