]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
* lily/context-def.cc (path_to_acceptable_context): bugfix, depth
[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   (stack-stencil-line
278    (chain-assoc-get 'word-space props)
279    (remove ly:stencil-empty?
280            (map (lambda (m) (interpret-markup layout props m)) args))))
281
282 (def-markup-command (fromproperty layout props symbol) (symbol?)
283   "Read the @var{symbol} from property settings, and produce a stencil
284   from the markup contained within. If @var{symbol} is not defined, it
285   returns an empty markup"
286   (let* ((m (chain-assoc-get symbol props)))
287     (if (markup? m)
288         (interpret-markup layout props m)
289         (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
290
291
292 (def-markup-command (on-the-fly layout props procedure arg) (symbol? markup?)
293   "Apply the @var{procedure} markup command to
294 @var{arg}. @var{procedure} should take a single argument."
295   (let* ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
296     (set-object-property! anonymous-with-signature
297                           'markup-signature
298                           (list markup?))
299     (interpret-markup layout props (list anonymous-with-signature arg))))
300
301
302 (def-markup-command (combine layout props m1 m2) (markup? markup?)
303   "Print two markups on top of each other."
304   (let* ((s1 (interpret-markup layout props m1))
305          (s2 (interpret-markup layout props m2)))
306     (ly:stencil-add s1 s2)))
307
308 (def-markup-command (finger layout props arg) (markup?)
309   "Set the argument as small numbers."
310   (interpret-markup layout
311                     (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
312                     arg))
313
314 (def-markup-command (fontsize layout props mag arg) (number? markup?)
315   "Set the relative font size, e.g.
316 @example
317 A \\fontsize #2 @{ B C @} D
318 @end example
319
320
321 This will enlarge the B and the C by two steps.
322 "
323   (interpret-markup
324    layout 
325    (prepend-alist-chain 'font-size mag props)
326    arg))
327
328
329 ;; FIXME -> should convert to font-size.
330 (def-markup-command (magnify layout props sz arg) (number? markup?)
331   "Set the font magnification for the its argument. In the following
332 example, the middle A will be 10% larger:
333 @example
334 A \\magnify #1.1 @{ A @} A
335 @end example
336
337 Note: magnification only works if a font-name is explicitly selected.
338 Use @code{\\fontsize} otherwise."
339   (interpret-markup
340    layout 
341    (prepend-alist-chain 'font-magnification sz props)
342    arg))
343
344 (def-markup-command (bold layout props arg) (markup?)
345   "Switch to bold font-series"
346   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
347
348 (def-markup-command (sans layout props arg) (markup?)
349   "Switch to the sans serif family"
350   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
351
352 (def-markup-command (number layout props arg) (markup?)
353   "Set font family to @code{number}, which yields the font used for
354 time signatures and fingerings.  This font only contains numbers and
355 some punctuation. It doesn't have any letters.  "
356   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
357
358 (def-markup-command (roman layout props arg) (markup?)
359   "Set font family to @code{roman}."
360   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
361
362 (def-markup-command (huge layout props arg) (markup?)
363   "Set font size to +2."
364   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
365
366 (def-markup-command (large layout props arg) (markup?)
367   "Set font size to +1."
368   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
369
370 (def-markup-command (normalsize layout props arg) (markup?)
371   "Set font size to default."
372   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
373
374 (def-markup-command (small layout props arg) (markup?)
375   "Set font size to -1."
376   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
377
378 (def-markup-command (tiny layout props arg) (markup?)
379   "Set font size to -2."
380   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
381
382 (def-markup-command (teeny layout props arg) (markup?)
383   "Set font size to -3."
384   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
385
386 (def-markup-command (caps layout props arg) (markup?)
387   "Set @code{font-shape} to @code{caps}."
388   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
389
390 ;(def-markup-command (latin-i layout props arg) (markup?)
391 ;  "TEST latin1 encoding."
392 ;  (interpret-markup layout (prepend-alist-chain 'font-shape 'latin1 props) arg))
393
394 (def-markup-command (dynamic layout props arg) (markup?)
395   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
396 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
397 normal words (like ``pi@`{u}'') should be done in a different font.  The
398 recommend font for this is bold and italic"
399   (interpret-markup
400    layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
401
402 (def-markup-command (italic layout props arg) (markup?)
403   "Use italic @code{font-shape} for @var{arg}. "
404   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
405
406 (def-markup-command (typewriter layout props arg) (markup?)
407   "Use @code{font-family} typewriter for @var{arg}."
408   (interpret-markup
409    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
410
411 (def-markup-command (upright layout props arg) (markup?)
412   "Set font shape to @code{upright}."
413   (interpret-markup
414    layout (prepend-alist-chain 'font-shape 'upright props) arg))
415
416 (def-markup-command (doublesharp layout props) ()
417   "Draw a double sharp symbol."
418
419   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
420 (def-markup-command (sesquisharp layout props) ()
421   "Draw a 3/2 sharp symbol."
422   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
423
424 (def-markup-command (sharp layout props) ()
425   "Draw a sharp symbol."
426   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
427
428 (def-markup-command (semisharp layout props) ()
429   "Draw a semi sharp symbol."
430   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
431
432 (def-markup-command (natural layout props) ()
433   "Draw a natural symbol."
434   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
435
436 (def-markup-command (semiflat layout props) ()
437   "Draw a semiflat."
438   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
439
440 (def-markup-command (flat layout props) ()
441   "Draw a flat symbol."
442   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
443
444 (def-markup-command (sesquiflat layout props) ()
445   "Draw a 3/2 flat symbol."
446   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
447
448 (def-markup-command (doubleflat layout props) ()
449   "Draw a double flat symbol."
450   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
451
452 (def-markup-command (with-color layout props color arg) (color? markup?)
453   "Draw @var{arg} in color specified by @var{color}"
454
455   (let* ((stil (interpret-markup layout props arg)))
456
457     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
458                      (ly:stencil-extent stil X)
459                      (ly:stencil-extent stil Y))))
460
461 ;;
462 ;; TODO: should extract baseline-skip from each argument somehow..
463 ;; 
464 (def-markup-command (column layout props args) (markup-list?)
465   "Stack the markups in @var{args} vertically.  The property
466 @code{baseline-skip} determines the space between each markup in @var{args}."
467   (stack-lines
468    -1 0.0 (chain-assoc-get 'baseline-skip props)
469    (remove ly:stencil-empty?
470            (map (lambda (m) (interpret-markup layout props m)) args))))
471
472 (def-markup-command (dir-column layout props args) (markup-list?)
473   "Make a column of args, going up or down, depending on the setting
474 of the @code{#'direction} layout property."
475   (let* ((dir (chain-assoc-get 'direction props)))
476     (stack-lines
477      (if (number? dir) dir -1)
478      0.0
479      (chain-assoc-get 'baseline-skip props)
480      (map (lambda (x) (interpret-markup layout props x)) args))))
481
482 (def-markup-command (center-align layout props args) (markup-list?)
483   "Put @code{args} in a centered column. "
484   (let* ((mols (map (lambda (x) (interpret-markup layout props x)) args))
485          (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
486     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) cmols)))
487
488 (def-markup-command (vcenter layout props arg) (markup?)
489   "Align @code{arg} to its Y center. "
490   (let* ((mol (interpret-markup layout props arg)))
491     (ly:stencil-aligned-to mol Y CENTER)))
492
493 (def-markup-command (hcenter layout props arg) (markup?)
494   "Align @code{arg} to its X center. "
495   (let* ((mol (interpret-markup layout props arg)))
496     (ly:stencil-aligned-to mol X CENTER)))
497
498 (def-markup-command (right-align layout props arg) (markup?)
499   "Align @var{arg} on its right edge. "
500   (let* ((m (interpret-markup layout props arg)))
501     (ly:stencil-aligned-to m X RIGHT)))
502
503 (def-markup-command (left-align layout props arg) (markup?)
504   "Align @var{arg} on its left edge. "
505   (let* ((m (interpret-markup layout props arg)))
506     (ly:stencil-aligned-to m X LEFT)))
507
508 (def-markup-command (general-align layout props axis dir arg)  (integer? number? markup?)
509   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
510   (let* ((m (interpret-markup layout props arg)))
511     (ly:stencil-aligned-to m axis dir)))
512
513 (def-markup-command (halign layout props dir arg) (number? markup?)
514   "Set horizontal alignment. If @var{dir} is @code{-1}, then it is
515 left-aligned, while @code{+1} is right. Values in between interpolate
516 alignment accordingly."
517   (let* ((m (interpret-markup layout props arg)))
518     (ly:stencil-aligned-to m X dir)))
519
520 (def-markup-command (musicglyph layout props glyph-name) (string?)
521   "This is converted to a musical symbol, e.g. @code{\\musicglyph
522 #\"accidentals.0\"} will select the natural sign from the music font.
523 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
524   (ly:font-get-glyph
525    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
526                                    props))
527    glyph-name))
528
529 (def-markup-command (lookup layout props glyph-name) (string?)
530   "Lookup a glyph by name."
531   (ly:font-get-glyph (ly:paper-get-font layout props)
532                      glyph-name))
533
534 (def-markup-command (char layout props num) (integer?)
535   "Produce a single character, e.g. @code{\\char #65} produces the 
536 letter 'A'."
537   (ly:get-glyph (ly:paper-get-font layout props) num))
538
539 (def-markup-command (lower layout props amount arg) (number? markup?)
540   "
541 Lower @var{arg}, by the distance @var{amount}.
542 A negative @var{amount} indicates raising, see also @code{\raise}.
543 "
544   (ly:stencil-translate-axis (interpret-markup layout props arg)
545                              (- amount) Y))
546
547 (def-markup-command (raise layout props amount arg) (number? markup?)
548   "
549 Raise @var{arg}, by the distance @var{amount}.
550 A negative @var{amount} indicates lowering, see also @code{\\lower}.
551 @c
552 @lilypond[verbatim,fragment,relative=1]
553  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
554 @end lilypond
555 The argument to @code{\\raise} is the vertical displacement amount,
556 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
557 raise objects in relation to their surrounding markups.
558
559 If the text object itself is positioned above or below the staff, then
560 @code{\\raise} cannot be used to move it, since the mechanism that
561 positions it next to the staff cancels any shift made with
562 @code{\\raise}. For vertical positioning, use the @code{padding}
563 and/or @code{extra-offset} properties. "
564   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
565
566 (def-markup-command (fraction layout props arg1 arg2) (markup? markup?)
567   "Make a fraction of two markups."
568   (let* ((m1 (interpret-markup layout props arg1))
569          (m2 (interpret-markup layout props arg2)))
570     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
571     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
572     (let* ((x1 (ly:stencil-extent m1 X))
573            (x2 (ly:stencil-extent m2 X))
574            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
575            ;; should stack mols separately, to maintain LINE on baseline
576            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
577       (set! stack
578             (ly:stencil-aligned-to stack Y CENTER))
579       (set! stack
580             (ly:stencil-aligned-to stack X LEFT))
581       ;; should have EX dimension
582       ;; empirical anyway
583       (ly:stencil-translate-axis stack 0.75 Y))))
584
585
586
587 (def-markup-command (filled-box layout props xext yext blot)
588   (number-pair? number-pair? number?)
589   "Draw a box with rounded corners of dimensions @var{xext} and @var{yext}."
590   (ly:round-filled-box
591    xext yext blot))
592
593 (def-markup-command (whiteout layout props arg) (markup?)
594   "Provide a white underground for @var{arg}"
595   (let* ((stil (interpret-markup layout props
596                                  (make-with-color-markup black arg)))
597          (white
598           (interpret-markup layout props
599                             (make-with-color-markup
600                              white
601                              (make-filled-box-markup
602                               (ly:stencil-extent stil X)
603                               (ly:stencil-extent stil Y)
604                               0.0)))))
605
606     (ly:stencil-add white stil)))
607
608 ;; TODO: better syntax.
609
610 (def-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
611   "Construct a note symbol, with stem.  By using fractional values for
612 @var{dir}, you can obtain longer or shorter stems."
613   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
614          (size (chain-assoc-get 'font-size props 0))
615          (stem-length (* (magstep size) (max 3 (- log 1))))
616          (head-glyph (ly:font-get-glyph
617                       font
618                       (string-append "noteheads.s" (number->string (min log 2)))))
619          (stem-thickness 0.13)
620          (stemy (* dir stem-length))
621          (attachx (if (> dir 0)
622                       (- (cdr (ly:stencil-extent head-glyph X)) stem-thickness)
623                       0))
624          (attachy (* dir 0.28))
625          (stem-glyph (and (> log 0)
626                           (ly:round-filled-box
627                            (cons attachx (+ attachx  stem-thickness))
628                            (cons (min stemy attachy)
629                                  (max stemy attachy))
630                            (/ stem-thickness 3))))
631          (dot (ly:font-get-glyph font "dots.dot"))
632          (dotwid (interval-length (ly:stencil-extent dot X)))
633          (dots (and (> dot-count 0)
634                     (apply ly:stencil-add
635                            (map (lambda (x)
636                                   (ly:stencil-translate-axis
637                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
638                                 (iota dot-count 1)))))
639          (flaggl (and (> log 2)
640                       (ly:stencil-translate
641                        (ly:font-get-glyph font
642                                           (string-append "flags."
643                                                          (if (> dir 0) "u" "d")
644                                                          (number->string log)))
645                        (cons (+ attachx (/ stem-thickness 2)) stemy)))))
646     (if flaggl
647         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
648     (if (ly:stencil? stem-glyph)
649         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
650         (set! stem-glyph head-glyph))
651     (if (ly:stencil? dots)
652         (set! stem-glyph
653               (ly:stencil-add
654                (ly:stencil-translate-axis
655                 dots
656                 (+ (if (and (> dir 0) (> log 2))
657                        (* 1.5 dotwid)
658                        0)
659                    ;; huh ? why not necessary?
660                    ;;(cdr (ly:stencil-extent head-glyph X))
661                    dotwid)
662                 X)
663                stem-glyph)))
664     stem-glyph))
665
666 (define-public log2 
667   (let ((divisor (log 2)))
668     (lambda (z) (inexact->exact (/ (log z) divisor)))))
669
670 (define (parse-simple-duration duration-string)
671   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
672   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
673     (if (and match (string=? duration-string (match:substring match 0)))
674         (let ((len  (match:substring match 1))
675               (dots (match:substring match 2)))
676           (list (cond ((string=? len "breve") -1)
677                       ((string=? len "longa") -2)
678                       ((string=? len "maxima") -3)
679                       (else (log2 (string->number len))))
680                 (if dots (string-length dots) 0)))
681         (ly:error (_ "not a valid duration string: ~a") duration-string))))
682
683 (def-markup-command (note layout props duration dir) (string? number?)
684   "This produces a note with a stem pointing in @var{dir} direction, with
685 the @var{duration} for the note head type and augmentation dots. For
686 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
687 a shortened down stem."
688   (let ((parsed (parse-simple-duration duration)))
689     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
690
691 (def-markup-command (normal-size-super layout props arg) (markup?)
692   "Set @var{arg} in superscript with a normal font size."
693   (ly:stencil-translate-axis
694    (interpret-markup layout props arg)
695    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
696
697 (def-markup-command (super layout props arg) (markup?)
698   "
699 @cindex raising text
700 @cindex lowering text
701 @cindex moving text
702 @cindex translating text
703
704 @cindex @code{\\super}
705
706
707 Raising and lowering texts can be done with @code{\\super} and
708 @code{\\sub}:
709
710 @lilypond[verbatim,fragment,relative=1]
711  c1^\\markup { E \"=\" mc \\super \"2\" }
712 @end lilypond
713
714 "
715   (ly:stencil-translate-axis
716    (interpret-markup
717     layout
718     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
719     arg)
720    (* 0.5 (chain-assoc-get 'baseline-skip props))
721    Y))
722
723 (def-markup-command (translate layout props offset arg) (number-pair? markup?)
724   "This translates an object. Its first argument is a cons of numbers
725 @example
726 A \\translate #(cons 2 -3) @{ B C @} D
727 @end example
728 This moves `B C' 2 spaces to the right, and 3 down, relative to its
729 surroundings. This command cannot be used to move isolated scripts
730 vertically, for the same reason that @code{\\raise} cannot be used for
731 that.
732
733 "
734   (ly:stencil-translate (interpret-markup  layout props arg)
735                         offset))
736
737 (def-markup-command (sub layout props arg) (markup?)
738   "Set @var{arg} in subscript."
739   (ly:stencil-translate-axis
740    (interpret-markup
741     layout
742     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
743     arg)
744    (* -0.5 (chain-assoc-get 'baseline-skip props))
745    Y))
746
747 (def-markup-command (beam layout props width slope thickness)
748   (number? number? number?)
749   "Create a beam with the specified parameters."
750   (let* ((y (* slope width))
751          (yext (cons (min 0 y) (max 0 y)))
752          (half (/ thickness 2)))
753
754     (ly:make-stencil
755      (list 'beam width
756            slope
757            thickness
758            (ly:output-def-lookup layout 'blotdiameter))
759      (cons 0 width)
760      (cons (+ (- half) (car yext))
761            (+ half (cdr yext))))))
762
763 (def-markup-command (normal-size-sub layout props arg) (markup?)
764   "Set @var{arg} in subscript, in a normal font size."
765   (ly:stencil-translate-axis
766    (interpret-markup layout props arg)
767    (* -0.5 (chain-assoc-get 'baseline-skip props))
768    Y))
769
770 (def-markup-command (hbracket layout props arg) (markup?)
771   "Draw horizontal brackets around @var{arg}."  
772   (let ((th 0.1) ;; todo: take from GROB.
773         (m (interpret-markup layout props arg)))
774     (bracketify-stencil m X th (* 2.5 th) th)))
775
776 (def-markup-command (bracket layout props arg) (markup?)
777   "Draw vertical brackets around @var{arg}."  
778   (let ((th 0.1) ;; todo: take from GROB.
779         (m (interpret-markup layout props arg)))
780     (bracketify-stencil m Y th (* 2.5 th) th)))
781
782 ;; todo: fix negative space
783 (def-markup-command (hspace layout props amount) (number?)
784   "This produces a invisible object taking horizontal space.
785 @example 
786 \\markup @{ A \\hspace #2.0 B @} 
787 @end example
788 will put extra space between A and B, on top of the space that is
789 normally inserted before elements on a line.
790 "
791   (if (> amount 0)
792       (ly:make-stencil "" (cons 0 amount) '(-1 . 1))
793       (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
794
795 (def-markup-command (override layout props new-prop arg) (pair? markup?)
796   "Add the first argument in to the property list.  Properties may be
797 any sort of property supported by @internalsref{font-interface} and
798 @internalsref{text-interface}, for example
799
800 @verbatim
801 \\override #'(font-family . married) \"bla\"
802 @end verbatim
803
804 "
805   (interpret-markup layout (cons (list new-prop) props) arg))
806
807
808 (def-markup-command (fontsize layout props increment arg) (number? markup?)
809   "Add @var{increment} to the font-size. Adjust baseline skip accordingly."
810
811   (let* ((fs (chain-assoc-get 'font-size props 0))
812          (bs (chain-assoc-get 'baseline-skip props 2)) 
813          (entries (list
814                    (cons 'baseline-skip (* bs (magstep increment)))
815                    (cons 'font-size (+ fs increment )))))
816
817     (interpret-markup layout (cons entries props) arg)))
818   
819 (def-markup-command (bigger layout props arg) (markup?)
820   "Increase the font size relative to current setting"
821   (interpret-markup layout props
822    `(,fontsize-markup 1 ,arg)))
823
824 (def-markup-command (smaller layout props arg) (markup?)
825   "Decrease the font size relative to current setting"
826   (interpret-markup layout props
827    `(,fontsize-markup -1 ,arg)))
828
829 (def-markup-command larger (markup?) bigger-markup)
830
831 (def-markup-command (box layout props arg) (markup?)
832   "Draw a box round @var{arg}.  Looks at @code{thickness},
833 @code{box-padding} and @code{font-size} properties to determine line
834 thickness and padding around the markup."
835   (let* ((th (chain-assoc-get 'thickness props  0.1))
836          (size (chain-assoc-get 'font-size props 0))
837          (pad (* (magstep size)
838                  (chain-assoc-get 'box-padding props 0.2)))
839          (m (interpret-markup layout props arg)))
840     (box-stencil m th pad)))
841
842 ;;FIXME: is this working? 
843 (def-markup-command (strut layout props) ()
844   "Create a box of the same height as the space in the current font."
845   (let ((m (Text_interface::interpret_markup layout props " ")))
846     (ly:make-stencil (ly:stencil-expr m)
847                      (ly:stencil-extent m X)
848                      '(1000 . -1000))))
849
850 (define number->mark-letter-vector (make-vector 25 #\A))
851
852 (do ((i 0 (1+ i))
853      (j 0 (1+ j)))
854     ((>= i 26))
855   (if (= i (- (char->integer #\I) (char->integer #\A)))
856       (set! i (1+ i)))
857   (vector-set! number->mark-letter-vector j
858                (integer->char (+ i (char->integer #\A)))))
859
860 (define number->mark-alphabet-vector (list->vector
861   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
862
863 (define (number->markletter-string vec n)
864   "Double letters for big marks."
865   (let* ((lst (vector-length vec)))
866     
867     (if (>= n lst)
868         (string-append (number->markletter-string vec (1- (quotient n lst)))
869                        (number->markletter-string vec (remainder n lst)))
870         (make-string 1 (vector-ref vec n)))))
871
872 (def-markup-command (markletter layout props num) (integer?)
873   "Make a markup letter for @var{num}.  The letters start with A to Z
874  (skipping I), and continues with double letters."
875   (Text_interface::interpret_markup layout props
876     (number->markletter-string number->mark-letter-vector num)))
877
878 (def-markup-command (markalphabet layout props num) (integer?)
879    "Make a markup letter for @var{num}.  The letters start with A to Z
880  and continues with double letters."
881    (Text_interface::interpret_markup layout props
882      (number->markletter-string number->mark-alphabet-vector num)))
883
884 (def-markup-command (bracketed-y-column layout props indices args)
885   (list? markup-list?)
886   "Make a column of the markups in @var{args}, putting brackets around
887 the elements marked in @var{indices}, which is a list of numbers."
888   (define (sublist lst start stop)
889     (take (drop lst start) (- (1+ stop) start)))
890
891   (define (stencil-list-extent ss axis)
892     (cons
893      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
894      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
895   
896   (define (stack-stencils stencils bskip last-stencil)
897     (cond
898      ((null? stencils) '())
899      ((not (ly:stencil? last-stencil))
900       (cons (car stencils)
901             (stack-stencils (cdr stencils) bskip (car stencils))))
902      (else
903       (let* ((orig (car stencils))
904              (dir (chain-assoc-get 'direction  props DOWN))
905              (new (ly:stencil-moved-to-edge last-stencil Y dir
906                                             orig
907                                             0.1 bskip)))
908
909         (cons new (stack-stencils (cdr stencils) bskip new))))))
910
911   (define (make-brackets stencils indices acc)
912     (if (and stencils
913              (pair? indices)
914              (pair? (cdr indices)))
915         (let* ((encl (sublist stencils (car indices) (cadr indices)))
916                (x-ext (stencil-list-extent encl X))
917                (y-ext (stencil-list-extent encl Y))
918                (thick 0.10)
919                (pad 0.35)
920                (protusion (* 2.5 thick))
921                (lb
922                 (ly:stencil-translate-axis 
923                  (ly:bracket Y y-ext thick protusion)
924                  (- (car x-ext) pad) X))
925                (rb (ly:stencil-translate-axis
926                     (ly:bracket Y y-ext thick (- protusion))
927                     (+ (cdr x-ext) pad) X)))
928
929           (make-brackets
930            stencils (cddr indices)
931            (append
932             (list lb rb)
933             acc)))
934         acc))
935
936   (let* ((stencils
937           (map (lambda (x)
938                  (interpret-markup
939                   layout
940                   props
941                   x)) args))
942          (leading
943           (chain-assoc-get 'baseline-skip props))
944          (stacked (stack-stencils
945                    (remove ly:stencil-empty? stencils) 1.25 #f))
946          (brackets (make-brackets stacked indices '())))
947
948     (apply ly:stencil-add
949            (append stacked brackets))))