]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
* scm/define-markup-commands.scm (normal-text): Added 2 new
[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}.  This is the opposite of @code{italic}."
762   (interpret-markup
763    layout (prepend-alist-chain 'font-shape 'upright props) arg))
764
765 (def-markup-command (medium layout props arg) (markup?)
766   "Switch to medium font-series (in contrast to bold)."
767   (interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
768                     arg))
769
770 (def-markup-command (normal-text layout props arg) (markup?)
771   "Set all font related properties (except the size) to get the default normal text font, no matter what font was used earlier."
772   ;; ugh - latin1
773   (interpret-markup layout
774                     (cons '((font-family . roman) (font-shape . upright) (font-series . medium) (font-encoding . latin1)) props)
775                     arg)) 
776
777 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
778 ;; symbols.
779 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
780
781 (def-markup-command (doublesharp layout props) ()
782   "Draw a double sharp symbol."
783
784   (interpret-markup layout props (markup #:musicglyph "accidentals.4")))
785
786 (def-markup-command (sesquisharp layout props) ()
787   "Draw a 3/2 sharp symbol."
788   (interpret-markup layout props (markup #:musicglyph "accidentals.3")))
789
790 (def-markup-command (sharp layout props) ()
791   "Draw a sharp symbol."
792   (interpret-markup layout props (markup #:musicglyph "accidentals.2")))
793
794 (def-markup-command (semisharp layout props) ()
795   "Draw a semi sharp symbol."
796   (interpret-markup layout props (markup #:musicglyph "accidentals.1")))
797
798 (def-markup-command (natural layout props) ()
799   "Draw a natural symbol."
800   (interpret-markup layout props (markup #:musicglyph "accidentals.0")))
801
802 (def-markup-command (semiflat layout props) ()
803   "Draw a semiflat."
804   (interpret-markup layout props (markup #:musicglyph "accidentals.M1")))
805
806 (def-markup-command (flat layout props) ()
807   "Draw a flat symbol."
808   (interpret-markup layout props (markup #:musicglyph "accidentals.M2")))
809
810 (def-markup-command (sesquiflat layout props) ()
811   "Draw a 3/2 flat symbol."
812   (interpret-markup layout props (markup #:musicglyph "accidentals.M3")))
813
814 (def-markup-command (doubleflat layout props) ()
815   "Draw a double flat symbol."
816   (interpret-markup layout props (markup #:musicglyph "accidentals.M4")))
817
818 (def-markup-command (with-color layout props color arg) (color? markup?)
819   "Draw @var{arg} in color specified by @var{color}"
820
821   (let* ((stil (interpret-markup layout props arg)))
822
823     (ly:make-stencil (list 'color color (ly:stencil-expr stil))
824                      (ly:stencil-extent stil X)
825                      (ly:stencil-extent stil Y))))
826
827 \f
828 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
829 ;; glyphs
830 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
831
832
833 (def-markup-command (arrow-head layout props axis direction filled)
834   (integer? ly:dir? boolean?)
835   "produce an arrow head in specified direction and axis. Use the filled head if @var{filled} is  specified."
836   (let*
837       ((name (format "arrowheads.~a.~a~a"
838                      (if filled
839                          "close"
840                          "open")
841                      axis
842                      direction)))
843     (ly:font-get-glyph
844      (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
845                                      props))
846      name)))
847
848 (def-markup-command (musicglyph layout props glyph-name) (string?)
849   "This is converted to a musical symbol, e.g. @code{\\musicglyph
850 #\"accidentals.0\"} will select the natural sign from the music font.
851 See @usermanref{The Feta font} for  a complete listing of the possible glyphs."
852   (ly:font-get-glyph
853    (ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
854                                    props))
855    glyph-name))
856
857 (def-markup-command (lookup layout props glyph-name) (string?)
858   "Lookup a glyph by name."
859   (ly:font-get-glyph (ly:paper-get-font layout props)
860                      glyph-name))
861
862 (def-markup-command (char layout props num) (integer?)
863   "Produce a single character, e.g. @code{\\char #65} produces the 
864 letter 'A'."
865   (ly:get-glyph (ly:paper-get-font layout props) num))
866
867
868 (define number->mark-letter-vector (make-vector 25 #\A))
869
870 (do ((i 0 (1+ i))
871      (j 0 (1+ j)))
872     ((>= i 26))
873   (if (= i (- (char->integer #\I) (char->integer #\A)))
874       (set! i (1+ i)))
875   (vector-set! number->mark-letter-vector j
876                (integer->char (+ i (char->integer #\A)))))
877
878 (define number->mark-alphabet-vector (list->vector
879   (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
880
881 (define (number->markletter-string vec n)
882   "Double letters for big marks."
883   (let* ((lst (vector-length vec)))
884     
885     (if (>= n lst)
886         (string-append (number->markletter-string vec (1- (quotient n lst)))
887                        (number->markletter-string vec (remainder n lst)))
888         (make-string 1 (vector-ref vec n)))))
889
890 (def-markup-command (markletter layout props num) (integer?)
891   "Make a markup letter for @var{num}.  The letters start with A to Z
892  (skipping I), and continues with double letters."
893   (Text_interface::interpret_markup layout props
894     (number->markletter-string number->mark-letter-vector num)))
895
896 (def-markup-command (markalphabet layout props num) (integer?)
897    "Make a markup letter for @var{num}.  The letters start with A to Z
898  and continues with double letters."
899    (Text_interface::interpret_markup layout props
900      (number->markletter-string number->mark-alphabet-vector num)))
901
902 \f
903 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
904 ;; the note command.
905 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
906
907
908 ;; TODO: better syntax.
909
910 (def-markup-command (note-by-number layout props log dot-count dir) (number? number? number?)
911   "Construct a note symbol, with stem.  By using fractional values for
912 @var{dir}, you can obtain longer or shorter stems."
913   (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
914          (size (chain-assoc-get 'font-size props 0))
915          (stem-length (* (magstep size) (max 3 (- log 1))))
916          (head-glyph (ly:font-get-glyph
917                       font
918                       (string-append "noteheads.s" (number->string (min log 2)))))
919          (stem-thickness 0.13)
920          (stemy (* dir stem-length))
921          (attachx (if (> dir 0)
922                       (- (cdr (ly:stencil-extent head-glyph X)) stem-thickness)
923                       0))
924          (attachy (* dir 0.28))
925          (stem-glyph (and (> log 0)
926                           (ly:round-filled-box
927                            (cons attachx (+ attachx  stem-thickness))
928                            (cons (min stemy attachy)
929                                  (max stemy attachy))
930                            (/ stem-thickness 3))))
931          (dot (ly:font-get-glyph font "dots.dot"))
932          (dotwid (interval-length (ly:stencil-extent dot X)))
933          (dots (and (> dot-count 0)
934                     (apply ly:stencil-add
935                            (map (lambda (x)
936                                   (ly:stencil-translate-axis
937                                    dot  (* (+ 1 (* 2 x)) dotwid) X))
938                                 (iota dot-count 1)))))
939          (flaggl (and (> log 2)
940                       (ly:stencil-translate
941                        (ly:font-get-glyph font
942                                           (string-append "flags."
943                                                          (if (> dir 0) "u" "d")
944                                                          (number->string log)))
945                        (cons (+ attachx (/ stem-thickness 2)) stemy)))))
946     (if flaggl
947         (set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
948     (if (ly:stencil? stem-glyph)
949         (set! stem-glyph (ly:stencil-add stem-glyph head-glyph))
950         (set! stem-glyph head-glyph))
951     (if (ly:stencil? dots)
952         (set! stem-glyph
953               (ly:stencil-add
954                (ly:stencil-translate-axis
955                 dots
956                 (+ (if (and (> dir 0) (> log 2))
957                        (* 1.5 dotwid)
958                        0)
959                    ;; huh ? why not necessary?
960                    ;;(cdr (ly:stencil-extent head-glyph X))
961                    dotwid)
962                 X)
963                stem-glyph)))
964     stem-glyph))
965
966 (define-public log2 
967   (let ((divisor (log 2)))
968     (lambda (z) (inexact->exact (/ (log z) divisor)))))
969
970 (define (parse-simple-duration duration-string)
971   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
972   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
973     (if (and match (string=? duration-string (match:substring match 0)))
974         (let ((len  (match:substring match 1))
975               (dots (match:substring match 2)))
976           (list (cond ((string=? len "breve") -1)
977                       ((string=? len "longa") -2)
978                       ((string=? len "maxima") -3)
979                       (else (log2 (string->number len))))
980                 (if dots (string-length dots) 0)))
981         (ly:error (_ "not a valid duration string: ~a") duration-string))))
982
983 (def-markup-command (note layout props duration dir) (string? number?)
984   "This produces a note with a stem pointing in @var{dir} direction, with
985 the @var{duration} for the note head type and augmentation dots. For
986 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
987 a shortened down stem."
988   (let ((parsed (parse-simple-duration duration)))
989     (note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
990
991 \f
992 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
993 ;; translating.
994 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
995
996 (def-markup-command (lower layout props amount arg) (number? markup?)
997   "
998 Lower @var{arg}, by the distance @var{amount}.
999 A negative @var{amount} indicates raising, see also @code{\raise}.
1000 "
1001   (ly:stencil-translate-axis (interpret-markup layout props arg)
1002                              (- amount) Y))
1003
1004
1005 (def-markup-command (raise layout props amount arg) (number? markup?)
1006   "
1007 Raise @var{arg}, by the distance @var{amount}.
1008 A negative @var{amount} indicates lowering, see also @code{\\lower}.
1009 @c
1010 @lilypond[verbatim,fragment,relative=1]
1011  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
1012 @end lilypond
1013 The argument to @code{\\raise} is the vertical displacement amount,
1014 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
1015 raise objects in relation to their surrounding markups.
1016
1017 If the text object itself is positioned above or below the staff, then
1018 @code{\\raise} cannot be used to move it, since the mechanism that
1019 positions it next to the staff cancels any shift made with
1020 @code{\\raise}. For vertical positioning, use the @code{padding}
1021 and/or @code{extra-offset} properties. "
1022   (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
1023
1024 (def-markup-command (fraction layout props arg1 arg2) (markup? markup?)
1025   "Make a fraction of two markups."
1026   (let* ((m1 (interpret-markup layout props arg1))
1027          (m2 (interpret-markup layout props arg2)))
1028     (set! m1 (ly:stencil-aligned-to m1 X CENTER))
1029     (set! m2 (ly:stencil-aligned-to m2 X CENTER))
1030     (let* ((x1 (ly:stencil-extent m1 X))
1031            (x2 (ly:stencil-extent m2 X))
1032            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
1033            ;; should stack mols separately, to maintain LINE on baseline
1034            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
1035       (set! stack
1036             (ly:stencil-aligned-to stack Y CENTER))
1037       (set! stack
1038             (ly:stencil-aligned-to stack X LEFT))
1039       ;; should have EX dimension
1040       ;; empirical anyway
1041       (ly:stencil-translate-axis stack 0.75 Y))))
1042
1043
1044
1045
1046
1047 (def-markup-command (normal-size-super layout props arg) (markup?)
1048   "Set @var{arg} in superscript with a normal font size."
1049   (ly:stencil-translate-axis
1050    (interpret-markup layout props arg)
1051    (* 0.5 (chain-assoc-get 'baseline-skip props)) Y))
1052
1053 (def-markup-command (super layout props arg) (markup?)
1054   "
1055 @cindex raising text
1056 @cindex lowering text
1057 @cindex moving text
1058 @cindex translating text
1059
1060 @cindex @code{\\super}
1061
1062
1063 Raising and lowering texts can be done with @code{\\super} and
1064 @code{\\sub}:
1065
1066 @lilypond[verbatim,fragment,relative=1]
1067  c1^\\markup { E \"=\" mc \\super \"2\" }
1068 @end lilypond
1069
1070 "
1071   (ly:stencil-translate-axis
1072    (interpret-markup
1073     layout
1074     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1075     arg)
1076    (* 0.5 (chain-assoc-get 'baseline-skip props))
1077    Y))
1078
1079 (def-markup-command (translate layout props offset arg) (number-pair? markup?)
1080   "This translates an object. Its first argument is a cons of numbers
1081 @example
1082 A \\translate #(cons 2 -3) @{ B C @} D
1083 @end example
1084 This moves `B C' 2 spaces to the right, and 3 down, relative to its
1085 surroundings. This command cannot be used to move isolated scripts
1086 vertically, for the same reason that @code{\\raise} cannot be used for
1087 that.
1088
1089 "
1090   (ly:stencil-translate (interpret-markup  layout props arg)
1091                         offset))
1092
1093 (def-markup-command (sub layout props arg) (markup?)
1094   "Set @var{arg} in subscript."
1095   (ly:stencil-translate-axis
1096    (interpret-markup
1097     layout
1098     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
1099     arg)
1100    (* -0.5 (chain-assoc-get 'baseline-skip props))
1101    Y))
1102
1103 (def-markup-command (normal-size-sub layout props arg) (markup?)
1104   "Set @var{arg} in subscript, in a normal font size."
1105   (ly:stencil-translate-axis
1106    (interpret-markup layout props arg)
1107    (* -0.5 (chain-assoc-get 'baseline-skip props))
1108    Y))
1109 \f
1110 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1111 ;; brackets.
1112 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1113
1114 (def-markup-command (hbracket layout props arg) (markup?)
1115   "Draw horizontal brackets around @var{arg}."  
1116   (let ((th 0.1) ;; todo: take from GROB.
1117         (m (interpret-markup layout props arg)))
1118     (bracketify-stencil m X th (* 2.5 th) th)))
1119
1120 (def-markup-command (bracket layout props arg) (markup?)
1121   "Draw vertical brackets around @var{arg}."  
1122   (let ((th 0.1) ;; todo: take from GROB.
1123         (m (interpret-markup layout props arg)))
1124     (bracketify-stencil m Y th (* 2.5 th) th)))
1125
1126 (def-markup-command (bracketed-y-column layout props indices args)
1127   (list? markup-list?)
1128   "Make a column of the markups in @var{args}, putting brackets around
1129 the elements marked in @var{indices}, which is a list of numbers."
1130   (define (sublist lst start stop)
1131     (take (drop lst start) (- (1+ stop) start)))
1132
1133   (define (stencil-list-extent ss axis)
1134     (cons
1135      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
1136      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
1137   
1138
1139   (define (stack-stencils-vertically stencils bskip last-stencil)
1140     (cond
1141      ((null? stencils) '())
1142      ((not (ly:stencil? last-stencil))
1143       (cons (car stencils)
1144             (stack-stencils-vertically (cdr stencils) bskip (car stencils))))
1145      (else
1146       (let* ((orig (car stencils))
1147              (dir (chain-assoc-get 'direction  props DOWN))
1148              (new (ly:stencil-moved-to-edge last-stencil Y dir
1149                                             orig
1150                                             0.1 bskip)))
1151
1152         (cons new (stack-stencils-vertically (cdr stencils) bskip new))))))
1153
1154   (define (make-brackets stencils indices acc)
1155     (if (and stencils
1156              (pair? indices)
1157              (pair? (cdr indices)))
1158         (let* ((encl (sublist stencils (car indices) (cadr indices)))
1159                (x-ext (stencil-list-extent encl X))
1160                (y-ext (stencil-list-extent encl Y))
1161                (thick 0.10)
1162                (pad 0.35)
1163                (protusion (* 2.5 thick))
1164                (lb
1165                 (ly:stencil-translate-axis 
1166                  (ly:bracket Y y-ext thick protusion)
1167                  (- (car x-ext) pad) X))
1168                (rb (ly:stencil-translate-axis
1169                     (ly:bracket Y y-ext thick (- protusion))
1170                     (+ (cdr x-ext) pad) X)))
1171
1172           (make-brackets
1173            stencils (cddr indices)
1174            (append
1175             (list lb rb)
1176             acc)))
1177         acc))
1178
1179   (let* ((stencils
1180           (map (lambda (x)
1181                  (interpret-markup
1182                   layout
1183                   props
1184                   x)) args))
1185          (leading
1186           (chain-assoc-get 'baseline-skip props))
1187          (stacked (stack-stencils-vertically
1188                    (remove ly:stencil-empty? stencils) 1.25 #f))
1189          (brackets (make-brackets stacked indices '())))
1190
1191     (apply ly:stencil-add
1192            (append stacked brackets))))