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