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