;;;; This file is part of LilyPond, the GNU music typesetter.
;;;;
-;;;; Copyright (C) 2000--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
+;;;; Copyright (C) 2000--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
;;;; Jan Nieuwenhuizen <janneke@gnu.org>
;;;;
;;;; LilyPond is free software: you can redistribute it and/or modify
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
+ (lambda (stil pt) (ly:stencil-translate stil pt))
+ (circular-list init-bow-up init-bow-down)
+ list-of-starts))))
+
(define-markup-command (draw-hline layout props)
()
#:category graphic
(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
(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))
- (page-number (if (list? table)
- (assoc-get label table)
- #f)))
- (list 'page-link page-number
- `(quote ,x-ext) `(quote ,y-ext)))))
- x-ext
- y-ext)
- arg-stencil)))
-
+ (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?)
@end lilypond"
(let* ((thick (ly:output-def-lookup layout 'line-thickness))
(underline-thick (* thickness thick))
- (markup (interpret-markup layout props arg))
- (x1 (car (ly:stencil-extent markup X)))
- (x2 (cdr (ly:stencil-extent markup X)))
+ (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 markup line)))
+ (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 {
+ \\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"
+ (interpret-markup layout (prepend-alist-chain 'direction UP props)
+ (make-tie-markup arg)))
(define-markup-command (box layout props arg)
(markup?)
(define-markup-command (whiteout layout props arg)
(markup?)
#: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-markup-command (pad-markup layout props amount arg)
(number? markup?)
"
@cindex paths, drawing
@cindex drawing paths
-Draws a path with line thickness @var{thickness} according to the
+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.
-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.
-
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
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)
\\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))
(define-markup-list-command (score-lines layout props score)
(ly:score?)
- "
-This is the same as the @code{\\score} markup but delivers its
-systems as a list of lines. This is not usually called directly by
-the user. Instead, it is called when the parser encounters
-@code{\\score} in a context where only markup lists are allowed. When
-used as the argument of a toplevel @code{\\markuplist}, the result can
-be split across pages."
+ "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)
@end lilypond"
(interpret-markup layout props str))
-(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.
+(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 \\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.\"
+\\markup {
+ \\first-visible {
+ \\fromproperty #'header:composer
+ \\italic Unknown
+ }
}
@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))))
+ (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 word-space 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.
- All paddings are checked to be at least word-space, to ensure that
- no texts collide.
- 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 word-space (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
- (let ((default-padding
- (- (/ line-width (1- word-count))
- (/ (+ (car text-widths) (car (cdr 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)))))))
+ ((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?)
}
}
@end lilypond"
- (let* ((orig-stencils (interpret-markup-list layout props args))
- (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))
- (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))))
-
- (line-contents (if (= word-count 1)
- (list
- point-stencil
- (car stencils)
- point-stencil)
- stencils)))
+ (justify-line-helper
+ layout props args text-direction word-space line-width #f))
- (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 (line layout props args)
+(define-markup-command (justify-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}.
+ #: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 {
- one two three
+ \\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 stencils)))
+ (justify-line-helper
+ layout props args text-direction word-space line-width #t))
(define-markup-command (concat layout props args)
(markup-list?)
@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))))
;; justify only stretches lines.
(* 0.7 base-space)
base-space))
- (define (stencil-space stencil line-start)
- (if (ly:stencil-empty? stencil X)
- 0
- (cdr (ly:stencil-extent
- (ly:stencil-stack (if line-start
- empty-stencil
- point-stencil)
- X RIGHT stencil)
- X))))
- (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 (stencil-space first (null? accumulator)))
- (newwid (+ (if (or (ly:stencil-empty? first Y)
- (ly:stencil-empty? first X))
- 0 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
- (stencil-space
- (stack-stencil-line 0 line-stencils)
- #t)))
- (line-words (count (lambda (s) (not (or (ly:stencil-empty? s Y)
- (ly:stencil-empty? s 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)
- ((< line-words 2) space)
- (else (/ space-left (1- line-words)))))
- (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 (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?)
justify word-space
line-width text-direction)))
list-para-words)))
- (apply append para-lines)))
+ (concatenate para-lines)))
(define-markup-command (wordwrap-string layout props arg)
(string?)
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 {
(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..
;;
`(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-markup-command (pad-around layout props amount arg)
(number? markup?)
#:category align
(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))))
+@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?)
(define-markup-command (abs-fontsize layout props size arg)
(number? markup?)
#:category font
- "Use @var{size} as the absolute font size to display @var{arg}.
+ "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]
(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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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.
-Supported flag-styles are @code{default}, @code{old-straight-flag} and
-@code{modern-straight-flag}.
+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 #f "noteheads.~a~a" dir-name
+ (format #f "noteheads.~a~a"
+ dir-name
(if (and (symbol? style)
(not (equal? 'default style)))
(select-head-glyph style (min log 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))
+ (flag-end (if (= angle 0)
+ (cons flag-length (* half-stem-thickness dir))
+ (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)))
- ;; The points of a round-filled-polygon need to be given in
- ;; clockwise order, otherwise the polygon will be enlarged by
- ;; blot-size*2!
- (points (if stem-up (list start flag-end
- (offset-add flag-end thickness-offset)
- (offset-add start thickness-offset))
- (list start
- (offset-add start thickness-offset)
- (offset-add flag-end thickness-offset)
- flag-end)))
+ (points (list start
+ flag-end
+ (offset-add flag-end thickness-offset)
+ (offset-add start thickness-offset)))
(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))
+ (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))
(sign dir) log 'default))
result)))
(head-glyph (ly:font-get-glyph font head-glyph-name))
- (ancient-flags? (or (eq? style 'mensural) (eq? style 'neomensural)))
+ (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
(cdr attach-indices)))))
;; For a tighter stem (with ancient-flags) the stem-width has to be
;; adjusted.
- (stem-X-corr (if ancient-flags? (* 0.5 dir stem-thickness) 0))
+ (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 (+ stem-X-corr (car attach-off))
;; 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 'old-straight-flag)
+ (eq? flag-style 'flat-flag))
(/ blot 10 (* -1 dir))
0))
(flaggl (and (> log 2)
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 ancient-flags?
- "flags.mensural~a2~a"
- "flags.~a~a")
- (if (> dir 0) "u" "d")
- log))))
+ (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.
(let* ((font
(ly:paper-get-font layout
- (cons '((font-encoding . fetaMusic)) props)))
+ (cons '((font-encoding . fetaMusic)
+ (font-name . #f))
+ props)))
(rest-glyph-name
(let ((result
(get-glyph-name font
@code{\\fermata} articulation instead of a markup.
@lilypond[verbatim,quote]
- { c1^\\markup \\fermata d1_\\markup \\fermata }
+ { c''1^\\markup \\fermata d''1_\\markup \\fermata }
\\markup { \\fermata \\override #`(direction . ,DOWN) \\fermata }
@end lilypond
(define-markup-command (normal-size-super layout props arg)
(markup?)
#:category font
- #:properties ((baseline-skip))
+ #: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-markup-command (super layout props arg)
(markup?)
#:category font
- #:properties ((font-size 0)
- (baseline-skip))
+ #:properties ((font-size 0))
"
@cindex superscript text
layout
(cons `((font-size . ,(- font-size 3))) props)
arg)
- (* 0.5 baseline-skip)
+ (* 1.0 (magstep font-size)) ; original font-size
Y))
(define-markup-command (translate layout props offset arg)
(define-markup-command (sub layout props arg)
(markup?)
#:category font
- #:properties ((font-size 0)
- (baseline-skip))
+ #: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-markup-command (normal-size-sub layout props arg)
(markup?)
#:category font
- #:properties ((baseline-skip))
+ #:properties ((font-size 0))
"
@cindex setting subscript in standard font size
@end lilypond"
(ly:stencil-translate-axis
(interpret-markup layout props arg)
- (* -0.5 baseline-skip)
+ (* -0.75 (magstep font-size))
Y))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
}
}
@end lilypond"
- (let* ((markup (interpret-markup layout props arg))
+ (let* ((m (interpret-markup layout props arg))
(scaled-width (* size width))
(scaled-thickness
(* (chain-assoc-get 'line-thickness props 0.1)
(* (/ 4 3.0) scaled-width)))
(padding (chain-assoc-get 'padding props half-thickness)))
(parenthesize-stencil
- markup half-thickness scaled-width angularity padding)))
+ m half-thickness scaled-width angularity padding)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)))
(page-number (if (list? table)
(assoc-get label table)
#f))
- (page-markup (if page-number (format #f "~a" page-number) default))
+ (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)))))
"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