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