>From ce43c71a46035b75ad5020796c8dcc4d021371af Mon Sep 17 00:00:00 2001 From: Ian Hulin Date: Tue, 29 Nov 2011 17:46:50 +0000 Subject: [PATCH] T2026: Use new (scm markup-facility-defs) scheme module for markup commands. This affects the following files. New file scm/markup-utility-defs.scm ly/music-functions-init.ly Add #(use-modules (scm markup-facility-defs)) statement. ly/titling-init.ly Add #(use-modules (scm markup-facility-defs)) statement. scm/lily.scm Add (use-modules (scm markup-facility-defs)) statement. Group I18n declarations together at the beginning. Add some comments. Add better --loglevel=DEBUG statement when running with Guile V2. scm/markup.scm Gutted. scm/markup-macros.scm (will be deleted) scm/define-markup-commands.scm Add (use-modules (scm markup-facility-defs)) statement. scm/define-woodwind-diagrams.scm Add (use-modules (scm markup-facility-defs)) statement. scm/display-woodwind-diagrams.scm Add (use-modules (scm markup-facility-defs)) statement. scm/font.scm Add (use-modules (oops goops)) statement. scm/fret-diagrams.scm Add (use-modules (scm markup-facility-defs)) statement. scm/harp-pedals.scm Add (use-modules (scm markup-facility-defs)) statement. scm/tablature.scm Add (use-modules (scm markup-facility-defs)) statement. --- ly/music-functions-init.ly | 2 + ly/titling-init.ly | 1 + scm/define-markup-commands.scm | 4612 +++++++++++++++++++------------------ scm/define-woodwind-diagrams.scm | 24 +- scm/display-woodwind-diagrams.scm | 1 + scm/font.scm | 2 +- scm/fret-diagrams.scm | 2 +- scm/harp-pedals.scm | 2 +- scm/lily.scm | 50 +- scm/markup-facility-defs.scm | 646 ++++++ scm/markup.scm | 257 ++- scm/page.scm | 1 + scm/tablature.scm | 2 +- 13 files changed, 3158 insertions(+), 2444 deletions(-) create mode 100644 scm/markup-facility-defs.scm diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 8b5cddf..88d6ee4 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -28,6 +28,8 @@ %% need SRFI-1 for filter; optargs for lambda* #(use-modules (srfi srfi-1) (ice-9 optargs)) +%% need (scm markup-facility-defs)for markup? +#(use-modules (scm markup-facility-defs)) %% TODO: using define-music-function in a .scm causes crash. diff --git a/ly/titling-init.ly b/ly/titling-init.ly index f6daacd..8a883b2 100644 --- a/ly/titling-init.ly +++ b/ly/titling-init.ly @@ -1,5 +1,6 @@ \version "2.15.20" +(use-modules (scm markup-facility-defs)) slashSeparator = \markup { \center-align \vcenter \combine diff --git a/scm/define-markup-commands.scm b/scm/define-markup-commands.scm index 4b2aff5..9466074 100644 --- a/scm/define-markup-commands.scm +++ b/scm/define-markup-commands.scm @@ -106,6 +106,8 @@ ;;; with description, syntax and example. (use-modules (ice-9 regex)) +(use-modules (scm markup-facility-defs)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; utility functions @@ -113,2351 +115,1917 @@ (define-public empty-stencil (ly:make-stencil '() '(1 . -1) '(1 . -1))) (define-public point-stencil (ly:make-stencil "" '(0 . 0) '(0 . 0))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; geometric shapes +;; basic formatting. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (draw-line layout props dest) - (number-pair?) - #:category graphic - #:properties ((thickness 1)) +(define-markup-command (simple layout props str) + (string?) + #:category font " address@hidden drawing lines within text address@hidden simple text strings -A simple line. address@hidden,quote] -\\markup { - \\draw-line #'(4 . 4) - \\override #'(thickness . 5) - \\draw-line #'(-3 . 0) -} address@hidden lilypond" - (let ((th (* (ly:output-def-lookup layout 'line-thickness) - thickness)) - (x (car dest)) - (y (cdr dest))) - (make-line-stencil th 0 0 x y))) +A simple text string; @code{\\markup @{ foo @}} is equivalent with address@hidden @{ \\simple #\"foo\" @}}. -(define-markup-command (draw-hline layout props) - () - #:category graphic - #:properties ((draw-line-markup) - (line-width) - (span-factor 1)) - " address@hidden drawing a line across a page +Note: for creating standard text markup or defining new markup commands, +the use of @code{\\simple} is unnecessary. -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 - } + \\simple #\"simple\" + \\simple #\"text\" + \\simple #\"strings\" } @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 - " address@hidden drawing circles within text - -A circle of radius @var{radius} and thickness @var{thickness}, -optionally filled. + (interpret-markup layout props str)) +(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 { - \\draw-circle #2 #0.5 ##f - \\hspace #2 - \\draw-circle #2 #0 ##t + \\line { + one two three + } } @end lilypond" - (make-circle-stencil radius thickness filled)) - -(define-markup-command (triangle layout props filled) - (boolean?) - #:category graphic - #:properties ((thickness 0.1) - (font-size 0) - (baseline-skip 2)) - " address@hidden drawing triangles within text - -A triangle, either filled or empty. + (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)))) +(define-markup-command (fill-line layout props args) + (markup-list?) + #: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. @lilypond[verbatim,quote] \\markup { - \\triangle ##t - \\hspace #2 - \\triangle ##f + \\column { + \\fill-line { + Words evenly spaced across the page + } + \\null + \\fill-line { + \\line { Text markups } + \\line { + \\italic { evenly spaced } + } + \\line { across the page } + } + } } @end lilypond" - (let ((ex (* (magstep font-size) 0.8 baseline-skip))) - (ly:make-stencil - `(polygon '(0.0 0.0 - ,ex 0.0 - ,(* 0.5 ex) - ,(* 0.86 ex)) - ,thickness - ,filled) - (cons 0 ex) - (cons 0 (* .86 ex))))) + (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)))) -(define-markup-command (circle layout props arg) - (markup?) - #:category graphic - #:properties ((thickness 1) - (font-size 0) - (circle-padding 0.2)) - " address@hidden circling text + (line-contents (if (= word-count 1) + (list + point-stencil + (car stencils) + point-stencil) + stencils))) -Draw a circle around @var{arg}. Use @code{thickness}, address@hidden and @code{font-size} properties to determine line -thickness and padding around the markup. + (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)))) address@hidden,quote] -\\markup { - \\circle { - Hi - } -} address@hidden lilypond" - (let ((th (* (ly:output-def-lookup layout 'line-thickness) - thickness)) - (pad (* (magstep font-size) circle-padding)) - (m (interpret-markup layout props arg))) - (circle-stencil m th pad))) -(define-markup-command (with-url layout props url arg) - (string? markup?) - #:category graphic +(define-markup-command (concat layout props args) + (markup-list?) + #:category align " address@hidden inserting URL links into text address@hidden concatenating text address@hidden ligatures in text -Add a link to URL @var{url} around @var{arg}. This only works in -the PDF backend. +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 { - \\with-url #\"http://lilypond.org/web/\" { - LilyPond ... \\italic { - music notation for everyone - } + \\concat { + one + two + three } } @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)))) - - (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil))) + (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))) + (if (and (string? result) (string? arg)) + (cons (string-append arg result) (cdr result-list)) + (cons arg result-list)))) + '() + arg-list)) -(define-markup-command (page-link layout props page-number arg) - (number? markup?) - #:category other - " address@hidden referencing page numbers in text + (interpret-markup layout + (prepend-alist-chain 'word-space 0 props) + (make-line-markup (if (markup-command-list? args) + args + (concat-string-args args))))) -Add a link to the page @var{page-number} around @var{arg}. This only works -in the PDF backend. +(define (wordwrap-stencils stencils + 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-markup-list-command (wordwrap-internal layout props justify args) + (boolean? markup-list?) + #: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)) + justify + word-space + (or line-width + (ly:output-def-lookup layout 'line-width)) + text-direction)) + +(define-markup-command (justify layout props args) + (markup-list?) + #:category align + #:properties ((baseline-skip) + wordwrap-internal-markup-list) + " address@hidden justifying text + +Like @code{\\wordwrap}, but with lines stretched to justify the margins. +Use @code{\\override #'(line-width . @var{X})} to set the line width; address@hidden@tie{}is the number of staff spaces. @lilypond[verbatim,quote] \\markup { - \\page-link #2 { \\italic { This links to page 2... } } + \\justify { + 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. + } } @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-markup-command (with-link layout props label arg) - (symbol? markup?) - #:category other - " address@hidden referencing page labels in text + (stack-lines DOWN 0.0 baseline-skip + (wordwrap-internal-markup-list layout props #t args))) -Add a link to the page holding label @var{label} around @var{arg}. This -only works in the PDF backend. +(define-markup-command (wordwrap layout props args) + (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. @lilypond[verbatim,quote] \\markup { - \\with-link #'label { - \\italic { This links to the page containing the label... } + \\wordwrap { + 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. } } @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:make-stencil - `(delay-stencil-evaluation - ,(delay (ly:stencil-expr - (let* ((table (ly:output-def-lookup layout 'label-page-table)) - (page-number (if (list? table) - (assoc-get label table) - #f)) - (link-expr (list 'page-link page-number - `(quote ,x-ext) `(quote ,y-ext)))) - (ly:stencil-add (ly:make-stencil link-expr x-ext y-ext) -arg-stencil))))) - x-ext - y-ext))) + (stack-lines DOWN 0.0 baseline-skip + (wordwrap-internal-markup-list layout props #f args))) +(define-markup-list-command (wordwrap-string-internal layout props justify arg) + (boolean? string?) + #:properties ((line-width) + (word-space) + (text-direction RIGHT)) + "Internal markup list command used to define @code{\\justify-string} and address@hidden" + (let* ((para-strings (regexp-split + (string-regexp-substitute + "\r" "\n" + (string-regexp-substitute "\r\n" "\n" arg)) + "\n[ \t\n]*\n[ \t\n]*")) + (list-para-words (map (lambda (str) + (regexp-split str "[ \t\n]+")) + para-strings)) + (para-lines (map (lambda (words) + (let* ((stencils + (remove ly:stencil-empty? + (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))) -(define-markup-command (beam layout props width slope thickness) - (number? number? number?) - #:category graphic - " address@hidden drawing beams within text +(define-markup-command (wordwrap-string layout props arg) + (string?) + #:category align + #:properties ((baseline-skip) + wordwrap-string-internal-markup-list) + "Wordwrap a string. Paragraphs may be separated with double newlines. -Create a beam with the specified parameters. @lilypond[verbatim,quote] \\markup { - \\beam #5 #1 #2 -} address@hidden lilypond" - (let* ((y (* slope width)) - (yext (cons (min 0 y) (max 0 y))) - (half (/ thickness 2))) + \\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. - (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) - (cons 0 width) - (cons (+ (- half) (car yext)) - (+ half (cdr yext)))))) -(define-markup-command (underline layout props arg) - (markup?) - #:category font - #:properties ((thickness 1) (offset 2)) - " address@hidden underlining text + Ut enim ad minim veniam, quis nostrud exercitation ullamco + laboris nisi ut aliquip ex ea commodo consequat. -Underline @var{arg}. Looks at @code{thickness} to determine line -thickness, and @code{offset} to determine line y-offset. address@hidden,quote] -\\markup \\fill-line { - \\underline \"underlined\" - \\override #'(offset . 5) - \\override #'(thickness . 1) - \\underline \"underlined\" - \\override #'(offset . 1) - \\override #'(thickness . 5) - \\underline \"underlined\" + Excepteur sint occaecat cupidatat non proident, sunt in culpa + qui officia deserunt mollit anim id est laborum\" } @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))) - (y (* thick (- offset))) - (line (make-line-stencil underline-thick x1 y x2 y))) - (ly:stencil-add markup line))) - -(define-markup-command (box layout props arg) - (markup?) - #:category font - #:properties ((thickness 1) - (font-size 0) - (box-padding 0.2)) - " address@hidden enclosing text within a box + (stack-lines DOWN 0.0 baseline-skip + (wordwrap-string-internal-markup-list layout props #f arg))) -Draw a box round @var{arg}. Looks at @code{thickness}, address@hidden and @code{font-size} properties to determine line -thickness and padding around the markup. +(define-markup-command (justify-string layout props arg) + (string?) + #: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 #'(box-padding . 0.5) - \\box - \\line { V. S. } -} address@hidden lilypond" - (let* ((th (* (ly:output-def-lookup layout 'line-thickness) - thickness)) - (pad (* (magstep font-size) box-padding)) - (m (interpret-markup layout props arg))) - (box-stencil m th pad))) - -(define-markup-command (filled-box layout props xext yext blot) - (number-pair? number-pair? number?) - #:category graphic - " address@hidden drawing solid boxes within text address@hidden drawing boxes with rounded corners + \\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. -Draw a box with rounded corners of dimensions @var{xext} and address@hidden For example, address@hidden -\\filled-box #'(-.3 . 1.8) #'(-.3 . 1.8) #0 address@hidden 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 address@hidden (i.e., sharp corners). + + 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\" +} address@hidden lilypond" + (stack-lines DOWN 0.0 baseline-skip + (wordwrap-string-internal-markup-list layout props #t arg))) + +(define-markup-command (wordwrap-field layout props symbol) + (symbol?) + #:category align + "Wordwrap the data which has been assigned to @var{symbol}. @lilypond[verbatim,quote] +\\header { + title = \"My title\" + 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 { + bookTitleMarkup = \\markup { + \\column { + \\fill-line { \\fromproperty #'header:title } + \\null + \\wordwrap-field #'header:myText + } + } +} + \\markup { - \\filled-box #'(0 . 4) #'(0 . 4) #0 - \\filled-box #'(0 . 2) #'(-4 . 2) #0.4 - \\filled-box #'(1 . 8) #'(0 . 7) #0.2 - \\with-color #white - \\filled-box #'(-4.5 . -2.5) #'(3.5 . 5.5) #0.7 + \\null } @end lilypond" - (ly:round-filled-box - xext yext blot)) + (let* ((m (chain-assoc-get symbol props))) + (if (string? m) + (wordwrap-string-markup layout props m) + empty-stencil))) -(define-markup-command (rounded-box layout props arg) - (markup?) - #: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}, address@hidden and @code{font-size} properties to determine line -thickness and padding around the markup; the @code{corner-radius} property -makes it possible to define another shape for the corners (default is 1). +(define-markup-command (justify-field layout props symbol) + (symbol?) + #:category align + "Justify the data which has been assigned to @var{symbol}. address@hidden,verbatim,relative=2] -c4^\\markup { - \\rounded-box { - Overtura address@hidden,quote] +\\header { + title = \"My title\" + 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 { + bookTitleMarkup = \\markup { + \\column { + \\fill-line { \\fromproperty #'header:title } + \\null + \\justify-field #'header:myText + } } } -c,8. c16 c4 r + +\\markup { + \\null +} @end lilypond" - (let ((th (* (ly:output-def-lookup layout 'line-thickness) - thickness)) - (pad (* (magstep font-size) box-padding)) - (m (interpret-markup layout props arg))) - (ly:stencil-add (rounded-box-stencil m th pad corner-radius) - m))) + (let* ((m (chain-assoc-get symbol props))) + (if (string? m) + (justify-string-markup layout props m) + empty-stencil))) -(define-markup-command (rotate layout props ang arg) - (number? markup?) +(define-markup-command (combine layout props arg1 arg2) + (markup? markup?) #:category align " address@hidden rotating text address@hidden merging text -Rotate object with @var{ang} degrees around its center. +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: + address@hidden +\\combine @{ a list @} address@hidden example @lilypond[verbatim,quote] \\markup { - default - \\hspace #2 - \\rotate #45 - \\line { - rotated 45° - } + \\fontsize #5 + \\override #'(thickness . 2) + \\combine + \\draw-line #'(0 . 4) + \\arrow-head #Y #DOWN ##f } @end lilypond" - (let* ((stil (interpret-markup layout props arg))) - (ly:stencil-rotate stil ang 0 0))) + (let* ((s1 (interpret-markup layout props arg1)) + (s2 (interpret-markup layout props arg2))) + (ly:stencil-add s1 s2))) -(define-markup-command (whiteout layout props arg) - (markup?) - #:category other +;; +;; TODO: should extract baseline-skip from each argument somehow.. +;; +(define-markup-command (column layout props args) + (markup-list?) + #:category align + #:properties ((baseline-skip)) " address@hidden adding a white background to text address@hidden stacking text in a column -Provide a white background for @var{arg}. +Stack the markups in @var{args} vertically. The property address@hidden determines the space between markups +in @var{args}. @lilypond[verbatim,quote] \\markup { - \\combine - \\filled-box #'(-1 . 10) #'(-3 . 4) #1 - \\whiteout whiteout + \\column { + one + two + three + } } @end lilypond" - (stencil-whiteout (interpret-markup layout props arg))) + (let ((arg-stencils (interpret-markup-list layout props args))) + (stack-lines -1 0.0 baseline-skip + (remove ly:stencil-empty? arg-stencils)))) -(define-markup-command (pad-markup layout props amount arg) - (number? markup?) +(define-markup-command (dir-column layout props args) + (markup-list?) #:category align + #:properties ((direction) + (baseline-skip)) " address@hidden padding text address@hidden putting space around text address@hidden changing direction of text columns -Add space around a markup object. +Make a column of @var{args}, going up or down, depending on the +setting of the @code{direction} layout property. @lilypond[verbatim,quote] \\markup { - \\box { - default + \\override #`(direction . ,UP) { + \\dir-column { + going up + } } - \\hspace #2 - \\box { - \\pad-markup #1 { - padded + \\hspace #1 + \\dir-column { + going down + } + \\hspace #1 + \\override #'(direction . 1) { + \\dir-column { + going up } } } @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 amount) - (interval-widen yext amount)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; space -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (stack-lines (if (number? direction) direction -1) + 0.0 + baseline-skip + (interpret-markup-list layout props args))) -(define-markup-command (strut layout props) - () - #:category other - " address@hidden creating vertical spaces in text +(define (general-column align-dir baseline mols) + "Stack @var{mols} vertically, aligned to @var{align-dir} horizontally." -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) - ))) + (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 ))) -;; todo: fix negative space -(define-markup-command (hspace layout props amount) - (number?) +(define-markup-command (center-column layout props args) + (markup-list?) #:category align - #:properties ((word-space)) + #:properties ((baseline-skip)) " address@hidden creating horizontal spaces in text address@hidden centering a column of text -Create an invisible object taking up horizontal space @var{amount}. +Put @code{args} in a centered column. @lilypond[verbatim,quote] \\markup { - one - \\hspace #2 - two - \\hspace #8 - three -} + \\center-column { + one + two + three + } +} @end lilypond" - (let ((corrected-space (- amount word-space))) - (if (> corrected-space 0) - (ly:make-stencil "" (cons 0 corrected-space) '(0 . 0)) - (ly:make-stencil "" (cons corrected-space corrected-space) '(0 . 0))))) + (general-column CENTER baseline-skip (interpret-markup-list layout props args))) -;; todo: fix negative space -(define-markup-command (vspace layout props amount) - (number?) - #:category align +(define-markup-command (left-column layout props args) + (markup-list?) + #:category align + #:properties ((baseline-skip)) " address@hidden creating vertical spaces in text address@hidden text columns, left-aligned -Create an invisible object taking up vertical space -of @var{amount} multiplied by 3. +Put @code{args} in a left-aligned column. @lilypond[verbatim,quote] \\markup { - \\center-column { + \\left-column { one - \\vspace #2 two - \\vspace #5 three } } @end lilypond" - (let ((amount (* amount 3.0))) - (if (> amount 0) - (ly:make-stencil "" (cons 0 0) (cons 0 amount)) - (ly:make-stencil "" (cons 0 0) (cons amount amount))))) + (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)) + " address@hidden text columns, right-aligned -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; importing graphics. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Put @code{args} in a right-aligned column. -(define-markup-command (stencil layout props stil) - (ly:stencil?) - #:category other address@hidden,quote] +\\markup { + \\right-column { + one + two + three + } +} address@hidden lilypond" + (general-column RIGHT baseline-skip (interpret-markup-list layout props args))) + +(define-markup-command (vcenter layout props arg) + (markup?) + #:category align " address@hidden importing stencils into text address@hidden vertically centering text -Use a stencil as markup. +Align @code{arg} to its address@hidden @lilypond[verbatim,quote] \\markup { - \\stencil #(make-circle-stencil 2 0 #t) + one + \\vcenter + two + three } @end lilypond" - stil) - -(define bbox-regexp - (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)")) + (let* ((mol (interpret-markup layout props arg))) + (ly:stencil-aligned-to mol Y CENTER))) -(define (get-postscript-bbox string) - "Extract the bbox from STRING, or return #f if not present." - (let* - ((match (regexp-exec bbox-regexp string))) +(define-markup-command (center-align layout props arg) + (markup?) + #:category align + " address@hidden horizontally centering text - (if match - (map (lambda (x) - (string->number (match:substring match x))) - (cdr (iota 5))) +Align @code{arg} to its address@hidden - #f))) address@hidden,quote] +\\markup { + \\column { + one + \\center-align + two + three + } +} address@hidden lilypond" + (let* ((mol (interpret-markup layout props arg))) + (ly:stencil-aligned-to mol X CENTER))) -(define-markup-command (epsfile layout props axis size file-name) - (number? number? string?) - #:category graphic +(define-markup-command (right-align layout props arg) + (markup?) + #:category align " address@hidden inlining an Encapsulated PostScript image address@hidden right aligning text -Inline an EPS image. The image is scaled along @var{axis} to address@hidden +Align @var{arg} on its right edge. @lilypond[verbatim,quote] \\markup { - \\general-align #Y #DOWN { - \\epsfile #X #20 #\"context-example.eps\" - \\epsfile #Y #20 #\"context-example.eps\" + \\column { + one + \\right-align + two + three } } @end lilypond" - (if (ly:get-option 'safe) - (interpret-markup layout props "not allowed in safe") - (eps-file->stencil axis size file-name) - )) + (let* ((m (interpret-markup layout props arg))) + (ly:stencil-aligned-to m X RIGHT))) -(define-markup-command (postscript layout props str) - (string?) - #:category graphic +(define-markup-command (left-align layout props arg) + (markup?) + #:category align " address@hidden inserting PostScript directly into text -This inserts @var{str} directly into the output as a PostScript -command string. - address@hidden,quote] -ringsps = #\" - 0.15 setlinewidth - 0.9 0.6 moveto - 0.4 0.6 0.5 0 361 arc - stroke - 1.0 0.6 0.5 0 361 arc - stroke - \" address@hidden left aligning text -rings = \\markup { - \\with-dimensions #'(-0.2 . 1.6) #'(0 . 1.2) - \\postscript #ringsps -} +Align @var{arg} on its left edge. -\\relative c'' { - c2^\\rings - a2_\\rings address@hidden,quote] +\\markup { + \\column { + one + \\left-align + two + three + } } @end lilypond" - ;; FIXME - (ly:make-stencil - (list 'embedded-ps - (format #f " -gsave currentpoint translate -0.1 setlinewidth - ~a -grestore -" - str)) - '(0 . 0) '(0 . 0))) + (let* ((m (interpret-markup layout props arg))) + (ly:stencil-aligned-to m X LEFT))) -(define-markup-command (path layout props thickness commands) (number? list?) - #:category graphic - #:properties ((line-cap-style 'round) - (line-join-style 'round) - (filled #f)) +(define-markup-command (general-align layout props axis dir arg) + (integer? number? markup?) + #:category align " address@hidden paths, drawing address@hidden drawing paths -Draws a path with line thickness @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 address@hidden'butt}, @code{'round}, and @code{'square}. Available -line-join styles are @code{'miter}, @code{'round}, and address@hidden'bevel}. - -The property @code{filled} specifies whether or not the path is -filled with color. address@hidden controlling general text alignment -There are seven commands available to use in the list address@hidden: @code{moveto}, @code{rmoveto}, @code{lineto}, address@hidden, @code{curveto}, @code{rcurveto}, and address@hidden Note that the commands that begin with @emph{r} -are the relative variants of the other three commands. +Align @var{arg} in @var{axis} direction to the @var{dir} side. -The commands @code{moveto}, @code{rmoveto}, @code{lineto}, and address@hidden take 2 arguments; they are the X and Y coordinates -for the destination point. address@hidden,quote] +\\markup { + \\column { + one + \\general-align #X #LEFT + two + three + \\null + one + \\general-align #X #CENTER + two + three + \\null + \\line { + one + \\general-align #Y #UP + two + three + } + \\null + \\line { + one + \\general-align #Y #3.2 + two + three + } + } +} address@hidden lilypond" + (let* ((m (interpret-markup layout props arg))) + (ly:stencil-aligned-to m axis dir))) -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. +(define-markup-command (halign layout props dir arg) + (number? markup?) + #:category align + " address@hidden setting horizontal text alignment -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 address@hidden or @code{rmoveto} to work with the SVG output. +Set horizontal alignment. If @var{dir} is @address@hidden, then it is +left-aligned, while @code{+1} is right. Values in between interpolate +alignment accordingly. @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 + \\column { + one + \\halign #LEFT + two + three + \\null + one + \\halign #CENTER + two + three + \\null + one + \\halign #RIGHT + two + three + \\null + one + \\halign #-5 + two + three + } } @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) (eq? 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) (eq? 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 - ((eq? 3 (length x)) - (set-point (cons (second x) - (third x))) - (drop x 1)) - ;; for curveto - ((eq? 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))) + (let* ((m (interpret-markup layout props arg))) + (ly:stencil-aligned-to m X dir))) -(define-markup-command (score layout props score) - (ly:score?) - #:category music - #:properties ((baseline-skip)) +(define-markup-command (with-dimensions layout props x y arg) + (number-pair? number-pair? markup?) + #:category other " address@hidden inserting music into text address@hidden setting extent of text objects -Inline an image of music. +Set the dimensions of @var{arg} to @var{x} address@hidden@var{y}." + (let* ((m (interpret-markup layout props arg))) + (ly:make-stencil (ly:stencil-expr m) x y))) + +(define-markup-command (pad-around layout props amount arg) + (number? markup?) + #:category align + "Add padding @var{amount} all around @var{arg}. @lilypond[verbatim,quote] \\markup { - \\score { - \\new PianoStaff << - \\new Staff \\relative c' { - \\key f \\major - \\time 3/4 - \\mark \\markup { Allegro } - f2\\p( a4) - c2( a4) - bes2( g'4) - f8( e) e4 r - } - \\new Staff \\relative c { - \\clef bass - \\key f \\major - \\time 3/4 - f8( a c a c a - f c' es c es c) - f,( bes d bes d bes) - f( g bes g bes g) - } - >> - \\layout { - indent = 0.0\\cm - \\context { - \\Score - \\override RehearsalMark - #'break-align-symbols = #'(time-signature key-signature) - \\override RehearsalMark - #'self-alignment-X = #LEFT - } - \\context { - \\Staff - \\override TimeSignature - #'break-align-anchor-alignment = #LEFT - } + \\box { + default + } + \\hspace #2 + \\box { + \\pad-around #0.5 { + padded } } } @end lilypond" - (let ((output (ly:score-embedded-format score layout))) - - (if (ly:music-output? output) - (stack-stencils Y DOWN baseline-skip - (map paper-system-stencil - (vector->list - (ly:paper-score-paper-systems output)))) - (begin - (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?")) - empty-stencil)))) + (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)))) -(define-markup-command (null layout props) - () - #:category other +(define-markup-command (pad-x layout props amount arg) + (number? markup?) + #:category align " address@hidden creating empty text objects address@hidden padding text horizontally -An empty markup with extents of a single point. +Add padding @var{amount} around @var{arg} in the address@hidden @lilypond[verbatim,quote] \\markup { - \\null + \\box { + default + } + \\hspace #4 + \\box { + \\pad-x #2 { + padded + } + } } @end lilypond" - point-stencil) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; basic formatting. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-markup-command (simple layout props str) - (string?) - #:category font - " address@hidden simple text strings + (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) + y))) -A simple text string; @code{\\markup @{ foo @}} is equivalent with address@hidden @{ \\simple #\"foo\" @}}. +(define-markup-command (put-adjacent layout props axis dir arg1 arg2) + (integer? ly:dir? markup? markup?) + #: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))) -Note: for creating standard text markup or defining new markup commands, -the use of @code{\\simple} is unnecessary. +(define-markup-command (transparent layout props arg) + (markup?) + #:category other + "Make @var{arg} transparent. @lilypond[verbatim,quote] \\markup { - \\simple #\"simple\" - \\simple #\"text\" - \\simple #\"strings\" + \\transparent { + invisible text + } } @end lilypond" - (interpret-markup layout props str)) + (let* ((m (interpret-markup layout props arg)) + (x (ly:stencil-extent m X)) + (y (ly:stencil-extent m Y))) + (ly:make-stencil "" x y))) -(define-markup-command (tied-lyric layout props str) - (string?) - #:category music - #:properties ((word-space)) - " address@hidden simple text strings with tie characters - -Like simple-markup, but use tie characters for @q{~} tilde symbols. +(define-markup-command (pad-to-box layout props x-ext y-ext arg) + (number-pair? number-pair? markup?) + #:category align + "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space. @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 { + \\box { + default + } + \\hspace #4 + \\box { + \\pad-to-box #'(0 . 10) #'(0 . 3) { + padded + } + } } @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-public empty-markup - (make-simple-markup "")) + (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)))) -;; 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." - (cond - ((null? text-widths) '()) +(define-markup-command (hcenter-in layout props length arg) + (number? markup?) + #:category align + "Center @var{arg} horizontally within a box of extending address@hidden/2 to the left and right. - ;; 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))))))) address@hidden,verbatim] +\\new StaffGroup << + \\new Staff { + \\set Staff.instrumentName = \\markup { + \\hcenter-in #12 + Oboe + } + c''1 + } + \\new Staff { + \\set Staff.instrumentName = \\markup { + \\hcenter-in #12 + Bassoon + } + \\clef tenor + c'1 + } +>> address@hidden lilypond" + (interpret-markup layout props + (make-pad-to-box-markup + (cons (/ length -2) (/ length 2)) + '(0 . 0) + (make-center-align-markup arg)))) -(define-markup-command (fill-line layout props args) - (markup-list?) +(define-markup-command (rotate layout props ang arg) + (number? markup?) #: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. + " address@hidden rotating text + +Rotate object with @var{ang} degrees around its center. @lilypond[verbatim,quote] \\markup { - \\column { - \\fill-line { - Words evenly spaced across the page - } - \\null - \\fill-line { - \\line { Text markups } - \\line { - \\italic { evenly spaced } - } - \\line { across the page } - } + default + \\hspace #2 + \\rotate #45 + \\line { + rotated 45° } } @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))) + (let* ((stil (interpret-markup layout props arg))) + (ly:stencil-rotate stil ang 0 0))) - (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 (whiteout layout props arg) + (markup?) + #:category other + " address@hidden adding a white background to text -(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}. +Provide a white background for @var{arg}. @lilypond[verbatim,quote] \\markup { - \\line { - one two three - } + \\combine + \\filled-box #'(-1 . 10) #'(-3 . 4) #1 + \\whiteout whiteout } @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)))) + (stencil-whiteout (interpret-markup layout props arg))) -(define-markup-command (concat layout props args) - (markup-list?) +(define-markup-command (pad-markup layout props amount arg) + (number? markup?) #:category align " address@hidden concatenating text address@hidden ligatures in text address@hidden padding text address@hidden putting space around text -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\"}. +Add space around a markup object. @lilypond[verbatim,quote] \\markup { - \\concat { - one - two - three + \\box { + default + } + \\hspace #2 + \\box { + \\pad-markup #1 { + padded + } } } @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))) - (if (and (string? result) (string? arg)) - (cons (string-append arg result) (cdr result-list)) - (cons arg result-list)))) - '() - arg-list)) + (let* + ((stil (interpret-markup layout props arg)) + (xext (ly:stencil-extent stil X)) + (yext (ly:stencil-extent stil Y))) - (interpret-markup layout - (prepend-alist-chain 'word-space 0 props) - (make-line-markup (if (markup-command-list? args) - args - (concat-string-args args))))) + (ly:make-stencil + (ly:stencil-expr stil) + (interval-widen xext amount) + (interval-widen yext amount)))) -(define (wordwrap-stencils stencils - 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))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; space +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-list-command (wordwrap-internal layout props justify args) - (boolean? markup-list?) - #: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)) - justify - word-space - (or line-width - (ly:output-def-lookup layout 'line-width)) - text-direction)) +(define-markup-command (strut layout props) + () + #:category other + " address@hidden creating vertical spaces in text -(define-markup-command (justify layout props args) - (markup-list?) +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) + ))) + +;; todo: fix negative space +(define-markup-command (hspace layout props amount) + (number?) #:category align - #:properties ((baseline-skip) - wordwrap-internal-markup-list) + #:properties ((word-space)) " address@hidden justifying text address@hidden creating horizontal spaces in text -Like @code{\\wordwrap}, but with lines stretched to justify the margins. -Use @code{\\override #'(line-width . @var{X})} to set the line width; address@hidden@tie{}is the number of staff spaces. +Create an invisible object taking up horizontal space @var{amount}. @lilypond[verbatim,quote] \\markup { - \\justify { - 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. - } + one + \\hspace #2 + two + \\hspace #8 + three } @end lilypond" - (stack-lines DOWN 0.0 baseline-skip - (wordwrap-internal-markup-list layout props #t args))) + (let ((corrected-space (- amount word-space))) + (if (> corrected-space 0) + (ly:make-stencil "" (cons 0 corrected-space) '(0 . 0)) + (ly:make-stencil "" (cons corrected-space corrected-space) '(0 . 0))))) -(define-markup-command (wordwrap layout props args) - (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. +;; todo: fix negative space +(define-markup-command (vspace layout props amount) + (number?) + #:category align + " address@hidden creating vertical spaces in text + +Create an invisible object taking up vertical space +of @var{amount} multiplied by 3. @lilypond[verbatim,quote] \\markup { - \\wordwrap { - 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. + \\center-column { + one + \\vspace #2 + two + \\vspace #5 + three } } @end lilypond" - (stack-lines DOWN 0.0 baseline-skip - (wordwrap-internal-markup-list layout props #f args))) + (let ((amount (* amount 3.0))) + (if (> amount 0) + (ly:make-stencil "" (cons 0 0) (cons 0 amount)) + (ly:make-stencil "" (cons 0 0) (cons amount amount))))) -(define-markup-list-command (wordwrap-string-internal layout props justify arg) - (boolean? string?) - #:properties ((line-width) - (word-space) - (text-direction RIGHT)) - "Internal markup list command used to define @code{\\justify-string} and address@hidden" - (let* ((para-strings (regexp-split - (string-regexp-substitute - "\r" "\n" - (string-regexp-substitute "\r\n" "\n" arg)) - "\n[ \t\n]*\n[ \t\n]*")) - (list-para-words (map (lambda (str) - (regexp-split str "[ \t\n]+")) - para-strings)) - (para-lines (map (lambda (words) - (let* ((stencils - (remove ly:stencil-empty? - (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))) -(define-markup-command (wordwrap-string layout props arg) - (string?) - #:category align - #:properties ((baseline-skip) - wordwrap-string-internal-markup-list) - "Wordwrap a string. Paragraphs may be separated with double newlines. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; importing graphics. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (stencil layout props stil) + (ly:stencil?) + #:category other + " address@hidden importing stencils into text + +Use a stencil as markup. @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. + \\stencil #(make-circle-stencil 2 0 #t) +} address@hidden lilypond" + stil) +(define bbox-regexp + (make-regexp "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)")) - Ut enim ad minim veniam, quis nostrud exercitation ullamco - laboris nisi ut aliquip ex ea commodo consequat. +(define (get-postscript-bbox string) + "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))) - Excepteur sint occaecat cupidatat non proident, sunt in culpa - qui officia deserunt mollit anim id est laborum\" -} address@hidden lilypond" - (stack-lines DOWN 0.0 baseline-skip - (wordwrap-string-internal-markup-list layout props #f arg))) + #f))) -(define-markup-command (justify-string layout props arg) - (string?) - #:category align - #:properties ((baseline-skip) - wordwrap-string-internal-markup-list) - "Justify a string. Paragraphs may be separated with double newlines +(define-markup-command (epsfile layout props axis size file-name) + (number? number? string?) + #:category graphic + " address@hidden inlining an Encapsulated PostScript image + +Inline an EPS image. The image is scaled along @var{axis} to address@hidden @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\" + \\general-align #Y #DOWN { + \\epsfile #X #20 #\"context-example.eps\" + \\epsfile #Y #20 #\"context-example.eps\" + } } @end lilypond" - (stack-lines DOWN 0.0 baseline-skip - (wordwrap-string-internal-markup-list layout props #t arg))) + (if (ly:get-option 'safe) + (interpret-markup layout props "not allowed in safe") + (eps-file->stencil axis size file-name) + )) -(define-markup-command (wordwrap-field layout props symbol) - (symbol?) - #:category align - "Wordwrap the data which has been assigned to @var{symbol}. +(define-markup-command (postscript layout props str) + (string?) + #:category graphic + " address@hidden inserting PostScript directly into text +This inserts @var{str} directly into the output as a PostScript +command string. @lilypond[verbatim,quote] -\\header { - title = \"My title\" - 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.\" -} +ringsps = #\" + 0.15 setlinewidth + 0.9 0.6 moveto + 0.4 0.6 0.5 0 361 arc + stroke + 1.0 0.6 0.5 0 361 arc + stroke + \" -\\paper { - bookTitleMarkup = \\markup { - \\column { - \\fill-line { \\fromproperty #'header:title } - \\null - \\wordwrap-field #'header:myText - } - } +rings = \\markup { + \\with-dimensions #'(-0.2 . 1.6) #'(0 . 1.2) + \\postscript #ringsps } -\\markup { - \\null +\\relative c'' { + c2^\\rings + a2_\\rings } @end lilypond" - (let* ((m (chain-assoc-get symbol props))) - (if (string? m) - (wordwrap-string-markup layout props m) - empty-stencil))) + ;; FIXME + (ly:make-stencil + (list 'embedded-ps + (format #f " +gsave currentpoint translate +0.1 setlinewidth + ~a +grestore +" + str)) + '(0 . 0) '(0 . 0))) -(define-markup-command (justify-field layout props symbol) - (symbol?) - #:category align - "Justify the data which has been assigned to @var{symbol}. +(define-markup-command (path layout props thickness commands) (number? list?) + #:category graphic + #:properties ((line-cap-style 'round) + (line-join-style 'round) + (filled #f)) + " address@hidden paths, drawing address@hidden drawing paths +Draws a path with line thickness @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. address@hidden,quote] -\\header { - title = \"My title\" - 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.\" -} +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 address@hidden'butt}, @code{'round}, and @code{'square}. Available +line-join styles are @code{'miter}, @code{'round}, and address@hidden'bevel}. -\\paper { - bookTitleMarkup = \\markup { - \\column { - \\fill-line { \\fromproperty #'header:title } - \\null - \\justify-field #'header:myText - } - } -} +The property @code{filled} specifies whether or not the path is +filled with color. -\\markup { - \\null -} address@hidden lilypond" - (let* ((m (chain-assoc-get symbol props))) - (if (string? m) - (justify-string-markup layout props m) - empty-stencil))) +There are seven commands available to use in the list address@hidden: @code{moveto}, @code{rmoveto}, @code{lineto}, address@hidden, @code{curveto}, @code{rcurveto}, and address@hidden Note that the commands that begin with @emph{r} +are the relative variants of the other three commands. -(define-markup-command (combine layout props arg1 arg2) - (markup? markup?) - #:category align - " address@hidden merging text +The commands @code{moveto}, @code{rmoveto}, @code{lineto}, and address@hidden take 2 arguments; they are the X and Y coordinates +for the destination point. -Print two markups on top of each other. +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. -Note: @code{\\combine} cannot take a list of markups enclosed in -curly braces as an argument; the follow example will not compile: +The @code{closepath} command takes zero arguments and closes the +current subpath in the active path. address@hidden -\\combine @{ a list @} address@hidden example +Note that a sequence of commands @emph{must} begin with a address@hidden or @code{rmoveto} to work with the SVG output. @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 { - \\fontsize #5 - \\override #'(thickness . 2) - \\combine - \\draw-line #'(0 . 4) - \\arrow-head #Y #DOWN ##f + \\path #0.25 #samplePath } @end lilypond" - (let* ((s1 (interpret-markup layout props arg1)) - (s2 (interpret-markup layout props arg2))) - (ly:stencil-add s1 s2))) + (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) (eq? 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) (eq? 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 + ((eq? 3 (length x)) + (set-point (cons (second x) + (third x))) + (drop x 1)) + ;; for curveto + ((eq? 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))) -;; -;; TODO: should extract baseline-skip from each argument somehow.. -;; -(define-markup-command (column layout props args) - (markup-list?) - #:category align + ;; 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-command (score layout props score) + (ly:score?) + #:category music #:properties ((baseline-skip)) " address@hidden stacking text in a column address@hidden inserting music into text -Stack the markups in @var{args} vertically. The property address@hidden determines the space between markups -in @var{args}. - address@hidden,quote] -\\markup { - \\column { - one - two - three - } -} address@hidden lilypond" - (let ((arg-stencils (interpret-markup-list layout props args))) - (stack-lines -1 0.0 baseline-skip - (remove ly:stencil-empty? arg-stencils)))) - -(define-markup-command (dir-column layout props args) - (markup-list?) - #:category align - #:properties ((direction) - (baseline-skip)) - " address@hidden changing direction of text columns - -Make a column of @var{args}, going up or down, depending on the -setting of the @code{direction} layout property. +Inline an image of music. @lilypond[verbatim,quote] \\markup { - \\override #`(direction . ,UP) { - \\dir-column { - going up - } - } - \\hspace #1 - \\dir-column { - going down - } - \\hspace #1 - \\override #'(direction . 1) { - \\dir-column { - going up + \\score { + \\new PianoStaff << + \\new Staff \\relative c' { + \\key f \\major + \\time 3/4 + \\mark \\markup { Allegro } + f2\\p( a4) + c2( a4) + bes2( g'4) + f8( e) e4 r + } + \\new Staff \\relative c { + \\clef bass + \\key f \\major + \\time 3/4 + f8( a c a c a + f c' es c es c) + f,( bes d bes d bes) + f( g bes g bes g) + } + >> + \\layout { + indent = 0.0\\cm + \\context { + \\Score + \\override RehearsalMark + #'break-align-symbols = #'(time-signature key-signature) + \\override RehearsalMark + #'self-alignment-X = #LEFT + } + \\context { + \\Staff + \\override TimeSignature + #'break-align-anchor-alignment = #LEFT + } } } } @end lilypond" - (stack-lines (if (number? direction) direction -1) - 0.0 - baseline-skip - (interpret-markup-list layout props args))) - -(define (general-column align-dir baseline mols) - "Stack @var{mols} vertically, aligned to @var{align-dir} horizontally." + (let ((output (ly:score-embedded-format score layout))) - (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 ))) + (if (ly:music-output? output) + (stack-stencils Y DOWN baseline-skip + (map paper-system-stencil + (vector->list + (ly:paper-score-paper-systems output)))) + (begin + (ly:warning (_"no systems found in \\score markup, does it have a \\layout block?")) + empty-stencil)))) -(define-markup-command (center-column layout props args) - (markup-list?) - #:category align - #:properties ((baseline-skip)) +(define-markup-command (null layout props) + () + #:category other " address@hidden centering a column of text address@hidden creating empty text objects -Put @code{args} in a centered column. +An empty markup with extents of a single point. @lilypond[verbatim,quote] \\markup { - \\center-column { - one - two - three - } + \\null } @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)) - " address@hidden text columns, left-aligned + point-stencil) -Put @code{args} in a left-aligned column. address@hidden,quote] -\\markup { - \\left-column { - one - two - three - } -} address@hidden lilypond" - (general-column LEFT baseline-skip (interpret-markup-list layout props args))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; property +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (right-column layout props args) - (markup-list?) - #:category align - #:properties ((baseline-skip)) - " address@hidden text columns, right-aligned +(define-markup-command (property-recursive layout props symbol) + (symbol?) + #: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) -Put @code{args} in a right-aligned column. +(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. @lilypond[verbatim,quote] -\\markup { - \\right-column { - one - two - three +\\header { + myTitle = \"myTitle\" + title = \\markup { + from + \\italic + \\fromproperty #'header:myTitle } } address@hidden lilypond" - (general-column RIGHT baseline-skip (interpret-markup-list layout props args))) - -(define-markup-command (vcenter layout props arg) - (markup?) - #:category align - " address@hidden vertically centering text - -Align @code{arg} to its address@hidden - address@hidden,quote] \\markup { - one - \\vcenter - two - three + \\null } @end lilypond" - (let* ((mol (interpret-markup layout props arg))) - (ly:stencil-aligned-to mol Y CENTER))) + (let ((m (chain-assoc-get symbol props))) + (if (markup? 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-markup-command (center-align layout props arg) - (markup?) - #:category align - " address@hidden horizontally centering text +(define-markup-command (on-the-fly layout props procedure arg) + (procedure? markup?) + #:category other + "Apply the @var{procedure} markup command to @var{arg}. address@hidden 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)))) -Align @code{arg} to its address@hidden +(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 { - \\column { - one - \\center-align - two - three - } + \\auto-footnote a b + \\override #'(padding . 0.2) + \\auto-footnote c d } address@hidden lilypond" - (let* ((mol (interpret-markup layout props arg))) - (ly:stencil-aligned-to mol X CENTER))) - -(define-markup-command (right-align layout props arg) - (markup?) - #:category align - " address@hidden right aligning text address@hidden 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)) -Align @var{arg} on its right edge. +(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 { - \\column { - one - \\right-align - two - three - } + \\auto-footnote a b + \\override #'(padding . 0.2) + \\auto-footnote c d } address@hidden lilypond" - (let* ((m (interpret-markup layout props arg))) - (ly:stencil-aligned-to m X RIGHT))) address@hidden lilypond +The footnote will be annotated automatically." + (let* ((markup-stencil (interpret-markup layout props mkup)) + (auto-numbering (ly:output-def-lookup layout + 'footnote-auto-numbering)) + (footnote-hash (gensym "footnote")) + (stencil-seed 0) + (gauge-stencil (if auto-numbering + (interpret-markup + layout + props + ((ly:output-def-lookup + layout + 'footnote-numbering-function) + stencil-seed)) + empty-stencil)) + (x-ext (if auto-numbering + (ly:stencil-extent gauge-stencil X) + '(0 . 0))) + (y-ext (if auto-numbering + (ly:stencil-extent gauge-stencil Y) + '(0 . 0))) + (footnote-number + (if auto-numbering + `(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 (left-align layout props arg) - (markup?) - #:category align +(define-markup-command (override layout props new-prop arg) + (pair? markup?) + #:category other " address@hidden left aligning text address@hidden overriding properties within text markup -Align @var{arg} on its left edge. +Add the argument @var{new-prop} to the property list. Properties +may be any property supported by @rinternals{font-interface}, address@hidden and address@hidden @lilypond[verbatim,quote] \\markup { - \\column { - one - \\left-align - two - three + \\line { + \\column { + default + baseline-skip + } + \\hspace #2 + \\override #'(baseline-skip . 4) { + \\column { + increased + baseline-skip + } + } } } @end lilypond" - (let* ((m (interpret-markup layout props arg))) - (ly:stencil-aligned-to m X LEFT))) + (interpret-markup layout (cons (list new-prop) props) arg)) -(define-markup-command (general-align layout props axis dir arg) - (integer? number? markup?) - #:category align - " address@hidden controlling general text alignment +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; files +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -Align @var{arg} in @var{axis} direction to the @var{dir} side. +(define-markup-command (verbatim-file layout props name) + (string?) + #:category other + "Read the contents of file @var{name}, and include it verbatim. @lilypond[verbatim,quote] \\markup { - \\column { - one - \\general-align #X #LEFT - two - three - \\null - one - \\general-align #X #CENTER - two - three - \\null - \\line { - one - \\general-align #Y #UP - two - three - } - \\null - \\line { - one - \\general-align #Y #3.2 - two - three - } - } + \\verbatim-file #\"simple.ly\" } @end lilypond" - (let* ((m (interpret-markup layout props arg))) - (ly:stencil-aligned-to m axis dir))) + (interpret-markup layout props + (if (ly:get-option 'safe) + "verbatim-file disabled in safe mode" + (let* ((str (ly:gulp-file name)) + (lines (string-split str #\nl))) + (make-typewriter-markup + (make-column-markup lines)))))) -(define-markup-command (halign layout props dir arg) - (number? markup?) - #:category align - " address@hidden setting horizontal text alignment +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; fonts. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -Set horizontal alignment. If @var{dir} is @address@hidden, then it is -left-aligned, while @code{+1} is right. Values in between interpolate -alignment accordingly. + +(define-markup-command (smaller layout props arg) + (markup?) + #:category font + "Decrease the font size relative to the current setting. @lilypond[verbatim,quote] \\markup { - \\column { - one - \\halign #LEFT - two - three - \\null - one - \\halign #CENTER - two - three - \\null - one - \\halign #RIGHT - two - three - \\null - one - \\halign #-5 - two - three + \\fontsize #3.5 { + some large text + \\hspace #2 + \\smaller { + a bit smaller + } + \\hspace #2 + more large text } } @end lilypond" - (let* ((m (interpret-markup layout props arg))) - (ly:stencil-aligned-to m X dir))) - -(define-markup-command (with-dimensions layout props x y arg) - (number-pair? number-pair? markup?) - #:category other - " address@hidden setting extent of text objects - -Set the dimensions of @var{arg} to @var{x} address@hidden@var{y}." - (let* ((m (interpret-markup layout props arg))) - (ly:make-stencil (ly:stencil-expr m) x y))) + (interpret-markup layout props + `(,fontsize-markup -1 ,arg))) -(define-markup-command (pad-around layout props amount arg) - (number? markup?) - #:category align - "Add padding @var{amount} all around @var{arg}. +(define-markup-command (larger layout props arg) + (markup?) + #:category font + "Increase the font size relative to the current setting. @lilypond[verbatim,quote] \\markup { - \\box { - default - } + default \\hspace #2 - \\box { - \\pad-around #0.5 { - padded - } - } + \\larger + larger } @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)))) - -(define-markup-command (pad-x layout props amount arg) - (number? markup?) - #:category align - " address@hidden padding text horizontally + (interpret-markup layout props + `(,fontsize-markup 1 ,arg))) -Add padding @var{amount} around @var{arg} in the address@hidden +(define-markup-command (finger layout props arg) + (markup?) + #:category font + "Set @var{arg} as small numbers. @lilypond[verbatim,quote] \\markup { - \\box { - default - } - \\hspace #4 - \\box { - \\pad-x #2 { - padded - } + \\finger { + 1 2 3 4 5 } } @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) - y))) - -(define-markup-command (put-adjacent layout props axis dir arg1 arg2) - (integer? ly:dir? markup? markup?) - #: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))) + (interpret-markup layout + (cons '((font-size . -5) (font-encoding . fetaText)) props) + arg)) -(define-markup-command (transparent layout props arg) - (markup?) - #:category other - "Make @var{arg} transparent. +(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}. +Adjusts @code{baseline-skip} and @code{word-space} accordingly. @lilypond[verbatim,quote] \\markup { - \\transparent { - invisible text - } + default text font size + \\hspace #2 + \\abs-fontsize #16 { text font size 16 } + \\hspace #2 + \\abs-fontsize #12 { text font size 12 } } @end lilypond" - (let* ((m (interpret-markup layout props arg)) - (x (ly:stencil-extent m X)) - (y (ly:stencil-extent m Y))) - (ly:make-stencil "" x y))) + (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-markup-command (pad-to-box layout props x-ext y-ext arg) - (number-pair? number-pair? markup?) - #:category align - "Make @var{arg} take at least @var{x-ext}, @var{y-ext} space. +(define-markup-command (fontsize layout props increment arg) + (number? markup?) + #: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 { - \\box { - default - } - \\hspace #4 - \\box { - \\pad-to-box #'(0 . 10) #'(0 . 3) { - padded - } - } + default + \\hspace #2 + \\fontsize #-1.5 + smaller } @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)))) + (interpret-markup + layout + (cons + `((baseline-skip . ,(* baseline-skip (magstep increment))) + (word-space . ,(* word-space (magstep increment))) + (font-size . ,(+ font-size increment))) + props) + arg)) -(define-markup-command (hcenter-in layout props length arg) +(define-markup-command (magnify layout props sz arg) (number? markup?) - #:category align - "Center @var{arg} horizontally within a box of extending address@hidden/2 to the left and right. + #:category font + " address@hidden magnifying text address@hidden,verbatim] -\\new StaffGroup << - \\new Staff { - \\set Staff.instrumentName = \\markup { - \\hcenter-in #12 - Oboe - } - c''1 - } - \\new Staff { - \\set Staff.instrumentName = \\markup { - \\hcenter-in #12 - Bassoon - } - \\clef tenor - c'1 +Set the font magnification for its argument. In the following +example, the address@hidden is 10% larger: + address@hidden +A \\magnify #1.1 @{ A @} A address@hidden example + +Note: Magnification only works if a font name is explicitly selected. +Use @code{\\fontsize} otherwise. + address@hidden,quote] +\\markup { + default + \\hspace #2 + \\magnify #1.5 { + 50% larger } ->> +} @end lilypond" - (interpret-markup layout props - (make-pad-to-box-markup - (cons (/ length -2) (/ length 2)) - '(0 . 0) - (make-center-align-markup arg)))) + (interpret-markup + layout + (prepend-alist-chain 'font-size (magnification->font-size sz) props) + arg)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; property -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(define-markup-command (bold layout props arg) + (markup?) + #:category font + "Switch to bold font-series. -(define-markup-command (property-recursive layout props symbol) - (symbol?) - #: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) address@hidden,quote] +\\markup { + default + \\hspace #2 + \\bold + bold +} address@hidden lilypond" + (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg)) -(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. +(define-markup-command (sans layout props arg) + (markup?) + #:category font + "Switch to the sans serif font family. @lilypond[verbatim,quote] -\\header { - myTitle = \"myTitle\" - title = \\markup { - from - \\italic - \\fromproperty #'header:myTitle +\\markup { + default + \\hspace #2 + \\sans { + sans serif } } address@hidden lilypond" + (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg)) + +(define-markup-command (number layout props arg) + (markup?) + #:category font + "Set font family to @code{number}, which yields the font used for +time signatures and fingerings. This font contains numbers and +some punctuation; it has no letters. + address@hidden,quote] \\markup { - \\null + \\number { + 0 1 2 3 4 5 6 7 8 9 . , + } } @end lilypond" - (let ((m (chain-assoc-get symbol props))) - (if (markup? 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-markup-command (on-the-fly layout props procedure arg) - (procedure? markup?) - #:category other - "Apply the @var{procedure} markup command to @var{arg}. address@hidden 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)))) + (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText 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}. +(define-markup-command (roman layout props arg) + (markup?) + #:category font + "Set font family to @code{roman}. @lilypond[verbatim,quote] \\markup { - \\auto-footnote a b - \\override #'(padding . 0.2) - \\auto-footnote c d + \\sans \\bold { + sans serif, bold + \\hspace #2 + \\roman { + text in roman font family + } + \\hspace #2 + return to sans + } } address@hidden 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)) address@hidden lilypond" + (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg)) -(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}. +(define-markup-command (huge layout props arg) + (markup?) + #:category font + "Set font size to +2. @lilypond[verbatim,quote] \\markup { - \\auto-footnote a b - \\override #'(padding . 0.2) - \\auto-footnote c d -} address@hidden lilypond -The footnote will be annotated automatically." - (let* ((markup-stencil (interpret-markup layout props mkup)) - (auto-numbering (ly:output-def-lookup layout - 'footnote-auto-numbering)) - (footnote-hash (gensym "footnote")) - (stencil-seed 0) - (gauge-stencil (if auto-numbering - (interpret-markup - layout - props - ((ly:output-def-lookup - layout - 'footnote-numbering-function) - stencil-seed)) - empty-stencil)) - (x-ext (if auto-numbering - (ly:stencil-extent gauge-stencil X) - '(0 . 0))) - (y-ext (if auto-numbering - (ly:stencil-extent gauge-stencil Y) - '(0 . 0))) - (footnote-number - (if auto-numbering - `(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?) - #:category other - " address@hidden overriding properties within text markup - -Add the argument @var{new-prop} to the property list. Properties -may be any property supported by @rinternals{font-interface}, address@hidden and address@hidden - address@hidden,quote] -\\markup { - \\line { - \\column { - default - baseline-skip - } - \\hspace #2 - \\override #'(baseline-skip . 4) { - \\column { - increased - baseline-skip - } - } - } + default + \\hspace #2 + \\huge + huge } @end lilypond" - (interpret-markup layout (cons (list new-prop) props) arg)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; files -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg)) -(define-markup-command (verbatim-file layout props name) - (string?) - #:category other - "Read the contents of file @var{name}, and include it verbatim. +(define-markup-command (large layout props arg) + (markup?) + #:category font + "Set font size to +1. @lilypond[verbatim,quote] \\markup { - \\verbatim-file #\"simple.ly\" + default + \\hspace #2 + \\large + large } @end lilypond" - (interpret-markup layout props - (if (ly:get-option 'safe) - "verbatim-file disabled in safe mode" - (let* ((str (ly:gulp-file name)) - (lines (string-split str #\nl))) - (make-typewriter-markup - (make-column-markup lines)))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; fonts. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - + (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg)) -(define-markup-command (smaller layout props arg) +(define-markup-command (normalsize layout props arg) (markup?) #:category font - "Decrease the font size relative to the current setting. + "Set font size to default. @lilypond[verbatim,quote] \\markup { - \\fontsize #3.5 { - some large text + \\teeny { + this is very small \\hspace #2 - \\smaller { - a bit smaller + \\normalsize { + normal size } \\hspace #2 - more large text + teeny again } } @end lilypond" - (interpret-markup layout props - `(,fontsize-markup -1 ,arg))) + (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg)) -(define-markup-command (larger layout props arg) +(define-markup-command (small layout props arg) (markup?) #:category font - "Increase the font size relative to the current setting. + "Set font size to -1. @lilypond[verbatim,quote] \\markup { default \\hspace #2 - \\larger - larger + \\small + small } @end lilypond" - (interpret-markup layout props - `(,fontsize-markup 1 ,arg))) + (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg)) -(define-markup-command (finger layout props arg) +(define-markup-command (tiny layout props arg) (markup?) #:category font - "Set @var{arg} as small numbers. - address@hidden,quote] -\\markup { - \\finger { - 1 2 3 4 5 - } -} address@hidden lilypond" - (interpret-markup layout - (cons '((font-size . -5) (font-encoding . fetaText)) props) - arg)) - -(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}. -Adjusts @code{baseline-skip} and @code{word-space} accordingly. + "Set font size to -2. @lilypond[verbatim,quote] \\markup { - default text font size - \\hspace #2 - \\abs-fontsize #16 { text font size 16 } + default \\hspace #2 - \\abs-fontsize #12 { text font size 12 } + \\tiny + tiny } @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))) + (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg)) -(define-markup-command (fontsize layout props increment arg) - (number? markup?) +(define-markup-command (teeny layout props arg) + (markup?) #:category font - #:properties ((font-size 0) - (word-space 1) - (baseline-skip 2)) - "Add @var{increment} to the font-size. Adjusts @code{baseline-skip} -accordingly. + "Set font size to -3. @lilypond[verbatim,quote] \\markup { default \\hspace #2 - \\fontsize #-1.5 - smaller + \\teeny + teeny } @end lilypond" - (interpret-markup - layout - (cons - `((baseline-skip . ,(* baseline-skip (magstep increment))) - (word-space . ,(* word-space (magstep increment))) - (font-size . ,(+ font-size increment))) - props) - arg)) + (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg)) -(define-markup-command (magnify layout props sz arg) - (number? markup?) +(define-markup-command (fontCaps layout props arg) + (markup?) #:category font - " address@hidden magnifying text + "Set @code{font-shape} to @code{caps} -Set the font magnification for its argument. In the following -example, the address@hidden is 10% larger: +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)) address@hidden -A \\magnify #1.1 @{ A @} A address@hidden example +;; Poor man's caps -Note: Magnification only works if a font name is explicitly selected. -Use @code{\\fontsize} otherwise. +;;(define-markup-command (smallCaps layout props arg) +;; (markup?) +;; #:category font +;; "Emit @var{arg} as small caps. +;; +;;Note: @code{\\smallCaps} does not support accented characters. +;; +;;@lilypond[verbatim,quote] +;;\\markup { +;; default +;; \\hspace #2 +;; \\smallCaps { +;; Text in small caps +;; } +;;} +;;@end lilypond" +;; (define (char-list->markup chars lower) +;; (let ((final-string (string-upcase (reverse-list->string chars)))) +;; (if lower +;; (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))))))) +;; (interpret-markup layout props +;; (if (string? arg) +;; (make-small-caps (string->list arg) (list) #f (list)) +;; arg))) + +(define-markup-command (caps layout props arg) + (markup?) + #:category font + "Copy of the @code{\\smallCaps} command. @lilypond[verbatim,quote] \\markup { default \\hspace #2 - \\magnify #1.5 { - 50% larger + \\caps { + Text in small caps } } @end lilypond" - (interpret-markup - layout - (prepend-alist-chain 'font-size (magnification->font-size sz) props) - arg)) + (interpret-markup layout props (make-smallCaps-markup arg))) -(define-markup-command (bold layout props arg) - (markup?) - #:category font - "Switch to bold font-series. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\bold - bold -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-series 'bold props) arg)) - -(define-markup-command (sans layout props arg) - (markup?) - #:category font - "Switch to the sans serif font family. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\sans { - sans serif - } -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-family 'sans props) arg)) - -(define-markup-command (number layout props arg) - (markup?) - #:category font - "Set font family to @code{number}, which yields the font used for -time signatures and fingerings. This font contains numbers and -some punctuation; it has no letters. - address@hidden,quote] -\\markup { - \\number { - 0 1 2 3 4 5 6 7 8 9 . , - } -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-encoding 'fetaText props) arg)) - -(define-markup-command (roman layout props arg) - (markup?) - #:category font - "Set font family to @code{roman}. - address@hidden,quote] -\\markup { - \\sans \\bold { - sans serif, bold - \\hspace #2 - \\roman { - text in roman font family - } - \\hspace #2 - return to sans - } -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-family 'roman props) arg)) - -(define-markup-command (huge layout props arg) - (markup?) - #:category font - "Set font size to +2. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\huge - huge -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size 2 props) arg)) - -(define-markup-command (large layout props arg) - (markup?) - #:category font - "Set font size to +1. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\large - large -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size 1 props) arg)) - -(define-markup-command (normalsize layout props arg) - (markup?) - #:category font - "Set font size to default. - address@hidden,quote] -\\markup { - \\teeny { - this is very small - \\hspace #2 - \\normalsize { - normal size - } - \\hspace #2 - teeny again - } -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size 0 props) arg)) - -(define-markup-command (small layout props arg) - (markup?) - #:category font - "Set font size to -1. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\small - small -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size -1 props) arg)) - -(define-markup-command (tiny layout props arg) - (markup?) - #:category font - "Set font size to -2. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\tiny - tiny -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size -2 props) arg)) - -(define-markup-command (teeny layout props arg) - (markup?) - #:category font - "Set font size to -3. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\teeny - teeny -} address@hidden lilypond" - (interpret-markup layout (prepend-alist-chain 'font-size -3 props) arg)) - -(define-markup-command (fontCaps layout props arg) - (markup?) - #: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-markup-command (smallCaps layout props arg) - (markup?) - #:category font - "Emit @var{arg} as small caps. - -Note: @code{\\smallCaps} does not support accented characters. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\smallCaps { - Text in small caps - } -} address@hidden lilypond" - (define (char-list->markup chars lower) - (let ((final-string (string-upcase (reverse-list->string chars)))) - (if lower - (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))))))) - (interpret-markup layout props - (if (string? arg) - (make-small-caps (string->list arg) (list) #f (list)) - arg))) - -(define-markup-command (caps layout props arg) - (markup?) - #:category font - "Copy of the @code{\\smallCaps} command. - address@hidden,quote] -\\markup { - default - \\hspace #2 - \\caps { - Text in small caps - } -} address@hidden lilypond" - (interpret-markup layout props (make-smallCaps-markup arg))) - -(define-markup-command (dynamic layout props arg) +(define-markup-command (dynamic layout props arg) (markup?) #:category font "Use the dynamic font. This font only contains @b{s}, @b{f}, @b{m}, @@ -2624,7 +2192,7 @@ the possible glyphs. (ly:warning (_ "Cannot find glyph ~a") glyph-name)) glyph)) - +;(start-repl) (define-markup-command (doublesharp layout props) () #:category music @@ -2758,39 +2326,127 @@ Draw @var{arg} in color specified by @var{color}. (ly:stencil-extent stil X) (ly:stencil-extent stil Y)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; glyphs -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (arrow-head layout props axis dir filled) - (integer? ly:dir? boolean?) - #:category graphic - "Produce an arrow head in specified direction and axis. -Use the filled head if @var{filled} is specified. +(define-markup-command (tied-lyric layout props str) + (string?) + #:category music + #:properties ((word-space)) + " address@hidden simple text strings with tie characters + +Like simple-markup, but use tie characters for @q{~} tilde symbols. + @lilypond[verbatim,quote] -\\markup { - \\fontsize #5 { - \\general-align #Y #DOWN { - \\arrow-head #Y #UP ##t - \\arrow-head #Y #DOWN ##f - \\hspace #2 - \\arrow-head #X #RIGHT ##f - \\arrow-head #X #LEFT ##f - } - } +\\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" - (let* - ((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)) - name))) + (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-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." + (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))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; glyphs +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (arrow-head layout props axis dir filled) + (integer? ly:dir? boolean?) + #:category graphic + "Produce an arrow head in specified direction and axis. +Use the filled head if @var{filled} is specified. address@hidden,quote] +\\markup { + \\fontsize #5 { + \\general-align #Y #DOWN { + \\arrow-head #Y #UP ##t + \\arrow-head #Y #DOWN ##f + \\hspace #2 + \\arrow-head #X #RIGHT ##f + \\arrow-head #X #LEFT ##f + } + } +} address@hidden lilypond" + (let* + ((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)) + name))) (define-markup-command (lookup layout props glyph-name) (string?) @@ -3215,507 +2871,867 @@ the @var{duration} for the note head type and augmentation dots. For example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with a shortened down stem. address@hidden,quote] -\\markup { - \\override #'(style . cross) { - \\note #\"4..\" #UP - } - \\hspace #2 - \\note #\"breve\" #0 -} address@hidden lilypond" - (let ((parsed (parse-simple-duration duration))) - (note-by-number-markup layout props (car parsed) (cadr parsed) dir))) address@hidden,quote] +\\markup { + \\override #'(style . cross) { + \\note #\"4..\" #UP + } + \\hspace #2 + \\note #\"breve\" #0 +} address@hidden lilypond" + (let ((parsed (parse-simple-duration duration))) + (note-by-number-markup layout props (car parsed) (cadr parsed) dir))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; translating. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (lower layout props amount arg) + (number? markup?) + #:category align + " address@hidden lowering text + +Lower @var{arg} by the distance @var{amount}. +A negative @var{amount} indicates raising; see also @code{\\raise}. + address@hidden,quote] +\\markup { + one + \\lower #3 + two + three +} address@hidden lilypond" + (ly:stencil-translate-axis (interpret-markup layout props arg) + (- amount) Y)) + +(define-markup-command (translate-scaled layout props offset arg) + (number-pair? markup?) + #:category align + #:properties ((font-size 0)) + " address@hidden translating text address@hidden scaling text + +Translate @var{arg} by @var{offset}, scaling the offset by the address@hidden + address@hidden,quote] +\\markup { + \\fontsize #5 { + * \\translate #'(2 . 3) translate + \\hspace #2 + * \\translate-scaled #'(2 . 3) translate-scaled + } +} address@hidden lilypond" + (let* ((factor (magstep font-size)) + (scaled (cons (* factor (car offset)) + (* factor (cdr offset))))) + (ly:stencil-translate (interpret-markup layout props arg) + scaled))) + +(define-markup-command (raise layout props amount arg) + (number? markup?) + #:category align + " address@hidden raising text + +Raise @var{arg} by the distance @var{amount}. +A negative @var{amount} indicates lowering, see also @code{\\lower}. + +The argument to @code{\\raise} is the vertical displacement amount, +measured in (global) staff spaces. @code{\\raise} and @code{\\super} +raise objects in relation to their surrounding markups. + +If the text object itself is positioned above or below the staff, then address@hidden cannot be used to move it, since the mechanism that +positions it next to the staff cancels any shift made with address@hidden For vertical positioning, use the @code{padding} +and/or @code{extra-offset} properties. + address@hidden,quote] +\\markup { + C + \\small + \\bold + \\raise #1.0 + 9/7+ +} address@hidden lilypond" + (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y)) + +(define-markup-command (fraction layout props arg1 arg2) + (markup? markup?) + #:category other + #:properties ((font-size 0)) + " address@hidden creating text fractions + +Make a fraction of two markups. address@hidden,quote] +\\markup { + π ≈ + \\fraction 355 113 +} address@hidden lilypond" + (let* ((m1 (interpret-markup layout props arg1)) + (m2 (interpret-markup layout props arg2)) + (factor (magstep font-size)) + (boxdimen (cons (* factor -0.05) (* factor 0.05))) + (padding (* factor 0.2)) + (baseline (* factor 0.6)) + (offset (* factor 0.75))) + (set! m1 (ly:stencil-aligned-to m1 X CENTER)) + (set! m2 (ly:stencil-aligned-to m2 X CENTER)) + (let* ((x1 (ly:stencil-extent m1 X)) + (x2 (ly:stencil-extent m2 X)) + (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0)) + ;; 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)) + (set! stack + (ly:stencil-aligned-to stack X LEFT)) + ;; should have EX dimension + ;; empirical anyway + (ly:stencil-translate-axis stack offset Y)))) + +(define-markup-command (normal-size-super layout props arg) + (markup?) + #:category font + #:properties ((baseline-skip)) + " address@hidden setting superscript in standard font size + +Set @var{arg} in superscript with a normal font size. + address@hidden,quote] +\\markup { + default + \\normal-size-super { + superscript in standard size + } +} address@hidden lilypond" + (ly:stencil-translate-axis + (interpret-markup layout props arg) + (* 0.5 baseline-skip) Y)) + +(define-markup-command (super layout props arg) + (markup?) + #:category font + #:properties ((font-size 0) + (baseline-skip)) + " address@hidden superscript text + +Set @var{arg} in superscript. + address@hidden,quote] +\\markup { + E = + \\concat { + mc + \\super + 2 + } +} address@hidden lilypond" + (ly:stencil-translate-axis + (interpret-markup + layout + (cons `((font-size . ,(- font-size 3))) props) + arg) + (* 0.5 baseline-skip) + Y)) + +(define-markup-command (translate layout props offset arg) + (number-pair? markup?) + #:category align + " address@hidden translating text + +Translate @var{arg} relative to its surroundings. @var{offset} +is a pair of numbers representing the displacement in the X and Y axis. + address@hidden,quote] +\\markup { + * + \\translate #'(2 . 3) + \\line { translated two spaces right, three up } +} address@hidden lilypond" + (ly:stencil-translate (interpret-markup layout props arg) + offset)) + +(define-markup-command (sub layout props arg) + (markup?) + #:category font + #:properties ((font-size 0) + (baseline-skip)) + " address@hidden subscript text + +Set @var{arg} in subscript. + address@hidden,quote] +\\markup { + \\concat { + H + \\sub { + 2 + } + O + } +} address@hidden lilypond" + (ly:stencil-translate-axis + (interpret-markup + layout + (cons `((font-size . ,(- font-size 3))) props) + arg) + (* -0.5 baseline-skip) + Y)) + +(define-markup-command (normal-size-sub layout props arg) + (markup?) + #:category font + #:properties ((baseline-skip)) + " address@hidden setting subscript in standard font size + +Set @var{arg} in subscript with a normal font size. + address@hidden,quote] +\\markup { + default + \\normal-size-sub { + subscript in standard size + } +} address@hidden lilypond" + (ly:stencil-translate-axis + (interpret-markup layout props arg) + (* -0.5 baseline-skip) + Y)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; brackets. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (hbracket layout props arg) + (markup?) + #:category graphic + " address@hidden placing horizontal brackets around text + +Draw horizontal brackets around @var{arg}. + address@hidden,quote] +\\markup { + \\hbracket { + \\line { + one two three + } + } +} address@hidden lilypond" + (let ((th 0.1) ;; todo: take from GROB. + (m (interpret-markup layout props arg))) + (bracketify-stencil m X th (* 2.5 th) th))) + +(define-markup-command (bracket layout props arg) + (markup?) + #:category graphic + " address@hidden placing vertical brackets around text + +Draw vertical brackets around @var{arg}. + address@hidden,quote] +\\markup { + \\bracket { + \\note #\"2.\" #UP + } +} address@hidden lilypond" + (let ((th 0.1) ;; todo: take from GROB. + (m (interpret-markup layout props arg))) + (bracketify-stencil m Y th (* 2.5 th) th))) + +(define-markup-command (parenthesize layout props arg) + (markup?) + #:category graphic + #:properties ((angularity 0) + (padding) + (size 1) + (thickness 1) + (width 0.25)) + " address@hidden placing parentheses around text + +Draw parentheses around @var{arg}. This is useful for parenthesizing +a column containing several lines of text. + address@hidden,quote] +\\markup { + \\line { + \\parenthesize { + \\column { + foo + bar + } + } + \\override #'(angularity . 2) { + \\parenthesize { + \\column { + bah + baz + } + } + } + } +} address@hidden lilypond" + (let* ((markup (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 + markup half-thickness scaled-width angularity padding))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; translating. +;; Delayed markup evaluation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (lower layout props amount arg) - (number? markup?) - #:category align +(define-markup-command (page-ref layout props label gauge default) + (symbol? markup? markup?) + #:category other " address@hidden lowering text address@hidden referencing page numbers in text -Lower @var{arg} by the distance @var{amount}. -A negative @var{amount} indicates raising; see also @code{\\raise}. +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." + (let* ((gauge-stencil (interpret-markup layout props gauge)) + (x-ext (ly:stencil-extent gauge-stencil X)) + (y-ext (ly:stencil-extent gauge-stencil Y))) + (ly:make-stencil + `(delay-stencil-evaluation + ,(delay (ly:stencil-expr + (let* ((table (ly:output-def-lookup layout 'label-page-table)) + (page-number (if (list? table) + (assoc-get label table) + #f)) + (page-markup (if page-number (format #f "~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))))))) + x-ext + y-ext))) address@hidden,quote] -\\markup { - one - \\lower #3 - two - three -} address@hidden lilypond" - (ly:stencil-translate-axis (interpret-markup layout props arg) - (- amount) Y)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; scaling +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (translate-scaled layout props offset arg) +(define-markup-command (scale layout props factor-pair arg) (number-pair? markup?) - #:category align - #:properties ((font-size 0)) + #:category graphic " address@hidden translating text address@hidden scaling text address@hidden scaling markup address@hidden mirroring markup -Translate @var{arg} by @var{offset}, scaling the offset by the address@hidden +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 { - \\fontsize #5 { - * \\translate #'(2 . 3) translate - \\hspace #2 - * \\translate-scaled #'(2 . 3) translate-scaled + \\line { + \\scale #'(2 . 1) + stretched + \\scale #'(1 . -1) + mirrored } } @end lilypond" - (let* ((factor (magstep font-size)) - (scaled (cons (* factor (car offset)) - (* factor (cdr offset))))) - (ly:stencil-translate (interpret-markup layout props arg) - scaled))) - -(define-markup-command (raise layout props amount arg) - (number? markup?) - #:category align - " address@hidden raising text - -Raise @var{arg} by the distance @var{amount}. -A negative @var{amount} indicates lowering, see also @code{\\lower}. + (let ((stil (interpret-markup layout props arg)) + (sx (car factor-pair)) + (sy (cdr factor-pair))) + (ly:stencil-scale stil sx sy))) -The argument to @code{\\raise} is the vertical displacement amount, -measured in (global) staff spaces. @code{\\raise} and @code{\\super} -raise objects in relation to their surrounding markups. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Repeating +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -If the text object itself is positioned above or below the staff, then address@hidden cannot be used to move it, since the mechanism that -positions it next to the staff cancels any shift made with address@hidden For vertical positioning, use the @code{padding} -and/or @code{extra-offset} properties. +(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}. address@hidden,quote] -\\markup { - C - \\small - \\bold - \\raise #1.0 - 9/7+ address@hidden, quote] +\\markup \\column { + \"Horizontally repeated :\" + \\pattern #7 #X #2 \\flat + \\null + \"Vertically repeated :\" + \\pattern #3 #Y #0.5 \\flat } @end lilypond" - (ly:stencil-translate-axis (interpret-markup layout props arg) amount Y)) + (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 (fraction layout props arg1 arg2) - (markup? markup?) - #:category other - #:properties ((font-size 0)) +(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)) " address@hidden creating text fractions +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. -Make a fraction of two markups. address@hidden,quote] -\\markup { - π ≈ - \\fraction 355 113 address@hidden, 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* ((m1 (interpret-markup layout props arg1)) - (m2 (interpret-markup layout props arg2)) - (factor (magstep font-size)) - (boxdimen (cons (* factor -0.05) (* factor 0.05))) - (padding (* factor 0.2)) - (baseline (* factor 0.6)) - (offset (* factor 0.75))) - (set! m1 (ly:stencil-aligned-to m1 X CENTER)) - (set! m2 (ly:stencil-aligned-to m2 X CENTER)) - (let* ((x1 (ly:stencil-extent m1 X)) - (x2 (ly:stencil-extent m2 X)) - (line (ly:round-filled-box (interval-union x1 x2) boxdimen 0.0)) - ;; 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)) - (set! stack - (ly:stencil-aligned-to stack X LEFT)) - ;; should have EX dimension - ;; empirical anyway - (ly:stencil-translate-axis stack offset Y)))) + (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)))) -(define-markup-command (normal-size-super layout props arg) - (markup?) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Replacements +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (replace layout props replacements arg) + (list? markup?) #:category font - #:properties ((baseline-skip)) " address@hidden setting superscript in standard font size - -Set @var{arg} in superscript with a normal font size. +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. address@hidden,quote] -\\markup { - default - \\normal-size-super { - superscript in standard size - } -} address@hidden, quote] +\\markup \\replace #'((\"thx\" . \"Thanks!\")) thx @end lilypond" - (ly:stencil-translate-axis - (interpret-markup layout props arg) - (* 0.5 baseline-skip) Y)) + (interpret-markup + layout + (internal-add-text-replacements + props + replacements) + (markup arg))) -(define-markup-command (super layout props arg) - (markup?) - #:category font - #:properties ((font-size 0) - (baseline-skip)) - " address@hidden superscript text +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; geometric shapes +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -Set @var{arg} in superscript. +(define-markup-command (draw-line layout props dest) + (number-pair?) + #:category graphic + #:properties ((thickness 1)) + " address@hidden drawing lines within text +A simple line. @lilypond[verbatim,quote] \\markup { - E = - \\concat { - mc - \\super - 2 - } + \\draw-line #'(4 . 4) + \\override #'(thickness . 5) + \\draw-line #'(-3 . 0) } @end lilypond" - (ly:stencil-translate-axis - (interpret-markup - layout - (cons `((font-size . ,(- font-size 3))) props) - arg) - (* 0.5 baseline-skip) - Y)) + (let ((th (* (ly:output-def-lookup layout 'line-thickness) + thickness)) + (x (car dest)) + (y (cdr dest))) + (make-line-stencil th 0 0 x y))) -(define-markup-command (translate layout props offset arg) - (number-pair? markup?) - #:category align +(define-markup-command (draw-hline layout props) + () + #:category graphic + #:properties ((draw-line-markup) + (line-width) + (span-factor 1)) " address@hidden translating text - -Translate @var{arg} relative to its surroundings. @var{offset} -is a pair of numbers representing the displacement in the X and Y axis. address@hidden 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 { - * - \\translate #'(2 . 3) - \\line { translated two spaces right, three up } + \\column { + \\draw-hline + \\override #'(span-factor . 1/3) + \\draw-hline + } } @end lilypond" - (ly:stencil-translate (interpret-markup layout props arg) - offset)) + (interpret-markup layout + props + (markup #:draw-line (cons (* line-width + span-factor) + 0)))) -(define-markup-command (sub layout props arg) - (markup?) - #:category font - #:properties ((font-size 0) - (baseline-skip)) +(define-markup-command (draw-circle layout props radius thickness filled) + (number? number? boolean?) + #:category graphic " address@hidden subscript text address@hidden drawing circles within text -Set @var{arg} in subscript. +A circle of radius @var{radius} and thickness @var{thickness}, +optionally filled. @lilypond[verbatim,quote] \\markup { - \\concat { - H - \\sub { - 2 - } - O - } + \\draw-circle #2 #0.5 ##f + \\hspace #2 + \\draw-circle #2 #0 ##t } @end lilypond" - (ly:stencil-translate-axis - (interpret-markup - layout - (cons `((font-size . ,(- font-size 3))) props) - arg) - (* -0.5 baseline-skip) - Y)) + (make-circle-stencil radius thickness filled)) -(define-markup-command (normal-size-sub layout props arg) - (markup?) - #:category font - #:properties ((baseline-skip)) +(define-markup-command (triangle layout props filled) + (boolean?) + #:category graphic + #:properties ((thickness 0.1) + (font-size 0) + (baseline-skip 2)) " address@hidden setting subscript in standard font size address@hidden drawing triangles within text -Set @var{arg} in subscript with a normal font size. +A triangle, either filled or empty. @lilypond[verbatim,quote] \\markup { - default - \\normal-size-sub { - subscript in standard size - } + \\triangle ##t + \\hspace #2 + \\triangle ##f } @end lilypond" - (ly:stencil-translate-axis - (interpret-markup layout props arg) - (* -0.5 baseline-skip) - Y)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; brackets. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (let ((ex (* (magstep font-size) 0.8 baseline-skip))) + (ly:make-stencil + `(polygon '(0.0 0.0 + ,ex 0.0 + ,(* 0.5 ex) + ,(* 0.86 ex)) + ,thickness + ,filled) + (cons 0 ex) + (cons 0 (* .86 ex))))) -(define-markup-command (hbracket layout props arg) +(define-markup-command (circle layout props arg) (markup?) #:category graphic + #:properties ((thickness 1) + (font-size 0) + (circle-padding 0.2)) " address@hidden placing horizontal brackets around text address@hidden circling text -Draw horizontal brackets around @var{arg}. +Draw a circle around @var{arg}. Use @code{thickness}, address@hidden and @code{font-size} properties to determine line +thickness and padding around the markup. @lilypond[verbatim,quote] \\markup { - \\hbracket { - \\line { - one two three - } + \\circle { + Hi } } @end lilypond" - (let ((th 0.1) ;; todo: take from GROB. - (m (interpret-markup layout props arg))) - (bracketify-stencil m X th (* 2.5 th) th))) + (let ((th (* (ly:output-def-lookup layout 'line-thickness) + thickness)) + (pad (* (magstep font-size) circle-padding)) + (m (interpret-markup layout props arg))) + (circle-stencil m th pad))) -(define-markup-command (bracket layout props arg) - (markup?) +(define-markup-command (with-url layout props url arg) + (string? markup?) #:category graphic " address@hidden placing vertical brackets around text address@hidden inserting URL links into text -Draw vertical brackets around @var{arg}. +Add a link to URL @var{url} around @var{arg}. This only works in +the PDF backend. @lilypond[verbatim,quote] \\markup { - \\bracket { - \\note #\"2.\" #UP + \\with-url #\"http://lilypond.org/web/\" { + LilyPond ... \\italic { + music notation for everyone + } } } @end lilypond" - (let ((th 0.1) ;; todo: take from GROB. - (m (interpret-markup layout props arg))) - (bracketify-stencil m Y th (* 2.5 th) th))) + (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)))) -(define-markup-command (parenthesize layout props arg) - (markup?) - #:category graphic - #:properties ((angularity 0) - (padding) - (size 1) - (thickness 1) - (width 0.25)) + (ly:stencil-add (ly:make-stencil url-expr xextent yextent) stil))) + +(define-markup-command (page-link layout props page-number arg) + (number? markup?) + #:category other " address@hidden placing parentheses around text address@hidden referencing page numbers in text -Draw parentheses around @var{arg}. This is useful for parenthesizing -a column containing several lines of text. +Add a link to the page @var{page-number} around @var{arg}. This only works +in the PDF backend. @lilypond[verbatim,quote] \\markup { - \\line { - \\parenthesize { - \\column { - foo - bar - } - } - \\override #'(angularity . 2) { - \\parenthesize { - \\column { - bah - baz - } - } - } - } + \\page-link #2 { \\italic { This links to page 2... } } } @end lilypond" - (let* ((markup (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 - markup half-thickness scaled-width angularity padding))) - + (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)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Delayed markup evaluation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (ly:stencil-add (ly:make-stencil link-expr xextent yextent) stil))) -(define-markup-command (page-ref layout props label gauge default) - (symbol? markup? markup?) +(define-markup-command (with-link layout props label arg) + (symbol? markup?) #:category other " address@hidden referencing page numbers in text address@hidden referencing page labels in text -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." - (let* ((gauge-stencil (interpret-markup layout props gauge)) - (x-ext (ly:stencil-extent gauge-stencil X)) - (y-ext (ly:stencil-extent gauge-stencil Y))) +Add a link to the page holding label @var{label} around @var{arg}. This +only works in the PDF backend. + address@hidden,quote] +\\markup { + \\with-link #'label { + \\italic { This links to the page containing the label... } + } +} address@hidden 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:make-stencil `(delay-stencil-evaluation ,(delay (ly:stencil-expr - (let* ((table (ly:output-def-lookup layout 'label-page-table)) - (page-number (if (list? table) - (assoc-get label table) - #f)) - (page-markup (if page-number (format #f "~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)) + (link-expr (list 'page-link page-number + `(quote ,x-ext) `(quote ,y-ext)))) + (ly:stencil-add (ly:make-stencil link-expr x-ext y-ext) +arg-stencil))))) x-ext y-ext))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; scaling -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-markup-command (scale layout props factor-pair arg) - (number-pair? markup?) +(define-markup-command (beam layout props width slope thickness) + (number? number? number?) #:category graphic " address@hidden scaling markup address@hidden 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. address@hidden drawing beams within text +Create a beam with the specified parameters. @lilypond[verbatim,quote] \\markup { - \\line { - \\scale #'(2 . 1) - stretched - \\scale #'(1 . -1) - mirrored - } + \\beam #5 #1 #2 } @end lilypond" - (let ((stil (interpret-markup layout props arg)) - (sx (car factor-pair)) - (sy (cdr factor-pair))) - (ly:stencil-scale stil sx sy))) + (let* ((y (* slope width)) + (yext (cons (min 0 y) (max 0 y))) + (half (/ thickness 2))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Repeating -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (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) + (cons 0 width) + (cons (+ (- half) (car yext)) + (+ half (cdr yext)))))) -(define-markup-command (pattern layout props count axis space pattern) - (integer? integer? number? markup?) - #:category other +(define-markup-command (underline layout props arg) + (markup?) + #:category font + #:properties ((thickness 1) (offset 2)) " -Prints @var{count} times a @var{pattern} markup. -Patterns are spaced apart by @var{space}. -Patterns are distributed on @var{axis}. address@hidden underlining text address@hidden, quote] -\\markup \\column { - \"Horizontally repeated :\" - \\pattern #7 #X #2 \\flat - \\null - \"Vertically repeated :\" - \\pattern #3 #Y #0.5 \\flat +Underline @var{arg}. Looks at @code{thickness} to determine line +thickness, and @code{offset} to determine line y-offset. + address@hidden,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 ((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)))))))) + (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))) + (y (* thick (- offset))) + (line (make-line-stencil underline-thick x1 y x2 y))) + (ly:stencil-add markup line))) -(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)) +(define-markup-command (box layout props arg) + (markup?) + #:category font + #:properties ((thickness 1) + (font-size 0) + (box-padding 0.2)) " -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. address@hidden enclosing text within a box address@hidden, 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 +Draw a box round @var{arg}. Looks at @code{thickness}, address@hidden and @code{font-size} properties to determine line +thickness and padding around the markup. + address@hidden,quote] +\\markup { + \\override #'(box-padding . 0.5) + \\box + \\line { V. S. } } @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 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (let* ((th (* (ly:output-def-lookup layout 'line-thickness) + thickness)) + (pad (* (magstep font-size) box-padding)) + (m (interpret-markup layout props arg))) + (box-stencil m th pad))) -(define-markup-command (replace layout props replacements arg) - (list? markup?) - #:category font +(define-markup-command (filled-box layout props xext yext blot) + (number-pair? number-pair? number?) + #:category graphic " -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. address@hidden drawing solid boxes within text address@hidden drawing boxes with rounded corners address@hidden, quote] -\\markup \\replace #'((\"thx\" . \"Thanks!\")) thx +Draw a box with rounded corners of dimensions @var{xext} and address@hidden For example, address@hidden +\\filled-box #'(-.3 . 1.8) #'(-.3 . 1.8) #0 address@hidden 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 address@hidden (i.e., sharp corners). + address@hidden,quote] +\\markup { + \\filled-box #'(0 . 4) #'(0 . 4) #0 + \\filled-box #'(0 . 2) #'(-4 . 2) #0.4 + \\filled-box #'(1 . 8) #'(0 . 7) #0.2 + \\with-color #white + \\filled-box #'(-4.5 . -2.5) #'(3.5 . 5.5) #0.7 +} @end lilypond" - (interpret-markup - layout - (internal-add-text-replacements - props - replacements) - (markup arg))) + (ly:round-filled-box + xext yext blot)) + +(define-markup-command (rounded-box layout props arg) + (markup?) + #: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}, address@hidden and @code{font-size} properties to determine line +thickness and padding around the markup; the @code{corner-radius} property +makes it possible to define another shape for the corners (default is 1). + address@hidden,verbatim,relative=2] +c4^\\markup { + \\rounded-box { + Overtura + } +} +c,8. c16 c4 r address@hidden lilypond" + (let ((th (* (ly:output-def-lookup layout 'line-thickness) + thickness)) + (pad (* (magstep font-size) box-padding)) + (m (interpret-markup layout props arg))) + (ly:stencil-add (rounded-box-stencil m th pad corner-radius) + m))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Woodwind Diagram commands +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Draws a circle around markup if (= trigger 0.5) +(define-markup-command + (conditional-circle-markup layout props trigger in-markup) + (number? markup?) + (interpret-markup layout props + (if (eqv? trigger 0.5) + (markup #:circle (markup in-markup)) + (markup in-markup)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Markup list commands diff --git a/scm/define-woodwind-diagrams.scm b/scm/define-woodwind-diagrams.scm index 29825fd..6a373eb 100644 --- a/scm/define-woodwind-diagrams.scm +++ b/scm/define-woodwind-diagrams.scm @@ -16,7 +16,9 @@ ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . - +; for define-markup-command used below for conditional-circle-markup +; +(use-modules (scm markup-facility-defs)) (define HOLE-FILL-LIST '((R . 3) (1q . 5) (1h . 7) (3q . 11) (F . 13))) ;; Utility functions @@ -256,16 +258,17 @@ returns @samp{1/3}." ;;; Commands for text layout -; Draws a circle around markup if (= trigger 0.5) -(define-markup-command - (conditional-circle-markup layout props trigger in-markup) - (number? markup?) - (interpret-markup layout props - (if (eqv? trigger 0.5) - (markup #:circle (markup in-markup)) - (markup in-markup)))) +;;; Draws a circle around markup if (= trigger 0.5) +;;(define-markup-command +;; (conditional-circle-markup layout props trigger in-markup) +;; (number? markup?) +;; (interpret-markup layout props +;; (if (eqv? trigger 0.5) +;; (markup #:circle (markup in-markup)) +;; (markup in-markup)))) ; Makes a list of named-keys +;(eval-when (compile load eval) ; Guile V2 only (define (make-name-keylist input-list key-list font-size) (map (lambda (x y) (if (< x 1) @@ -294,8 +297,7 @@ returns @samp{1/3}." #:flat)) (markup #:null))))) (markup #:null))) - input-list key-list)) - + input-list key-list)) ; ) ; Guile V2 only ; Makes a list of number-keys (define (make-number-keylist input-list key-list font-size) (map (lambda (x y) diff --git a/scm/display-woodwind-diagrams.scm b/scm/display-woodwind-diagrams.scm index 7f6def7..7e80beb 100644 --- a/scm/display-woodwind-diagrams.scm +++ b/scm/display-woodwind-diagrams.scm @@ -14,6 +14,7 @@ ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . +(use-modules (scm markup-facility-defs)) ;; Constants diff --git a/scm/font.scm b/scm/font.scm index 73287b6..7cd5f25 100644 --- a/scm/font.scm +++ b/scm/font.scm @@ -14,7 +14,7 @@ ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . - +(use-modules (oop goops)) ;; TODO: ;; ;; lookup-font should be written in C. diff --git a/scm/fret-diagrams.scm b/scm/fret-diagrams.scm index 9247a11..e6017b2 100644 --- a/scm/fret-diagrams.scm +++ b/scm/fret-diagrams.scm @@ -14,7 +14,7 @@ ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . - +(use-modules (scm markup-facility-defs)) ; ; Utility functions ; diff --git a/scm/harp-pedals.scm b/scm/harp-pedals.scm index 3576182..491552b 100644 --- a/scm/harp-pedals.scm +++ b/scm/harp-pedals.scm @@ -14,7 +14,7 @@ ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . - +(use-modules (scm markup-facility-defs)) (define-markup-command (harp-pedal layout props definition-string) (string?) diff --git a/scm/lily.scm b/scm/lily.scm index e9f728f..c696c64 100644 --- a/scm/lily.scm +++ b/scm/lily.scm @@ -16,16 +16,17 @@ ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . -;; Internationalisation: (_i "to be translated") gets an entry in the -;; POT file; (gettext ...) must be invoked explicitly to do the actual -;; "translation". -;; -;; (define-macro (_i x) x) -;; (define-macro-public _i (x) x) -;; (define-public-macro _i (x) x) -;; Abbrv-PWR! +;;; Internationalisation: (_i "to be translated") gets an entry in the +;;; POT file; (gettext ...) must be invoked explicitly to do the actual +;;; "translation". +;;; +;;; (define-macro (_i x) x) +;;; (define-macro-public _i (x) x) +;;; (define-public-macro _i (x) x) +;;; Abbrv-PWR! (defmacro-public _i (x) x) +(define-public _ gettext) ;;; Boolean thunk - are we integrating Guile V2.0 or higher with LilyPond? (define-public (guile-v2) @@ -43,6 +44,11 @@ (string-downcase (car (string-tokenize (utsname:sysname (uname))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Set things up to handle LilyPond command-line options from +;;; Scheme code. +;;; (define scheme-options-definitions `( ;; NAMING: either @@ -220,6 +226,7 @@ messages into errors.") (scm clip-region) (scm memory-trace) (scm coverage) + (scm markup-facility-defs) (scm safe-utility-defs)) (define-public _ gettext) @@ -231,7 +238,7 @@ messages into errors.") (cond ((guile-v2) - (ly:debug (_ "Using (ice-9 curried-definitions) module\n")) + (ly:debug (_ "Guile 2.0\n\tUsing (ice-9 curried-definitions) module\n")) (use-modules (ice-9 curried-definitions))) (else (ly:debug (_ "Guile 1.8\n")))) @@ -345,6 +352,13 @@ messages into errors.") (fresh-interface!)))) (set-module-obarray! iface (module-obarray mod)))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Safe definitions utility +;; +; safe-objects definition and define-safe-public macro now moved +; to (scm safe-utility-defs) module +; + (define-safe-public (lilypond-version) (string-join @@ -360,8 +374,13 @@ messages into errors.") (ly:set-default-scale (ly:make-scale #(0 1 2 5/2 7/2 9/2 11/2))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; other files. +;; +;; Load the files +;; to complete the bindings for the (lily) module contained in +;; other scm/*.scm files. +;;; +;; Loading Scheme files ;; ;; List of Scheme files to be loaded into the (lily) module. ;; @@ -371,8 +390,8 @@ messages into errors.") "output-lib.scm")) ;; - Files containing definitions used later by other files later in load (define init-scheme-files-used - '("markup-macros.scm" - "parser-ly-from-scheme.scm")) + '("markup-facility-defs.scm" + "parser-ly-from-scheme.scm")) ;; - Main body of files to be loaded (define init-scheme-files-body '("file-cache.scm" @@ -440,6 +459,10 @@ messages into errors.") (for-each ly:load init-scheme-files) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; record all predicates used in LilyPond +;; and make them available in Scheme.code + (define-public r5rs-primary-predicates `((,boolean? . "boolean") (,char? . "character") @@ -560,7 +583,8 @@ messages into errors.") guile-predicates lilypond-scheme-predicates lilypond-exported-predicates)) - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; other files. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; timing diff --git a/scm/markup-facility-defs.scm b/scm/markup-facility-defs.scm new file mode 100644 index 0000000..324d0ed --- /dev/null +++ b/scm/markup-facility-defs.scm @@ -0,0 +1,646 @@ +;;;; This file is part of LilyPond, the GNU music typesetter. +;;;; +;;;; Copyright (C) 2003--2011 Han-Wen Nienhuys +;;;; +;;;; 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 . +(define-public lilypond-module (current-module)) + +(define-module (scm markup-facility-defs) + #:use-module (ice-9 optargs) + #:use-module (ice-9 receive) + #:autoload (lily) (lilypond-module ly:debug ly:text-interface::interpret-markup) + #:re-export (receive) + #:re-export-syntax (defmacro*-public) + #:export (markup? + compile-all-markup-expressions + compile-markup-expression) + #:export-syntax (markup + define-markup-command + define-markup-list-command + markup* ) + #:replace (markup)) + +" +Internally a markup is stored as a list, whose head is a function. + + (FUNCTION ARG1 ARG2 ... ) + +When the markup is formatted, the FUNCTION is called as follows + + (FUNCTION GROB PROPS ARG1 ARG2 ... ) + +GROB is the current grob, PROPS is a list of alists, and ARG1.. are +the rest of the arguments. + +The function should return a stencil (i.e. a formatted, +ready-to-print object). + + +To add a markup command, use the define-markup-command utility. + + (define-markup-command (mycommand layout prop arg1 ...) (arg1-type? ...) + \"my command usage and description\" + ...function body...) + +The command is now available in markup mode, e.g. + + \\markup { .... \\MYCOMMAND #1 argument ... } + +" ; " + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; markup definer utilities + +;; For documentation purposes +;; category -> markup functions +(define-public markup-functions-by-category (make-hash-table 150)) +;; markup function -> used properties +(define-public markup-functions-properties (make-weak-key-hash-table 151)) +;; List of markup list functions +(define-public markup-list-functions (make-weak-key-hash-table 151)) + +(defmacro*-public markup (#:rest body) + "The `markup' macro provides a lilypond-like syntax for building markups. + + - #:COMMAND is used instead of \\COMMAND + - #:line ( ... ) is used instead of \\line { ... } + - etc. + +Example: + \\markup { foo + \\raise #0.2 \\hbracket \\bold bar + \\override #'(baseline-skip . 4) + \\bracket \\column { baz bazr bla } + } + <==> + (markup \"foo\" + #:raise 0.2 #:hbracket #:bold \"bar\" + #:override '(baseline-skip . 4) + #:bracket #:column (\"baz\" \"bazr\" \"bla\")) +Use `markup*' in a \\notemode context." + (car (compile-all-markup-expressions `(#:line ,body)))) + +(defmacro*-public define-markup-command + (command-and-args signature + #:key (category '()) (properties '()) + #:rest body) + " +* Define a COMMAND-markup function after command-and-args and body, +register COMMAND-markup and its signature, + +* add COMMAND-markup to markup-functions-by-category, + +* sets COMMAND-markup markup-signature object property, + +* define a make-COMMAND-markup function. + +Syntax: + (define-markup-command (COMMAND layout props . arguments) + argument-types + [ #:properties properties ] + \"documentation string\" + ...command body...) + +where: + `argument-types' is a list of type predicates for arguments + `properties' a list of (property default-value) lists + +The specified properties are available as let-bound variables in the +command body, using the respective `default-value' as fallback in case +`property' is not found in `props'. `props' itself is left unchanged: +if you want defaults specified in that manner passed down into other +markup functions, you need to adjust `props' yourself. + +The autogenerated documentation makes use of some optional +specifications that are otherwise ignored: + +After `argument-types', you may also specify + [ #:category category ] +where: + `category' is either a symbol or a symbol list specifying the + category for this markup command in the docs. + +As an element of the `properties' list, you may directly use a +COMMANDx-markup symbol instead of a `(prop value)' list to indicate +that this markup command is called by the newly defined command, +adding its properties to the documented properties of the new +command. There is no protection against circular definitions. +" + (let* ((command (car command-and-args)) + (args (cdr command-and-args)) + (command-name (string->symbol (format #f "~a-markup" command))) + (make-markup-name (string->symbol (format #f "make-~a-markup" command)))) + (while (and (pair? body) (keyword? (car body))) + (set! body (cddr body))) + `(begin + ;; define the COMMAND-markup function + ,(let* ((documentation (if (string? (car body)) + (list (car body)) + '())) + (real-body (if (or (null? documentation) + (null? (cdr body))) + body (cdr body)))) + `(define-public (,command-name ,@args) + ,@documentation + (let ,(map (lambda (prop-spec) + (let ((prop (car prop-spec)) + (default-value (if (null? (cdr prop-spec)) + #f + (cadr prop-spec))) + (props (cadr args))) + `(,prop (chain-assoc-get ',prop ,props ,default-value)))) + (filter pair? properties)) + ,@real-body))) + (set! (markup-command-signature ,command-name) (list ,@signature)) + ;; Register the new function, for markup documentation + ,@(map (lambda (category) + `(hashq-set! + (or (hashq-ref markup-functions-by-category ',category) + (let ((hash (make-weak-key-hash-table 151))) + (hashq-set! markup-functions-by-category ',category + hash) + hash)) + ,command-name #t)) + (if (list? category) category (list category))) + ;; Used properties, for markup documentation + (hashq-set! markup-functions-properties + ,command-name + (list ,@(map (lambda (prop-spec) + (cond ((symbol? prop-spec) + prop-spec) + ((not (null? (cdr prop-spec))) + `(list ',(car prop-spec) ,(cadr prop-spec))) + (else + `(list ',(car prop-spec))))) + (if (pair? args) + properties + (list))))) + ;; define the make-COMMAND-markup function + (define-public (,make-markup-name . args) + (let ((sig (list ,@signature))) + (make-markup ,command-name ,(symbol->string make-markup-name) sig args))) +; (ly:debug "Defined ~s function\n" ,(symbol->string make-markup-name)) + ))) + +(defmacro*-public define-markup-list-command + (command-and-args signature #:key (properties '()) #:rest body) + "Same as `define-markup-command', but defines a command that, when +interpreted, returns a list of stencils instead of a single one" + (let* ((command (car command-and-args)) + (args (cdr command-and-args)) + (command-name (string->symbol (format #f "~a-markup-list" command))) + (make-markup-name (string->symbol (format #f "make-~a-markup-list" command)))) + (while (and (pair? body) (keyword? (car body))) + (set! body (cddr body))) + `(begin + ;; define the COMMAND-markup-list function + ,(let* ((documentation (if (string? (car body)) + (list (car body)) + '())) + (real-body (if (or (null? documentation) + (null? (cdr body))) + body (cdr body)))) + `(define-public (,command-name ,@args) + ,@documentation + (let ,(map (lambda (prop-spec) + (let ((prop (car prop-spec)) + (default-value (if (null? (cdr prop-spec)) + #f + (cadr prop-spec))) + (props (cadr args))) + `(,prop (chain-assoc-get ',prop ,props ,default-value)))) + (filter pair? properties)) + ,@real-body))) + (set! (markup-command-signature ,command-name) (list ,@signature)) + ;; add the command to markup-list-function-list, for markup documentation + (hashq-set! markup-list-functions ,command-name #t) + ;; Used properties, for markup documentation + (hashq-set! markup-functions-properties + ,command-name + (list ,@(map (lambda (prop-spec) + (cond ((symbol? prop-spec) + prop-spec) + ((not (null? (cdr prop-spec))) + `(list ',(car prop-spec) ,(cadr prop-spec))) + (else + `(list ',(car prop-spec))))) + (if (pair? args) + properties + (list))))) + ;; it's a markup-list command: + (set-object-property! ,command-name 'markup-list-command #t) + ;; define the make-COMMAND-markup-list function + (define-public (,make-markup-name . args) + (let ((sig (list ,@signature))) + (list (make-markup ,command-name + ,(symbol->string make-markup-name) sig args))))))) + +;;;;;;;;;;;;;;; +;;; Utilities for storing and accessing markup commands signature +;;; Examples: +;;; +;;; (set! (markup-command-signature raise-markup) (list number? markup?)) +;;; ==> (# #) +;;; +;;; (markup-command-signature raise-markup) +;;; ==> (# #) +;;; + +(define-public (markup-command-signature-ref markup-command) + "Return markup-command's signature (the 'markup-signature object property)" + (object-property markup-command 'markup-signature)) + +(define-public (markup-command-signature-set! markup-command signature) + "Set markup-command's signature (as object property)" + (set-object-property! markup-command 'markup-signature signature) + signature) + +(define-public markup-command-signature + (make-procedure-with-setter markup-command-signature-ref + markup-command-signature-set!)) + +;;;;;;;;;;;;;;;;;;;;;; +;;; markup type predicates + +(define (markup-function? x) + (and (markup-command-signature x) + (not (object-property x 'markup-list-command)))) + +(define (markup-list-function? x) + (and (markup-command-signature x) + (object-property x 'markup-list-command))) + +(define-public (markup-command-list? x) + "Determine if `x' is a markup command list, ie. a list composed of +a markup list function and its arguments." + (and (pair? x) (markup-list-function? (car x)))) + +(define-public (markup-list? arg) + "Return a true value if `x' is a list of markups or markup command lists." + (define (markup-list-inner? lst) + (or (null? lst) + (and (or (markup? (car lst)) (markup-command-list? (car lst))) + (markup-list-inner? (cdr lst))))) + (not (not (and (list? arg) (markup-list-inner? arg))))) + +(define (markup-argument-list? signature arguments) + "Typecheck argument list." + (if (and (pair? signature) (pair? arguments)) + (and ((car signature) (car arguments)) + (markup-argument-list? (cdr signature) (cdr arguments))) + (and (null? signature) (null? arguments)))) + + +(define (markup-argument-list-error signature arguments number) + "return (ARG-NR TYPE-EXPECTED ARG-FOUND) if an error is detected, or +#f is no error found. +" + (if (and (pair? signature) (pair? arguments)) + (if (not ((car signature) (car arguments))) + (list number (type-name (car signature)) (car arguments)) + (markup-argument-list-error (cdr signature) (cdr arguments) (+ 1 number))) + #f)) + +;; +;; full recursive typecheck. +;; +(define (markup-typecheck? arg) + (or (string? arg) + (and (pair? arg) + (markup-function? (car arg)) + (markup-argument-list? (markup-command-signature (car arg)) + (cdr arg))))) + +;; +;; +;; full error reporting for markup typecheck +;; +(define (markup-thrower-typecheck arg) + "typecheck, and throw an error when something is amiss. + +Uncovered - cheap-markup? is used." + + (cond ((string? arg) #t) + ((not (pair? arg)) + (throw 'markup-format "Not a pair" arg)) + ((not (markup-function? (car arg))) + (throw 'markup-format "Not a markup function " (car arg))) + ((not (markup-argument-list? (markup-command-signature (car arg)) + (cdr arg))) + (throw 'markup-format "Arguments failed typecheck for " arg))) + #t) + +;; +;; good enough if you only use make-XXX-markup functions. +;; +(define (cheap-markup? x) + (or (string? x) + (and (pair? x) + (markup-function? (car x))))) + +;; +;; replace by markup-thrower-typecheck for more detailed diagnostics. +;; +(define-public markup? cheap-markup?) + +(define-public (make-markup markup-function make-name signature args) + " Construct a markup object from MARKUP-FUNCTION and ARGS. Typecheck +against SIGNATURE, reporting MAKE-NAME as the user-invoked function. +" + (let* ((arglen (length args)) + (siglen (length signature)) + (error-msg (if (and (> siglen 0) (> arglen 0)) + (markup-argument-list-error signature args 1) + #f))) + (if (or (not (= arglen siglen)) (< siglen 0) (< arglen 0)) + (ly:error (string-append make-name ": " + (_ "Wrong number of arguments. Expect: ~A, found ~A: ~S")) + siglen arglen args)) + (if error-msg + (ly:error + (string-append + make-name ": " + (_ "Invalid argument in position ~A. Expect: ~A, found: ~S.")) + (car error-msg) (cadr error-msg)(caddr error-msg)) + (cons markup-function args)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; markup constructors +;;; lilypond-like syntax for markup construction in scheme. + +;(use-modules (ice-9 receive)) + +(defmacro*-public markup* (#:rest body) + "Same as `markup', for use in a \\notes block." + `(ly:export (markup ,@body))) + + +(define (compile-all-markup-expressions expr) + "Return a list of canonical markups expressions, e.g.: + (#:COMMAND1 arg11 arg12 #:COMMAND2 arg21 arg22 arg23) + ===> + ((make-COMMAND1-markup arg11 arg12) + (make-COMMAND2-markup arg21 arg22 arg23) ...)" + (do ((rest expr rest) + (markps '() markps)) + ((null? rest) (reverse markps)) + (receive (m r) (compile-markup-expression rest) + (set! markps (cons m markps)) + (set! rest r)))) + +(define (keyword->make-markup key) + "Transform a keyword, e.g. #:COMMAND, in a make-COMMAND-markup symbol." + (string->symbol (string-append "make-" (symbol->string (keyword->symbol key)) "-markup"))) +;; (eval-when (load compile eval) ; Guile V2 only +(define (compile-markup-expression expr) + "Return two values: the first complete canonical markup expression + found in `expr', e.g. (make-COMMAND-markup arg1 arg2 ...), + and the rest expression." +; (ly:debug "Compiling markup: ~a" expr) +; (ly:debug "Current module: ~s" (current-module)) + (cond ((and (pair? expr) + (keyword? (car expr))) + ;; expr === (#:COMMAND arg1 ...) +; (ly:debug "expr === (#:COMMAND arg1 ...)\n~a" expr) + (let ((command (symbol->string (keyword->symbol (car expr))))) + (if (not (pair? (lookup-markup-command command))) + (begin + (ly:message "Current module: ~s" (current-module)) + (throw 'ly-not-markup-command command expr)) +; (ly:error (_ "Not a markup command: ~A,\n\tfrom token ~S") command expr) + (ly:debug (_ "Validated markup command: ~A") command)) + (let* ((sig (markup-command-signature + (car (lookup-markup-command command)))) + (sig-len (length sig))) + (do ((i 0 (1+ i)) + (args '() args) + (rest (cdr expr) rest)) + ((>= i sig-len) + (values (cons (keyword->make-markup (car expr)) (reverse args)) rest)) + (cond ((eqv? (list-ref sig i) markup-list?) + ;; (car rest) is a markup list + (set! args (cons `(list ,@(compile-all-markup-expressions (car rest))) args)) + (set! rest (cdr rest))) + (else + ;; pick up one arg in `rest' + (receive (a r) (compile-markup-arg rest) + (set! args (cons a args)) + (set! rest r)))))))) + ((and (pair? expr) + (pair? (car expr)) + (keyword? (caar expr))) + ;; expr === ((#:COMMAND arg1 ...) ...) +; (ly:debug " expr === ((#:COMMAND arg1 ...) ...)\n") + (receive (m r) (compile-markup-expression (car expr)) + (values m (cdr expr)))) + ((and (pair? expr) + (string? (car expr))) + ;; expr === ("string" ...) +; (ly:debug "expr === (\"string\" ...)\n~a" expr) + (values `(make-simple-markup ,(car expr)) (cdr expr))) + (else + ;; expr === (symbol ...) or ((funcall ...) ...) +; (ly:debug " expr === (symbol ...) or ((funcall ...) ...)\n~a" expr) + (values (car expr) + (cdr expr))))) +;;) ; Guile V2 only + +(define (compile-all-markup-args expr) + "Transform `expr' into markup arguments" + (do ((rest expr rest) + (args '() args)) + ((null? rest) (reverse args)) + (receive (a r) (compile-markup-arg rest) + (set! args (cons a args)) + (set! rest r)))) + +(define (compile-markup-arg expr) + "Return two values: the desired markup argument, and the rest arguments" + (cond ((null? expr) + ;; no more args + (values '() '())) + ((keyword? (car expr)) + ;; expr === (#:COMMAND ...) + ;; ==> build and return the whole markup expression + (compile-markup-expression expr)) + ((and (pair? (car expr)) + (keyword? (caar expr))) + ;; expr === ((#:COMMAND ...) ...) + ;; ==> build and return the whole markup expression(s) + ;; found in (car expr) + (receive (markup-expr rest-expr) (compile-markup-expression (car expr)) + (if (null? rest-expr) + (values markup-expr (cdr expr)) + (values `(list ,markup-expr ,@(compile-all-markup-args rest-expr)) + (cdr expr))))) + ((and (pair? (car expr)) + (pair? (caar expr))) + ;; expr === (((foo ...) ...) ...) + (values (cons 'list (compile-all-markup-args (car expr))) (cdr expr))) + (else (values (car expr) (cdr expr))))) + +(define (lookup-markup-command-aux symbol) + (let ((proc (catch 'misc-error + (lambda () + (module-ref (resolve-module '(lily)) symbol)) + (lambda (key . args) #f)))) + (and (procedure? proc) proc))) + +(define (lookup-markup-command-helper symbol) + (let ((proc '())) + (if (defined? 'symbol (resolve-module '(lily))) + (begin + (set! proc symbol) + (if (procedure? proc) + (proc) + #f)) + #f))) + + +(define-public (lookup-markup-command code) + (let ((proc (lookup-markup-command-aux + (string->symbol (format #f "~a-markup" code))))) + (and proc (markup-function? proc) + (cons proc (markup-command-signature proc))))) + +(define-public (lookup-markup-list-command code) + (let ((proc (lookup-markup-command-aux + (string->symbol (format #f "~a-markup-list" code))))) + (and proc (markup-list-function? proc) + (cons proc (markup-command-signature proc))))) + +;;;;;;;;;;;;;;;;;;;;;; +;;; used in parser.yy to map a list of markup commands on markup arguments +(define-public (map-markup-command-list commands markups) + "`markups' being a list of markups, eg (markup1 markup2 markup3), +and `commands' a list of commands with their scheme arguments, in reverse order, +eg: ((italic) (raise 4) (bold)), maps the commands on each markup argument, eg: + ((bold (raise 4 (italic markup1))) + (bold (raise 4 (italic markup2))) + (bold (raise 4 (italic markup3)))) +" + (map-in-order (lambda (arg) + (let ((result arg)) + (for-each (lambda (cmd) + (set! result (append cmd (list result)))) + commands) + result)) + markups)) +;; utility + +(define (markup-join markups sep) + "Return line-markup of MARKUPS, joining them with markup SEP" + (if (pair? markups) + (make-line-markup (list-insert-separator markups sep)) + empty-markup)) + +(cond-expand (guile-2 + (eval-when (compile load eval) + (define-public (interpret-markup layout props markup) + (ly:text-interface::interpret-markup layout props markup)))) + (guile + (define-public (interpret-markup layout props markup) + (ly:text-interface::interpret-markup layout props markup)))) + +(define-public (interpret-markup-list layout props markup-list) + (let ((stencils (list))) + (for-each (lambda (m) + (set! stencils + (if (markup-command-list? m) + (append! (reverse! (apply (car m) layout props (cdr m))) + stencils) + (cons (interpret-markup layout props m) stencils)))) + markup-list) + (reverse! stencils))) + +(define-public (prepend-alist-chain key val chain) + (cons (acons key val (car chain)) (cdr chain))) + +(define-public (stack-stencil-line space stencils) + "DOCME" + (if (and (pair? stencils) + (ly:stencil? (car stencils))) + + (if (and (pair? (cdr stencils)) + (ly:stencil? (cadr stencils))) + (let* ((tail (stack-stencil-line space (cdr stencils))) + (head (car stencils)) + (xoff (+ space (interval-length (ly:stencil-extent head X))))) + (ly:stencil-add head + (ly:stencil-translate-axis tail xoff X))) + (car stencils)) + (ly:make-stencil '() '(0 . 0) '(0 . 0)))) + + +;;; convert a full markup object to an approximate pure string representation + +(define-public (markup->string m) + ;; markup commands with one markup argument, formatting ignored + (define markups-first-argument '(list + bold-markup box-markup caps-markup dynamic-markup finger-markup + fontCaps-markup huge-markup italic-markup large-markup larger-markup + medium-markup normal-size-sub-markup normal-size-super-markup + normal-text-markup normalsize-markup number-markup roman-markup + sans-markup simple-markup small-markup smallCaps-markup smaller-markup + sub-markup super-markup teeny-markup text-markup tiny-markup + typewriter-markup underline-markup upright-markup bracket-markup + circle-markup hbracket-markup parenthesize-markup rounded-box-markup + + center-align-markup center-column-markup column-markup dir-column-markup + fill-line-markup justify-markup justify-string-markup left-align-markup + left-column-markup line-markup right-align-markup right-column-markup + vcenter-markup wordwrap-markup wordwrap-string-markup )) + + ;; markup commands with markup as second argument, first argument + ;; specifies some formatting and is ignored + (define markups-second-argument '(list + abs-fontsize-markup fontsize-markup magnify-markup lower-markup + pad-around-markup pad-markup-markup pad-x-markup raise-markup + halign-markup hcenter-in-markup rotate-markup translate-markup + translate-scaled-markup with-url-markup scale-markup )) + + ;; helper functions to handle string cons like string lists + (define (markup-cons->string-cons c) + (if (not (pair? c)) (markup->string c) + (cons (markup->string (car c)) (markup-cons->string-cons (cdr c))))) + (define (string-cons-join c) + (if (not (pair? c)) c + (string-join (list (car c) (string-cons-join (cdr c))) ""))) + + (cond + ((string? m) m) + ((null? m) "") + + ;; handle \concat (string-join without spaces) + ((and (pair? m) (equal? (car m) concat-markup)) + (string-cons-join (markup-cons->string-cons (cadr m))) ) + + ;; markup functions with the markup as first arg + ((member (car m) (primitive-eval markups-first-argument)) + (markup->string (cadr m))) + + ;; markup functions with markup as second arg + ((member (car m) (primitive-eval markups-second-argument)) + (markup->string (cddr m))) + + ;; ignore all other markup functions + ((markup-function? (car m)) "") + + ;; handle markup lists + ((list? m) + (string-join (map markup->string m) " ")) + + (else "ERROR, unable to extract string from markup"))) + ; ensure lily.scm get back to its module after loading use (see also end of file) +(set-current-module lilypond-module) \ No newline at end of file diff --git a/scm/markup.scm b/scm/markup.scm index 3204b67..9f00761 100644 --- a/scm/markup.scm +++ b/scm/markup.scm @@ -15,124 +15,145 @@ ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . -(defmacro*-public markup (#:rest body) - "The `markup' macro provides a lilypond-like syntax for building markups. - - - #:COMMAND is used instead of \\COMMAND - - #:line ( ... ) is used instead of \\line { ... } - - etc. - -Example: - \\markup { foo - \\raise #0.2 \\hbracket \\bold bar - \\override #'(baseline-skip . 4) - \\bracket \\column { baz bazr bla } - } - <==> - (markup \"foo\" - #:raise 0.2 #:hbracket #:bold \"bar\" - #:override '(baseline-skip . 4) - #:bracket #:column (\"baz\" \"bazr\" \"bla\"))" - - (car (compile-all-markup-expressions `(#:line ,body)))) +;;(defmacro*-public markup (#:rest body) +;; "The `markup' macro provides a lilypond-like syntax for building markups. +;; +;; - #:COMMAND is used instead of \\COMMAND +;; - #:line ( ... ) is used instead of \\line { ... } +;; - etc. +;; +;;Example: +;; \\markup { foo +;; \\raise #0.2 \\hbracket \\bold bar +;; \\override #'(baseline-skip . 4) +;; \\bracket \\column { baz bazr bla } +;; } +;; <==> +;; (markup \"foo\" +;; #:raise 0.2 #:hbracket #:bold \"bar\" +;; #:override '(baseline-skip . 4) +;; #:bracket #:column (\"baz\" \"bazr\" \"bla\"))" +;; +;; (car (compile-all-markup-expressions `(#:line ,body)))) +;; (defmacro*-public markup (#:rest body) +;; "The `markup' macro provides a lilypond-like syntax for building markups. + +;; - #:COMMAND is used instead of \\COMMAND +;; - #:line ( ... ) is used instead of \\line { ... } +;; - etc. + +;; Example: +;; \\markup { foo +;; \\raise #0.2 \\hbracket \\bold bar +;; \\override #'(baseline-skip . 4) +;; \\bracket \\column { baz bazr bla } +;; } +;; <==> +;; (markup \"foo\" +;; #:raise 0.2 #:hbracket #:bold \"bar\" +;; #:override '(baseline-skip . 4) +;; #:bracket #:column (\"baz\" \"bazr\" \"bla\")) +;; Use `markup*' in a \\notemode context." + +;; (car (compile-all-markup-expressions `(#:line ,body)))) ;; utility -(define (markup-join markups sep) - "Return line-markup of MARKUPS, joining them with markup SEP" - (if (pair? markups) - (make-line-markup (list-insert-separator markups sep)) - empty-markup)) - - -(define-public interpret-markup ly:text-interface::interpret-markup) - -(define-public (interpret-markup-list layout props markup-list) - (let ((stencils (list))) - (for-each (lambda (m) - (set! stencils - (if (markup-command-list? m) - (append! (reverse! (apply (car m) layout props (cdr m))) - stencils) - (cons (interpret-markup layout props m) stencils)))) - markup-list) - (reverse! stencils))) - -(define-public (prepend-alist-chain key val chain) - (cons (acons key val (car chain)) (cdr chain))) - -(define-public (stack-stencil-line space stencils) - "DOCME" - (if (and (pair? stencils) - (ly:stencil? (car stencils))) - - (if (and (pair? (cdr stencils)) - (ly:stencil? (cadr stencils))) - (let* ((tail (stack-stencil-line space (cdr stencils))) - (head (car stencils)) - (xoff (+ space (interval-length (ly:stencil-extent head X))))) - (ly:stencil-add head - (ly:stencil-translate-axis tail xoff X))) - (car stencils)) - (ly:make-stencil '() '(0 . 0) '(0 . 0)))) - - -;;; convert a full markup object to an approximate pure string representation - -(define-public (markup->string m) - ;; markup commands with one markup argument, formatting ignored - (define markups-first-argument '(list - bold-markup box-markup caps-markup dynamic-markup finger-markup - fontCaps-markup huge-markup italic-markup large-markup larger-markup - medium-markup normal-size-sub-markup normal-size-super-markup - normal-text-markup normalsize-markup number-markup roman-markup - sans-markup simple-markup small-markup smallCaps-markup smaller-markup - sub-markup super-markup teeny-markup text-markup tiny-markup - typewriter-markup underline-markup upright-markup bracket-markup - circle-markup hbracket-markup parenthesize-markup rounded-box-markup - - center-align-markup center-column-markup column-markup dir-column-markup - fill-line-markup justify-markup justify-string-markup left-align-markup - left-column-markup line-markup right-align-markup right-column-markup - vcenter-markup wordwrap-markup wordwrap-string-markup )) - - ;; markup commands with markup as second argument, first argument - ;; specifies some formatting and is ignored - (define markups-second-argument '(list - abs-fontsize-markup fontsize-markup magnify-markup lower-markup - pad-around-markup pad-markup-markup pad-x-markup raise-markup - halign-markup hcenter-in-markup rotate-markup translate-markup - translate-scaled-markup with-url-markup scale-markup )) - - ;; helper functions to handle string cons like string lists - (define (markup-cons->string-cons c) - (if (not (pair? c)) (markup->string c) - (cons (markup->string (car c)) (markup-cons->string-cons (cdr c))))) - (define (string-cons-join c) - (if (not (pair? c)) c - (string-join (list (car c) (string-cons-join (cdr c))) ""))) - - (cond - ((string? m) m) - ((null? m) "") - - ;; handle \concat (string-join without spaces) - ((and (pair? m) (equal? (car m) concat-markup)) - (string-cons-join (markup-cons->string-cons (cadr m))) ) - - ;; markup functions with the markup as first arg - ((member (car m) (primitive-eval markups-first-argument)) - (markup->string (cadr m))) - - ;; markup functions with markup as second arg - ((member (car m) (primitive-eval markups-second-argument)) - (markup->string (cddr m))) - - ;; ignore all other markup functions - ((markup-function? (car m)) "") - - ;; handle markup lists - ((list? m) - (string-join (map markup->string m) " ")) - - (else "ERROR, unable to extract string from markup"))) +;; (define (markup-join markups sep) +;; "Return line-markup of MARKUPS, joining them with markup SEP" +;; (if (pair? markups) +;; (make-line-markup (list-insert-separator markups sep)) +;; empty-markup)) + + +;; (define-public interpret-markup ly:text-interface::interpret-markup) + +;; (define-public (interpret-markup-list layout props markup-list) +;; (let ((stencils (list))) +;; (for-each (lambda (m) +;; (set! stencils +;; (if (markup-command-list? m) +;; (append! (reverse! (apply (car m) layout props (cdr m))) +;; stencils) +;; (cons (interpret-markup layout props m) stencils)))) +;; markup-list) +;; (reverse! stencils))) + +;; (define-public (prepend-alist-chain key val chain) +;; (cons (acons key val (car chain)) (cdr chain))) + +;; (define-public (stack-stencil-line space stencils) +;; "DOCME" +;; (if (and (pair? stencils) +;; (ly:stencil? (car stencils))) + +;; (if (and (pair? (cdr stencils)) +;; (ly:stencil? (cadr stencils))) +;; (let* ((tail (stack-stencil-line space (cdr stencils))) +;; (head (car stencils)) +;; (xoff (+ space (interval-length (ly:stencil-extent head X))))) +;; (ly:stencil-add head +;; (ly:stencil-translate-axis tail xoff X))) +;; (car stencils)) +;; (ly:make-stencil '() '(0 . 0) '(0 . 0)))) + + +;; ;;; convert a full markup object to an approximate pure string representation + +;; (define-public (markup->string m) +;; ;; markup commands with one markup argument, formatting ignored +;; (define markups-first-argument '(list +;; bold-markup box-markup caps-markup dynamic-markup finger-markup +;; fontCaps-markup huge-markup italic-markup large-markup larger-markup +;; medium-markup normal-size-sub-markup normal-size-super-markup +;; normal-text-markup normalsize-markup number-markup roman-markup +;; sans-markup simple-markup small-markup smallCaps-markup smaller-markup +;; sub-markup super-markup teeny-markup text-markup tiny-markup +;; typewriter-markup underline-markup upright-markup bracket-markup +;; circle-markup hbracket-markup parenthesize-markup rounded-box-markup + +;; center-align-markup center-column-markup column-markup dir-column-markup +;; fill-line-markup justify-markup justify-string-markup left-align-markup +;; left-column-markup line-markup right-align-markup right-column-markup +;; vcenter-markup wordwrap-markup wordwrap-string-markup )) + +;; ;; markup commands with markup as second argument, first argument +;; ;; specifies some formatting and is ignored +;; (define markups-second-argument '(list +;; abs-fontsize-markup fontsize-markup magnify-markup lower-markup +;; pad-around-markup pad-markup-markup pad-x-markup raise-markup +;; halign-markup hcenter-in-markup rotate-markup translate-markup +;; translate-scaled-markup with-url-markup scale-markup )) + +;; ;; helper functions to handle string cons like string lists +;; (define (markup-cons->string-cons c) +;; (if (not (pair? c)) (markup->string c) +;; (cons (markup->string (car c)) (markup-cons->string-cons (cdr c))))) +;; (define (string-cons-join c) +;; (if (not (pair? c)) c +;; (string-join (list (car c) (string-cons-join (cdr c))) ""))) + +;; (cond +;; ((string? m) m) +;; ((null? m) "") + +;; ;; handle \concat (string-join without spaces) +;; ((and (pair? m) (equal? (car m) concat-markup)) +;; (string-cons-join (markup-cons->string-cons (cadr m))) ) + +;; ;; markup functions with the markup as first arg +;; ((member (car m) (primitive-eval markups-first-argument)) +;; (markup->string (cadr m))) + +;; ;; markup functions with markup as second arg +;; ((member (car m) (primitive-eval markups-second-argument)) +;; (markup->string (cddr m))) + +;; ;; ignore all other markup functions +;; ((markup-function? (car m)) "") + +;; ;; handle markup lists +;; ((list? m) +;; (string-join (map markup->string m) " ")) + +;; (else "ERROR, unable to extract string from markup"))) diff --git a/scm/page.scm b/scm/page.scm index 0a30ace..dbab13b 100644 --- a/scm/page.scm +++ b/scm/page.scm @@ -36,6 +36,7 @@ (use-modules (lily) (scm paper-system) + (scm markup-facility-defs) (srfi srfi-1)) diff --git a/scm/tablature.scm b/scm/tablature.scm index c26af25..6a51363 100644 --- a/scm/tablature.scm +++ b/scm/tablature.scm @@ -15,7 +15,7 @@ ;;;; You should have received a copy of the GNU General Public License ;;;; along with LilyPond. If not, see . - +(use-modules (scm markup-facility-defs)) ;; for more control over glyph-name calculations, ;; we use a custom callback for tab note heads ;; which will ignore 'style = 'do -- 1.7.5.4