]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
Fix compile bug, add linewidth info to \justify.
[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 Use @code{\override #'(linewidth . X)} to set linewidth, where X
471 is the number of staff spaces."
472
473   (wordwrap-markups layout props args #t))
474
475 (def-markup-command (wordwrap layout props args) (markup-list?)
476   "Simple wordwrap.  Use @code{\override #'(linewidth . X)} to set
477 linewidth, where X is the number of staff spaces."
478
479   (wordwrap-markups layout props args #f))
480
481 (define (wordwrap-string layout props justify arg) 
482   (let*
483       ((baseline-skip (chain-assoc-get 'baseline-skip props))
484        (line-width (chain-assoc-get 'linewidth props))
485        (word-space (chain-assoc-get 'word-space props))
486        (para-strings (regexp-split arg "\n[ \t\n]*\n[ \t\n]*"))
487        
488        (list-para-words (map (lambda (str)
489                                (regexp-split str "[ \t\n]+"))
490                              para-strings))
491        (para-lines (map (lambda (words)
492                           (let*
493                               ((stencils
494                                 (remove
495                                  ly:stencil-empty? (map 
496                                       (lambda (x)
497                                         (interpret-markup layout props x))
498                                       words)))
499                                (lines (wordwrap-stencils stencils
500                                                          justify word-space line-width)))
501
502                             lines))
503                         
504                         list-para-words)))
505
506     (stack-lines DOWN 0.0 baseline-skip (apply append para-lines))))
507
508
509 (def-markup-command (wordwrap-string layout props arg) (string?)
510   "Wordwrap a string. Paragraphs may be separated with double newlines"
511   (wordwrap-string layout props  #f arg))
512   
513 (def-markup-command (justify-string layout props arg) (string?)
514   "Justify a string. Paragraphs may be separated with double newlines"
515   (wordwrap-string layout props #t arg))
516
517
518 (def-markup-command (wordwrap-field layout props symbol) (symbol?)
519    (let* ((m (chain-assoc-get symbol props)))
520      (if (string? m)
521       (interpret-markup layout props
522        (list wordwrap-string-markup m))
523       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
524
525 (def-markup-command (justify-field layout props symbol) (symbol?)
526    (let* ((m (chain-assoc-get symbol props)))
527      (if (string? m)
528       (interpret-markup layout props
529        (list justify-string-markup m))
530       (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
531
532
533
534 (def-markup-command (combine layout props m1 m2) (markup? markup?)
535   "Print two markups on top of each other."
536   (let* ((s1 (interpret-markup layout props m1))
537          (s2 (interpret-markup layout props m2)))
538     (ly:stencil-add s1 s2)))
539
540 ;;
541 ;; TODO: should extract baseline-skip from each argument somehow..
542 ;; 
543 (def-markup-command (column layout props args) (markup-list?)
544   "Stack the markups in @var{args} vertically.  The property
545 @code{baseline-skip} determines the space between each markup in @var{args}."
546   (stack-lines
547    -1 0.0 (chain-assoc-get 'baseline-skip props)
548    (remove ly:stencil-empty?
549            (map (lambda (m) (interpret-markup layout props m)) args))))
550
551 (def-markup-command (dir-column layout props args) (markup-list?)
552   "Make a column of args, going up or down, depending on the setting
553 of the @code{#'direction} layout property."
554   (let* ((dir (chain-assoc-get 'direction props)))
555     (stack-lines
556      (if (number? dir) dir -1)
557      0.0
558      (chain-assoc-get 'baseline-skip props)
559      (map (lambda (x) (interpret-markup layout props x)) args))))
560
561 (def-markup-command (center-align layout props args) (markup-list?)
562   "Put @code{args} in a centered column. "
563   (let* ((mols (map (lambda (x) (interpret-markup layout props x)) args))
564          (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
565     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) cmols)))
566
567 (def-markup-command (vcenter layout props arg) (markup?)
568   "Align @code{arg} to its Y center. "
569   (let* ((mol (interpret-markup layout props arg)))
570     (ly:stencil-aligned-to mol Y CENTER)))
571
572 (def-markup-command (hcenter layout props arg) (markup?)
573   "Align @code{arg} to its X center. "
574   (let* ((mol (interpret-markup layout props arg)))
575     (ly:stencil-aligned-to mol X CENTER)))
576
577 (def-markup-command (right-align layout props arg) (markup?)
578   "Align @var{arg} on its right edge. "
579   (let* ((m (interpret-markup layout props arg)))
580     (ly:stencil-aligned-to m X RIGHT)))
581
582 (def-markup-command (left-align layout props arg) (markup?)
583   "Align @var{arg} on its left edge. "
584   (let* ((m (interpret-markup layout props arg)))
585     (ly:stencil-aligned-to m X LEFT)))
586
587 (def-markup-command (general-align layout props axis dir arg)  (integer? number? markup?)
588   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
589   (let* ((m (interpret-markup layout props arg)))
590     (ly:stencil-aligned-to m axis dir)))
591
592 (def-markup-command (halign layout props dir arg) (number? markup?)
593   "Set horizontal alignment. If @var{dir} is @code{-1}, then it is
594 left-aligned, while @code{+1} is right. Values in between interpolate
595 alignment accordingly."
596   (let* ((m (interpret-markup layout props arg)))
597     (ly:stencil-aligned-to m X dir)))
598
599
600 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
601 ;; property
602 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
603
604 (def-markup-command (fromproperty layout props symbol) (symbol?)
605   "Read the @var{symbol} from property settings, and produce a stencil
606   from the markup contained within. If @var{symbol} is not defined, it
607   returns an empty markup"
608   (let* ((m (chain-assoc-get symbol props)))
609     (if (markup? m)
610         (interpret-markup layout props m)
611         (ly:make-stencil '()  '(1 . -1) '(1 . -1)))))
612
613
614 (def-markup-command (on-the-fly layout props procedure arg) (symbol? markup?)
615   "Apply the @var{procedure} markup command to
616 @var{arg}. @var{procedure} should take a single argument."
617   (let* ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
618     (set-object-property! anonymous-with-signature
619                           'markup-signature
620                           (list markup?))
621     (interpret-markup layout props (list anonymous-with-signature arg))))
622
623
624
625 (def-markup-command (override layout props new-prop arg) (pair? markup?)
626   "Add the first argument in to the property list.  Properties may be
627 any sort of property supported by @internalsref{font-interface} and
628 @internalsref{text-interface}, for example
629
630 @verbatim
631 \\override #'(font-family . married) \"bla\"
632 @end verbatim
633
634 "
635   (interpret-markup layout (cons (list new-prop) props) arg))
636
637 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
638 ;; fonts.
639 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
640
641
642 (def-markup-command (bigger layout props arg) (markup?)
643   "Increase the font size relative to current setting"
644   (interpret-markup layout props
645    `(,fontsize-markup 1 ,arg)))
646
647 (def-markup-command (smaller layout props arg) (markup?)
648   "Decrease the font size relative to current setting"
649   (interpret-markup layout props
650    `(,fontsize-markup -1 ,arg)))
651
652 (def-markup-command larger (markup?) bigger-markup)
653
654 (def-markup-command (finger layout props arg) (markup?)
655   "Set the argument as small numbers."
656   (interpret-markup layout
657                     (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
658                     arg))
659
660
661 (def-markup-command (fontsize layout props increment arg) (number? markup?)
662   "Add @var{increment} to the font-size. Adjust baseline skip accordingly."
663
664   (let* ((fs (chain-assoc-get 'font-size props 0))
665          (bs (chain-assoc-get 'baseline-skip props 2)) 
666          (entries (list
667                    (cons 'baseline-skip (* bs (magstep increment)))
668                    (cons 'font-size (+ fs increment )))))
669
670     (interpret-markup layout (cons entries props) arg)))
671   
672
673
674 ;; FIXME -> should convert to font-size.
675 (def-markup-command (magnify layout props sz arg) (number? markup?)
676   "Set the font magnification for the its argument. In the following
677 example, the middle A will be 10% larger:
678 @example
679 A \\magnify #1.1 @{ A @} A
680 @end example
681
682 Note: magnification only works if a font-name is explicitly selected.
683 Use @code{\\fontsize} otherwise."
684   (interpret-markup
685    layout 
686    (prepend-alist-chain 'font-magnification sz props)
687    arg))
688
689 (def-markup-command (bold layout props arg) (markup?)
690   "Switch to bold font-series"
691   (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
692
693 (def-markup-command (sans layout props arg) (markup?)
694   "Switch to the sans serif family"
695   (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
696
697 (def-markup-command (number layout props arg) (markup?)
698   "Set font family to @code{number}, which yields the font used for
699 time signatures and fingerings.  This font only contains numbers and
700 some punctuation. It doesn't have any letters.  "
701   (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
702
703 (def-markup-command (roman layout props arg) (markup?)
704   "Set font family to @code{roman}."
705   (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
706
707 (def-markup-command (huge layout props arg) (markup?)
708   "Set font size to +2."
709   (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
710
711 (def-markup-command (large layout props arg) (markup?)
712   "Set font size to +1."
713   (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
714
715 (def-markup-command (normalsize layout props arg) (markup?)
716   "Set font size to default."
717   (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
718
719 (def-markup-command (small layout props arg) (markup?)
720   "Set font size to -1."
721   (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
722
723 (def-markup-command (tiny layout props arg) (markup?)
724   "Set font size to -2."
725   (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
726
727 (def-markup-command (teeny layout props arg) (markup?)
728   "Set font size to -3."
729   (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
730
731 (def-markup-command (caps layout props arg) (markup?)
732   "Set @code{font-shape} to @code{caps}."
733   (interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
734
735 (def-markup-command (dynamic layout props arg) (markup?)
736   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
737 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
738 normal words (like ``pi@`{u}'') should be done in a different font.  The
739 recommend font for this is bold and italic"
740   (interpret-markup
741    layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
742
743 (def-markup-command (text layout props arg) (markup?)
744   "Use a text font instead of music symbol or music alphabet font."  
745
746   ;; ugh - latin1
747   (interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
748                     arg))
749
750
751 (def-markup-command (italic layout props arg) (markup?)
752   "Use italic @code{font-shape} for @var{arg}. "
753   (interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
754
755 (def-markup-command (typewriter layout props arg) (markup?)
756   "Use @code{font-family} typewriter for @var{arg}."
757   (interpret-markup
758    layout (prepend-alist-chain 'font-family 'typewriter props) arg))
759
760 (def-markup-command (upright layout props arg) (markup?)
761   "Set font shape to @code{upright}."
762   (interpret-markup
763    layout (prepend-alist-chain 'font-shape 'upright props) arg))
764
765
766 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
767 ;; symbols.
768 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
769
770 (def-markup-command (doublesharp layout props) ()
771   "Draw a double sharp symbol."
772
773   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
774
775 (def-markup-command (sesquisharp layout props) ()
776   "Draw a 3/2 sharp symbol."
777   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
778
779 (def-markup-command (sharp layout props) ()
780   "Draw a sharp symbol."
781   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
782
783 (def-markup-command (semisharp layout props) ()
784   "Draw a semi sharp symbol."
785   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
786
787 (def-markup-command (natural layout props) ()
788   "Draw a natural symbol."
789   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
790
791 (def-markup-command (semiflat layout props) ()
792   "Draw a semiflat."
793   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
794
795 (def-markup-command (flat layout props) ()
796   "Draw a flat symbol."
797   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
798
799 (def-markup-command (sesquiflat layout props) ()
800   "Draw a 3/2 flat symbol."
801   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
802
803 (def-markup-command (doubleflat layout props) ()
804   "Draw a double flat symbol."
805   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
806
807 (def-markup-command (with-color layout props color arg) (color? markup?)
808   "Draw @var{arg} in color specified by @var{color}"
809
810   (let* ((stil (interpret-markup layout props arg)))
811
812     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
813                      (ly:stencil-extent stil X)
814                      (ly:stencil-extent stil Y))))
815
816 \f
817 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
818 ;; glyphs
819 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
820
821
822 (def-markup-command (arrow-head layout props axis direction filled)
823   (integer? ly:dir? boolean?)
824   "produce an arrow head in specified direction and axis. Use the filled head if @var{filled} is  specified."
825   (let*
826       ((name (format "arrowheads.~a.~a~a"
827                      (if filled
828                          "close"
829                          "open")
830                      axis
831                      direction)))
832     (ly:font-get-glyph
833      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
834                                      props))
835      name)))
836
837 (def-markup-command (musicglyph layout props glyph-name) (string?)
838   "This is converted to a musical symbol, e.g. @code{\\musicglyph
839 #\"accidentals.0\"} will select the natural sign from the music font.
840 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
841   (ly:font-get-glyph
842    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
843                                    props))
844    glyph-name))
845
846 (def-markup-command (lookup layout props glyph-name) (string?)
847   "Lookup a glyph by name."
848   (ly:font-get-glyph (ly:paper-get-font layout props)
849                      glyph-name))
850
851 (def-markup-command (char layout props num) (integer?)
852   "Produce a single character, e.g. @code{\\char #65} produces the 
853 letter 'A'."
854   (ly:get-glyph (ly:paper-get-font layout props) num))
855
856
857 (define number->mark-letter-vector (make-vector 25 #\A))
858
859 (do ((i 0 (1+ i))
860      (j 0 (1+ j)))
861     ((>= i 26))
862   (if (= i (- (char->integer #\I) (char->integer #\A)))
863       (set! i (1+ i)))
864   (vector-set! number->mark-letter-vector j
865                (integer->char (+ i (char->integer #\A)))))
866
867 (define number->mark-alphabet-vector (list->vector
868   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
869
870 (define (number->markletter-string vec n)
871   "Double letters for big marks."
872   (let* ((lst (vector-length vec)))
873     
874     (if (>= n lst)
875         (string-append (number->markletter-string vec (1- (quotient n lst)))
876                        (number->markletter-string vec (remainder n lst)))
877         (make-string 1 (vector-ref vec n)))))
878
879 (def-markup-command (markletter layout props num) (integer?)
880   "Make a markup letter for @var{num}.  The letters start with A to Z
881  (skipping I), and continues with double letters."
882   (Text_interface::interpret_markup layout props
883     (number->markletter-string number->mark-letter-vector num)))
884
885 (def-markup-command (markalphabet layout props num) (integer?)
886    "Make a markup letter for @var{num}.  The letters start with A to Z
887  and continues with double letters."
888    (Text_interface::interpret_markup layout props
889      (number->markletter-string number->mark-alphabet-vector num)))
890
891 \f
892 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
893 ;; the note command.
894 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
895
896
897 ;; TODO: better syntax.
898
899 (def-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
900   "Construct a note symbol, with stem.  By using fractional values for
901 @var{dir}, you can obtain longer or shorter stems."
902   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
903          (size (chain-assoc-get 'font-size props 0))
904          (stem-length (* (magstep size) (max 3 (- log 1))))
905          (head-glyph (ly:font-get-glyph
906                       font
907                       (string-append "noteheads.s" (number->string (min log 2)))))
908          (stem-thickness 0.13)
909          (stemy (* dir stem-length))
910          (attachx (if (> dir 0)
911                       (- (cdr (ly:stencil-extent head-glyph X)) stem-thickness)
912                       0))
913          (attachy (* dir 0.28))
914          (stem-glyph (and (> log 0)
915                           (ly:round-filled-box
916                            (cons attachx (+ attachx  stem-thickness))
917                            (cons (min stemy attachy)
918                                  (max stemy attachy))
919                            (/ stem-thickness 3))))
920          (dot (ly:font-get-glyph font "dots.dot"))
921          (dotwid (interval-length (ly:stencil-extent dot X)))
922          (dots (and (> dot-count 0)
923                     (apply ly:stencil-add
924                            (map (lambda (x)
925                                   (ly:stencil-translate-axis
926                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
927                                 (iota dot-count 1)))))
928          (flaggl (and (> log 2)
929                       (ly:stencil-translate
930                        (ly:font-get-glyph font
931                                           (string-append "flags."
932                                                          (if (> dir 0) "u" "d")
933                                                          (number->string log)))
934                        (cons (+ attachx (/ stem-thickness 2)) stemy)))))
935     (if flaggl
936         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
937     (if (ly:stencil? stem-glyph)
938         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
939         (set! stem-glyph head-glyph))
940     (if (ly:stencil? dots)
941         (set! stem-glyph
942               (ly:stencil-add
943                (ly:stencil-translate-axis
944                 dots
945                 (+ (if (and (> dir 0) (> log 2))
946                        (* 1.5 dotwid)
947                        0)
948                    ;; huh ? why not necessary?
949                    ;;(cdr (ly:stencil-extent head-glyph X))
950                    dotwid)
951                 X)
952                stem-glyph)))
953     stem-glyph))
954
955 (define-public log2 
956   (let ((divisor (log 2)))
957     (lambda (z) (inexact->exact (/ (log z) divisor)))))
958
959 (define (parse-simple-duration duration-string)
960   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
961   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
962     (if (and match (string=? duration-string (match:substring match 0)))
963         (let ((len  (match:substring match 1))
964               (dots (match:substring match 2)))
965           (list (cond ((string=? len "breve") -1)
966                       ((string=? len "longa") -2)
967                       ((string=? len "maxima") -3)
968                       (else (log2 (string->number len))))
969                 (if dots (string-length dots) 0)))
970         (ly:error (_ "not a valid duration string: ~a") duration-string))))
971
972 (def-markup-command (note layout props duration dir) (string? number?)
973   "This produces a note with a stem pointing in @var{dir} direction, with
974 the @var{duration} for the note head type and augmentation dots. For
975 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
976 a shortened down stem."
977   (let ((parsed (parse-simple-duration duration)))
978     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
979
980 \f
981 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
982 ;; translating.
983 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
984
985 (def-markup-command (lower layout props amount arg) (number? markup?)
986   "
987 Lower @var{arg}, by the distance @var{amount}.
988 A negative @var{amount} indicates raising, see also @code{\raise}.
989 "
990   (ly:stencil-translate-axis (interpret-markup layout props arg)
991                              (- amount) Y))
992
993
994 (def-markup-command (raise layout props amount arg) (number? markup?)
995   "
996 Raise @var{arg}, by the distance @var{amount}.
997 A negative @var{amount} indicates lowering, see also @code{\\lower}.
998 @c
999 @lilypond[verbatim,fragment,relative=1]
1000  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
1001 @end lilypond
1002 The argument to @code{\\raise} is the vertical displacement amount,
1003 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
1004 raise objects in relation to their surrounding markups.
1005
1006 If the text object itself is positioned above or below the staff, then
1007 @code{\\raise} cannot be used to move it, since the mechanism that
1008 positions it next to the staff cancels any shift made with
1009 @code{\\raise}. For vertical positioning, use the @code{padding}
1010 and/or @code{extra-offset} properties. "
1011   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
1012
1013 (def-markup-command (fraction layout props arg1 arg2) (markup? markup?)
1014   "Make a fraction of two markups."
1015   (let* ((m1 (interpret-markup layout props arg1))
1016          (m2 (interpret-markup layout props arg2)))
1017     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
1018     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
1019     (let* ((x1 (ly:stencil-extent m1 X))
1020            (x2 (ly:stencil-extent m2 X))
1021            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
1022            ;; should stack mols separately, to maintain LINE on baseline
1023            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
1024       (set! stack
1025             (ly:stencil-aligned-to stack Y CENTER))
1026       (set! stack
1027             (ly:stencil-aligned-to stack X LEFT))
1028       ;; should have EX dimension
1029       ;; empirical anyway
1030       (ly:stencil-translate-axis stack 0.75 Y))))
1031
1032
1033
1034
1035
1036 (def-markup-command (normal-size-super layout props arg) (markup?)
1037   "Set @var{arg} in superscript with a normal font size."
1038   (ly:stencil-translate-axis
1039    (interpret-markup layout props arg)
1040    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
1041
1042 (def-markup-command (super layout props arg) (markup?)
1043   "
1044 @cindex raising text
1045 @cindex lowering text
1046 @cindex moving text
1047 @cindex translating text
1048
1049 @cindex @code{\\super}
1050
1051
1052 Raising and lowering texts can be done with @code{\\super} and
1053 @code{\\sub}:
1054
1055 @lilypond[verbatim,fragment,relative=1]
1056  c1^\\markup { E \"=\" mc \\super \"2\" }
1057 @end lilypond
1058
1059 "
1060   (ly:stencil-translate-axis
1061    (interpret-markup
1062     layout
1063     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1064     arg)
1065    (* 0.5 (chain-assoc-get 'baseline-skip props))
1066    Y))
1067
1068 (def-markup-command (translate layout props offset arg) (number-pair? markup?)
1069   "This translates an object. Its first argument is a cons of numbers
1070 @example
1071 A \\translate #(cons 2 -3) @{ B C @} D
1072 @end example
1073 This moves `B C' 2 spaces to the right, and 3 down, relative to its
1074 surroundings. This command cannot be used to move isolated scripts
1075 vertically, for the same reason that @code{\\raise} cannot be used for
1076 that.
1077
1078 "
1079   (ly:stencil-translate (interpret-markup  layout props arg)
1080                         offset))
1081
1082 (def-markup-command (sub layout props arg) (markup?)
1083   "Set @var{arg} in subscript."
1084   (ly:stencil-translate-axis
1085    (interpret-markup
1086     layout
1087     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1088     arg)
1089    (* -0.5 (chain-assoc-get 'baseline-skip props))
1090    Y))
1091
1092 (def-markup-command (normal-size-sub layout props arg) (markup?)
1093   "Set @var{arg} in subscript, in a normal font size."
1094   (ly:stencil-translate-axis
1095    (interpret-markup layout props arg)
1096    (* -0.5 (chain-assoc-get 'baseline-skip props))
1097    Y))
1098 \f
1099 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1100 ;; brackets.
1101 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1102
1103 (def-markup-command (hbracket layout props arg) (markup?)
1104   "Draw horizontal brackets around @var{arg}."  
1105   (let ((th 0.1) ;; todo: take from GROB.
1106         (m (interpret-markup layout props arg)))
1107     (bracketify-stencil m X th (* 2.5 th) th)))
1108
1109 (def-markup-command (bracket layout props arg) (markup?)
1110   "Draw vertical brackets around @var{arg}."  
1111   (let ((th 0.1) ;; todo: take from GROB.
1112         (m (interpret-markup layout props arg)))
1113     (bracketify-stencil m Y th (* 2.5 th) th)))
1114
1115 (def-markup-command (bracketed-y-column layout props indices args)
1116   (list? markup-list?)
1117   "Make a column of the markups in @var{args}, putting brackets around
1118 the elements marked in @var{indices}, which is a list of numbers."
1119   (define (sublist lst start stop)
1120     (take (drop lst start) (- (1+ stop) start)))
1121
1122   (define (stencil-list-extent ss axis)
1123     (cons
1124      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
1125      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
1126   
1127
1128   (define (stack-stencils-vertically stencils bskip last-stencil)
1129     (cond
1130      ((null? stencils) '())
1131      ((not (ly:stencil? last-stencil))
1132       (cons (car stencils)
1133             (stack-stencils-vertically (cdr stencils) bskip (car stencils))))
1134      (else
1135       (let* ((orig (car stencils))
1136              (dir (chain-assoc-get 'direction  props DOWN))
1137              (new (ly:stencil-moved-to-edge last-stencil Y dir
1138                                             orig
1139                                             0.1 bskip)))
1140
1141         (cons new (stack-stencils-vertically (cdr stencils) bskip new))))))
1142
1143   (define (make-brackets stencils indices acc)
1144     (if (and stencils
1145              (pair? indices)
1146              (pair? (cdr indices)))
1147         (let* ((encl (sublist stencils (car indices) (cadr indices)))
1148                (x-ext (stencil-list-extent encl X))
1149                (y-ext (stencil-list-extent encl Y))
1150                (thick 0.10)
1151                (pad 0.35)
1152                (protusion (* 2.5 thick))
1153                (lb
1154                 (ly:stencil-translate-axis 
1155                  (ly:bracket Y y-ext thick protusion)
1156                  (- (car x-ext) pad) X))
1157                (rb (ly:stencil-translate-axis
1158                     (ly:bracket Y y-ext thick (- protusion))
1159                     (+ (cdr x-ext) pad) X)))
1160
1161           (make-brackets
1162            stencils (cddr indices)
1163            (append
1164             (list lb rb)
1165             acc)))
1166         acc))
1167
1168   (let* ((stencils
1169           (map (lambda (x)
1170                  (interpret-markup
1171                   layout
1172                   props
1173                   x)) args))
1174          (leading
1175           (chain-assoc-get 'baseline-skip props))
1176          (stacked (stack-stencils-vertically
1177                    (remove ly:stencil-empty? stencils) 1.25 #f))
1178          (brackets (make-brackets stacked indices '())))
1179
1180     (apply ly:stencil-add
1181            (append stacked brackets))))