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