-;;;; define-markup-commands.scm -- markup commands
+;;;; This file is part of LilyPond, the GNU music typesetter.
;;;;
-;;;; source file of the GNU LilyPond music typesetter
-;;;;
-;;;; (c) 2000--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
+;;;; Copyright (C) 2000--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
;;;; Jan Nieuwenhuizen <janneke@gnu.org>
-
-
-;;; markup commands
-;;; * each markup function should have a doc string with
-;; syntax, description and example.
+;;;;
+;;;; LilyPond is free software: you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation, either version 3 of the License, or
+;;;; (at your option) any later version.
+;;;;
+;;;; LilyPond is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;;; GNU General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License
+;;;; along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
+
+;;;
+;;; Markup commands and markup-list commands definitions.
+;;;
+;;; Markup commands which are part of LilyPond, are defined
+;;; in the (lily) module, which is the current module in this file,
+;;; using the `define-markup-command' macro.
+;;;
+;;; Usage:
+;;;
+;;; (define-markup-command (command-name layout props args...)
+;;; args-signature
+;;; [ #:category category ]
+;;; [ #:properties property-bindings ]
+;;; documentation-string
+;;; ..body..)
+;;;
+;;; with:
+;;; command-name
+;;; the name of the markup command
+;;;
+;;; layout and props
+;;; arguments that are automatically passed to the command when it
+;;; is interpreted.
+;;; `layout' is an output def, which properties can be accessed
+;;; using `ly:output-def-lookup'.
+;;; `props' is a list of property settings which can be accessed
+;;; using `chain-assoc-get' (more on that below)
+;;;
+;;; args...
+;;; the command arguments.
+;;; There is no limitation on the order of command arguments.
+;;; However, markup functions taking a markup as their last
+;;; argument are somewhat special as you can apply them to a
+;;; markup list, and the result is a markup list where the
+;;; markup function (with the specified leading arguments) has
+;;; been applied to every element of the original markup list.
+;;;
+;;; Since replicating the leading arguments for applying a
+;;; markup function to a markup list is cheap mostly for
+;;; Scheme arguments, you avoid performance pitfalls by just
+;;; using Scheme arguments for the leading arguments of markup
+;;; functions that take a markup as their last argument.
+;;;
+;;; args-signature
+;;; the arguments signature, i.e., a list of type predicates which
+;;; are used to type check the arguments, and also to define the general
+;;; argument types (markup, markup-list, scheme) that the command is
+;;; expecting.
+;;; For instance, if a command expects a number, then a markup, the
+;;; signature would be: (number? markup?)
+;;;
+;;; category
+;;; for documentation purpose, builtin markup commands are grouped by
+;;; category. This can be any symbol. When documentation is generated,
+;;; the symbol is converted to a capitalized string, where hyphens are
+;;; replaced by spaces.
+;;;
+;;; property-bindings
+;;; this is used both for documentation generation, and to ease
+;;; programming the command itself. It is list of
+;;; (property-name default-value)
+;;; or (property-name)
+;;; elements. Each property is looked-up in the `props' argument, and
+;;; the symbol naming the property is bound to its value.
+;;; When the property is not found in `props', then the symbol is bound
+;;; to the given default value. When no default value is given, #f is
+;;; used instead.
+;;; Thus, using the following property bindings:
+;;; ((thickness 0.1)
+;;; (font-size 0))
+;;; is equivalent to writing:
+;;; (let ((thickness (chain-assoc-get 'thickness props 0.1))
+;;; (font-size (chain-assoc-get 'font-size props 0)))
+;;; ..body..)
+;;; When a command `B' internally calls an other command `A', it may
+;;; desirable to see in `B' documentation all the properties and
+;;; default values used by `A'. In that case, add `A-markup' to the
+;;; property-bindings of B. (This is used when generating
+;;; documentation, but won't create bindings.)
+;;;
+;;; documentation-string
+;;; the command documentation string (used to generate manuals)
+;;;
+;;; body
+;;; the command body. The function is supposed to return a stencil.
+;;;
+;;; Each markup command definition shall have a documentation string
+;;; with description, syntax and example.
(use-modules (ice-9 regex))
;; utility functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1)))
+(define-public empty-stencil (ly:make-stencil '()
+ empty-interval empty-interval))
(define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; line has to come early since it is often used implicitly from the
+;; markup macro since \markup { a b c } -> \markup \line { a b c }
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (line layout props args)
+ (markup-list?)
+ #:category align
+ #:properties ((word-space)
+ (text-direction RIGHT))
+ "Put @var{args} in a horizontal line. The property @code{word-space}
+determines the space between markups in @var{args}.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\line {
+ one two three
+ }
+}
+@end lilypond"
+ (let ((stencils (interpret-markup-list layout props args)))
+ (if (= text-direction LEFT)
+ (set! stencils (reverse stencils)))
+ (stack-stencil-line word-space stencils)))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; geometric shapes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (draw-line layout props dest)
+(define-markup-command (draw-line layout props dest)
(number-pair?)
- graphic
- ((thickness 1))
+ #:category graphic
+ #:properties ((thickness 1))
"
@cindex drawing lines within text
thickness))
(x (car dest))
(y (cdr dest)))
+ (make-line-stencil th 0 0 x y)))
+
+(define-markup-command (draw-dashed-line layout props dest)
+ (number-pair?)
+ #:category graphic
+ #:properties ((thickness 1)
+ (on 1)
+ (off 1)
+ (phase 0)
+ (full-length #t))
+ "
+@cindex drawing dashed lines within text
+
+A dashed line.
+
+If @code{full-length} is set to #t (default) the dashed-line extends to the
+whole length given by @var{dest}, without white space at beginning or end.
+@code{off} will then be altered to fit.
+To insist on the given (or default) values of @code{on}, @code{off} use
+@code{\\override #'(full-length . #f)}
+Manual settings for @code{on},@code{off} and @code{phase} are possible.
+@lilypond[verbatim,quote]
+\\markup {
+ \\draw-dashed-line #'(5.1 . 2.3)
+ \\override #'(on . 0.3)
+ \\override #'(off . 0.5)
+ \\draw-dashed-line #'(5.1 . 2.3)
+}
+@end lilypond"
+ (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
+ ;; Calculate the thickness to be used.
+ (th (* line-thickness thickness))
+ (half-thick (/ th 2))
+ ;; Get the extensions in x- and y-direction.
+ (x (car dest))
+ (y (cdr dest))
+ ;; Calculate the length of the dashed line.
+ (line-length (sqrt (+ (expt x 2) (expt y 2)))))
+
+ (if (and full-length (not (= (+ on off) 0)))
+ (begin
+ ;; Add double-thickness to avoid overlapping.
+ (set! off (+ (* 2 th) off))
+ (let* (;; Make a guess how often the off/on-pair should be printed
+ ;; after the initial `on´.
+ ;; Assume a minimum of 1 to avoid division by zero.
+ (guess (max 1 (round (/ (- line-length on) (+ off on)))))
+ ;; Not sure about the value or why corr is necessary at all,
+ ;; but it seems to be necessary.
+ (corr (if (= on 0)
+ (/ line-thickness 10)
+ 0))
+ ;; Calculate a new value for off to fit the
+ ;; line-length.
+ (new-off (/ (- line-length corr (* (1+ guess) on)) guess))
+ )
+ (cond
+
+ ;; Settings for (= on 0). Resulting in a dotted line.
+
+ ;; If line-length isn't shorter than `th´, change the given
+ ;; value for `off´ to fit the line-length.
+ ((and (= on 0) (< th line-length))
+ (set! off new-off))
+
+ ;; If the line-length is shorter than `th´, it makes no
+ ;; sense to adjust `off´. The rounded edges of the lines
+ ;; would prevent any nice output.
+ ;; Do nothing.
+ ;; This will result in a single dot for very short lines.
+ ((and (= on 0) (>= th line-length))
+ #f)
+
+ ;; Settings for (not (= on 0)). Resulting in a dashed line.
+
+ ;; If line-length isn't shorter than one go of on-off-on,
+ ;; change the given value for `off´ to fit the line-length.
+ ((< (+ (* 2 on) off) line-length)
+ (set! off new-off))
+ ;; If the line-length is too short, but greater than
+ ;; (* 4 th) set on/off to (/ line-length 3)
+ ((< (* 4 th) line-length)
+ (set! on (/ line-length 3))
+ (set! off (/ line-length 3)))
+ ;; If the line-length is shorter than (* 4 th), it makes
+ ;; no sense trying to adjust on/off. The rounded edges of
+ ;; the lines would prevent any nice output.
+ ;; Simply set `on´ to line-length.
+ (else
+ (set! on line-length))))))
+
+ ;; If `on´ or `off´ is negative, or the sum of `on' and `off' equals zero a
+ ;; ghostscript-error occurs while calling
+ ;; (ly:make-stencil (list 'dashed-line th on off x y phase) x-ext y-ext)
+ ;; Better be paranoid.
+ (if (or (= (+ on off) 0)
+ (negative? on)
+ (negative? off))
+ (begin
+ (ly:warning "Can't print a line - setting on/off to default")
+ (set! on 1)
+ (set! off 1)))
+
+ ;; To give the lines produced by \draw-line and \draw-dashed-line the same
+ ;; length, half-thick has to be added to the stencil-extensions.
(ly:make-stencil
- `(draw-line
- ,th
- 0 0
- ,x ,y)
- (cons (min x 0) (max x 0))
- (cons (min y 0) (max y 0)))))
-
-(define-builtin-markup-command (draw-circle layout props radius thickness fill)
- (number? number? boolean?)
- graphic
+ (list 'dashed-line th on off x y phase)
+ (interval-widen (ordered-cons 0 x) half-thick)
+ (interval-widen (ordered-cons 0 y) half-thick))))
+
+(define-markup-command (draw-dotted-line layout props dest)
+ (number-pair?)
+ #:category graphic
+ #:properties ((thickness 1)
+ (off 1)
+ (phase 0))
+ "
+@cindex drawing dotted lines within text
+
+A dotted line.
+
+The dotted-line always extends to the whole length given by @var{dest}, without
+white space at beginning or end.
+Manual settings for @code{off} are possible to get larger or smaller space
+between the dots.
+The given (or default) value of @code{off} will be altered to fit the
+line-length.
+@lilypond[verbatim,quote]
+\\markup {
+ \\draw-dotted-line #'(5.1 . 2.3)
+ \\override #'(thickness . 2)
+ \\override #'(off . 0.2)
+ \\draw-dotted-line #'(5.1 . 2.3)
+}
+@end lilypond"
+
+ (let ((new-props (prepend-alist-chain 'on 0
+ (prepend-alist-chain 'full-length #t props))))
+
+ (interpret-markup layout
+ new-props
+ (markup #:draw-dashed-line dest))))
+
+(define-markup-command (draw-squiggle-line layout props sq-length dest eq-end?)
+ (number? number-pair? boolean?)
+ #:category graphic
+ #:properties ((thickness 0.5)
+ (angularity 0)
+ (height 0.5)
+ (orientation 1))
+ "
+@cindex drawing squiggled lines within text
+
+A squiggled line.
+
+If @code{eq-end?} is set to @code{#t}, it is ensured the squiggled line ends
+with a bow in same direction as the starting one. @code{sq-length} is the
+length of the first bow. @code{dest} is the end point of the squiggled line.
+To match @code{dest} the squiggled line is scaled accordingly.
+Its appearance may be customized by overrides for @code{thickness},
+@code{angularity}, @code{height} and @code{orientation}.
+@lilypond[verbatim,quote]
+\\markup
+ \\column {
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##t
+ \\override #'(orientation . -1)
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##t
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##f
+ \\override #'(height . 1)
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##t
+ \\override #'(thickness . 5)
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##t
+ \\override #'(angularity . 2)
+ \\draw-squiggle-line #0.5 #'(6 . 0) ##t
+ }
+@end lilypond"
+ (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
+ (thick (* thickness line-thickness))
+ (x (car dest))
+ (y (cdr dest))
+ (length-to-print (magnitude (make-rectangular x y)))
+ ;; Make a guess how many bows may be needed
+ (guess (max 1 (truncate (/ length-to-print sq-length))))
+ ;; If `eq-end?' is set #t, make sure squiggle-line starts and ends
+ ;; with a bow in same direction
+ (amount (if (and (even? guess) eq-end?) (1+ guess) guess))
+ ;; The lined-up bows needs to fit `length-to-print'
+ ;; Thus scale the length of first bow accordingly
+ ;; Other bows are copies
+ (guessed-squiggle-line-length (* amount sq-length))
+ (line-length-diff (- length-to-print guessed-squiggle-line-length))
+ (line-length-diff-for-each-squiggle
+ (/ line-length-diff amount))
+ (first-bow-length (+ sq-length line-length-diff-for-each-squiggle))
+ ;; Get first bows
+ ;; TODO two bows are created via `make-bow-stencil'
+ ;; cheaper to use `ly:stencil-scale'?
+ (first-bow-end-coord
+ (cons
+ (/ (* first-bow-length x) length-to-print)
+ (/ (* first-bow-length y) length-to-print)))
+ (init-bow
+ (lambda (o)
+ (make-bow-stencil
+ '(0 . 0)
+ first-bow-end-coord
+ thick angularity height o)))
+ (init-bow-up (init-bow orientation))
+ (init-bow-down (init-bow (- orientation)))
+ ;; Get a list of starting-points for the bows
+ (list-of-starts
+ (map
+ (lambda (n)
+ (cons
+ (* n (car first-bow-end-coord))
+ (* n (cdr first-bow-end-coord))))
+ (iota amount))))
+ ;; The final stencil: lined-up bows
+ (apply ly:stencil-add
+ (map
+ ly:stencil-translate
+ (circular-list init-bow-up init-bow-down)
+ list-of-starts))))
+
+(define-markup-command (draw-hline layout props)
()
+ #:category graphic
+ #:properties ((draw-line-markup)
+ (line-width)
+ (span-factor 1))
+ "
+@cindex drawing a line across a page
+
+Draws a line across a page, where the property @code{span-factor}
+controls what fraction of the page is taken up.
+@lilypond[verbatim,quote]
+\\markup {
+ \\column {
+ \\draw-hline
+ \\override #'(span-factor . 1/3)
+ \\draw-hline
+ }
+}
+@end lilypond"
+ (interpret-markup layout
+ props
+ (markup #:draw-line (cons (* line-width
+ span-factor)
+ 0))))
+
+(define-markup-command (draw-circle layout props radius thickness filled)
+ (number? number? boolean?)
+ #:category graphic
"
@cindex drawing circles within text
-A circle of radius @var{radius}, thickness @var{thickness} and
+A circle of radius @var{radius} and thickness @var{thickness},
optionally filled.
@lilypond[verbatim,quote]
\\draw-circle #2 #0 ##t
}
@end lilypond"
- (make-circle-stencil radius thickness fill))
+ (make-circle-stencil radius thickness filled))
-(define-builtin-markup-command (triangle layout props filled)
+(define-markup-command (triangle layout props filled)
(boolean?)
- graphic
- ((thickness 0.1)
- (font-size 0)
- (baseline-skip 2))
+ #:category graphic
+ #:properties ((thickness 0.1)
+ (font-size 0)
+ (baseline-skip 2))
"
@cindex drawing triangles within text
,ex 0.0
,(* 0.5 ex)
,(* 0.86 ex))
- ,thickness
- ,filled)
+ ,thickness
+ ,filled)
(cons 0 ex)
(cons 0 (* .86 ex)))))
-(define-builtin-markup-command (circle layout props arg)
+(define-markup-command (circle layout props arg)
(markup?)
- graphic
- ((thickness 1)
- (font-size 0)
- (circle-padding 0.2))
+ #:category graphic
+ #:properties ((thickness 1)
+ (font-size 0)
+ (circle-padding 0.2))
"
@cindex circling text
@end lilypond"
(let ((th (* (ly:output-def-lookup layout 'line-thickness)
thickness))
- (pad (* (magstep font-size) circle-padding))
- (m (interpret-markup layout props arg)))
+ (pad (* (magstep font-size) circle-padding))
+ (m (interpret-markup layout props arg)))
(circle-stencil m th pad)))
-(define-builtin-markup-command (with-url layout props url arg)
+(define-markup-command (ellipse layout props arg)
+ (markup?)
+ #:category graphic
+ #:properties ((thickness 1)
+ (font-size 0)
+ (x-padding 0.2)
+ (y-padding 0.2))
+ "
+@cindex drawing ellipse around text
+
+Draw an ellipse around @var{arg}. Use @code{thickness},
+@code{x-padding}, @code{y-padding} and @code{font-size} properties to determine
+line thickness and padding around the markup.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\ellipse {
+ Hi
+ }
+}
+@end lilypond"
+ (let ((th (* (ly:output-def-lookup layout 'line-thickness)
+ thickness))
+ (pad-x (* (magstep font-size) x-padding))
+ (pad-y (* (magstep font-size) y-padding))
+ (m (interpret-markup layout props arg)))
+ (ellipse-stencil m th pad-x pad-y)))
+
+(define-markup-command (oval layout props arg)
+ (markup?)
+ #:category graphic
+ #:properties ((thickness 1)
+ (font-size 0)
+ (x-padding 0.75)
+ (y-padding 0.75))
+ "
+@cindex drawing oval around text
+
+Draw an oval around @var{arg}. Use @code{thickness},
+@code{x-padding}, @code{x-padding} and @code{font-size} properties to determine
+line thickness and padding around the markup.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\oval {
+ Hi
+ }
+}
+@end lilypond"
+ (let ((th (* (ly:output-def-lookup layout 'line-thickness)
+ thickness))
+ (pad-x (* (magstep font-size) x-padding))
+ (pad-y (* (magstep font-size) y-padding))
+ (m (interpret-markup layout props arg)))
+ (oval-stencil m th pad-x pad-y)))
+
+(define-markup-command (with-url layout props url arg)
(string? markup?)
- graphic
- ()
+ #:category graphic
"
@cindex inserting URL links into text
@lilypond[verbatim,quote]
\\markup {
- \\with-url #\"http://lilypond.org/web/\" {
+ \\with-url #\"http://lilypond.org/\" {
LilyPond ... \\italic {
music notation for everyone
}
}
@end lilypond"
(let* ((stil (interpret-markup layout props arg))
- (xextent (ly:stencil-extent stil X))
- (yextent (ly:stencil-extent stil Y))
- (old-expr (ly:stencil-expr stil))
- (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
+ (xextent (ly:stencil-extent stil X))
+ (yextent (ly:stencil-extent stil Y))
+ (old-expr (ly:stencil-expr stil))
+ (url-expr (list 'url-link url `(quote ,xextent) `(quote ,yextent))))
(ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil)))
-(define-builtin-markup-command (beam layout props width slope thickness)
+(define-markup-command (page-link layout props page-number arg)
+ (number? markup?)
+ #:category other
+ "
+@cindex referencing page numbers in text
+
+Add a link to the page @var{page-number} around @var{arg}. This only works
+in the PDF backend.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\page-link #2 { \\italic { This links to page 2... } }
+}
+@end lilypond"
+ (let* ((stil (interpret-markup layout props arg))
+ (xextent (ly:stencil-extent stil X))
+ (yextent (ly:stencil-extent stil Y))
+ (old-expr (ly:stencil-expr stil))
+ (link-expr (list 'page-link page-number `(quote ,xextent) `(quote ,yextent))))
+
+ (ly:stencil-add (ly:make-stencil link-expr xextent yextent) stil)))
+
+(define-public (book-first-page layout props)
+ "Return the @code{'first-page-number} of the entire book"
+ (define (ancestor layout)
+ "Return the topmost layout ancestor"
+ (let ((parent (ly:output-def-parent layout)))
+ (if (not (ly:output-def? parent))
+ layout
+ (ancestor parent))))
+ (ly:output-def-lookup (ancestor layout) 'first-page-number))
+
+(define-markup-command (with-link layout props label arg)
+ (symbol? markup?)
+ #:category other
+ "
+@cindex referencing page labels in text
+
+Add a link to the page holding label @var{label} around @var{arg}. This
+only works in the PDF backend.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\with-link #'label {
+ \\italic { This links to the page containing the label... }
+ }
+}
+@end lilypond"
+ (let* ((arg-stencil (interpret-markup layout props arg))
+ (x-ext (ly:stencil-extent arg-stencil X))
+ (y-ext (ly:stencil-extent arg-stencil Y)))
+ (ly:stencil-add
+ (ly:make-stencil
+ `(delay-stencil-evaluation
+ ,(delay (let* ((table (ly:output-def-lookup layout 'label-page-table))
+ (table-page-number
+ (if (list? table)
+ (assoc-get label table)
+ #f))
+ (first-page-number (book-first-page layout props))
+ (current-page-number
+ (if table-page-number
+ (1+ (- table-page-number first-page-number))
+ #f)))
+ (list 'page-link current-page-number
+ `(quote ,x-ext) `(quote ,y-ext)))))
+ x-ext
+ y-ext)
+ arg-stencil)))
+
+(define-markup-command (beam layout props width slope thickness)
(number? number? number?)
- graphic
- ()
+ #:category graphic
"
@cindex drawing beams within text
}
@end lilypond"
(let* ((y (* slope width))
- (yext (cons (min 0 y) (max 0 y)))
- (half (/ thickness 2)))
+ (yext (cons (min 0 y) (max 0 y)))
+ (half (/ thickness 2)))
(ly:make-stencil
- `(polygon ',(list
- 0 (/ thickness -2)
- width (+ (* width slope) (/ thickness -2))
- width (+ (* width slope) (/ thickness 2))
- 0 (/ thickness 2))
- ,(ly:output-def-lookup layout 'blot-diameter)
- #t)
+ `(polygon ',(list
+ 0 (/ thickness -2)
+ width (+ (* width slope) (/ thickness -2))
+ width (+ (* width slope) (/ thickness 2))
+ 0 (/ thickness 2))
+ ,(ly:output-def-lookup layout 'blot-diameter)
+ #t)
(cons 0 width)
(cons (+ (- half) (car yext))
- (+ half (cdr yext))))))
+ (+ half (cdr yext))))))
-(define-builtin-markup-command (underline layout props arg)
+(define-markup-command (underline layout props arg)
(markup?)
- font
- ((thickness 1))
+ #:category font
+ #:properties ((thickness 1) (offset 2))
"
@cindex underlining text
Underline @var{arg}. Looks at @code{thickness} to determine line
-thickness and y offset.
+thickness, and @code{offset} to determine line y-offset.
+
+@lilypond[verbatim,quote]
+\\markup \\fill-line {
+ \\underline \"underlined\"
+ \\override #'(offset . 5)
+ \\override #'(thickness . 1)
+ \\underline \"underlined\"
+ \\override #'(offset . 1)
+ \\override #'(thickness . 5)
+ \\underline \"underlined\"
+}
+@end lilypond"
+ (let* ((thick (ly:output-def-lookup layout 'line-thickness))
+ (underline-thick (* thickness thick))
+ (m (interpret-markup layout props arg))
+ (x1 (car (ly:stencil-extent m X)))
+ (x2 (cdr (ly:stencil-extent m X)))
+ (y (* thick (- offset)))
+ (line (make-line-stencil underline-thick x1 y x2 y)))
+ (ly:stencil-add m line)))
+
+(define-markup-command (tie layout props arg)
+ (markup?)
+ #:category font
+ #:properties ((thickness 1)
+ (offset 2)
+ (direction UP)
+ (shorten-pair '(0 . 0)))
+ "
+@cindex tie-ing text
+
+Adds a horizontal bow created with @code{make-tie-stencil} at bottom or top
+of @var{arg}. Looks at @code{thickness} to determine line thickness, and
+@code{offset} to determine y-offset. The added bow fits the extent of
+@var{arg}, @code{shorten-pair} may be used to modify this.
+@var{direction} may be set using an @code{override} or direction-modifiers or
+@code{voiceOne}, etc.
@lilypond[verbatim,quote]
\\markup {
- default
- \\hspace #2
- \\override #'(thickness . 2)
- \\underline {
- underline
- }
+ \\override #'(direction . 1)
+ \\tie \"above\"
+ \\override #'(direction . -1)
+ \\tie \"below\"
+}
+@end lilypond"
+ (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness))
+ (thick (* thickness line-thickness))
+ (stil (interpret-markup layout props arg))
+ (x1 (car (ly:stencil-extent stil X)))
+ (x2 (cdr (ly:stencil-extent stil X)))
+ (y-ext (ly:stencil-extent stil Y))
+ (y (+ (* line-thickness offset direction)
+ ;; we put out zero for positive text-direction, to make it
+ ;; consistent with `underline-markup'
+ ;; TODO: this will be problematic for args like "Eng"
+ ;; fix it here _and_ in `underline-markup'
+ (if (negative? direction) 0 (cdr y-ext))))
+ (tie
+ (make-tie-stencil
+ (cons (+ x1 (car shorten-pair) line-thickness) y)
+ (cons (- x2 (cdr shorten-pair) line-thickness) y)
+ thick
+ direction)))
+ (ly:stencil-add stil tie)))
+
+(define-markup-command (undertie layout props arg)
+ (markup?)
+ #:category font
+ #:properties (tie-markup)
+ "
+@cindex undertie-ing text
+
+@lilypond[verbatim,quote]
+\\markup \\line {
+ \\undertie \"undertied\"
+ \\override #'(offset . 5)
+ \\override #'(thickness . 1)
+ \\undertie \"undertied\"
+ \\override #'(offset . 1)
+ \\override #'(thickness . 5)
+ \\undertie \"undertied\"
+}
+@end lilypond"
+ (interpret-markup layout (prepend-alist-chain 'direction DOWN props)
+ (make-tie-markup arg)))
+
+(define-markup-command (overtie layout props arg)
+ (markup?)
+ #:category font
+ #:properties (tie-markup)
+ "
+@cindex overtie-ing text
+
+Overtie @var{arg}.
+
+@lilypond[verbatim,quote]
+\\markup \\line {
+ \\overtie \"overtied\"
+ \\override #'(offset . 5)
+ \\override #'(thickness . 1)
+ \\overtie \"overtied\"
+ \\override #'(offset . 1)
+ \\override #'(thickness . 5)
+ \\overtie \"overtied\"
}
@end lilypond"
- (let* ((thick (* (ly:output-def-lookup layout 'line-thickness)
- thickness))
- (markup (interpret-markup layout props arg))
- (x1 (car (ly:stencil-extent markup X)))
- (x2 (cdr (ly:stencil-extent markup X)))
- (y (* thick -2))
- (line (ly:make-stencil
- `(draw-line ,thick ,x1 ,y ,x2 ,y)
- (cons (min x1 0) (max x2 0))
- (cons thick thick))))
- (ly:stencil-add markup line)))
+ (interpret-markup layout (prepend-alist-chain 'direction UP props)
+ (make-tie-markup arg)))
-(define-builtin-markup-command (box layout props arg)
+(define-markup-command (box layout props arg)
(markup?)
- font
- ((thickness 1)
- (font-size 0)
- (box-padding 0.2))
+ #:category font
+ #:properties ((thickness 1)
+ (font-size 0)
+ (box-padding 0.2))
"
@cindex enclosing text within a box
(m (interpret-markup layout props arg)))
(box-stencil m th pad)))
-(define-builtin-markup-command (filled-box layout props xext yext blot)
+(define-markup-command (filled-box layout props xext yext blot)
(number-pair? number-pair? number?)
- graphic
- ()
+ #:category graphic
"
@cindex drawing solid boxes within text
@cindex drawing boxes with rounded corners
@end verbatim
creates a box extending horizontally from -0.3 to 1.8 and
vertically from -0.3 up to 1.8, with corners formed from a
-circle of diameter@tie{}0 (i.e. sharp corners).
+circle of diameter@tie{}0 (i.e., sharp corners).
@lilypond[verbatim,quote]
\\markup {
(ly:round-filled-box
xext yext blot))
-(define-builtin-markup-command (rounded-box layout props arg)
+(define-markup-command (rounded-box layout props arg)
(markup?)
- graphic
- ((thickness 1)
- (corner-radius 1)
- (font-size 0)
- (box-padding 0.5))
- "@cindex enclosing text in a bow with rounded corners
+ #:category graphic
+ #:properties ((thickness 1)
+ (corner-radius 1)
+ (font-size 0)
+ (box-padding 0.5))
+ "@cindex enclosing text in a box with rounded corners
@cindex drawing boxes with rounded corners around text
Draw a box with rounded corners around @var{arg}. Looks at @code{thickness},
@code{box-padding} and @code{font-size} properties to determine line
thickness and padding around the markup; the @code{corner-radius} property
-makes possible to define another shape for the corners (default is 1).
+makes it possible to define another shape for the corners (default is 1).
@lilypond[quote,verbatim,relative=2]
c4^\\markup {
}
}
c,8. c16 c4 r
-@end lilypond"
+@end lilypond"
(let ((th (* (ly:output-def-lookup layout 'line-thickness)
thickness))
(pad (* (magstep font-size) box-padding))
(ly:stencil-add (rounded-box-stencil m th pad corner-radius)
m)))
-(define-builtin-markup-command (rotate layout props ang arg)
+(define-markup-command (rotate layout props ang arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex rotating text
(let* ((stil (interpret-markup layout props arg)))
(ly:stencil-rotate stil ang 0 0)))
-(define-builtin-markup-command (whiteout layout props arg)
+(define-markup-command (whiteout layout props arg)
(markup?)
- other
- ()
+ #:category other
+ #:properties ((style 'box)
+ (thickness '()))
"
@cindex adding a white background to text
-Provide a white background for @var{arg}.
+Provide a white background for @var{arg}. The shape of the white
+background is determined by @code{style}. The default
+is @code{box} which produces a rectangle. @code{rounded-box}
+produces a rounded rectangle. @code{outline} approximates the
+outline of the markup.
@lilypond[verbatim,quote]
\\markup {
\\combine
- \\filled-box #'(-1 . 10) #'(-3 . 4) #1
- \\whiteout whiteout
+ \\filled-box #'(-1 . 15) #'(-3 . 4) #1
+ \\override #'(thickness . 1.5)
+ \\whiteout whiteout-box
+}
+\\markup {
+ \\combine
+ \\filled-box #'(-1 . 24) #'(-3 . 4) #1
+ \\override #'(style . rounded-box)
+ \\override #'(thickness . 3)
+ \\whiteout whiteout-rounded-box
+}
+\\markup {
+ \\combine
+ \\filled-box #'(-1 . 18) #'(-3 . 4) #1
+ \\override #'(style . outline)
+ \\override #'(thickness . 3)
+ \\whiteout whiteout-outline
}
@end lilypond"
- (stencil-whiteout (interpret-markup layout props arg)))
+ (stencil-whiteout
+ (interpret-markup layout props arg)
+ style
+ thickness
+ (ly:output-def-lookup layout 'line-thickness)))
-(define-builtin-markup-command (pad-markup layout props padding arg)
+(define-markup-command (pad-markup layout props amount arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex padding text
@cindex putting space around text
Add space around a markup object.
+Identical to @code{pad-around}.
@lilypond[verbatim,quote]
\\markup {
}
\\hspace #2
\\box {
- \\pad-around #1 {
+ \\pad-markup #1 {
padded
}
}
}
@end lilypond"
- (let*
- ((stil (interpret-markup layout props arg))
- (xext (ly:stencil-extent stil X))
- (yext (ly:stencil-extent stil Y)))
-
- (ly:make-stencil
- (ly:stencil-expr stil)
- (interval-widen xext padding)
- (interval-widen yext padding))))
+ (let* ((m (interpret-markup layout props arg))
+ (x (interval-widen (ly:stencil-extent m X) amount))
+ (y (interval-widen (ly:stencil-extent m Y) amount)))
+ (ly:stencil-add (make-transparent-box-stencil x y)
+ m)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; space
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (strut layout props)
- ()
- other
+(define-markup-command (strut layout props)
()
+ #:category other
"
@cindex creating vertical spaces in text
Create a box of the same height as the space in the current font."
(let ((m (ly:text-interface::interpret-markup layout props " ")))
(ly:make-stencil (ly:stencil-expr m)
- '(0 . 0)
- (ly:stencil-extent m X)
- )))
+ '(0 . 0)
+ (ly:stencil-extent m X)
+ )))
-;; todo: fix negative space
-(define-builtin-markup-command (hspace layout props amount)
+(define-markup-command (hspace layout props amount)
(number?)
- align
- ()
+ #:category align
"
@cindex creating horizontal spaces in text
-This produces an invisible object taking horizontal space. For example,
-
-@example
-\\markup @{ A \\hspace #2.0 B @}
-@end example
-
-@noindent
-puts extra space between A and@tie{}B, on top of the space that is
-normally inserted before elements on a line.
+Create an invisible object taking up horizontal space @var{amount}.
@lilypond[verbatim,quote]
\\markup {
three
}
@end lilypond"
- (if (> amount 0)
- (ly:make-stencil "" (cons 0 amount) '(-1 . 1))
- (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
+ (ly:make-stencil "" (cons 0 amount) empty-interval))
+
+(define-markup-command (vspace layout props amount)
+ (number?)
+ #:category align
+ "
+@cindex creating vertical spaces in text
+
+Create an invisible object taking up vertical space
+of @var{amount} multiplied by 3.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\center-column {
+ one
+ \\vspace #2
+ two
+ \\vspace #5
+ three
+ }
+}
+@end lilypond"
+ (let ((amount (* amount 3.0)))
+ (ly:make-stencil "" empty-interval (cons 0 amount))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; importing graphics.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (stencil layout props stil)
+(define-markup-command (stencil layout props stil)
(ly:stencil?)
- other
- ()
+ #:category other
"
@cindex importing stencils into text
"Extract the bbox from STRING, or return #f if not present."
(let*
((match (regexp-exec bbox-regexp string)))
-
+
(if match
- (map (lambda (x)
- (string->number (match:substring match x)))
- (cdr (iota 5)))
-
- #f)))
+ (map (lambda (x)
+ (string->number (match:substring match x)))
+ (cdr (iota 5)))
-(define-builtin-markup-command (epsfile layout props axis size file-name)
+ #f)))
+
+(define-markup-command (epsfile layout props axis size file-name)
(number? number? string?)
- graphic
- ()
+ #:category graphic
"
@cindex inlining an Encapsulated PostScript image
(eps-file->stencil axis size file-name)
))
-(define-builtin-markup-command (postscript layout props str)
+(define-markup-command (postscript layout props str)
(string?)
- graphic
- ()
+ #:category graphic
"
@cindex inserting PostScript directly into text
-
This inserts @var{str} directly into the output as a PostScript
-command string. Due to technicalities of the output backends,
-different scales should be used for the @TeX{} and PostScript backend,
-selected with @code{-f}.
-
-For the @TeX{} backend, the following string prints a rotated text
-
-@example
-0 0 moveto /ecrm10 findfont
-1.75 scalefont setfont 90 rotate (hello) show
-@end example
-
-@noindent
-The magical constant 1.75 scales from LilyPond units (staff spaces) to
-@TeX{} dimensions.
-
-For the postscript backend, use the following
-
-@example
-gsave /ecrm10 findfont
- 10.0 output-scale div
- scalefont setfont 90 rotate (hello) show grestore
-@end example
+command string.
@lilypond[verbatim,quote]
-eyeglassesps = #\"
+ringsps = #\"
0.15 setlinewidth
- -0.9 0 translate
- 1.1 1.1 scale
- 1.2 0.7 moveto
- 0.7 0.7 0.5 0 361 arc
- stroke
- 2.20 0.70 0.50 0 361 arc
- stroke
- 1.45 0.85 0.30 0 180 arc
+ 0.9 0.6 moveto
+ 0.4 0.6 0.5 0 361 arc
stroke
- 0.20 0.70 moveto
- 0.80 2.00 lineto
- 0.92 2.26 1.30 2.40 1.15 1.70 curveto
+ 1.0 0.6 0.5 0 361 arc
stroke
- 2.70 0.70 moveto
- 3.30 2.00 lineto
- 3.42 2.26 3.80 2.40 3.65 1.70 curveto
- stroke\"
+ \"
-eyeglasses = \\markup {
- \\with-dimensions #'(0 . 4.4) #'(0 . 2.5)
- \\postscript #eyeglassesps
+rings = \\markup {
+ \\with-dimensions #'(-0.2 . 1.6) #'(0 . 1.2)
+ \\postscript #ringsps
}
\\relative c'' {
- c2^\\eyeglasses
- a2_\\eyeglasses
+ c2^\\rings
+ a2_\\rings
}
@end lilypond"
;; FIXME
(ly:make-stencil
(list 'embedded-ps
- (format "
+ (format #f "
gsave currentpoint translate
0.1 setlinewidth
~a
grestore
"
- str))
+ str))
'(0 . 0) '(0 . 0)))
-(define-builtin-markup-command (score layout props score)
+(define-markup-command (path layout props thickness commands) (number? list?)
+ #:category graphic
+ #:properties ((line-cap-style 'round)
+ (line-join-style 'round)
+ (filled #f))
+ "
+@cindex paths, drawing
+@cindex drawing paths
+Draws a path with line @var{thickness} according to the
+directions given in @var{commands}. @var{commands} is a list of
+lists where the @code{car} of each sublist is a drawing command and
+the @code{cdr} comprises the associated arguments for each command.
+
+There are seven commands available to use in the list
+@code{commands}: @code{moveto}, @code{rmoveto}, @code{lineto},
+@code{rlineto}, @code{curveto}, @code{rcurveto}, and
+@code{closepath}. Note that the commands that begin with @emph{r}
+are the relative variants of the other three commands.
+
+The commands @code{moveto}, @code{rmoveto}, @code{lineto}, and
+@code{rlineto} take 2 arguments; they are the X and Y coordinates
+for the destination point.
+
+The commands @code{curveto} and @code{rcurveto} create cubic
+Bézier curves, and take 6 arguments; the first two are the X and Y
+coordinates for the first control point, the second two are the X
+and Y coordinates for the second control point, and the last two
+are the X and Y coordinates for the destination point.
+
+The @code{closepath} command takes zero arguments and closes the
+current subpath in the active path.
+
+Note that a sequence of commands @emph{must} begin with a
+@code{moveto} or @code{rmoveto} to work with the SVG output.
+
+Line-cap styles and line-join styles may be customized by
+overriding the @code{line-cap-style} and @code{line-join-style}
+properties, respectively. Available line-cap styles are
+@code{'butt}, @code{'round}, and @code{'square}. Available
+line-join styles are @code{'miter}, @code{'round}, and
+@code{'bevel}.
+
+The property @code{filled} specifies whether or not the path is
+filled with color.
+
+@lilypond[verbatim,quote]
+samplePath =
+ #'((moveto 0 0)
+ (lineto -1 1)
+ (lineto 1 1)
+ (lineto 1 -1)
+ (curveto -5 -5 -5 5 -1 0)
+ (closepath))
+
+\\markup {
+ \\path #0.25 #samplePath
+
+ \\override #'(line-join-style . miter) \\path #0.25 #samplePath
+
+ \\override #'(filled . #t) \\path #0.25 #samplePath
+}
+@end lilypond"
+ (let* ((half-thickness (/ thickness 2))
+ (current-point '(0 . 0))
+ (set-point (lambda (lst) (set! current-point lst)))
+ (relative? (lambda (x)
+ (string-prefix? "r" (symbol->string (car x)))))
+ ;; For calculating extents, we want to modify the command
+ ;; list so that all coordinates are absolute.
+ (new-commands (map (lambda (x)
+ (cond
+ ;; for rmoveto, rlineto
+ ((and (relative? x) (= 3 (length x)))
+ (let ((cp (cons
+ (+ (car current-point)
+ (second x))
+ (+ (cdr current-point)
+ (third x)))))
+ (set-point cp)
+ (list (car cp)
+ (cdr cp))))
+ ;; for rcurveto
+ ((and (relative? x) (= 7 (length x)))
+ (let* ((old-cp current-point)
+ (cp (cons
+ (+ (car old-cp)
+ (sixth x))
+ (+ (cdr old-cp)
+ (seventh x)))))
+ (set-point cp)
+ (list (+ (car old-cp) (second x))
+ (+ (cdr old-cp) (third x))
+ (+ (car old-cp) (fourth x))
+ (+ (cdr old-cp) (fifth x))
+ (car cp)
+ (cdr cp))))
+ ;; for moveto, lineto
+ ((= 3 (length x))
+ (set-point (cons (second x)
+ (third x)))
+ (drop x 1))
+ ;; for curveto
+ ((= 7 (length x))
+ (set-point (cons (sixth x)
+ (seventh x)))
+ (drop x 1))
+ ;; keep closepath for filtering;
+ ;; see `without-closepath'.
+ (else x)))
+ commands))
+ ;; path-min-max does not accept 0-arg lists,
+ ;; and since closepath does not affect extents, filter
+ ;; out those commands here.
+ (without-closepath (filter (lambda (x)
+ (not (equal? 'closepath (car x))))
+ new-commands))
+ (extents (path-min-max
+ ;; set the origin to the first moveto
+ (list (list-ref (car without-closepath) 0)
+ (list-ref (car without-closepath) 1))
+ without-closepath))
+ (X-extent (cons (list-ref extents 0) (list-ref extents 1)))
+ (Y-extent (cons (list-ref extents 2) (list-ref extents 3)))
+ (command-list (fold-right append '() commands)))
+
+ ;; account for line thickness
+ (set! X-extent (interval-widen X-extent half-thickness))
+ (set! Y-extent (interval-widen Y-extent half-thickness))
+
+ (ly:make-stencil
+ `(path ,thickness `(,@',command-list)
+ ',line-cap-style ',line-join-style ,filled)
+ X-extent
+ Y-extent)))
+
+(define-markup-list-command (score-lines layout props score)
(ly:score?)
- music
- ()
+ "This is the same as the @code{\\score} markup but delivers its
+systems as a list of lines. Its @var{score} argument is entered in
+braces like it would be for @code{\\score}."
+ (let ((output (ly:score-embedded-format score layout)))
+
+ (if (ly:music-output? output)
+ (map
+ (lambda (paper-system)
+ ;; shift such that the refpoint of the bottom staff of
+ ;; the first system is the baseline of the score
+ (ly:stencil-translate-axis
+ (paper-system-stencil paper-system)
+ (- (car (paper-system-staff-extents paper-system)))
+ Y))
+ (vector->list (ly:paper-score-paper-systems output)))
+ (begin
+ (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
+ '()))))
+
+(define-markup-command (score layout props score)
+ (ly:score?)
+ #:category music
+ #:properties ((baseline-skip))
"
@cindex inserting music into text
-Inline an image of music.
+Inline an image of music. The reference point (usually the middle
+staff line) of the lowest staff in the top system is placed on the
+baseline.
@lilypond[verbatim,quote]
\\markup {
indent = 0.0\\cm
\\context {
\\Score
- \\override RehearsalMark #'break-align-symbols =
- #'(time-signature key-signature)
- \\override RehearsalMark #'self-alignment-X = #LEFT
+ \\override RehearsalMark
+ #'break-align-symbols = #'(time-signature key-signature)
+ \\override RehearsalMark
+ #'self-alignment-X = #LEFT
}
\\context {
\\Staff
- \\override TimeSignature #'break-align-anchor-alignment = #LEFT
+ \\override TimeSignature
+ #'break-align-anchor-alignment = #LEFT
}
}
}
}
@end lilypond"
- (let* ((output (ly:score-embedded-format score layout)))
+ (stack-stencils Y DOWN baseline-skip
+ (score-lines-markup-list layout props score)))
- (if (ly:music-output? output)
- (paper-system-stencil
- (vector-ref (ly:paper-score-paper-systems output) 0))
- (begin
- (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?"))
- empty-stencil))))
-
-(define-builtin-markup-command (null layout props)
- ()
- other
+(define-markup-command (null layout props)
()
+ #:category other
"
@cindex creating empty text objects
;; basic formatting.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (simple layout props str)
+(define-markup-command (simple layout props str)
(string?)
- font
- ()
+ #:category font
"
@cindex simple text strings
@end lilypond"
(interpret-markup layout props str))
-(define-builtin-markup-command (tied-lyric layout props str)
- (string?)
- music
- ()
- "
-@cindex simple text strings with tie characters
-
-Like simple-markup, but use tie characters for @q{~} tilde symbols.
+(define-markup-command (first-visible layout props args)
+ (markup-list?)
+ #:category other
+ "Use the first markup in @var{args} that yields a non-empty stencil
+and ignore the rest.
@lilypond[verbatim,quote]
\\markup {
- \\tied-lyric #\"Lasciate~i monti\"
+ \\first-visible {
+ \\fromproperty #'header:composer
+ \\italic Unknown
+ }
}
@end lilypond"
- (if (string-contains str "~")
- (let*
- ((parts (string-split str #\~))
- (tie-str (ly:wide-char->utf-8 #x203f))
- (joined (list-join parts tie-str))
- (join-stencil (interpret-markup layout props tie-str))
- )
-
- (interpret-markup layout
- (prepend-alist-chain
- 'word-space
- (/ (interval-length (ly:stencil-extent join-stencil X)) -3.5)
- props)
- (make-line-markup joined)))
- ;(map (lambda (s) (interpret-markup layout props s)) parts))
- (interpret-markup layout props str)))
+ (define (false-if-empty stencil)
+ (if (ly:stencil-empty? stencil) #f stencil))
+ (or
+ (any
+ (lambda (m)
+ (if (markup? m)
+ (false-if-empty (interpret-markup layout props m))
+ (any false-if-empty (interpret-markup-list layout props (list m)))))
+ args)
+ empty-stencil))
(define-public empty-markup
(make-simple-markup ""))
;; helper for justifying lines.
-(define (get-fill-space word-count line-width text-widths)
- "Calculate the necessary paddings between each two adjacent texts.
- The lengths of all texts are stored in @var{text-widths}.
- The normal formula for the padding between texts a and b is:
- padding = line-width/(word-count - 1) - (length(a) + length(b))/2
- The first and last padding have to be calculated specially using the
- whole length of the first or last text.
- Return a list of paddings."
+(define (get-fill-space
+ word-count line-width word-space text-widths constant-space?)
+ "Calculate the necessary paddings between adjacent texts in a
+single justified line. The lengths of all texts are stored in
+@var{text-widths}.
+When @var{constant-space?} is @code{#t}, the formula for the padding
+between texts is:
+padding = (line-width - total-text-width)/(word-count - 1)
+When @var{constant-space?} is @code{#f}, the formula for the
+padding between interior texts a and b is:
+padding = line-width/(word-count - 1) - (length(a) + length(b))/2
+In this case, the first and last padding have to be calculated
+specially using the whole length of the first or last text.
+All paddings are checked to be at least word-space, to ensure that
+no texts collide.
+Return a list of paddings."
(cond
- ((null? text-widths) '())
-
- ;; special case first padding
- ((= (length text-widths) word-count)
- (cons
- (- (- (/ line-width (1- word-count)) (car text-widths))
- (/ (car (cdr text-widths)) 2))
- (get-fill-space word-count line-width (cdr text-widths))))
- ;; special case last padding
- ((= (length text-widths) 2)
- (list (- (/ line-width (1- word-count))
- (+ (/ (car text-widths) 2) (car (cdr text-widths)))) 0))
- (else
- (cons
- (- (/ line-width (1- word-count))
- (/ (+ (car text-widths) (car (cdr text-widths))) 2))
- (get-fill-space word-count line-width (cdr text-widths))))))
-
-(define-builtin-markup-command (fill-line layout props markups)
+ ((null? text-widths) '())
+ (constant-space?
+ (make-list
+ (1- word-count)
+ ;; Ensure that space between words cannot be
+ ;; less than word-space.
+ (max
+ word-space
+ (/ (- line-width (apply + text-widths))
+ (1- word-count)))))
+
+ ;; special case first padding
+ ((= (length text-widths) word-count)
+ (cons
+ (- (- (/ line-width (1- word-count)) (car text-widths))
+ (/ (cadr text-widths) 2))
+ (get-fill-space
+ word-count line-width word-space (cdr text-widths)
+ constant-space?)))
+ ;; special case last padding
+ ((= (length text-widths) 2)
+ (list (- (/ line-width (1- word-count))
+ (+ (/ (car text-widths) 2) (cadr text-widths)))
+ 0))
+ (else
+ (let ((default-padding
+ (- (/ line-width (1- word-count))
+ (/ (+ (car text-widths) (cadr text-widths)) 2))))
+ (cons
+ (if (> word-space default-padding)
+ word-space
+ default-padding)
+ (get-fill-space
+ word-count line-width word-space (cdr text-widths)
+ constant-space?))))))
+
+(define (justify-line-helper
+ layout props args text-direction word-space line-width constant-space?)
+ "Return a stencil which spreads @var{args} along a line of width
+@var{line-width}. If @var{constant-space?} is set to @code{#t}, the
+space between words is constant. If @code{#f}, the distance between
+words varies according to their relative lengths."
+ (let* ((orig-stencils (interpret-markup-list layout props args))
+ (stencils
+ (map (lambda (stc)
+ (if (ly:stencil-empty? stc X)
+ (ly:make-stencil (ly:stencil-expr stc)
+ '(0 . 0) (ly:stencil-extent stc Y))
+ stc))
+ orig-stencils))
+ (text-widths
+ (map (lambda (stc)
+ (interval-length (ly:stencil-extent stc X)))
+ stencils))
+ (text-width (apply + text-widths))
+ (word-count (length stencils))
+ (line-width (or line-width (ly:output-def-lookup layout 'line-width)))
+ (fill-space
+ (cond
+ ((= word-count 1)
+ (list
+ (/ (- line-width text-width) 2)
+ (/ (- line-width text-width) 2)))
+ ((= word-count 2)
+ (list
+ (- line-width text-width)))
+ (else
+ (get-fill-space
+ word-count line-width word-space text-widths
+ constant-space?))))
+ (line-contents (if (= word-count 1)
+ (list
+ point-stencil
+ (car stencils)
+ point-stencil)
+ stencils)))
+
+ (if (null? (remove ly:stencil-empty? orig-stencils))
+ empty-stencil
+ (begin
+ (if (= text-direction LEFT)
+ (set! line-contents (reverse line-contents)))
+ (set! line-contents
+ (stack-stencils-padding-list
+ X RIGHT fill-space line-contents))
+ (if (> word-count 1)
+ ;; shift s.t. stencils align on the left edge, even if
+ ;; first stencil had negative X-extent (e.g. center-column)
+ ;; (if word-count = 1, X-extents are already normalized in
+ ;; the definition of line-contents)
+ (set! line-contents
+ (ly:stencil-translate-axis
+ line-contents
+ (- (car (ly:stencil-extent (car stencils) X)))
+ X)))
+ line-contents))))
+
+(define-markup-command (fill-line layout props args)
(markup-list?)
- align
- ((text-direction RIGHT)
- (word-space 1)
- (line-width #f))
+ #:category align
+ #:properties ((text-direction RIGHT)
+ (word-space 0.6)
+ (line-width #f))
"Put @var{markups} in a horizontal line of width @var{line-width}.
The markups are spaced or flushed to fill the entire line.
If there are no arguments, return an empty stencil.
}
}
@end lilypond"
- (let* ((orig-stencils (interpret-markup-list layout props markups))
- (stencils
- (map (lambda (stc)
- (if (ly:stencil-empty? stc)
- point-stencil
- stc)) orig-stencils))
- (text-widths
- (map (lambda (stc)
- (if (ly:stencil-empty? stc)
- 0.0
- (interval-length (ly:stencil-extent stc X))))
- stencils))
- (text-width (apply + text-widths))
- (word-count (length stencils))
- (prop-line-width (chain-assoc-get 'line-width props #f))
- (line-width (or line-width (ly:output-def-lookup layout 'line-width)))
- (fill-space
- (cond
- ((= word-count 1)
- (list
- (/ (- line-width text-width) 2)
- (/ (- line-width text-width) 2)))
- ((= word-count 2)
- (list
- (- line-width text-width)))
- (else
- (get-fill-space word-count line-width text-widths))))
- (fill-space-normal
- (map (lambda (x)
- (if (< x word-space)
- word-space
- x))
- fill-space))
-
- (line-stencils (if (= word-count 1)
- (list
- point-stencil
- (car stencils)
- point-stencil)
- stencils)))
-
- (if (= text-direction LEFT)
- (set! line-stencils (reverse line-stencils)))
+ (justify-line-helper
+ layout props args text-direction word-space line-width #f))
- (if (null? (remove ly:stencil-empty? orig-stencils))
- empty-stencil
- (stack-stencils-padding-list X
- RIGHT fill-space-normal line-stencils))))
-
-(define-builtin-markup-command (line layout props args)
+(define-markup-command (justify-line layout props args)
(markup-list?)
- align
- ((word-space)
- (text-direction RIGHT))
- "Put @var{args} in a horizontal line. The property @code{word-space}
-determines the space between each markup in @var{args}.
+ #:category align
+ #:properties ((text-direction RIGHT)
+ (word-space 0.6)
+ (line-width #f))
+ "Put @var{markups} in a horizontal line of width @var{line-width}.
+The markups are spread to fill the entire line and separated by equal
+space. If there are no arguments, return an empty stencil.
@lilypond[verbatim,quote]
\\markup {
- \\line {
- A simple line of text
+ \\justify-line {
+ Space between neighboring words is constant
}
}
@end lilypond"
- (let ((stencils (interpret-markup-list layout props args)))
- (if (= text-direction LEFT)
- (set! stencils (reverse stencils)))
- (stack-stencil-line
- word-space
- (remove ly:stencil-empty? stencils))))
+ (justify-line-helper
+ layout props args text-direction word-space line-width #t))
-(define-builtin-markup-command (concat layout props args)
+(define-markup-command (concat layout props args)
(markup-list?)
- align
- ()
+ #:category align
"
@cindex concatenating text
@cindex ligatures in text
-Concatenate @var{args} in a horizontal line, without spaces inbetween.
+Concatenate @var{args} in a horizontal line, without spaces in between.
Strings and simple markups are concatenated on the input level, allowing
ligatures. For example, @code{\\concat @{ \"f\" \\simple #\"i\" @}} is
equivalent to @code{\"fi\"}.
@lilypond[verbatim,quote]
\\markup {
- \\bold {
- au
- \\concat {
- Mouv
- \\super
- t
- }
+ \\concat {
+ one
+ two
+ three
}
}
@end lilypond"
(define (concat-string-args arg-list)
(fold-right (lambda (arg result-list)
- (let ((result (if (pair? result-list)
- (car result-list)
- '())))
- (if (and (pair? arg) (eqv? (car arg) simple-markup))
- (set! arg (cadr arg)))
+ (let ((result (and (pair? result-list)
+ (car result-list))))
+ (cond ((not (pair? arg)))
+ ((eq? (car arg) simple-markup)
+ (set! arg (cadr arg)))
+ ((eq? (car arg) char-markup)
+ (set! arg (ly:wide-char->utf-8 (cadr arg)))))
(if (and (string? result) (string? arg))
(cons (string-append arg result) (cdr result-list))
- (cons arg result-list))))
+ (cons arg result-list))))
'()
arg-list))
(interpret-markup layout
(prepend-alist-chain 'word-space 0 props)
- (make-line-markup (if (markup-command-list? args)
- args
- (concat-string-args args)))))
+ (make-line-markup
+ (make-override-lines-markup-list
+ (cons 'word-space
+ (chain-assoc-get 'word-space props))
+ (if (markup-command-list? args)
+ args
+ (concat-string-args args))))))
(define (wordwrap-stencils stencils
- justify base-space line-width text-dir)
- "Perform simple wordwrap, return stencil of each line."
+ justify base-space line-width text-dir)
+ "Perform simple wordwrap, return stencil of each line."
(define space (if justify
;; justify only stretches lines.
- (* 0.7 base-space)
- base-space))
- (define (take-list width space stencils
- accumulator accumulated-width)
- "Return (head-list . tail) pair, with head-list fitting into width"
- (if (null? stencils)
- (cons accumulator stencils)
- (let* ((first (car stencils))
- (first-wid (cdr (ly:stencil-extent (car stencils) X)))
- (newwid (+ space first-wid accumulated-width)))
- (if (or (null? accumulator)
- (< newwid width))
- (take-list width space
- (cdr stencils)
- (cons first accumulator)
- newwid)
- (cons accumulator stencils)))))
- (let loop ((lines '())
- (todo stencils))
- (let* ((line-break (take-list line-width space todo
- '() 0.0))
- (line-stencils (car line-break))
- (space-left (- line-width
- (apply + (map (lambda (x) (cdr (ly:stencil-extent x X)))
- line-stencils))))
- (line-word-space (cond ((not justify) space)
- ;; don't stretch last line of paragraph.
- ;; hmmm . bug - will overstretch the last line in some case.
- ((null? (cdr line-break))
- base-space)
- ((null? line-stencils) 0.0)
- ((null? (cdr line-stencils)) 0.0)
- (else (/ space-left (1- (length line-stencils))))))
- (line (stack-stencil-line line-word-space
- (if (= text-dir RIGHT)
- (reverse line-stencils)
- line-stencils))))
- (if (pair? (cdr line-break))
- (loop (cons line lines)
- (cdr line-break))
- (begin
- (if (= text-dir LEFT)
- (set! line
- (ly:stencil-translate-axis
- line
- (- line-width (interval-end (ly:stencil-extent line X)))
- X)))
- (reverse (cons line lines)))))))
-
-(define-builtin-markup-list-command (wordwrap-internal layout props justify args)
+ (* 0.7 base-space)
+ base-space))
+ (define (stencil-len s)
+ (interval-end (ly:stencil-extent s X)))
+ (define (maybe-shift line)
+ (if (= text-dir LEFT)
+ (ly:stencil-translate-axis
+ line
+ (- line-width (stencil-len line))
+ X)
+ line))
+ (if (null? stencils)
+ '()
+ (let loop ((lines '())
+ (todo stencils))
+ (let word-loop
+ ((line (first todo))
+ (todo (cdr todo))
+ (word-list (list (first todo))))
+ (cond
+ ((pair? todo)
+ (let ((new (if (= text-dir LEFT)
+ (ly:stencil-stack (car todo) X RIGHT line space)
+ (ly:stencil-stack line X RIGHT (car todo) space))))
+ (cond
+ ((<= (stencil-len new) line-width)
+ (word-loop new (cdr todo)
+ (cons (car todo) word-list)))
+ (justify
+ (let* ((word-list
+ ;; This depends on stencil stacking being
+ ;; associative so that stacking
+ ;; left-to-right and right-to-left leads to
+ ;; the same result
+ (if (= text-dir LEFT)
+ word-list
+ (reverse! word-list)))
+ (len (stencil-len line))
+ (stretch (- line-width len))
+ (spaces
+ (- (stencil-len
+ (stack-stencils X RIGHT (1+ space) word-list))
+ len)))
+ (if (zero? spaces)
+ ;; Uh oh, nothing to fill.
+ (loop (cons (maybe-shift line) lines) todo)
+ (loop (cons
+ (stack-stencils X RIGHT
+ (+ space (/ stretch spaces))
+ word-list)
+ lines)
+ todo))))
+ (else ;; not justify
+ (loop (cons (maybe-shift line) lines) todo)))))
+ ;; todo is null
+ (justify
+ ;; Now we have the last line assembled with space
+ ;; which is compressed. We want to use the
+ ;; uncompressed version instead if it fits, and the
+ ;; justified version if it doesn't.
+ (let* ((word-list
+ ;; This depends on stencil stacking being
+ ;; associative so that stacking
+ ;; left-to-right and right-to-left leads to
+ ;; the same result
+ (if (= text-dir LEFT)
+ word-list
+ (reverse! word-list)))
+ (big-line (stack-stencils X RIGHT base-space word-list))
+ (big-len (stencil-len big-line))
+ (len (stencil-len line)))
+ (reverse! lines
+ (list
+ (if (> big-len line-width)
+ (stack-stencils X RIGHT
+ (/
+ (+
+ (* (- big-len line-width)
+ space)
+ (* (- line-width len)
+ base-space))
+ (- big-len len))
+ word-list)
+ (maybe-shift big-line))))))
+ (else ;; not justify
+ (reverse! lines (list (maybe-shift line)))))))))
+
+
+(define-markup-list-command (wordwrap-internal layout props justify args)
(boolean? markup-list?)
- ((line-width #f)
- (word-space)
- (text-direction RIGHT))
+ #:properties ((line-width #f)
+ (word-space)
+ (text-direction RIGHT))
"Internal markup list command used to define @code{\\justify} and @code{\\wordwrap}."
- (wordwrap-stencils (remove ly:stencil-empty?
- (interpret-markup-list layout props args))
+ (wordwrap-stencils (interpret-markup-list layout props args)
justify
word-space
(or line-width
(ly:output-def-lookup layout 'line-width))
text-direction))
-(define-builtin-markup-command (justify layout props args)
+(define-markup-command (justify layout props args)
(markup-list?)
- align
- ((baseline-skip)
- wordwrap-internal-markup-list)
+ #:category align
+ #:properties ((baseline-skip)
+ wordwrap-internal-markup-list)
"
@cindex justifying text
-Like wordwrap, but with lines stretched to justify the margins.
+Like @code{\\wordwrap}, but with lines stretched to justify the margins.
Use @code{\\override #'(line-width . @var{X})} to set the line width;
@var{X}@tie{}is the number of staff spaces.
(stack-lines DOWN 0.0 baseline-skip
(wordwrap-internal-markup-list layout props #t args)))
-(define-builtin-markup-command (wordwrap layout props args)
+(define-markup-command (wordwrap layout props args)
(markup-list?)
- align
- ((baseline-skip)
- wordwrap-internal-markup-list)
+ #:category align
+ #:properties ((baseline-skip)
+ wordwrap-internal-markup-list)
"Simple wordwrap. Use @code{\\override #'(line-width . @var{X})} to set
the line width, where @var{X} is the number of staff spaces.
}
@end lilypond"
(stack-lines DOWN 0.0 baseline-skip
- (wordwrap-internal-markup-list layout props #f args)))
+ (wordwrap-internal-markup-list layout props #f args)))
-(define-builtin-markup-list-command (wordwrap-string-internal layout props justify arg)
+(define-markup-list-command (wordwrap-string-internal layout props justify arg)
(boolean? string?)
- ((line-width)
- (word-space)
- (text-direction RIGHT))
+ #:properties ((line-width)
+ (word-space)
+ (text-direction RIGHT))
"Internal markup list command used to define @code{\\justify-string} and
@code{\\wordwrap-string}."
(let* ((para-strings (regexp-split
para-strings))
(para-lines (map (lambda (words)
(let* ((stencils
- (remove ly:stencil-empty?
- (map (lambda (x)
- (interpret-markup layout props x))
- words))))
+ (map (lambda (x)
+ (interpret-markup layout props x))
+ words)))
(wordwrap-stencils stencils
justify word-space
line-width text-direction)))
list-para-words)))
- (apply append para-lines)))
+ (concatenate para-lines)))
-(define-builtin-markup-command (wordwrap-string layout props arg)
+(define-markup-command (wordwrap-string layout props arg)
(string?)
- align
- ((baseline-skip)
- wordwrap-string-internal-markup-list)
+ #:category align
+ #:properties ((baseline-skip)
+ wordwrap-string-internal-markup-list)
"Wordwrap a string. Paragraphs may be separated with double newlines.
-
+
@lilypond[verbatim,quote]
\\markup {
\\override #'(line-width . 40)
\\wordwrap-string #\"Lorem ipsum dolor sit amet, consectetur
- adipisicing elit, sed do eiusmod tempor incididunt ut labore
- et dolore magna aliqua.
-
-
- Ut enim ad minim veniam, quis nostrud exercitation ullamco
- laboris nisi ut aliquip ex ea commodo consequat.
-
-
- Excepteur sint occaecat cupidatat non proident, sunt in culpa
- qui officia deserunt mollit anim id est laborum\"
+ adipisicing elit, sed do eiusmod tempor incididunt ut labore
+ et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco
+ laboris nisi ut aliquip ex ea commodo consequat.
+
+
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa
+ qui officia deserunt mollit anim id est laborum\"
}
@end lilypond"
(stack-lines DOWN 0.0 baseline-skip
(wordwrap-string-internal-markup-list layout props #f arg)))
-(define-builtin-markup-command (justify-string layout props arg)
+(define-markup-command (justify-string layout props arg)
(string?)
- align
- ((baseline-skip)
- wordwrap-string-internal-markup-list)
+ #:category align
+ #:properties ((baseline-skip)
+ wordwrap-string-internal-markup-list)
"Justify a string. Paragraphs may be separated with double newlines
-
+
@lilypond[verbatim,quote]
\\markup {
\\override #'(line-width . 40)
\\justify-string #\"Lorem ipsum dolor sit amet, consectetur
- adipisicing elit, sed do eiusmod tempor incididunt ut labore
- et dolore magna aliqua.
-
-
- Ut enim ad minim veniam, quis nostrud exercitation ullamco
- laboris nisi ut aliquip ex ea commodo consequat.
-
-
- Excepteur sint occaecat cupidatat non proident, sunt in culpa
- qui officia deserunt mollit anim id est laborum\"
+ adipisicing elit, sed do eiusmod tempor incididunt ut labore
+ et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco
+ laboris nisi ut aliquip ex ea commodo consequat.
+
+
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa
+ qui officia deserunt mollit anim id est laborum\"
}
@end lilypond"
(stack-lines DOWN 0.0 baseline-skip
(wordwrap-string-internal-markup-list layout props #t arg)))
-(define-builtin-markup-command (wordwrap-field layout props symbol)
+(define-markup-command (wordwrap-field layout props symbol)
(symbol?)
- align
- ()
+ #:category align
"Wordwrap the data which has been assigned to @var{symbol}.
-
+
@lilypond[verbatim,quote]
\\header {
title = \"My title\"
- descr = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit,
- sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
- nisi ut aliquip ex ea commodo consequat.\"
+ myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
+ elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat.\"
}
\\paper {
\\column {
\\fill-line { \\fromproperty #'header:title }
\\null
- \\wordwrap-field #'header:descr
+ \\wordwrap-field #'header:myText
}
}
}
(wordwrap-string-markup layout props m)
empty-stencil)))
-(define-builtin-markup-command (justify-field layout props symbol)
+(define-markup-command (justify-field layout props symbol)
(symbol?)
- align
- ()
+ #:category align
"Justify the data which has been assigned to @var{symbol}.
-
+
@lilypond[verbatim,quote]
\\header {
title = \"My title\"
- descr = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit,
- sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
- nisi ut aliquip ex ea commodo consequat.\"
+ myText = \"Lorem ipsum dolor sit amet, consectetur adipisicing
+ elit, sed do eiusmod tempor incididunt ut labore et dolore magna
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+ laboris nisi ut aliquip ex ea commodo consequat.\"
}
\\paper {
\\column {
\\fill-line { \\fromproperty #'header:title }
\\null
- \\justify-field #'header:descr
+ \\justify-field #'header:myText
}
}
}
(justify-string-markup layout props m)
empty-stencil)))
-(define-builtin-markup-command (combine layout props m1 m2)
+(define-markup-command (combine layout props arg1 arg2)
(markup? markup?)
- align
- ()
+ #:category align
"
@cindex merging text
Print two markups on top of each other.
Note: @code{\\combine} cannot take a list of markups enclosed in
-curly braces as an argument; the follow example will not compile:
-
-@example
-\\combine @{ a list @}
-@end example
+curly braces as an argument; for this purpose use @code{\\overlay} instead.
@lilypond[verbatim,quote]
\\markup {
\\arrow-head #Y #DOWN ##f
}
@end lilypond"
- (let* ((s1 (interpret-markup layout props m1))
- (s2 (interpret-markup layout props m2)))
+ (let* ((s1 (interpret-markup layout props arg1))
+ (s2 (interpret-markup layout props arg2)))
(ly:stencil-add s1 s2)))
+(define-markup-command (overlay layout props args)
+ (markup-list?)
+ #:category align
+ "
+@cindex merging text
+
+Takes a list of markups combining them.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\fontsize #5
+ \\override #'(thickness . 2)
+ \\overlay {
+ \\draw-line #'(0 . 4)
+ \\arrow-head #Y #DOWN ##f
+ \\translate #'(0 . 4)\\arrow-head #Y #UP ##f
+ }
+}
+@end lilypond"
+ (apply ly:stencil-add (interpret-markup-list layout props args)))
+
;;
;; TODO: should extract baseline-skip from each argument somehow..
-;;
-(define-builtin-markup-command (column layout props args)
+;;
+(define-markup-command (column layout props args)
(markup-list?)
- align
- ((baseline-skip))
+ #:category align
+ #:properties ((baseline-skip))
"
@cindex stacking text in a column
Stack the markups in @var{args} vertically. The property
-@code{baseline-skip} determines the space between each
-markup in @var{args}.
+@code{baseline-skip} determines the space between markups
+in @var{args}.
@lilypond[verbatim,quote]
\\markup {
}
@end lilypond"
(let ((arg-stencils (interpret-markup-list layout props args)))
- (stack-lines -1 0.0 baseline-skip
- (remove ly:stencil-empty? arg-stencils))))
+ (stack-lines -1 0.0 baseline-skip arg-stencils)))
-(define-builtin-markup-command (dir-column layout props args)
+(define-markup-command (dir-column layout props args)
(markup-list?)
- align
- ((direction)
- (baseline-skip))
+ #:category align
+ #:properties ((direction)
+ (baseline-skip))
"
@cindex changing direction of text columns
-Make a column of args, going up or down, depending on the setting
-of the @code{#'direction} layout property.
+Make a column of @var{args}, going up or down, depending on the
+setting of the @code{direction} layout property.
@lilypond[verbatim,quote]
\\markup {
- \\override #'(direction . 1) {
+ \\override #`(direction . ,UP) {
\\dir-column {
going up
}
}
+ \\hspace #1
\\dir-column {
going down
}
+ \\hspace #1
+ \\override #'(direction . 1) {
+ \\dir-column {
+ going up
+ }
+ }
}
@end lilypond"
(stack-lines (if (number? direction) direction -1)
baseline-skip
(interpret-markup-list layout props args)))
-(define-builtin-markup-command (center-align layout props args)
+(define (general-column align-dir baseline mols)
+ "Stack @var{mols} vertically, aligned to @var{align-dir} horizontally."
+
+ (let* ((aligned-mols (map (lambda (x) (ly:stencil-aligned-to x X align-dir)) mols))
+ (stacked-stencil (stack-lines -1 0.0 baseline aligned-mols))
+ (stacked-extent (ly:stencil-extent stacked-stencil X)))
+ (ly:stencil-translate-axis stacked-stencil (- (car stacked-extent)) X )))
+
+(define-markup-command (center-column layout props args)
(markup-list?)
- align
- ((baseline-skip))
+ #:category align
+ #:properties ((baseline-skip))
"
@cindex centering a column of text
@lilypond[verbatim,quote]
\\markup {
- \\center-align {
+ \\center-column {
+ one
+ two
+ three
+ }
+}
+@end lilypond"
+ (general-column CENTER baseline-skip (interpret-markup-list layout props args)))
+
+(define-markup-command (left-column layout props args)
+ (markup-list?)
+ #:category align
+ #:properties ((baseline-skip))
+ "
+@cindex text columns, left-aligned
+
+Put @code{args} in a left-aligned column.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\left-column {
+ one
+ two
+ three
+ }
+}
+@end lilypond"
+ (general-column LEFT baseline-skip (interpret-markup-list layout props args)))
+
+(define-markup-command (right-column layout props args)
+ (markup-list?)
+ #:category align
+ #:properties ((baseline-skip))
+ "
+@cindex text columns, right-aligned
+
+Put @code{args} in a right-aligned column.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\right-column {
one
two
three
}
}
@end lilypond"
- (let* ((mols (interpret-markup-list layout props args))
- (cmols (map (lambda (x) (ly:stencil-aligned-to x X CENTER)) mols)))
- (stack-lines -1 0.0 baseline-skip cmols)))
+ (general-column RIGHT baseline-skip (interpret-markup-list layout props args)))
-(define-builtin-markup-command (vcenter layout props arg)
+(define-markup-command (vcenter layout props arg)
(markup?)
- align
- ()
+ #:category align
"
@cindex vertically centering text
@lilypond[verbatim,quote]
\\markup {
- \\arrow-head #X #RIGHT ##f
+ one
\\vcenter
- Centered
- \\arrow-head #X #LEFT ##f
+ two
+ three
}
@end lilypond"
(let* ((mol (interpret-markup layout props arg)))
(ly:stencil-aligned-to mol Y CENTER)))
-(define-builtin-markup-command (hcenter layout props arg)
+(define-markup-command (center-align layout props arg)
(markup?)
- align
- ()
+ #:category align
"
@cindex horizontally centering text
@lilypond[verbatim,quote]
\\markup {
\\column {
- ↓
- \\hcenter
- centered
+ one
+ \\center-align
+ two
+ three
}
}
@end lilypond"
(let* ((mol (interpret-markup layout props arg)))
(ly:stencil-aligned-to mol X CENTER)))
-(define-builtin-markup-command (right-align layout props arg)
+(define-markup-command (right-align layout props arg)
(markup?)
- align
- ()
+ #:category align
"
@cindex right aligning text
@lilypond[verbatim,quote]
\\markup {
\\column {
- ↓
+ one
\\right-align
- right-aligned
+ two
+ three
}
}
@end lilypond"
(let* ((m (interpret-markup layout props arg)))
(ly:stencil-aligned-to m X RIGHT)))
-(define-builtin-markup-command (left-align layout props arg)
+(define-markup-command (left-align layout props arg)
(markup?)
- align
- ()
+ #:category align
"
@cindex left aligning text
@lilypond[verbatim,quote]
\\markup {
\\column {
- ↓
+ one
\\left-align
- left-aligned
+ two
+ three
}
}
@end lilypond"
(let* ((m (interpret-markup layout props arg)))
(ly:stencil-aligned-to m X LEFT)))
-(define-builtin-markup-command (general-align layout props axis dir arg)
+(define-markup-command (general-align layout props axis dir arg)
(integer? number? markup?)
- align
- ()
+ #:category align
"
@cindex controlling general text alignment
@lilypond[verbatim,quote]
\\markup {
\\column {
- ↓
+ one
\\general-align #X #LEFT
- \\line { X, Left }
- ↓
+ two
+ three
+ \\null
+ one
\\general-align #X #CENTER
- \\line { X, Center }
+ two
+ three
\\null
\\line {
- \\arrow-head #X #RIGHT ##f
- \\general-align #Y #DOWN
- \\line { Y, Down }
- \\arrow-head #X #LEFT ##f
+ one
+ \\general-align #Y #UP
+ two
+ three
}
+ \\null
\\line {
- \\arrow-head #X #RIGHT ##f
+ one
\\general-align #Y #3.2
- \\line {
- \\line { Y, Arbitrary alignment }
- }
- \\arrow-head #X #LEFT ##f
+ two
+ three
}
}
}
(let* ((m (interpret-markup layout props arg)))
(ly:stencil-aligned-to m axis dir)))
-(define-builtin-markup-command (halign layout props dir arg)
+(define-markup-command (halign layout props dir arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex setting horizontal text alignment
-Set horizontal alignment. If @var{dir} is @code{-1}, then it is
+Set horizontal alignment. If @var{dir} is @w{@code{-1}}, then it is
left-aligned, while @code{+1} is right. Values in between interpolate
alignment accordingly.
@lilypond[verbatim,quote]
\\markup {
\\column {
- ↓
+ one
\\halign #LEFT
- Left
- ↓
+ two
+ three
+ \\null
+ one
\\halign #CENTER
- Center
- ↓
+ two
+ three
+ \\null
+ one
\\halign #RIGHT
- Right
- ↓
- \\halign #1.2
- \\line {
- Arbitrary alignment
- }
+ two
+ three
+ \\null
+ one
+ \\halign #-5
+ two
+ three
}
}
@end lilypond"
(let* ((m (interpret-markup layout props arg)))
(ly:stencil-aligned-to m X dir)))
-(define-builtin-markup-command (with-dimensions layout props x y arg)
+(define-markup-command (with-dimensions layout props x y arg)
(number-pair? number-pair? markup?)
- other
- ()
+ #:category other
"
@cindex setting extent of text objects
-Set the dimensions of @var{arg} to @var{x} and@tie{}@var{y}."
- (let* ((m (interpret-markup layout props arg)))
- (ly:make-stencil (ly:stencil-expr m) x y)))
+Set the dimensions of @var{arg} to @var{x} and@tie{}@var{y}."
+ (let* ((expr (ly:stencil-expr (interpret-markup layout props arg))))
+ (ly:stencil-add
+ (make-transparent-box-stencil x y)
+ (ly:make-stencil
+ `(delay-stencil-evaluation ,(delay expr))
+ x y))))
+
+(define-markup-command (with-dimensions-from layout props arg1 arg2)
+ (markup? markup?)
+ #:category other
+ "
+Print @var{arg2} with the dimensions of @var{arg1}."
+ (let* ((stil1 (interpret-markup layout props arg1))
+ (x (ly:stencil-extent stil1 0))
+ (y (ly:stencil-extent stil1 1)))
+ (interpret-markup layout props (markup #:with-dimensions x y arg2))))
-(define-builtin-markup-command (pad-around layout props amount arg)
+(define-markup-command (pad-around layout props amount arg)
(number? markup?)
- align
- ()
+ #:category align
"Add padding @var{amount} all around @var{arg}.
-
+
@lilypond[verbatim,quote]
\\markup {
\\box {
}
@end lilypond"
(let* ((m (interpret-markup layout props arg))
- (x (ly:stencil-extent m X))
- (y (ly:stencil-extent m Y)))
- (ly:make-stencil (ly:stencil-expr m)
- (interval-widen x amount)
- (interval-widen y amount))))
+ (x (interval-widen (ly:stencil-extent m X) amount))
+ (y (interval-widen (ly:stencil-extent m Y) amount)))
+ (ly:stencil-add (make-transparent-box-stencil x y)
+ m)))
-(define-builtin-markup-command (pad-x layout props amount arg)
+(define-markup-command (pad-x layout props amount arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex padding text horizontally
(interval-widen x amount)
y)))
-(define-builtin-markup-command (put-adjacent layout props axis dir arg1 arg2)
+(define-markup-command (put-adjacent layout props axis dir arg1 arg2)
(integer? ly:dir? markup? markup?)
- align
- ()
+ #:category align
"Put @var{arg2} next to @var{arg1}, without moving @var{arg1}."
(let ((m1 (interpret-markup layout props arg1))
(m2 (interpret-markup layout props arg2)))
(ly:stencil-combine-at-edge m1 axis dir m2 0.0)))
-(define-builtin-markup-command (transparent layout props arg)
+(define-markup-command (transparent layout props arg)
(markup?)
- other
- ()
- "Make the argument transparent.
-
+ #:category other
+ "Make @var{arg} transparent.
+
@lilypond[verbatim,quote]
\\markup {
\\transparent {
(let* ((m (interpret-markup layout props arg))
(x (ly:stencil-extent m X))
(y (ly:stencil-extent m Y)))
- (ly:make-stencil "" x y)))
+ (ly:make-stencil (list 'transparent-stencil (ly:stencil-expr m)) x y)))
-(define-builtin-markup-command (pad-to-box layout props x-ext y-ext arg)
+(define-markup-command (pad-to-box layout props x-ext y-ext arg)
(number-pair? number-pair? markup?)
- align
- ()
+ #:category align
"Make @var{arg} take at least @var{x-ext}, @var{y-ext} space.
@lilypond[verbatim,quote]
}
}
@end lilypond"
- (let* ((m (interpret-markup layout props arg))
- (x (ly:stencil-extent m X))
- (y (ly:stencil-extent m Y)))
- (ly:make-stencil (ly:stencil-expr m)
- (interval-union x-ext x)
- (interval-union y-ext y))))
+ (ly:stencil-add (make-transparent-box-stencil x-ext y-ext)
+ (interpret-markup layout props arg)))
-(define-builtin-markup-command (hcenter-in layout props length arg)
+(define-markup-command (hcenter-in layout props length arg)
(number? markup?)
- align
- ()
+ #:category align
"Center @var{arg} horizontally within a box of extending
@var{length}/2 to the left and right.
(make-pad-to-box-markup
(cons (/ length -2) (/ length 2))
'(0 . 0)
- (make-hcenter-markup arg))))
+ (make-center-align-markup arg))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; property
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (fromproperty layout props symbol)
+(define-markup-command (property-recursive layout props symbol)
(symbol?)
- other
- ()
+ #:category other
+ "Print out a warning when a header field markup contains some recursive
+markup definition."
+ (ly:warning "Recursive definition of property ~a detected!" symbol)
+ empty-stencil)
+
+(define-markup-command (fromproperty layout props symbol)
+ (symbol?)
+ #:category other
"Read the @var{symbol} from property settings, and produce a stencil
from the markup contained within. If @var{symbol} is not defined, it
returns an empty markup.
@end lilypond"
(let ((m (chain-assoc-get symbol props)))
(if (markup? m)
- (interpret-markup layout props m)
+ ;; prevent infinite loops by clearing the interpreted property:
+ (interpret-markup layout (cons (list (cons symbol `(,property-recursive-markup ,symbol))) props) m)
empty-stencil)))
-(define-builtin-markup-command (on-the-fly layout props procedure arg)
- (symbol? markup?)
- other
- ()
+(define-markup-command (on-the-fly layout props procedure arg)
+ (procedure? markup?)
+ #:category other
"Apply the @var{procedure} markup command to @var{arg}.
-@var{procedure} should take a single argument."
- (let ((anonymous-with-signature (lambda (layout props arg) (procedure layout props arg))))
- (set-object-property! anonymous-with-signature
- 'markup-signature
- (list markup?))
- (interpret-markup layout props (list anonymous-with-signature arg))))
-
-(define-builtin-markup-command (override layout props new-prop arg)
+@var{procedure} takes the same arguments as @code{interpret-markup}
+and returns a stencil."
+ (procedure layout props arg))
+
+(define-markup-command (footnote layout props mkup note)
+ (markup? markup?)
+ #:category other
+ "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\auto-footnote a b
+ \\override #'(padding . 0.2)
+ \\auto-footnote c d
+}
+@end lilypond
+The footnote will not be annotated automatically."
+ (ly:stencil-combine-at-edge
+ (interpret-markup layout props mkup)
+ X
+ RIGHT
+ (ly:make-stencil
+ `(footnote (gensym "footnote") #f ,(interpret-markup layout props note))
+ '(0 . 0)
+ '(0 . 0))
+ 0.0))
+
+(define-markup-command (auto-footnote layout props mkup note)
+ (markup? markup?)
+ #:category other
+ #:properties ((raise 0.5)
+ (padding 0.0))
+ "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\auto-footnote a b
+ \\override #'(padding . 0.2)
+ \\auto-footnote c d
+}
+@end lilypond
+The footnote will be annotated automatically."
+ (let* ((markup-stencil (interpret-markup layout props mkup))
+ (footnote-hash (gensym "footnote"))
+ (stencil-seed 0)
+ (gauge-stencil (interpret-markup
+ layout
+ props
+ ((ly:output-def-lookup
+ layout
+ 'footnote-numbering-function)
+ stencil-seed)))
+ (x-ext (ly:stencil-extent gauge-stencil X))
+ (y-ext (ly:stencil-extent gauge-stencil Y))
+ (footnote-number
+ `(delay-stencil-evaluation
+ ,(delay
+ (ly:stencil-expr
+ (let* ((table
+ (ly:output-def-lookup layout
+ 'number-footnote-table))
+ (footnote-stencil (if (list? table)
+ (assoc-get footnote-hash
+ table)
+ empty-stencil))
+ (footnote-stencil (if (ly:stencil? footnote-stencil)
+ footnote-stencil
+ (begin
+ (ly:programming-error
+ "Cannot find correct footnote for a markup object.")
+ empty-stencil)))
+ (gap (- (interval-length x-ext)
+ (interval-length
+ (ly:stencil-extent footnote-stencil X))))
+ (y-trans (- (+ (cdr y-ext)
+ raise)
+ (cdr (ly:stencil-extent footnote-stencil
+ Y)))))
+ (ly:stencil-translate footnote-stencil
+ (cons gap y-trans)))))))
+ (main-stencil (ly:stencil-combine-at-edge
+ markup-stencil
+ X
+ RIGHT
+ (ly:make-stencil footnote-number x-ext y-ext)
+ padding)))
+ (ly:stencil-add
+ main-stencil
+ (ly:make-stencil
+ `(footnote ,footnote-hash #t ,(interpret-markup layout props note))
+ '(0 . 0)
+ '(0 . 0)))))
+
+(define-markup-command (override layout props new-prop arg)
(pair? markup?)
- other
- ()
+ #:category other
"
@cindex overriding properties within text markup
-Add the first argument in to the property list. Properties may be
-any sort of property supported by @rinternals{font-interface} and
-@rinternals{text-interface}, for example
-
-@example
-\\override #'(font-family . married) \"bla\"
-@end example
+Add the argument @var{new-prop} to the property list. Properties
+may be any property supported by @rinternals{font-interface},
+@rinternals{text-interface} and
+@rinternals{instrument-specific-markup-interface}.
@lilypond[verbatim,quote]
\\markup {
;; files
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (verbatim-file layout props name)
+(define-markup-command (verbatim-file layout props name)
(string?)
- other
- ()
- "Read the contents of a file, and include it verbatim.
+ #:category other
+ "Read the contents of file @var{name}, and include it verbatim.
@lilypond[verbatim,quote]
\\markup {
;; fonts.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (bigger layout props arg)
- (markup?)
- font
- ()
- "Increase the font size relative to current setting.
-
-@lilypond[verbatim,quote]
-\\markup {
- \\huge {
- huge
- \\hspace #2
- \\bigger {
- bigger
- }
- \\hspace #2
- huge
- }
-}
-@end lilypond"
- (interpret-markup layout props
- `(,fontsize-markup 1 ,arg)))
-(define-builtin-markup-command (smaller layout props arg)
+(define-markup-command (smaller layout props arg)
(markup?)
- font
- ()
- "Decrease the font size relative to current setting.
-
+ #:category font
+ "Decrease the font size relative to the current setting.
+
@lilypond[verbatim,quote]
\\markup {
\\fontsize #3.5 {
}
@end lilypond"
(interpret-markup layout props
- `(,fontsize-markup -1 ,arg)))
+ `(,fontsize-markup -1 ,arg)))
-(define-builtin-markup-command (larger layout props arg)
+(define-markup-command (larger layout props arg)
(markup?)
- font
- ()
- "Copy of the @code{\\bigger} command.
+ #:category font
+ "Increase the font size relative to the current setting.
@lilypond[verbatim,quote]
\\markup {
larger
}
@end lilypond"
- (interpret-markup layout props (make-bigger-markup arg)))
+ (interpret-markup layout props
+ `(,fontsize-markup 1 ,arg)))
-(define-builtin-markup-command (finger layout props arg)
+(define-markup-command (finger layout props arg)
(markup?)
- font
- ()
- "Set the argument as small numbers.
+ #:category font
+ "Set @var{arg} as small numbers.
+
@lilypond[verbatim,quote]
\\markup {
\\finger {
}
@end lilypond"
(interpret-markup layout
- (cons '((font-size . -5) (font-encoding . fetaNumber)) props)
+ (cons '((font-size . -5) (font-encoding . fetaText)) props)
arg))
-(define-builtin-markup-command (abs-fontsize layout props size arg)
+(define-markup-command (abs-fontsize layout props size arg)
(number? markup?)
- font
- ()
- "Use @var{size} as the absolute font size to display @var{arg}.
-Adjust baseline skip and word space accordingly.
+ #:category font
+ "Use @var{size} as the absolute font size (in points) to display @var{arg}.
+Adjusts @code{baseline-skip} and @code{word-space} accordingly.
+
@lilypond[verbatim,quote]
\\markup {
default text font size
}
@end lilypond"
(let* ((ref-size (ly:output-def-lookup layout 'text-font-size 12))
- (text-props (list (ly:output-def-lookup layout 'text-font-defaults)))
- (ref-word-space (chain-assoc-get 'word-space text-props 0.6))
- (ref-baseline (chain-assoc-get 'baseline-skip text-props 3))
- (magnification (/ size ref-size)))
- (interpret-markup layout
- (cons `((baseline-skip . ,(* magnification ref-baseline))
- (word-space . ,(* magnification ref-word-space))
- (font-size . ,(magnification->font-size magnification)))
- props)
- arg)))
-
-(define-builtin-markup-command (fontsize layout props increment arg)
+ (text-props (list (ly:output-def-lookup layout 'text-font-defaults)))
+ (ref-word-space (chain-assoc-get 'word-space text-props 0.6))
+ (ref-baseline (chain-assoc-get 'baseline-skip text-props 3))
+ (magnification (/ size ref-size)))
+ (interpret-markup
+ layout
+ (cons
+ `((baseline-skip . ,(* magnification ref-baseline))
+ (word-space . ,(* magnification ref-word-space))
+ (font-size . ,(magnification->font-size magnification)))
+ props)
+ arg)))
+
+(define-markup-command (fontsize layout props increment arg)
(number? markup?)
- font
- ((font-size 0)
- (word-space 1)
- (baseline-skip 2))
- "Add @var{increment} to the font-size. Adjust baseline skip accordingly.
+ #:category font
+ #:properties ((font-size 0)
+ (word-space 1)
+ (baseline-skip 2))
+ "Add @var{increment} to the font-size. Adjusts @code{baseline-skip}
+accordingly.
+
@lilypond[verbatim,quote]
\\markup {
default
smaller
}
@end lilypond"
- (let ((entries (list
- (cons 'baseline-skip (* baseline-skip (magstep increment)))
- (cons 'word-space (* word-space (magstep increment)))
- (cons 'font-size (+ font-size increment)))))
- (interpret-markup layout (cons entries props) arg)))
+ (interpret-markup
+ layout
+ (cons
+ `((baseline-skip . ,(* baseline-skip (magstep increment)))
+ (word-space . ,(* word-space (magstep increment)))
+ (font-size . ,(+ font-size increment)))
+ props)
+ arg))
-(define-builtin-markup-command (magnify layout props sz arg)
+(define-markup-command (magnify layout props sz arg)
(number? markup?)
- font
- ()
+ #:category font
"
@cindex magnifying text
}
@end lilypond"
(interpret-markup
- layout
+ layout
(prepend-alist-chain 'font-size (magnification->font-size sz) props)
arg))
-(define-builtin-markup-command (bold layout props arg)
+(define-markup-command (bold layout props arg)
(markup?)
- font
- ()
+ #:category font
"Switch to bold font-series.
-
+
@lilypond[verbatim,quote]
\\markup {
default
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg))
-(define-builtin-markup-command (sans layout props arg)
+(define-markup-command (sans layout props arg)
(markup?)
- font
- ()
- "Switch to the sans serif family.
-
+ #:category font
+ "Switch to the sans serif font family.
+
@lilypond[verbatim,quote]
\\markup {
default
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg))
-(define-builtin-markup-command (number layout props arg)
+(define-markup-command (number layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font family to @code{number}, which yields the font used for
-time signatures and fingerings. This font only contains numbers and
-some punctuation. It doesn't have any letters.
+time signatures and fingerings. This font contains numbers and
+some punctuation; it has no letters.
@lilypond[verbatim,quote]
\\markup {
}
}
@end lilypond"
- (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
+ (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
-(define-builtin-markup-command (roman layout props arg)
+(define-markup-command (roman layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font family to @code{roman}.
-
+
@lilypond[verbatim,quote]
\\markup {
\\sans \\bold {
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg))
-(define-builtin-markup-command (huge layout props arg)
+(define-markup-command (huge layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to +2.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg))
-(define-builtin-markup-command (large layout props arg)
+(define-markup-command (large layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to +1.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg))
-(define-builtin-markup-command (normalsize layout props arg)
+(define-markup-command (normalsize layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to default.
-
+
@lilypond[verbatim,quote]
\\markup {
\\teeny {
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg))
-(define-builtin-markup-command (small layout props arg)
+(define-markup-command (small layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to -1.
-
+
@lilypond[verbatim,quote]
\\markup {
default
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg))
-(define-builtin-markup-command (tiny layout props arg)
+(define-markup-command (tiny layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to -2.
-
+
@lilypond[verbatim,quote]
\\markup {
default
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg))
-(define-builtin-markup-command (teeny layout props arg)
+(define-markup-command (teeny layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set font size to -3.
-
+
@lilypond[verbatim,quote]
\\markup {
default
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg))
-(define-builtin-markup-command (fontCaps layout props arg)
+(define-markup-command (fontCaps layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set @code{font-shape} to @code{caps}
-
+
Note: @code{\\fontCaps} requires the installation and selection of
fonts which support the @code{caps} font shape."
(interpret-markup layout (prepend-alist-chain 'font-shape 'caps props) arg))
;; Poor man's caps
-(define-builtin-markup-command (smallCaps layout props text)
+(define-markup-command (smallCaps layout props arg)
(markup?)
- font
- ()
+ #:category font
"Emit @var{arg} as small caps.
Note: @code{\\smallCaps} does not support accented characters.
(define (char-list->markup chars lower)
(let ((final-string (string-upcase (reverse-list->string chars))))
(if lower
- (markup #:fontsize -2 final-string)
- final-string)))
+ (markup #:fontsize -2 final-string)
+ final-string)))
(define (make-small-caps rest-chars currents current-is-lower prev-result)
(if (null? rest-chars)
- (make-concat-markup
- (reverse! (cons (char-list->markup currents current-is-lower)
- prev-result)))
- (let* ((ch (car rest-chars))
- (is-lower (char-lower-case? ch)))
- (if (or (and current-is-lower is-lower)
- (and (not current-is-lower) (not is-lower)))
- (make-small-caps (cdr rest-chars)
- (cons ch currents)
- is-lower
- prev-result)
- (make-small-caps (cdr rest-chars)
- (list ch)
- is-lower
- (if (null? currents)
- prev-result
- (cons (char-list->markup
- currents current-is-lower)
- prev-result)))))))
+ (make-concat-markup
+ (reverse! (cons (char-list->markup currents current-is-lower)
+ prev-result)))
+ (let* ((ch (car rest-chars))
+ (is-lower (char-lower-case? ch)))
+ (if (or (and current-is-lower is-lower)
+ (and (not current-is-lower) (not is-lower)))
+ (make-small-caps (cdr rest-chars)
+ (cons ch currents)
+ is-lower
+ prev-result)
+ (make-small-caps (cdr rest-chars)
+ (list ch)
+ is-lower
+ (if (null? currents)
+ prev-result
+ (cons (char-list->markup
+ currents current-is-lower)
+ prev-result)))))))
(interpret-markup layout props
- (if (string? text)
- (make-small-caps (string->list text) (list) #f (list))
- text)))
+ (if (string? arg)
+ (make-small-caps (string->list arg) (list) #f (list))
+ arg)))
-(define-builtin-markup-command (caps layout props arg)
+(define-markup-command (caps layout props arg)
(markup?)
- font
- ()
+ #:category font
"Copy of the @code{\\smallCaps} command.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (make-smallCaps-markup arg)))
-(define-builtin-markup-command (dynamic layout props arg)
+(define-markup-command (dynamic layout props arg)
(markup?)
- font
- ()
+ #:category font
"Use the dynamic font. This font only contains @b{s}, @b{f}, @b{m},
@b{z}, @b{p}, and @b{r}. When producing phrases, like
@q{pi@`{u}@tie{}@b{f}}, the normal words (like @q{pi@`{u}}) should be
}
@end lilypond"
(interpret-markup
- layout (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
+ layout (prepend-alist-chain 'font-encoding 'fetaText props) arg))
-(define-builtin-markup-command (text layout props arg)
+(define-markup-command (text layout props arg)
(markup?)
- font
- ()
+ #:category font
"Use a text font instead of music symbol or music alphabet font.
-
+
@lilypond[verbatim,quote]
\\markup {
\\number {
;; ugh - latin1
(interpret-markup layout (prepend-alist-chain 'font-encoding 'latin1 props)
- arg))
+ arg))
-(define-builtin-markup-command (italic layout props arg)
+(define-markup-command (italic layout props arg)
(markup?)
- font
- ()
+ #:category font
"Use italic @code{font-shape} for @var{arg}.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-shape 'italic props) arg))
-(define-builtin-markup-command (typewriter layout props arg)
+(define-markup-command (typewriter layout props arg)
(markup?)
- font
- ()
+ #:category font
"Use @code{font-family} typewriter for @var{arg}.
-
+
@lilypond[verbatim,quote]
\\markup {
default
(interpret-markup
layout (prepend-alist-chain 'font-family 'typewriter props) arg))
-(define-builtin-markup-command (upright layout props arg)
+(define-markup-command (upright layout props arg)
(markup?)
- font
- ()
- "Set font shape to @code{upright}. This is the opposite of @code{italic}.
+ #:category font
+ "Set @code{font-shape} to @code{upright}. This is the opposite
+of @code{italic}.
@lilypond[verbatim,quote]
\\markup {
(interpret-markup
layout (prepend-alist-chain 'font-shape 'upright props) arg))
-(define-builtin-markup-command (medium layout props arg)
+(define-markup-command (medium layout props arg)
(markup?)
- font
- ()
- "Switch to medium font series (in contrast to bold).
+ #:category font
+ "Switch to medium font-series (in contrast to bold).
@lilypond[verbatim,quote]
\\markup {
}
@end lilypond"
(interpret-markup layout (prepend-alist-chain 'font-series 'medium props)
- arg))
+ arg))
-(define-builtin-markup-command (normal-text layout props arg)
+(define-markup-command (normal-text layout props arg)
(markup?)
- font
- ()
+ #:category font
"Set all font related properties (except the size) to get the default
normal text font, no matter what font was used earlier.
@lilypond[verbatim,quote]
\\markup {
\\huge \\bold \\sans \\caps {
- Some text with font overrides
+ huge bold sans caps
\\hspace #2
\\normal-text {
- Default text, same font-size
+ huge normal
}
\\hspace #2
- More text as before
+ as before
}
}
@end lilypond"
;; ugh - latin1
(interpret-markup layout
(cons '((font-family . roman) (font-shape . upright)
- (font-series . medium) (font-encoding . latin1))
- props)
+ (font-series . medium) (font-encoding . latin1))
+ props)
arg))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; symbols.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (doublesharp layout props)
- ()
- music
+(define-markup-command (musicglyph layout props glyph-name)
+ (string?)
+ #:category music
+ "@var{glyph-name} is converted to a musical symbol; for example,
+@code{\\musicglyph #\"accidentals.natural\"} selects the natural sign from
+the music font. See @ruser{The Feta font} for a complete listing of
+the possible glyphs.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\musicglyph #\"f\"
+ \\musicglyph #\"rests.2\"
+ \\musicglyph #\"clefs.G_change\"
+}
+@end lilypond"
+ (let* ((font (ly:paper-get-font layout
+ (cons '((font-encoding . fetaMusic)
+ (font-name . #f))
+
+ props)))
+ (glyph (ly:font-get-glyph font glyph-name)))
+ (if (null? (ly:stencil-expr glyph))
+ (ly:warning (_ "Cannot find glyph ~a") glyph-name))
+
+ glyph))
+
+(define-markup-command (doublesharp layout props)
()
+ #:category music
"Draw a double sharp symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get 1 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (sesquisharp layout props)
- ()
- music
+(define-markup-command (sesquisharp layout props)
()
+ #:category music
"Draw a 3/2 sharp symbol.
@lilypond[verbatim,quote]
\\sesquisharp
}
@end lilypond"
- (interpret-markup layout props (markup #:musicglyph (assoc-get 3/4 standard-alteration-glyph-name-alist ""))))
+ (interpret-markup layout props (markup #:musicglyph (assoc-get 3/4 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (sharp layout props)
- ()
- music
+(define-markup-command (sharp layout props)
()
+ #:category music
"Draw a sharp symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get 1/2 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (semisharp layout props)
- ()
- music
+(define-markup-command (semisharp layout props)
()
- "Draw a semi sharp symbol.
+ #:category music
+ "Draw a semisharp symbol.
@lilypond[verbatim,quote]
\\markup {
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get 1/4 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (natural layout props)
- ()
- music
+(define-markup-command (natural layout props)
()
+ #:category music
"Draw a natural symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get 0 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (semiflat layout props)
- ()
- music
+(define-markup-command (semiflat layout props)
()
+ #:category music
"Draw a semiflat symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get -1/4 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (flat layout props)
- ()
- music
+(define-markup-command (flat layout props)
()
+ #:category music
"Draw a flat symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get -1/2 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (sesquiflat layout props)
- ()
- music
+(define-markup-command (sesquiflat layout props)
()
+ #:category music
"Draw a 3/2 flat symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get -3/4 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (doubleflat layout props)
- ()
- music
+(define-markup-command (doubleflat layout props)
()
+ #:category music
"Draw a double flat symbol.
@lilypond[verbatim,quote]
@end lilypond"
(interpret-markup layout props (markup #:musicglyph (assoc-get -1 standard-alteration-glyph-name-alist ""))))
-(define-builtin-markup-command (with-color layout props color arg)
+(define-markup-command (with-color layout props color arg)
(color? markup?)
- other
- ()
+ #:category other
"
@cindex coloring text
@end lilypond"
(let ((stil (interpret-markup layout props arg)))
(ly:make-stencil (list 'color color (ly:stencil-expr stil))
- (ly:stencil-extent stil X)
- (ly:stencil-extent stil Y))))
-\f
+ (ly:stencil-extent stil X)
+ (ly:stencil-extent stil Y))))
+
+(define-markup-command (tied-lyric layout props str)
+ (string?)
+ #:category music
+ #:properties ((word-space))
+ "
+@cindex simple text strings with tie characters
+
+Like simple-markup, but use tie characters for @q{~} tilde symbols.
+
+@lilypond[verbatim,quote]
+\\markup \\column {
+ \\tied-lyric #\"Siam navi~all'onde~algenti Lasciate~in abbandono\"
+ \\tied-lyric #\"Impetuosi venti I nostri~affetti sono\"
+ \\tied-lyric #\"Ogni diletto~e scoglio Tutta la vita~e~un mar.\"
+}
+@end lilypond"
+ (define (replace-ties tie str)
+ (if (string-contains str "~")
+ (let*
+ ((half-space (/ word-space 2))
+ (parts (string-split str #\~))
+ (tie-str (markup #:hspace half-space
+ #:musicglyph tie
+ #:hspace half-space))
+ (joined (list-join parts tie-str)))
+ (make-concat-markup joined))
+ str))
+
+ (define short-tie-regexp (make-regexp "~[^.]~"))
+ (define (match-short str) (regexp-exec short-tie-regexp str))
+
+ (define (replace-short str mkp)
+ (let ((match (match-short str)))
+ (if (not match)
+ (make-concat-markup (list
+ mkp
+ (replace-ties "ties.lyric.default" str)))
+ (let ((new-str (match:suffix match))
+ (new-mkp (make-concat-markup (list
+ mkp
+ (replace-ties "ties.lyric.default"
+ (match:prefix match))
+ (replace-ties "ties.lyric.short"
+ (match:substring match))))))
+ (replace-short new-str new-mkp)))))
+
+ (interpret-markup layout
+ props
+ (replace-short str (markup))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; glyphs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (arrow-head layout props axis direction filled)
+(define-markup-command (arrow-head layout props axis dir filled)
(integer? ly:dir? boolean?)
- graphic
- ()
+ #:category graphic
"Produce an arrow head in specified direction and axis.
Use the filled head if @var{filled} is specified.
@lilypond[verbatim,quote]
}
@end lilypond"
(let*
- ((name (format "arrowheads.~a.~a~a"
- (if filled
- "close"
- "open")
- axis
- direction)))
+ ((name (format #f "arrowheads.~a.~a~a"
+ (if filled
+ "close"
+ "open")
+ axis
+ dir)))
(ly:font-get-glyph
(ly:paper-get-font layout (cons '((font-encoding . fetaMusic))
- props))
+ props))
name)))
-(define-builtin-markup-command (musicglyph layout props glyph-name)
- (string?)
- music
- ()
- "@var{glyph-name} is converted to a musical symbol; for example,
-@code{\\musicglyph #\"accidentals.natural\"} selects the natural sign from
-the music font. See @ruser{The Feta font} for a complete listing of
-the possible glyphs.
-
-@lilypond[verbatim,quote]
-\\markup {
- \\musicglyph #\"f\"
- \\musicglyph #\"rests.2\"
- \\musicglyph #\"clefs.G_change\"
-}
-@end lilypond"
- (let* ((font (ly:paper-get-font layout
- (cons '((font-encoding . fetaMusic)
- (font-name . #f))
-
- props)))
- (glyph (ly:font-get-glyph font glyph-name)))
- (if (null? (ly:stencil-expr glyph))
- (ly:warning (_ "Cannot find glyph ~a") glyph-name))
-
- glyph))
-
-
-(define-builtin-markup-command (lookup layout props glyph-name)
+(define-markup-command (lookup layout props glyph-name)
(string?)
- other
- ()
+ #:category other
"Lookup a glyph by name.
-
+
@lilypond[verbatim,quote]
\\markup {
\\override #'(font-encoding . fetaBraces) {
}
@end lilypond"
(ly:font-get-glyph (ly:paper-get-font layout props)
- glyph-name))
+ glyph-name))
-(define-builtin-markup-command (char layout props num)
+(define-markup-command (char layout props num)
(integer?)
- other
- ()
- "Produce a single character. For example, @code{\\char #65} produces the
-letter @q{A}.
+ #:category other
+ "Produce a single character. Characters encoded in hexadecimal
+format require the prefix @code{#x}.
@lilypond[verbatim,quote]
\\markup {
- \\char #65
+ \\char #65 \\char ##x00a9
}
@end lilypond"
(ly:text-interface::interpret-markup layout props (ly:wide-char->utf-8 num)))
(integer->char (+ i (char->integer #\A)))))
(define number->mark-alphabet-vector (list->vector
- (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
+ (map (lambda (i) (integer->char (+ i (char->integer #\A)))) (iota 26))))
(define (number->markletter-string vec n)
"Double letters for big marks."
(let* ((lst (vector-length vec)))
-
+
(if (>= n lst)
- (string-append (number->markletter-string vec (1- (quotient n lst)))
- (number->markletter-string vec (remainder n lst)))
- (make-string 1 (vector-ref vec n)))))
+ (string-append (number->markletter-string vec (1- (quotient n lst)))
+ (number->markletter-string vec (remainder n lst)))
+ (make-string 1 (vector-ref vec n)))))
-(define-builtin-markup-command (markletter layout props num)
+(define-markup-command (markletter layout props num)
(integer?)
- other
- ()
- "Make a markup letter for @var{num}. The letters start with A to@tie{}Z
-(skipping letter@tie{}I), and continue with double letters.
+ #:category other
+ "Make a markup letter for @var{num}. The letters start with A
+to@tie{}Z (skipping letter@tie{}I), and continue with double letters.
@lilypond[verbatim,quote]
\\markup {
}
@end lilypond"
(ly:text-interface::interpret-markup layout props
- (number->markletter-string number->mark-letter-vector num)))
+ (number->markletter-string number->mark-letter-vector num)))
-(define-builtin-markup-command (markalphabet layout props num)
+(define-markup-command (markalphabet layout props num)
(integer?)
- other
- ()
- "Make a markup letter for @var{num}. The letters start with A to@tie{}Z
+ #:category other
+ "Make a markup letter for @var{num}. The letters start with A to@tie{}Z
and continue with double letters.
@lilypond[verbatim,quote]
\\markalphabet #26
}
@end lilypond"
- (ly:text-interface::interpret-markup layout props
- (number->markletter-string number->mark-alphabet-vector num)))
+ (ly:text-interface::interpret-markup layout props
+ (number->markletter-string number->mark-alphabet-vector num)))
(define-public (horizontal-slash-interval num forward number-interval mag)
- (ly:message "Mag step: ~a" mag)
(if forward
- (cond ;((= num 6) (interval-widen number-interval (* mag 0.5)))
- ;((= num 5) (interval-widen number-interval (* mag 0.5)))
- (else (interval-widen number-interval (* mag 0.25))))
- (cond ((= num 6) (interval-widen number-interval (* mag 0.5)))
- ;((= num 5) (interval-widen number-interval (* mag 0.5)))
- (else (interval-widen number-interval (* mag 0.25))))
- ))
+ (cond ;; ((= num 6) (interval-widen number-interval (* mag 0.5)))
+ ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
+ (else (interval-widen number-interval (* mag 0.25))))
+ (cond ((= num 6) (interval-widen number-interval (* mag 0.5)))
+ ;; ((= num 5) (interval-widen number-interval (* mag 0.5)))
+ (else (interval-widen number-interval (* mag 0.25))))
+ ))
(define-public (adjust-slash-stencil num forward stencil mag)
(if forward
- (cond ((= num 2)
- (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
- ((= num 3)
- (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
- ;((= num 5)
- ;(ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.07))))
- ;((= num 7)
- ; (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
- (else stencil))
- (cond ((= num 6)
- (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.15))))
- ;((= num 8)
- ; (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
- (else stencil))
+ (cond ((= num 2)
+ (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
+ ((= num 3)
+ (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.2))))
+ ;; ((= num 5)
+ ;; (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.07))))
+ ;; ((= num 7)
+ ;; (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
+ (else stencil))
+ (cond ((= num 6)
+ (ly:stencil-translate stencil (cons (* mag -0.00) (* mag 0.15))))
+ ;; ((= num 8)
+ ;; (ly:stencil-translate stencil (cons (* mag -0.00) (* mag -0.15))))
+ (else stencil))
+ )
)
-)
(define (slashed-digit-internal layout props num forward font-size thickness)
(let* ((mag (magstep font-size))
(thickness (* mag
(ly:output-def-lookup layout 'line-thickness)
thickness))
- ; backward slashes might use slope and point in the other direction!
+ ;; backward slashes might use slope and point in the other direction!
(dy (* mag (if forward 0.4 -0.4)))
(number-stencil (interpret-markup layout
- (prepend-alist-chain 'font-encoding 'fetaNumber props)
+ (prepend-alist-chain 'font-encoding 'fetaText props)
(number->string num)))
(num-x (horizontal-slash-interval num forward (ly:stencil-extent number-stencil X) mag))
(center (interval-center (ly:stencil-extent number-stencil Y)))
- ; Use the real extents of the slash, not the whole number, because we
- ; might translate the slash later on!
+ ;; Use the real extents of the slash, not the whole number,
+ ;; because we might translate the slash later on!
(num-y (interval-widen (cons center center) (abs dy)))
(is-sane (and (interval-sane? num-x) (interval-sane? num-y)))
(slash-stencil (if is-sane
- (ly:make-stencil
- `(draw-line ,thickness
- ,(car num-x) ,(- (interval-center num-y) dy)
- ,(cdr num-x) ,(+ (interval-center num-y) dy))
- num-x num-y)
+ (make-line-stencil thickness
+ (car num-x) (- (interval-center num-y) dy)
+ (cdr num-x) (+ (interval-center num-y) dy))
#f)))
-(ly:message "Num: ~a, X-interval: ~a" num num-x)
(if (ly:stencil? slash-stencil)
- (begin
- ; for some numbers we need to shift the slash/backslash up or down to make
- ; the slashed digit look better
- (set! slash-stencil (adjust-slash-stencil num forward slash-stencil mag))
- (set! number-stencil
- (ly:stencil-add number-stencil slash-stencil)))
- (ly:warning "Unable to create slashed digit ~a" num))
+ (begin
+ ;; for some numbers we need to shift the slash/backslash up or
+ ;; down to make the slashed digit look better
+ (set! slash-stencil (adjust-slash-stencil num forward slash-stencil mag))
+ (set! number-stencil
+ (ly:stencil-add number-stencil slash-stencil)))
+ (ly:warning "Unable to create slashed digit ~a" num))
number-stencil))
-(define-builtin-markup-command (slashed-digit layout props num)
+(define-markup-command (slashed-digit layout props num)
(integer?)
- other
- ((font-size 0)
- (thickness 1.6))
+ #:category other
+ #:properties ((font-size 0)
+ (thickness 1.6))
"
@cindex slashed digits
@end lilypond"
(slashed-digit-internal layout props num #t font-size thickness))
-(define-builtin-markup-command (backslashed-digit layout props num)
+(define-markup-command (backslashed-digit layout props num)
(integer?)
- other
- ((font-size 0)
- (thickness 1.6))
+ #:category other
+ #:properties ((font-size 0)
+ (thickness 1.6))
"
@cindex backslashed digits
@end lilypond"
(slashed-digit-internal layout props num #f font-size thickness))
+;; eyeglasses
+(define eyeglassespath
+ '((moveto 0.42 0.77)
+ (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
+ (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
+ (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
+ (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
+ (closepath)
+ (moveto 2.07 0.77)
+ (rcurveto 0 0.304 -0.246 0.55 -0.55 0.55)
+ (rcurveto -0.304 0 -0.55 -0.246 -0.55 -0.55)
+ (rcurveto 0 -0.304 0.246 -0.55 0.55 -0.55)
+ (rcurveto 0.304 0 0.55 0.246 0.55 0.55)
+ (closepath)
+ (moveto 1.025 0.935)
+ (rcurveto 0 0.182 -0.148 0.33 -0.33 0.33)
+ (rcurveto -0.182 0 -0.33 -0.148 -0.33 -0.33)
+ (moveto -0.68 0.77)
+ (rlineto 0.66 1.43)
+ (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)
+ (moveto 2.07 0.77)
+ (rlineto 0.66 1.43)
+ (rcurveto 0.132 0.286 0.55 0.44 0.385 -0.33)))
+
+(define-markup-command (eyeglasses layout props)
+ ()
+ #:category other
+ "Prints out eyeglasses, indicating strongly to look at the conductor.
+@lilypond[verbatim,quote]
+\\markup { \\eyeglasses }
+@end lilypond"
+ (interpret-markup layout props
+ (make-override-markup '(line-cap-style . butt)
+ (make-path-markup 0.15 eyeglassespath))))
+
+(define-markup-command (left-brace layout props size)
+ (number?)
+ #:category other
+ "
+A feta brace in point size @var{size}.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\left-brace #35
+ \\hspace #2
+ \\left-brace #45
+}
+@end lilypond"
+ (let* ((font (ly:paper-get-font layout
+ (cons '((font-encoding . fetaBraces)
+ (font-name . #f))
+ props)))
+ (glyph-count (1- (ly:otf-glyph-count font)))
+ (scale (ly:output-def-lookup layout 'output-scale))
+ (scaled-size (/ (ly:pt size) scale))
+ (glyph (lambda (n)
+ (ly:font-get-glyph font (string-append "brace"
+ (number->string n)))))
+ (get-y-from-brace (lambda (brace)
+ (interval-length
+ (ly:stencil-extent (glyph brace) Y))))
+ (find-brace (binary-search 0 glyph-count get-y-from-brace scaled-size))
+ (glyph-found (glyph find-brace)))
+
+ (if (or (null? (ly:stencil-expr glyph-found))
+ (< scaled-size (interval-length (ly:stencil-extent (glyph 0) Y)))
+ (> scaled-size (interval-length
+ (ly:stencil-extent (glyph glyph-count) Y))))
+ (begin
+ (ly:warning (_ "no brace found for point size ~S ") size)
+ (ly:warning (_ "defaulting to ~S pt")
+ (/ (* scale (interval-length
+ (ly:stencil-extent glyph-found Y)))
+ (ly:pt 1)))))
+ glyph-found))
+
+(define-markup-command (right-brace layout props size)
+ (number?)
+ #:category other
+ "
+A feta brace in point size @var{size}, rotated 180 degrees.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\right-brace #45
+ \\hspace #2
+ \\right-brace #35
+}
+@end lilypond"
+ (interpret-markup layout props (markup #:rotate 180 #:left-brace size)))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; the note command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: better syntax.
-(define-builtin-markup-command (note-by-number layout props log dot-count dir)
+(define-markup-command (note-by-number layout props log dot-count dir)
(number? number? number?)
- music
- ((font-size 0)
- (style '()))
+ #:category music
+ #:properties ((font-size 0)
+ (flag-style '())
+ (style '()))
"
@cindex notes within text by log and dot-count
-Construct a note symbol, with stem. By using fractional values for
-@var{dir}, you can obtain longer or shorter stems.
+Construct a note symbol, with stem and flag. By using fractional values for
+@var{dir}, longer or shorter stems can be obtained.
+Supports all note-head-styles. Ancient note-head-styles will get
+mensural-style-flags. @code{flag-style} may be overridden independently.
+Supported flag-styles are @code{default}, @code{old-straight-flag},
+@code{modern-straight-flag}, @code{flat-flag}, @code{mensural} and
+@code{neomensural}. The latter two flag-styles will both result in
+mensural-flags. Both are supplied for convenience.
@lilypond[verbatim,quote]
\\markup {
@end lilypond"
(define (get-glyph-name-candidates dir log style)
(map (lambda (dir-name)
- (format "noteheads.~a~a~a" dir-name (min log 2)
- (if (and (symbol? style)
- (not (equal? 'default style)))
- (symbol->string style)
- "")))
- (list (if (= dir UP) "u" "d")
- "s")))
-
+ (format #f "noteheads.~a~a"
+ dir-name
+ (if (and (symbol? style)
+ (not (equal? 'default style)))
+ (select-head-glyph style (min log 2))
+ (min log 2))))
+ (list (if (= dir UP) "u" "d")
+ "s")))
+
(define (get-glyph-name font cands)
(if (null? cands)
- ""
- (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
- (get-glyph-name font (cdr cands))
- (car cands))))
-
- (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)) props)))
- (size-factor (magstep font-size))
- (stem-length (* size-factor (max 3 (- log 1))))
- (head-glyph-name (get-glyph-name font (get-glyph-name-candidates (sign dir) log style)))
+ ""
+ (if (ly:stencil-empty? (ly:font-get-glyph font (car cands)))
+ (get-glyph-name font (cdr cands))
+ (car cands))))
+
+ (define (buildflags flag-stencil remain curr-stencil spacing)
+ ;; Function to recursively create a stencil with @code{remain} flags
+ ;; from the single-flag stencil @code{curr-stencil}, which is already
+ ;; translated to the position of the previous flag position.
+ ;;
+ ;; Copy and paste from /scm/flag-styles.scm
+ (if (> remain 0)
+ (let* ((translated-stencil
+ (ly:stencil-translate-axis curr-stencil spacing Y))
+ (new-stencil (ly:stencil-add flag-stencil translated-stencil)))
+ (buildflags new-stencil (- remain 1) translated-stencil spacing))
+ flag-stencil))
+
+ (define (straight-flag-mrkp flag-thickness flag-spacing
+ upflag-angle upflag-length
+ downflag-angle downflag-length
+ dir)
+ ;; Create a stencil for a straight flag. @var{flag-thickness} and
+ ;; @var{flag-spacing} are given in staff spaces, @var{upflag-angle} and
+ ;; @var{downflag-angle} are given in degrees, and @var{upflag-length} and
+ ;; @var{downflag-length} are given in staff spaces.
+ ;;
+ ;; All lengths are scaled according to the font size of the note.
+ ;;
+ ;; From /scm/flag-styles.scm, modified to fit here.
+
+ (let* ((stem-up (> dir 0))
+ ;; scale with the note size
+ (factor (magstep font-size))
+ (stem-thickness (* factor 0.1))
+ (line-thickness (ly:output-def-lookup layout 'line-thickness))
+ (half-stem-thickness (/ (* stem-thickness line-thickness) 2))
+ (raw-length (if stem-up upflag-length downflag-length))
+ (angle (if stem-up upflag-angle downflag-angle))
+ (flag-length (+ (* raw-length factor) half-stem-thickness))
+ (flag-end (polar->rectangular flag-length angle))
+ (thickness (* flag-thickness factor))
+ (thickness-offset (cons 0 (* -1 thickness dir)))
+ (spacing (* -1 flag-spacing factor dir))
+ (start (cons (- half-stem-thickness) (* half-stem-thickness dir)))
+ (raw-points
+ (list
+ '(0 . 0)
+ flag-end
+ (offset-add flag-end thickness-offset)
+ thickness-offset))
+ (points (map (lambda (coord) (offset-add coord start)) raw-points))
+ (stencil (ly:round-filled-polygon points half-stem-thickness))
+ ;; Log for 1/8 is 3, so we need to subtract 3
+ (flag-stencil (buildflags stencil (- log 3) stencil spacing)))
+ flag-stencil))
+
+ (let* ((font (ly:paper-get-font layout (cons '((font-encoding . fetaMusic)
+ (font-name . #f))
+ props)))
+ (size-factor (magstep font-size))
+ (blot (ly:output-def-lookup layout 'blot-diameter))
+ (head-glyph-name
+ (let ((result (get-glyph-name font
+ (get-glyph-name-candidates
+ (sign dir) log style))))
+ (if (string-null? result)
+ ;; If no glyph name can be found, select default heads.
+ ;; Though this usually means an unsupported style has been
+ ;; chosen, it also prevents unrelated 'style settings from
+ ;; other grobs (e.g., TextSpanner and TimeSignature) leaking
+ ;; into markup.
+ (get-glyph-name font
+ (get-glyph-name-candidates
+ (sign dir) log 'default))
+ result)))
(head-glyph (ly:font-get-glyph font head-glyph-name))
- (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
- (stem-thickness (* size-factor 0.13))
+ (ancient-flags?
+ (member style
+ '(mensural neomensural petrucci semipetrucci blackpetrucci)))
+ (attach-indices (ly:note-head::stem-attachment font head-glyph-name))
+ (stem-length (* size-factor (max 3 (- log 1))))
+ ;; With ancient-flags we want a tighter stem
+ (stem-thickness (* size-factor (if ancient-flags? 0.1 0.13)))
(stemy (* dir stem-length))
(attach-off (cons (interval-index
- (ly:stencil-extent head-glyph X)
- (* (sign dir) (car attach-indices)))
- (* (sign dir) ; fixme, this is inconsistent between X & Y.
- (interval-index
- (ly:stencil-extent head-glyph Y)
- (cdr attach-indices)))))
+ (ly:stencil-extent head-glyph X)
+ (* (sign dir) (car attach-indices)))
+ ;; fixme, this is inconsistent between X & Y.
+ (* (sign dir)
+ (interval-index
+ (ly:stencil-extent head-glyph Y)
+ (cdr attach-indices)))))
+ ;; For a tighter stem (with ancient-flags) the stem-width has to be
+ ;; adjusted.
+ (stem-X-corr
+ (if (or ancient-flags?
+ (member flag-style '(mensural neomensural)))
+ (* 0.5 dir stem-thickness) 0))
(stem-glyph (and (> log 0)
- (ly:round-filled-box
- (ordered-cons (car attach-off)
- (+ (car attach-off) (* (- (sign dir)) stem-thickness)))
- (cons (min stemy (cdr attach-off))
- (max stemy (cdr attach-off)))
- (/ stem-thickness 3))))
-
+ (ly:round-filled-box
+ (ordered-cons (+ stem-X-corr (car attach-off))
+ (+ stem-X-corr (car attach-off)
+ (* (- (sign dir)) stem-thickness)))
+ (cons (min stemy (cdr attach-off))
+ (max stemy (cdr attach-off)))
+ (/ stem-thickness 3))))
(dot (ly:font-get-glyph font "dots.dot"))
(dotwid (interval-length (ly:stencil-extent dot X)))
(dots (and (> dot-count 0)
(ly:stencil-translate-axis
dot (* 2 x dotwid) X))
(iota dot-count)))))
+ ;; Straight-flags. Values taken from /scm/flag-style.scm
+ (modern-straight-flag (straight-flag-mrkp 0.55 1 -18 1.1 22 1.2 dir))
+ (old-straight-flag (straight-flag-mrkp 0.55 1 -45 1.2 45 1.4 dir))
+ (flat-flag (straight-flag-mrkp 0.55 1.0 0 1.0 0 1.0 dir))
+ ;; Calculate a corrective to avoid a gap between
+ ;; straight-flags and the stem.
+ (flag-style-Y-corr (if (or (eq? flag-style 'modern-straight-flag)
+ (eq? flag-style 'old-straight-flag)
+ (eq? flag-style 'flat-flag))
+ (/ blot 10 (* -1 dir))
+ 0))
(flaggl (and (> log 2)
(ly:stencil-translate
- (ly:font-get-glyph font
- (string-append "flags."
- (if (> dir 0) "u" "d")
- (number->string log)))
- (cons (+ (car attach-off) (if (< dir 0) stem-thickness 0)) stemy)))))
-
- ; If there is a flag on an upstem and the stem is short, move the dots to avoid the flag.
- ; 16th notes get a special case because their flags hang lower than any other flags.
- (if (and dots (> dir 0) (> log 2) (or (< dir 1.15) (and (= log 4) (< dir 1.3))))
- (set! dots (ly:stencil-translate-axis dots 0.5 X)))
+ (cond ((eq? flag-style 'modern-straight-flag)
+ modern-straight-flag)
+ ((eq? flag-style 'old-straight-flag)
+ old-straight-flag)
+ ((eq? flag-style 'flat-flag)
+ flat-flag)
+ (else
+ (ly:font-get-glyph font
+ (format #f
+ (if (or (member flag-style
+ '(mensural neomensural))
+ (and ancient-flags?
+ (null? flag-style)))
+ "flags.mensural~a2~a"
+ "flags.~a~a")
+ (if (> dir 0) "u" "d")
+ log))))
+ (cons (+ (car attach-off)
+ ;; For tighter stems (with ancient-flags) the
+ ;; flag has to be adjusted different.
+ (if (and (not ancient-flags?) (< dir 0))
+ stem-thickness
+ 0))
+ (+ stemy flag-style-Y-corr))))))
+
+ ;; If there is a flag on an upstem and the stem is short, move the dots
+ ;; to avoid the flag. 16th notes get a special case because their flags
+ ;; hang lower than any other flags.
+ ;; Not with ancient flags or straight-flags.
+ (if (and dots (> dir 0) (> log 2)
+ (or (eq? flag-style 'default) (null? flag-style))
+ (not ancient-flags?)
+ (or (< dir 1.15) (and (= log 4) (< dir 1.3))))
+ (set! dots (ly:stencil-translate-axis dots 0.5 X)))
(if flaggl
(set! stem-glyph (ly:stencil-add flaggl stem-glyph)))
(if (ly:stencil? stem-glyph)
(set! stem-glyph
(ly:stencil-add
(ly:stencil-translate-axis
- dots
- (+ (cdr (ly:stencil-extent head-glyph X)) dotwid)
- X)
+ dots
+ (+ (cdr (ly:stencil-extent head-glyph X)) dotwid)
+ X)
stem-glyph)))
stem-glyph))
-(define-public log2
+(define-public log2
(let ((divisor (log 2)))
(lambda (z) (inexact->exact (/ (log z) divisor)))))
(define (parse-simple-duration duration-string)
- "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
- (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
+ "Parse the `duration-string', e.g. ''4..'' or ''breve.'',
+and return a (log dots) list."
+ (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)")
+ duration-string)))
(if (and match (string=? duration-string (match:substring match 0)))
- (let ((len (match:substring match 1))
+ (let ((len (match:substring match 1))
(dots (match:substring match 2)))
(list (cond ((string=? len "breve") -1)
((string=? len "longa") -2)
(if dots (string-length dots) 0)))
(ly:error (_ "not a valid duration string: ~a") duration-string))))
-(define-builtin-markup-command (note layout props duration dir)
+(define-markup-command (note layout props duration dir)
(string? number?)
- music
- (note-by-number-markup)
+ #:category music
+ #:properties (note-by-number-markup)
"
@cindex notes within text by string
(let ((parsed (parse-simple-duration duration)))
(note-by-number-markup layout props (car parsed) (cadr parsed) dir)))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; the rest command.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (rest-by-number layout props log dot-count)
+ (number? number?)
+ #:category music
+ #:properties ((font-size 0)
+ (style '())
+ (multi-measure-rest #f))
+ "
+@cindex rests or multi-measure-rests within text by log and dot-count
+
+A rest or multi-measure-rest symbol.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\rest-by-number #3 #2
+ \\hspace #2
+ \\rest-by-number #0 #1
+ \\hspace #2
+ \\override #'(multi-measure-rest . #t)
+ \\rest-by-number #0 #0
+}
+@end lilypond"
+
+ (define (get-glyph-name-candidates log style)
+ (let* (;; Choose the style-string to be added.
+ ;; If no glyph exists, select others for the specified styles
+ ;; otherwise defaulting.
+ (style-strg
+ (cond (
+ ;; 'baroque needs to be special-cased, otherwise
+ ;; `select-head-glyph´ would catch neomensural-glyphs for
+ ;; this style, if (< log 0).
+ (eq? style 'baroque)
+ (string-append (number->string log) ""))
+ ((eq? style 'petrucci)
+ (string-append (number->string log) "mensural"))
+ ;; In other cases `select-head-glyph´ from output-lib.scm
+ ;; works for rest-glyphs, too.
+ ((and (symbol? style) (not (eq? style 'default)))
+ (select-head-glyph style log))
+ (else log)))
+ ;; Choose ledgered glyphs for whole and half rest.
+ ;; Except for the specified styles, logs and MultiMeasureRests.
+ (ledger-style-rests
+ (if (and (or (list? style)
+ (not (member style
+ '(neomensural mensural petrucci))))
+ (not multi-measure-rest)
+ (or (= log 0) (= log 1)))
+ "o"
+ "")))
+ (format #f "rests.~a~a" style-strg ledger-style-rests)))
+
+ (define (get-glyph-name font cands)
+ (if (ly:stencil-empty? (ly:font-get-glyph font cands))
+ ""
+ cands))
+
+ (let* ((font
+ (ly:paper-get-font layout
+ (cons '((font-encoding . fetaMusic)
+ (font-name . #f))
+ props)))
+ (rest-glyph-name
+ (let ((result
+ (get-glyph-name font
+ (get-glyph-name-candidates log style))))
+ (if (string-null? result)
+ ;; If no glyph name can be found, select default rests. Though
+ ;; this usually means an unsupported style has been chosen, it
+ ;; also prevents unrelated 'style settings from other grobs
+ ;; (e.g., TextSpanner and TimeSignature) leaking into markup.
+ (get-glyph-name font (get-glyph-name-candidates log 'default))
+ result)))
+ (rest-glyph (ly:font-get-glyph font rest-glyph-name))
+ (dot (ly:font-get-glyph font "dots.dot"))
+ (dot-width (interval-length (ly:stencil-extent dot X)))
+ (dots (and (> dot-count 0)
+ (apply ly:stencil-add
+ (map (lambda (x)
+ (ly:stencil-translate-axis
+ dot (* 2 x dot-width) X))
+ (iota dot-count))))))
+
+ ;; Apart from mensural-, neomensural- and petrucci-style ledgered
+ ;; glyphs are taken for whole and half rests.
+ ;; If they are dotted, move the dots in X-direction to avoid collision.
+ (if (and dots
+ (< log 2)
+ (>= log 0)
+ (not (member style '(neomensural mensural petrucci))))
+ (set! dots (ly:stencil-translate-axis dots dot-width X)))
+
+ ;; Add dots to the rest-glyph.
+ ;;
+ ;; Not sure how to vertical align dots.
+ ;; For now the dots are centered for half, whole or longer rests.
+ ;; Otherwise placed near the top of the rest.
+ ;;
+ ;; Dots for rests with (< log 0) dots are allowed, but not
+ ;; if multi-measure-rest is set #t.
+ (if (and (not multi-measure-rest) dots)
+ (set! rest-glyph
+ (ly:stencil-add
+ (ly:stencil-translate
+ dots
+ (cons
+ (+ (cdr (ly:stencil-extent rest-glyph X)) dot-width)
+ (if (< log 2)
+ (interval-center (ly:stencil-extent rest-glyph Y))
+ (- (interval-end (ly:stencil-extent rest-glyph Y))
+ (/ (* 2 dot-width) 3)))))
+ rest-glyph)))
+ rest-glyph))
+
+(define-markup-command (rest layout props duration)
+ (string?)
+ #:category music
+ #:properties ((style '())
+ (multi-measure-rest #f)
+ (multi-measure-rest-number #t)
+ (word-space 0.6))
+ "
+@cindex rests or multi-measure-rests within text by string
+
+This produces a rest, with the @var{duration} for the rest type and
+augmentation dots.
+@code{\"breve\"}, @code{\"longa\"} and @code{\"maxima\"} are valid
+input-strings.
+
+Printing MultiMeasureRests could be enabled with
+@code{\\override #'(multi-measure-rest . #t)}
+If MultiMeasureRests are taken, the MultiMeasureRestNumber is printed above.
+This is enabled for all styles using default-glyphs.
+Could be disabled with @code{\\override #'(multi-measure-rest-number . #f)}
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\rest #\"4..\"
+ \\hspace #2
+ \\rest #\"breve\"
+ \\hspace #2
+ \\override #'(multi-measure-rest . #t)
+ {
+ \\rest #\"7\"
+ \\hspace #2
+ \\override #'(multi-measure-rest-number . #f)
+ \\rest #\"7\"
+ }
+}
+@end lilypond"
+ ;; Get the number of mmr-glyphs.
+ ;; Store them in a list.
+ ;; example: (mmr-numbers 25) -> '(3 0 0 1)
+ (define (mmr-numbers nmbr)
+ (let* ((8-bar-glyph (floor (/ nmbr 8)))
+ (8-remainder (remainder nmbr 8))
+ (4-bar-glyph (floor (/ 8-remainder 4)))
+ (4-remainder (remainder nmbr 4))
+ (2-bar-glyph (floor (/ 4-remainder 2)))
+ (2-remainder (remainder 4-remainder 2))
+ (1-bar-glyph (floor (/ 2-remainder 1))))
+ (list 8-bar-glyph 4-bar-glyph 2-bar-glyph 1-bar-glyph)))
+
+ ;; Get the correct mmr-glyphs.
+ ;; Store them in a list.
+ ;; example:
+ ;; (get-mmr-glyphs '(1 0 1 0) '("rests.M3" "rests.M2" "rests.M1" "rests.0"))
+ ;; -> ("rests.M3" "rests.M1")
+ (define (get-mmr-glyphs lst1 lst2)
+ (define (helper l1 l2 l3)
+ (if (null? l1)
+ (reverse l3)
+ (helper (cdr l1)
+ (cdr l2)
+ (append (make-list (car l1) (car l2)) l3))))
+ (helper lst1 lst2 '()))
+
+ ;; If duration is not valid, print a warning and return empty-stencil
+ (if (or (and (not (integer? (car (parse-simple-duration duration))))
+ (not multi-measure-rest))
+ (and (= (string-length (car (string-split duration #\. ))) 1)
+ (= (string->number (car (string-split duration #\. ))) 0)))
+ (begin
+ (ly:warning (_ "not a valid duration string: ~a - ignoring") duration)
+ empty-stencil)
+ (let* (
+ ;; For simple rests:
+ ;; Get a (log dots) list.
+ (parsed (parse-simple-duration duration))
+ ;; Create the rest-stencil
+ (stil
+ (rest-by-number-markup layout props (car parsed) (cadr parsed)))
+ ;; For MultiMeasureRests:
+ ;; Get the duration-part of duration
+ (dur-part-string (car (string-split duration #\. )))
+ ;; Get the duration of MMR:
+ ;; If not a number (eg. "maxima") calculate it.
+ (mmr-duration
+ (or (string->number dur-part-string) (expt 2 (abs (car parsed)))))
+ ;; Get a list of the correct number of each mmr-glyph.
+ (count-mmr-glyphs-list (mmr-numbers mmr-duration))
+ ;; Create a list of mmr-stencils,
+ ;; translating the glyph for a whole rest.
+ (mmr-stils-list
+ (map
+ (lambda (x)
+ (let ((single-mmr-stil
+ (rest-by-number-markup layout props (* -1 x) 0)))
+ (if (= x 0)
+ (ly:stencil-translate-axis
+ single-mmr-stil
+ ;; Ugh, hard-coded, why 1?
+ 1
+ Y)
+ single-mmr-stil)))
+ (get-mmr-glyphs count-mmr-glyphs-list (reverse (iota 4)))))
+ ;; Adjust the space between the mmr-glyphs,
+ ;; if not default-glyphs are used.
+ (word-space (if (member style
+ '(neomensural mensural petrucci))
+ (/ (* word-space 2) 3)
+ word-space))
+ ;; Create the final mmr-stencil
+ ;; via `stack-stencil-line´ from /scm/markup.scm
+ (mmr-stil (stack-stencil-line word-space mmr-stils-list)))
+
+ ;; Print the number above a multi-measure-rest
+ ;; Depends on duration, style and multi-measure-rest-number set #t
+ (if (and multi-measure-rest
+ multi-measure-rest-number
+ (> mmr-duration 1)
+ (not (member style '(neomensural mensural petrucci))))
+ (let* ((mmr-stil-x-center
+ (interval-center (ly:stencil-extent mmr-stil X)))
+ (duration-markup
+ (markup
+ #:fontsize -2
+ #:override '(font-encoding . fetaText)
+ (number->string mmr-duration)))
+ (mmr-number-stil
+ (interpret-markup layout props duration-markup))
+ (mmr-number-stil-x-center
+ (interval-center (ly:stencil-extent mmr-number-stil X))))
+
+ (set! mmr-stil (ly:stencil-combine-at-edge
+ mmr-stil
+ Y UP
+ (ly:stencil-translate-axis
+ mmr-number-stil
+ (- mmr-stil-x-center mmr-number-stil-x-center)
+ X)
+ ;; Ugh, hardcoded
+ 0.8))))
+ (if multi-measure-rest
+ mmr-stil
+ stil))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fermata markup
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (fermata layout props) ()
+ #:category music
+ #:properties ((direction UP))
+ "Create a fermata glyph. When @var{direction} is @code{DOWN}, use
+an inverted glyph. Note that within music, one would usually use the
+@code{\\fermata} articulation instead of a markup.
+
+@lilypond[verbatim,quote]
+ { c''1^\\markup \\fermata d''1_\\markup \\fermata }
+
+\\markup { \\fermata \\override #`(direction . ,DOWN) \\fermata }
+@end lilypond
+"
+ (interpret-markup layout props
+ (if (eqv? direction DOWN)
+ (markup #:musicglyph "scripts.dfermata")
+ (markup #:musicglyph "scripts.ufermata"))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; translating.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (lower layout props amount arg)
+(define-markup-command (lower layout props amount arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex lowering text
@lilypond[verbatim,quote]
\\markup {
- default
- \\lower #3 {
- three spaces lower
- }
+ one
+ \\lower #3
+ two
+ three
}
@end lilypond"
(ly:stencil-translate-axis (interpret-markup layout props arg)
- (- amount) Y))
+ (- amount) Y))
-(define-builtin-markup-command (translate-scaled layout props offset arg)
+(define-markup-command (translate-scaled layout props offset arg)
(number-pair? markup?)
- align
- ((font-size 0))
+ #:category align
+ #:properties ((font-size 0))
"
@cindex translating text
@cindex scaling text
(ly:stencil-translate (interpret-markup layout props arg)
scaled)))
-(define-builtin-markup-command (raise layout props amount arg)
+(define-markup-command (raise layout props amount arg)
(number? markup?)
- align
- ()
+ #:category align
"
@cindex raising text
-
+
Raise @var{arg} by the distance @var{amount}.
A negative @var{amount} indicates lowering, see also @code{\\lower}.
@end lilypond"
(ly:stencil-translate-axis (interpret-markup layout props arg) amount Y))
-(define-builtin-markup-command (fraction layout props arg1 arg2)
+(define-markup-command (fraction layout props arg1 arg2)
(markup? markup?)
- other
- ((font-size 0))
+ #:category other
+ #:properties ((font-size 0))
"
@cindex creating text fractions
;; should stack mols separately, to maintain LINE on baseline
(stack (stack-lines DOWN padding baseline (list m1 line m2))))
(set! stack
- (ly:stencil-aligned-to stack Y CENTER))
+ (ly:stencil-aligned-to stack Y CENTER))
(set! stack
- (ly:stencil-aligned-to stack X LEFT))
+ (ly:stencil-aligned-to stack X LEFT))
;; should have EX dimension
;; empirical anyway
(ly:stencil-translate-axis stack offset Y))))
-(define-builtin-markup-command (normal-size-super layout props arg)
+(define-markup-command (normal-size-super layout props arg)
(markup?)
- font
- ((baseline-skip))
+ #:category font
+ #:properties ((font-size 0))
"
@cindex setting superscript in standard font size
@end lilypond"
(ly:stencil-translate-axis
(interpret-markup layout props arg)
- (* 0.5 baseline-skip) Y))
+ (* 1.0 (magstep font-size)) Y))
-(define-builtin-markup-command (super layout props arg)
+(define-markup-command (super layout props arg)
(markup?)
- font
- ((font-size 0)
- (baseline-skip))
- "
+ #:category font
+ #:properties ((font-size 0))
+ "
@cindex superscript text
-Raising and lowering texts can be done with @code{\\super} and
-@code{\\sub}:
+Set @var{arg} in superscript.
@lilypond[verbatim,quote]
\\markup {
layout
(cons `((font-size . ,(- font-size 3))) props)
arg)
- (* 0.5 baseline-skip)
+ (* 1.0 (magstep font-size)) ; original font-size
Y))
-(define-builtin-markup-command (translate layout props offset arg)
+(define-markup-command (translate layout props offset arg)
(number-pair? markup?)
- align
- ()
+ #:category align
"
@cindex translating text
-
-This translates an object. Its first argument is a cons of numbers.
-
-@example
-A \\translate #(cons 2 -3) @{ B C @} D
-@end example
-This moves @q{B C} 2@tie{}spaces to the right, and 3 down, relative to its
-surroundings. This command cannot be used to move isolated scripts
-vertically, for the same reason that @code{\\raise} cannot be used for
-that.
+Translate @var{arg} relative to its surroundings. @var{offset}
+is a pair of numbers representing the displacement in the X and Y axis.
@lilypond[verbatim,quote]
\\markup {
}
@end lilypond"
(ly:stencil-translate (interpret-markup layout props arg)
- offset))
+ offset))
-(define-builtin-markup-command (sub layout props arg)
+(define-markup-command (sub layout props arg)
(markup?)
- font
- ((font-size 0)
- (baseline-skip))
+ #:category font
+ #:properties ((font-size 0))
"
@cindex subscript text
layout
(cons `((font-size . ,(- font-size 3))) props)
arg)
- (* -0.5 baseline-skip)
+ (* -0.75 (magstep font-size)) ; original font-size
Y))
-(define-builtin-markup-command (normal-size-sub layout props arg)
+(define-markup-command (normal-size-sub layout props arg)
(markup?)
- font
- ((baseline-skip))
+ #:category font
+ #:properties ((font-size 0))
"
@cindex setting subscript in standard font size
-Set @var{arg} in subscript, in a normal font size.
+Set @var{arg} in subscript with a normal font size.
@lilypond[verbatim,quote]
\\markup {
@end lilypond"
(ly:stencil-translate-axis
(interpret-markup layout props arg)
- (* -0.5 baseline-skip)
+ (* -0.75 (magstep font-size))
Y))
-\f
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; brackets.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (hbracket layout props arg)
+(define-markup-command (hbracket layout props arg)
(markup?)
- graphic
- ()
+ #:category graphic
"
@cindex placing horizontal brackets around text
-
+
Draw horizontal brackets around @var{arg}.
@lilypond[verbatim,quote]
(m (interpret-markup layout props arg)))
(bracketify-stencil m X th (* 2.5 th) th)))
-(define-builtin-markup-command (bracket layout props arg)
+(define-markup-command (bracket layout props arg)
(markup?)
- graphic
- ()
+ #:category graphic
"
@cindex placing vertical brackets around text
-
+
Draw vertical brackets around @var{arg}.
@lilypond[verbatim,quote]
(let ((th 0.1) ;; todo: take from GROB.
(m (interpret-markup layout props arg)))
(bracketify-stencil m Y th (* 2.5 th) th)))
-\f
+
+(define-markup-command (parenthesize layout props arg)
+ (markup?)
+ #:category graphic
+ #:properties ((angularity 0)
+ (padding)
+ (size 1)
+ (thickness 1)
+ (width 0.25))
+ "
+@cindex placing parentheses around text
+
+Draw parentheses around @var{arg}. This is useful for parenthesizing
+a column containing several lines of text.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\line {
+ \\parenthesize {
+ \\column {
+ foo
+ bar
+ }
+ }
+ \\override #'(angularity . 2) {
+ \\parenthesize {
+ \\column {
+ bah
+ baz
+ }
+ }
+ }
+ }
+}
+@end lilypond"
+ (let* ((m (interpret-markup layout props arg))
+ (scaled-width (* size width))
+ (scaled-thickness
+ (* (chain-assoc-get 'line-thickness props 0.1)
+ thickness))
+ (half-thickness
+ (min (* size 0.5 scaled-thickness)
+ (* (/ 4 3.0) scaled-width)))
+ (padding (chain-assoc-get 'padding props half-thickness)))
+ (parenthesize-stencil
+ m half-thickness scaled-width angularity padding)))
+
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Delayed markup evaluation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(define-builtin-markup-command (page-ref layout props label gauge default)
+(define-markup-command (page-ref layout props label gauge default)
(symbol? markup? markup?)
- other
- ()
+ #:category other
"
@cindex referencing page numbers in text
-Reference to a page number. @var{label} is the label set on the referenced
+Reference to a page number. @var{label} is the label set on the referenced
page (using the @code{\\label} command), @var{gauge} a markup used to estimate
the maximum width of the page number, and @var{default} the value to display
-when @var{label} is not found."
+when @var{label} is not found.
+
+(If the current book or bookpart is set to use roman numerals for page numbers,
+the reference will be formatted accordingly -- in which case the @var{gauge}'s
+width may require additional tweaking.)"
(let* ((gauge-stencil (interpret-markup layout props gauge))
- (x-ext (ly:stencil-extent gauge-stencil X))
- (y-ext (ly:stencil-extent gauge-stencil Y)))
+ (x-ext (ly:stencil-extent gauge-stencil X))
+ (y-ext (ly:stencil-extent gauge-stencil Y)))
+ (ly:stencil-add
+ (make-transparent-box-stencil x-ext y-ext))
(ly:make-stencil
`(delay-stencil-evaluation
,(delay (ly:stencil-expr
- (let* ((table (ly:output-def-lookup layout 'label-page-table))
- (label-page (and (list? table) (assoc label table)))
- (page-number (and label-page (cdr label-page)))
- (page-markup (if page-number (format "~a" page-number) default))
- (page-stencil (interpret-markup layout props page-markup))
- (gap (- (interval-length x-ext)
- (interval-length (ly:stencil-extent page-stencil X)))))
- (interpret-markup layout props
- (markup #:concat (#:hspace gap page-markup)))))))
+ (let* ((table (ly:output-def-lookup layout 'label-page-table))
+ (page-number (if (list? table)
+ (assoc-get label table)
+ #f))
+ (number-type (ly:output-def-lookup layout 'page-number-type))
+ (page-markup (if page-number
+ (number-format number-type page-number)
+ default))
+ (page-stencil (interpret-markup layout props page-markup))
+ (gap (- (interval-length x-ext)
+ (interval-length (ly:stencil-extent page-stencil X)))))
+ (interpret-markup layout props
+ (markup #:hspace gap page-markup))))))
x-ext
y-ext)))
-\f
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; scaling
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (scale layout props factor-pair arg)
+ (number-pair? markup?)
+ #:category graphic
+ "
+@cindex scaling markup
+@cindex mirroring markup
+
+Scale @var{arg}. @var{factor-pair} is a pair of numbers
+representing the scaling-factor in the X and Y axes.
+Negative values may be used to produce mirror images.
+
+@lilypond[verbatim,quote]
+\\markup {
+ \\line {
+ \\scale #'(2 . 1)
+ stretched
+ \\scale #'(1 . -1)
+ mirrored
+ }
+}
+@end lilypond"
+ (let ((stil (interpret-markup layout props arg))
+ (sx (car factor-pair))
+ (sy (cdr factor-pair)))
+ (ly:stencil-scale stil sx sy)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Repeating
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (pattern layout props count axis space pattern)
+ (integer? integer? number? markup?)
+ #:category other
+ "
+Prints @var{count} times a @var{pattern} markup.
+Patterns are spaced apart by @var{space}.
+Patterns are distributed on @var{axis}.
+
+@lilypond[verbatim, quote]
+\\markup \\column {
+ \"Horizontally repeated :\"
+ \\pattern #7 #X #2 \\flat
+ \\null
+ \"Vertically repeated :\"
+ \\pattern #3 #Y #0.5 \\flat
+}
+@end lilypond"
+ (let ((pattern-width (interval-length
+ (ly:stencil-extent (interpret-markup layout props pattern) X)))
+ (new-props (prepend-alist-chain 'word-space 0 (prepend-alist-chain 'baseline-skip 0 props))))
+ (let loop ((i (1- count)) (patterns (markup)))
+ (if (zero? i)
+ (interpret-markup
+ layout
+ new-props
+ (if (= axis X)
+ (markup patterns pattern)
+ (markup #:column (patterns pattern))))
+ (loop (1- i)
+ (if (= axis X)
+ (markup patterns pattern #:hspace space)
+ (markup #:column (patterns pattern #:vspace space))))))))
+
+(define-markup-command (fill-with-pattern layout props space dir pattern left right)
+ (number? ly:dir? markup? markup? markup?)
+ #:category align
+ #:properties ((word-space)
+ (line-width))
+ "
+Put @var{left} and @var{right} in a horizontal line of width @code{line-width}
+with a line of markups @var{pattern} in between.
+Patterns are spaced apart by @var{space}.
+Patterns are aligned to the @var{dir} markup.
+
+@lilypond[verbatim, quote]
+\\markup \\column {
+ \"right-aligned :\"
+ \\fill-with-pattern #1 #RIGHT . first right
+ \\fill-with-pattern #1 #RIGHT . second right
+ \\null
+ \"center-aligned :\"
+ \\fill-with-pattern #1.5 #CENTER - left right
+ \\null
+ \"left-aligned :\"
+ \\override #'(line-width . 50)
+ \\fill-with-pattern #2 #LEFT : left first
+ \\override #'(line-width . 50)
+ \\fill-with-pattern #2 #LEFT : left second
+}
+@end lilypond"
+ (let* ((pattern-x-extent (ly:stencil-extent (interpret-markup layout props pattern) X))
+ (pattern-width (interval-length pattern-x-extent))
+ (left-width (interval-length (ly:stencil-extent (interpret-markup layout props left) X)))
+ (right-width (interval-length (ly:stencil-extent (interpret-markup layout props right) X)))
+ (middle-width (max 0 (- line-width (+ (+ left-width right-width) (* word-space 2)))))
+ (period (+ space pattern-width))
+ (count (truncate (/ (- middle-width pattern-width) period)))
+ (x-offset (+ (* (- (- middle-width (* count period)) pattern-width) (/ (1+ dir) 2)) (abs (car pattern-x-extent)))))
+ (interpret-markup layout props
+ (markup left
+ #:with-dimensions (cons 0 middle-width) '(0 . 0)
+ #:translate (cons x-offset 0)
+ #:pattern (1+ count) X space pattern
+ right))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Replacements
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-markup-command (replace layout props replacements arg)
+ (list? markup?)
+ #:category font
+ "
+Used to automatically replace a string by another in the markup @var{arg}.
+Each pair of the alist @var{replacements} specifies what should be replaced.
+The @code{key} is the string to be replaced by the @code{value} string.
+
+@lilypond[verbatim, quote]
+\\markup \\replace #'((\"thx\" . \"Thanks!\")) thx
+@end lilypond"
+ (interpret-markup
+ layout
+ (internal-add-text-replacements
+ props
+ replacements)
+ (markup arg)))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Markup list commands
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-public (space-lines baseline stils)
(let space-stil ((stils stils)
- (result (list)))
+ (result (list)))
(if (null? stils)
- (reverse! result)
- (let* ((stil (car stils))
- (dy-top (max (- (/ baseline 1.5)
- (interval-bound (ly:stencil-extent stil Y) UP))
- 0.0))
- (dy-bottom (max (+ (/ baseline 3.0)
- (interval-bound (ly:stencil-extent stil Y) DOWN))
- 0.0))
- (new-stil (ly:make-stencil
- (ly:stencil-expr stil)
- (ly:stencil-extent stil X)
- (cons (- (interval-bound (ly:stencil-extent stil Y) DOWN)
- dy-bottom)
- (+ (interval-bound (ly:stencil-extent stil Y) UP)
- dy-top)))))
- (space-stil (cdr stils) (cons new-stil result))))))
-
-(define-builtin-markup-list-command (justified-lines layout props args)
+ (reverse! result)
+ (let* ((stil (car stils))
+ (dy-top (max (- (/ baseline 1.5)
+ (interval-bound (ly:stencil-extent stil Y) UP))
+ 0.0))
+ (dy-bottom (max (+ (/ baseline 3.0)
+ (interval-bound (ly:stencil-extent stil Y) DOWN))
+ 0.0))
+ (new-stil (ly:make-stencil
+ (ly:stencil-expr stil)
+ (ly:stencil-extent stil X)
+ (cons (- (interval-bound (ly:stencil-extent stil Y) DOWN)
+ dy-bottom)
+ (+ (interval-bound (ly:stencil-extent stil Y) UP)
+ dy-top)))))
+ (space-stil (cdr stils) (cons new-stil result))))))
+
+(define-markup-list-command (justified-lines layout props args)
(markup-list?)
- ((baseline-skip)
- wordwrap-internal-markup-list)
+ #:properties ((baseline-skip)
+ wordwrap-internal-markup-list)
"
@cindex justifying lines of text
(interpret-markup-list layout props
(make-wordwrap-internal-markup-list #t args))))
-(define-builtin-markup-list-command (wordwrap-lines layout props args)
+(define-markup-list-command (wordwrap-lines layout props args)
(markup-list?)
- ((baseline-skip)
- wordwrap-internal-markup-list)
+ #:properties ((baseline-skip)
+ wordwrap-internal-markup-list)
"Like @code{\\wordwrap}, but return a list of lines instead of a single markup.
Use @code{\\override-lines #'(line-width . @var{X})} to set the line width,
where @var{X} is the number of staff spaces."
(interpret-markup-list layout props
(make-wordwrap-internal-markup-list #f args))))
-(define-builtin-markup-list-command (column-lines layout props args)
+(define-markup-list-command (column-lines layout props args)
(markup-list?)
- ((baseline-skip))
+ #:properties ((baseline-skip))
"Like @code{\\column}, but return a list of lines instead of a single markup.
@code{baseline-skip} determines the space between each markup in @var{args}."
- (space-lines (chain-assoc-get 'baseline-skip props)
- (interpret-markup-list layout props args)))
+ (space-lines baseline-skip
+ (interpret-markup-list layout props args)))
-(define-builtin-markup-list-command (override-lines layout props new-prop args)
+(define-markup-list-command (override-lines layout props new-prop args)
(pair? markup-list?)
- ()
"Like @code{\\override}, for markup lists."
(interpret-markup-list layout (cons (list new-prop) props) args))
+
+(define-markup-list-command (table layout props column-align lst)
+ (number-list? markup-list?)
+ #:properties ((padding 0)
+ (baseline-skip))
+ "@cindex creating a table.
+
+Returns a table.
+
+@var{column-align} specifies how each column is aligned, possible values are
+-1, 0, 1. The number of elements in @var{column-align} determines how many
+columns will be printed.
+The entries to print are given by @var{lst}, a markup-list. If needed, the last
+row is filled up with @code{point-stencil}s.
+Overriding @code{padding} may be used to increase columns horizontal distance.
+Overriding @code{baseline-skip} to increase rows vertical distance.
+@lilypond[verbatim,quote]
+\\markuplist {
+ \\override #'(padding . 2)
+ \\table
+ #'(0 1 0 -1)
+ {
+ \\underline { center-aligned right-aligned center-aligned left-aligned }
+ one \\number 1 thousandth \\number 0.001
+ eleven \\number 11 hundredth \\number 0.01
+ twenty \\number 20 tenth \\number 0.1
+ thousand \\number 1000 one \\number 1.0
+ }
+}
+@end lilypond
+"
+
+ (define (split-lst initial-lst lngth result-lst)
+ ;; split a list into a list of sublists of length lngth
+ ;; eg. (split-lst '(1 2 3 4 5 6) 2 '())
+ ;; -> ((1 2) (3 4) (5 6))
+ (cond ((not (integer? (/ (length initial-lst) lngth)))
+ (ly:warning
+ "Can't split list of length ~a into ~a parts, returning empty list"
+ (length initial-lst) lngth)
+ '())
+ ((null? initial-lst)
+ (reverse result-lst))
+ (else
+ (split-lst
+ (drop initial-lst lngth)
+ lngth
+ (cons (take initial-lst lngth) result-lst)))))
+
+ (define (dists-list init padding lst)
+ ;; Returns a list, where each element of `lst' is
+ ;; added to the sum of the previous elements of `lst' plus padding.
+ ;; `init' will be the first element of the resulting list. The addition
+ ;; starts with the values of `init', `padding' and `(car lst)'.
+ ;; eg. (dists-list 0.01 0.1 '(1 2 3 4)))
+ ;; -> (0.01 1.11 3.21 6.31 10.41)
+ (if (or (not (number? init))
+ (not (number? padding))
+ (not (number-list? lst)))
+ (begin
+ (ly:warning
+ "not fitting argument for `dists-list', return empty lst ")
+ '())
+ (reverse
+ (fold (lambda (elem rl) (cons (+ elem padding (car rl)) rl))
+ (list init)
+ lst))))
+
+ (let* (;; get the number of columns
+ (columns (length column-align))
+ (init-stils (interpret-markup-list layout props lst))
+ ;; If the given markup-list is the result of a markup-list call, their
+ ;; length may not be easily predictable, thus we add point-stencils
+ ;; to fill last row of the table.
+ (rem (remainder (length init-stils) columns))
+ (filled-stils
+ (if (zero? rem)
+ init-stils
+ (append init-stils (make-list (- columns rem) point-stencil))))
+ ;; get the stencils in sublists of length `columns'
+ (stils
+ (split-lst filled-stils columns '()))
+ ;; procedure to return stencil-length
+ ;; If it is nan, return 0
+ (lengths-proc
+ (lambda (m)
+ (let ((lngth (interval-length (ly:stencil-extent m X))))
+ (if (nan? lngth) 0 lngth))))
+ ;; get the max width of each column in a list
+ (columns-max-x-lengths
+ (map
+ (lambda (x)
+ (apply max 0
+ (map
+ lengths-proc
+ (map (lambda (l) (list-ref l x)) stils))))
+ (iota columns)))
+ ;; create a list of (basic) distances, which each column should
+ ;; moved, using `dists-list'. Some padding may be added.
+ (dist-sequence
+ (dists-list 0 padding columns-max-x-lengths))
+ ;; Get all stencils of a row, moved accurately to build columns.
+ ;; If the items of a column are aligned other than left, we need to
+ ;; move them to avoid collisions:
+ ;; center aligned: move all items half the width of the widest item
+ ;; right aligned: move all items the full width of the widest item.
+ ;; Added to the default-offset calculated in `dist-sequence'.
+ ;; `stencils-for-row-proc' needs four arguments:
+ ;; stil - a stencil
+ ;; dist - a numerical value as basic offset in X direction
+ ;; column - a numerical value for the column we're in
+ ;; x-align - a numerical value how current column should be
+ ;; aligned, where (-1, 0, 1) means (LEFT, CENTER, RIGHT)
+ (stencils-for-row-proc
+ (lambda (stil dist column x-align)
+ (ly:stencil-translate-axis
+ (ly:stencil-aligned-to stil X x-align)
+ (cond ((member x-align '(0 1))
+ (let* (;; get the stuff for relevant column
+ (stuff-for-column
+ (map
+ (lambda (s) (list-ref s column))
+ stils))
+ ;; get length of every column-item
+ (lengths-for-column
+ (map lengths-proc stuff-for-column))
+ (widest
+ (apply max 0 lengths-for-column)))
+ (+ dist (/ widest (if (= x-align 0) 2 1)))))
+ (else dist))
+ X)))
+ ;; get a list of rows using `ly:stencil-add' on a list of stencils
+ (rows
+ (map
+ (lambda (stil-list)
+ (apply ly:stencil-add
+ (map
+ ;; the procedure creating the stencils:
+ stencils-for-row-proc
+ ;; the procedure's args:
+ stil-list
+ dist-sequence
+ (iota columns)
+ column-align)))
+ stils)))
+ (space-lines baseline-skip rows)))
+
+(define-markup-list-command (map-markup-commands layout props compose args)
+ (procedure? markup-list?)
+ "This applies the function @var{compose} to every markup in
+@var{args} (including elements of markup list command calls) in order
+to produce a new markup list. Since the return value from a markup
+list command call is not a markup list but rather a list of stencils,
+this requires passing those stencils off as the results of individual
+markup calls. That way, the results should work out as long as no
+markups rely on side effects."
+ (let ((key (make-symbol "key")))
+ (catch
+ key
+ (lambda ()
+ ;; if `compose' does not actually interpret its markup
+ ;; argument, we still need to return a list of stencils,
+ ;; created from the single returned stencil
+ (list
+ (interpret-markup layout props
+ (compose
+ (make-on-the-fly-markup
+ (lambda (layout props m)
+ ;; here all effects of `compose' on the
+ ;; properties should be visible, so we
+ ;; call interpret-markup-list at this
+ ;; point of time and harvest its
+ ;; stencils
+ (throw key
+ (interpret-markup-list
+ layout props args)))
+ (make-null-markup))))))
+ (lambda (key stencils)
+ (map
+ (lambda (sten)
+ (interpret-markup layout props
+ (compose (make-stencil-markup sten))))
+ stencils)))))