Files
tux 286779fdac extract meta data from lilypond compilation
* write that as yaml file next to the other outputs
* extract lyrics
* makes chordpro export work in songbooks
* more metadata for chordpro export
2026-07-12 23:11:36 +02:00

639 lines
28 KiB
LilyPond

#(define song-list '())
#(define song-number 0)
#(define (files-in-directory dirname)
;;; Generate list containing filenames
(let ((dir (opendir dirname)))
(let next ((f (readdir dir))
(files '()))
(cond ((eof-object? f)
(closedir dir)
files)
(else
(next (readdir dir) (if (ly:regex-match? (ly:regex-exec (ly:make-regex "^(0|\\.)") f)) files (cons f files))))))))
#(define (file-to-stats filename)
(set! song-list (cons filename song-list)))
#(define additional-page-switch-label-list '())
#(define additional-page-numbers #f)
normalPageNumbers =
#(define-void-function (parser location) ()
(set! additional-page-numbers #f)
)
additionalPageNumbers =
#(define-void-function (parser location) ()
(set! additional-page-numbers #t)
)
#(define (real-page-number layout label)
(let ((table (ly:output-def-lookup layout 'label-page-table)))
(if (list? table)
(assoc-get label table)
#f))
)
#(define display-pages-list '())
#(define (build-display-pages-list layout)
(if (null? display-pages-list)
(let calculate-display-page ((switch-label-list additional-page-switch-label-list))
(let* ((label (caar switch-label-list))
(is-additional (cdar switch-label-list))
(real-page (real-page-number layout label))
(rest-switch-label-list (cdr switch-label-list))
(display-page (if (null? rest-switch-label-list)
(if is-additional (- real-page 1) real-page)
(let* ((previous-label (caar rest-switch-label-list))
(previous-is-additional (cdar rest-switch-label-list))
(previous-display-page (calculate-display-page rest-switch-label-list)))
(+ previous-display-page
(if previous-is-additional
(if is-additional 0 1)
(if is-additional
(- (- real-page (real-page-number layout previous-label)) 1)
(- real-page (real-page-number layout previous-label)))
)
)
)
))
)
(set! display-pages-list (acons label display-page display-pages-list))
display-page
)))
display-pages-list
)
% Fuer Zusatzseiten: Label der ersten Seite des zusammenhaengenden
% Zusatzseiten-Blocks, zu dem label gehoert (bestimmt den Buchstaben-Suffix a, b, ...).
#(define (earliest-additional-label label)
(let find-earliest-additional-label
((rest-additional-page-switch-label-list (member (cons label #t) additional-page-switch-label-list)))
(if (cdadr rest-additional-page-switch-label-list)
(find-earliest-additional-label (cdr rest-additional-page-switch-label-list))
(caar rest-additional-page-switch-label-list))))
% Sichtbare Seitenzahl der Seite mit diesem Label: normalerweise eine Zahl,
% auf Zusatzseiten ein String mit Buchstaben-Suffix (z.B. "12a"); gleiche
% Logik wie custom-page-number unten.
#(define (visible-page-value layout label)
(let ((display-page (assq-ref (build-display-pages-list layout) label)))
(if (assq-ref additional-page-switch-label-list label)
(ly:format "~a~a" display-page
(string (integer->char (+ 97 (- (real-page-number layout label)
(real-page-number layout (earliest-additional-label label)))))))
display-page)))
% TODO:
% Eigentlich können wir das direkt in oddFooderMarkup und evenFooterMarkup aufrufen
% vermutlich sogar ohne den delay kram. Wir sollten außerdem einfach nur die property
% page:page-number-string setzen dann klappts auch mit PDF Seiten
#(define-markup-command (custom-page-number layout props label real-current-page-number)
(symbol? number?)
#:category other
"
@cindex referencing page number, 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.
(If the current book or bookpart is set to use roman numerals for page numbers,
the reference will be formatted accordingly -- in which case the @var{gauge}'s
width may require additional tweaking.)"
(let* ((gauge-stencil (interpret-markup layout props "XXX"))
(x-ext (ly:stencil-extent gauge-stencil X))
(y-ext (ly:stencil-extent gauge-stencil Y)))
(ly:stencil-outline
(ly:make-stencil
`(delay-stencil-evaluation
,(delay (ly:stencil-expr
(let* ((display-page (assq-ref (build-display-pages-list layout) label))
(real-current-page (if (negative? real-current-page-number) (real-page-number layout label) real-current-page-number))
(number-type (ly:output-def-lookup layout 'page-number-type))
(page-markup
(if (assq-ref additional-page-switch-label-list label)
(make-concat-markup (list (number-format number-type display-page)
(make-char-markup (+ 97 (- real-current-page (real-page-number layout
(earliest-additional-label label)))))))
(number-format number-type (+ display-page (- real-current-page (real-page-number layout label))))
))
(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
(make-line-markup
(list
(make-hspace-markup gap)
page-markup)))))))
x-ext
y-ext)
(make-filled-box-stencil x-ext y-ext))))
%% Schreibt die Daten aller Lieder des Buchs (extract-song-data, siehe
%% data_extractor.ily) als YAML-Datei: oberster Key contents, darunter eine
%% Liste mit den Lied-Daten, jeweils angereichert um den Key page mit der
%% sichtbaren Seitenzahl des Liedanfangs. Einbinden wie \write-toc-csv in
%% einem Markup des Buchs; wie dort passiert das Schreiben ueber ein
%% delayed stencil, weil die Label->Seiten-Tabelle erst beim Ausgeben des
%% Buchs feststeht. Der Dateiname ist der Ausgabename des Buchs
%% (\bookOutputName, sonst der .ly-Basisname) mit angehaengtem .yml --
%% landet also wie das PDF relativ zum Arbeitsverzeichnis.
#(define-markup-command (write-book-yaml layout props) ()
(let ((filename (string-append (ly:parser-output-name) ".yml")))
(ly:make-stencil
`(delay-stencil-evaluation
,(delay
(begin
(scm->yml-file filename
(list (cons 'contents
(map (lambda (song)
(let ((songvars (cdr song)))
(cons (cons 'page (visible-page-value layout (assq-ref songvars 'label)))
(extract-song-data (assq-ref songvars 'header)
(assq-ref songvars 'music)
;; Textseiten-Strophen aus dem Render-
;; Collector-Store (Key = Liedname); beim
;; Ausgeben des Buchs sind alle Textseiten
;; bereits interpretiert.
(hash-ref chordpro-song-store
(symbol->string (car song)))
(symbol->string (car song))))))
(reverse (alist-delete 'markupPage
(alist-delete 'imagePage
(alist-delete 'emptyPage song-list))))))))
empty-stencil))))))
%% Schreibt den ChordPro-Export aller Lieder des Buchs (in Dokument-
%% reihenfolge) als EINE Datei statt als Einzeldateien pro Lied. Lieder
%% werden mit der ChordPro-Direktive {new_song} getrennt; vor dem ersten
%% Lied ist sie nicht noetig, sie gilt am Dateianfang implizit (siehe
%% https://www.chordpro.org/chordpro/directives-new_song/). Einbinden wie
%% \write-book-yaml an beliebiger Stelle im Buch -- unabhaengig von
%% chordpro-export-enabled (das steuert nur den Pro-Song-Export), beide
%% Befehle koennen auch gleichzeitig genutzt werden. Wie bei
%% \write-book-yaml passiert das Schreiben ueber ein delayed stencil,
%% weil der Render-Collector-Store erst beim Ausgeben des Buchs
%% vollstaendig gefuellt ist. Der Dateiname ist wie bei \write-book-yaml
%% der Ausgabename des Buchs mit angehaengtem .cho.
#(define-markup-command (write-book-chordpro layout props) ()
(let ((filename (string-append (ly:parser-output-name) ".cho")))
(ly:make-stencil
`(delay-stencil-evaluation
,(delay
(begin
(with-output-to-file filename
(lambda ()
(let loop ((songs (filter
;; \chordlyrics-Strophen ODER (bei
;; chordpro-include-music-lyrics) aus der
;; Musik nachgeholte Kandidaten.
(lambda (song) (or (> (chordpro-song-verse-index song) 0)
(pair? (chordpro-song-music-candidates song))))
(filter-map
(lambda (song) (hash-ref chordpro-song-store
(symbol->string (car song))))
(reverse (alist-delete 'markupPage
(alist-delete 'imagePage
(alist-delete 'emptyPage song-list)))))))
(first? #t))
(when (pair? songs)
(unless first? (display "{new_song}\n"))
(display (chordpro-format-song (car songs)))
(loop (cdr songs) #f)))))
empty-stencil))))))
includeSong =
#(define-void-function (filename) (string?)
#{
\bookOutputName #filename
#}
(ly:parser-parse-string (ly:parser-clone)
(ly:format "\\include \"~a/~a/~a.ly\"" songPath filename filename))
;; ChordPro: Titel/Autoren dieses Lieds im Render-Collector-Store
;; registrieren -- basicSongInfo haelt direkt nach dem Include genau
;; dieses Lied. Der Key ist der Liedname; die Collector-Daten (Verse,
;; Akkorde) kommen spaeter beim Rendern ueber \setsongfilename unter
;; demselben Key dazu. Unabhaengig von chordpro-export-enabled, damit
;; sowohl der Pro-Song-Export (der die Flag braucht) als auch
;; \write-book-chordpro (das nur explizit aufgerufen werden muss) auf
;; vollstaendige Metadaten zugreifen koennen.
(chordpro-register-song-metadata filename HEADER)
(let ((label (gensym "index")))
(set! additional-page-switch-label-list
(acons label additional-page-numbers additional-page-switch-label-list))
(set! song-list
(acons (string->symbol filename)
`(
(label . ,label)
(header . ,HEADER)
(music . ,MUSIC)
(layout . ,LAYOUT)
(text-pages . ,(map (lambda (text)
#{ \markuplist \setsongfilename $filename $text #})
TEXT_PAGES))
)
song-list))
))
blankpage =
#(define-void-function () ()
(set! song-list
(acons 'emptyPage
'()
song-list)))
imagepage =
#(define-void-function (xsize filename) (number? string?)
(set! song-list
(acons 'imagePage
`((xsize . ,xsize)
(filename . ,filename))
song-list)))
markuppage =
#(define-void-function (mkup) (markup?)
(set! song-list
(acons 'markupPage
`((markup . ,mkup))
song-list)))
#(define chapters '())
songsChapter =
#(define-void-function (title) (string?)
(set! chapters
(cons (cons title (length song-list)) chapters)))
#(define-markup-command (pagecenter layout props stuff)(markup?)
(interpret-markup layout props
(let ((halfpaperheight (/ (ly:output-def-lookup layout 'paper-height) 2))
(halfstuffheight (/ (interval-length (ly:stencil-extent (interpret-markup layout props stuff) Y)) 2)))
(make-fill-line-markup (list (make-pad-to-box-markup '(0 . 0) (cons (- (- halfpaperheight halfstuffheight)) (+ halfpaperheight halfstuffheight)) stuff)))
)))
songs =
#(define-void-function () ()
(let* ((ordered (reverse song-list))
(total (length ordered))
;; Ein Lied erzeugt nur dann einen PDF-Outline-Eintrag (/OUT), wenn
;; title oder starttext gesetzt sind (siehe build-full-title). Nur solche
;; Lieder duerfen als Kapitel-Kinder gezaehlt werden -- sonst wird die
;; /Count zu hoch und der erste Folgeeintrag (z.B. ein spaeteres Kapitel)
;; rutscht faelschlich als Kind mit hinein.
(emits-bookmark?
(lambda (e)
(and (not (eq? (car e) 'emptyPage))
(not (eq? (car e) 'imagePage))
(not (eq? (car e) 'markupPage))
(let ((vars (extract-and-check-vars-from-header
(assq-ref (cdr e) 'header) '(title starttext))))
(if (or (assq-ref vars 'title) (assq-ref vars 'starttext)) #t #f)))))
(count-bookmark-songs
(lambda (start end)
(let loop ((i 0) (l ordered) (c 0))
(cond ((or (null? l) (>= i end)) c)
((< i start) (loop (+ i 1) (cdr l) c))
(else (loop (+ i 1) (cdr l) (if (emits-bookmark? (car l)) (+ c 1) c)))))))
;; chapters: (title . start-index). start-index = Anzahl der song-list-
;; Eintraege beim Aufruf von songsChapter = Index in `ordered`, ab dem
;; das Kapitel beginnt.
(chapters-asc (sort chapters (lambda (a b) (< (cdr a) (cdr b)))))
;; pending: aufsteigende Liste von (start title count); count = Anzahl
;; der Lesezeichen-Lieder von diesem Kapitel bis zum Beginn des naechsten.
(pending
(let loop ((chs chapters-asc) (acc '()))
(if (null? chs)
(reverse acc)
(let* ((title (caar chs))
(start (cdar chs))
(end (if (null? (cdr chs)) total (cdadr chs)))
(cnt (count-bookmark-songs start end)))
(loop (cdr chs) (cons (list start title cnt) acc))))))
;; Aktuell aktives Kapitel (Titel-String) waehrend des Durchlaufs. Wird
;; gesetzt, sobald ein Kapitel aktiviert (aus `pending` entfernt) wird --
;; egal ob auf einer Seite (chapter-prefixed-markup) oder auf dem ersten
;; Song. Jeder danach folgende Song erhaelt dieses Kapitel als
;; chapterref-Header-Feld, woraus toc_include.ily den Kapitel-Index baut.
(current-chapter #f)
;; Stellt einer beliebigen Seite (markup-/image-/emptyPage) den Kapitel-
;; Eintrag (chapter-to-pdf-toc) bei, falls an Position `this-index` ein
;; Kapitel faellig ist, und entfernt es aus `pending`. So fuehrt die
;; Kapitel-Referenz zur ersten Seite nach dem \songsChapter -- egal
;; welcher Seitentyp das ist. Der Eintrag wird als unsichtbares Overlay
;; (\combine) beigelegt, damit er das Seitenlayout nicht veraendert;
;; \with-dimensions gibt dem sonst extent-losen pdfmark einen minimalen
;; Extent, damit er auch auf leeren Seiten nicht verworfen wird.
(chapter-prefixed-markup
(lambda (this-index base-markup)
(if (and (pair? pending) (<= (car (car pending)) this-index))
(let ((cnt (caddr (car pending)))
(title (cadr (car pending))))
(set! pending (cdr pending))
(set! current-chapter title)
(make-combine-markup
base-markup
(make-with-dimensions-markup '(0 . 0) '(0 . 0.1)
(make-chapter-to-pdf-toc-markup cnt title))))
base-markup)))
(index 0))
(for-each (lambda (songitems)
(let ((this-index index))
(set! index (+ index 1))
(ly:book-add-bookpart! (ly:parser-lookup '$current-book)
(let ((filename (car songitems))
(songvars (cdr songitems)))
(if (eq? filename 'emptyPage)
#{ \bookpart { \markup { $(chapter-prefixed-markup this-index (make-null-markup)) } } #}
(if (eq? filename 'markupPage)
;; Kapitelanfang auf einer markuppage: Kapitel-Eintrag wird dieser
;; Seite beigelegt (siehe chapter-prefixed-markup).
#{ \bookpart { \markup { $(chapter-prefixed-markup this-index (assq-ref songvars 'markup)) } } #}
(if (eq? filename 'imagePage)
(let* ((xsize (assq-ref songvars 'xsize))
(filename (ly:format "~a/~a" imagePagePath (assq-ref songvars 'filename)))
(img-markup #{ \markup { \pagecenter { \epsfile #X #xsize #filename } } #}))
#{ \bookpart {
\paper {
%{
inner-margin = 0
outer-margin = 0
binding-offset = 0
top-margin = 0
bottom-margin = 0
%}
print-page-number = ##f
last-bottom-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0))
page-count = 1
}
\markup { $(chapter-prefixed-markup this-index img-markup) }
} #}
)
;; Ist ein Kapitel faellig (Start <= aktuelle Position) und dies ein
;; echtes Lied, wird die Kapitelinfo an dieses Lied gehaengt und aus
;; `pending` entfernt.
(let* ((newnumber (+ 1 song-number))
(chaptertitle (if (and (pair? pending) (<= (car (car pending)) this-index))
(cadr (car pending)) #f))
(chaptercount (if chaptertitle (caddr (car pending)) 0))
;; Startet das Kapitel erst auf diesem Song (nicht schon auf
;; einer vorangehenden Seite), wird current-chapter jetzt
;; gesetzt und pending entfernt -- vor dem Bau des Headers,
;; damit chapterref bereits das neue Kapitel enthaelt.
(_ (if chaptertitle
(begin (set! current-chapter chaptertitle)
(set! pending (cdr pending)))))
(header #{ \bookpart { $(assq-ref songvars 'header) \header {
songfilename = $(symbol->string filename)
myindexlabel = #(assq-ref songvars 'label)
songnumber = #(number->string newnumber)
chaptertitle = #chaptertitle
chaptercount = #chaptercount
chapterref = #current-chapter
} } #})
(music (assq-ref songvars 'music))
(layout (assq-ref songvars 'layout))
(text-pages (assq-ref songvars 'text-pages))
(label (assq-ref songvars 'label)))
(set! song-number newnumber)
;; ChordPro_music_collector (Strophen unter den Noten, siehe
;; chordpro.ily) braucht den Song-Key beim tatsaechlichen
;; Interpretieren dieses Songs' Hauptscore. Im Buch ist DAS
;; NICHT synchron zu dieser for-each-Schleife -- LilyPond
;; interpretiert die \score-Bloecke aller Lieder gesammelt fuer
;; das Seitenlayout, oft NACHDEM die Schleife schon beim
;; naechsten (oder letzten) Lied angelangt ist. Ein globaler
;; chordpro-active-song-key waere zu diesem Zeitpunkt also auf
;; ein ANDERES Lied gesetzt -- die gesammelten Strophen landen
;; dann fälschlich im Store-Eintrag des zuletzt verarbeiteten
;; Lieds (beobachtet: mehrere Lieder ohne eigene \chordlyrics
;; wurden alle in den letzten Song der Test-Bibliothek
;; geschrieben). Deshalb setzen wir den Key per \applyContext
;; DIREKT IN DER MUSIK in chordpro-context-song-keys (siehe
;; chordpro.ily) -- das laeuft synchron zur tatsaechlichen
;; Interpretation dieses Scores und ist deshalb robust gegen die
;; verzoegerte/gesammelte Verarbeitung (eine LilyPond-Context-
;; Property ueber den \layout-Block waere hier keine Alternative:
;; LilyPond verwirft \applyContext-\ly:context-set-property!-
;; Aufrufe fuer nicht deklarierte Properties stillschweigend).
;; Der Music_collector liest bevorzugt diese Tabelle,
;; chordpro-active-song-key bleibt nur als Fallback
;; (Einzellied-Standalone-Pfad, dort synchron genug).
;; \consists nur bei chordpro-include-music-lyrics -- sonst
;; keine Kosten (Grob-Acknowledger fuer JEDEN Akkord/jede
;; Silbe/Strophenmarker des Hauptscores) fuer Buecher, die das
;; Feature nicht nutzen. Bewusst NICHT zusaetzlich an
;; chordpro-export-enabled gekoppelt -- die steuert nur, ob
;; \write-book-chordpro/\chordpro-delayed-write am Ende etwas
;; schreiben, nicht ob hier gesammelt wird.
(set! chordpro-active-song-key (symbol->string filename))
(let* ((collect-music-lyrics? (and (defined? 'chordpro-include-music-lyrics) chordpro-include-music-lyrics))
(song-layout
(if collect-music-lyrics?
#{
\layout {
$layout
songfilename = $(symbol->string filename)
\context {
\Score
\consists \ChordPro_music_collector
}
}
#}
#{
\layout {
$layout
songfilename = $(symbol->string filename)
}
#}))
(score-music
(if collect-music-lyrics?
#{
<<
\applyContext
#(let ((key (symbol->string filename)))
(lambda (ctx)
(hashq-set! chordpro-context-song-keys ctx key)))
$music
>>
#}
music)))
#{
\bookpart {
$header
\headerToTOC #header #label
\score {
$score-music
$song-layout
}
$(add-text-pages text-pages)
}
#})))))))))
ordered
)))
#(define (boernel-stats)
(let (
(songs (map (lambda (song) (symbol->string (car song))) (alist-delete 'emptyPage song-list)))
(opticalline "---------------------------------------------------------"))
(ly:warning (string-join (list
opticalline
(string-concatenate (list "Inkludiert: " (number->string (length songs)) " Lieder\n"))
;(string-join songs "\n")
"Nicht inkludiert:"
opticalline
(string-join (sort-list (lset-difference string=? (files-in-directory songPath) songs) string<?) "\n")
opticalline
) "\n" )
)))
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Include Images once and reference them:
#(define bbox-regexp
(ly:make-regex "%%BoundingBox:[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)[ \t]+([0-9-]+)"))
#(define (get-postscript-bbox string)
"Extract the bbox from STRING, or return #f if not present."
(let*
((match (ly:regex-exec bbox-regexp string)))
(if match
(map (lambda (x)
(string->number (ly:regex-match-substring match x)))
(cdr (iota 5)))
#f)))
#(define eps-references '())
#(define-public (eps-file-ref->stencil axis size just-embed file-name)
(let*
((already-embedded (assq (string->symbol file-name) eps-references))
(counter (if already-embedded (cadr already-embedded) (if (null-list? eps-references) 1 (+ 1 (cadar eps-references)))))
(form-name (ly:format "IDForm~a" counter))
(data-name (ly:format "ImageData~a" counter))
(eps-content (ly:gulp-file file-name))
(contents (ly:format "~a execform" form-name))
(bbox (if already-embedded (cddr already-embedded) (get-postscript-bbox (car (string-split eps-content #\nul)))))
(bbox-size (if (= axis X)
(- (list-ref bbox 2) (list-ref bbox 0))
(- (list-ref bbox 3) (list-ref bbox 1))
))
(factor (if (< 0 bbox-size)
(exact->inexact (/ size bbox-size))
0))
(scaled-bbox
(map (lambda (x) (* factor x)) bbox))
;; We need to shift the whole eps to (0,0), otherwise it will appear
;; displaced in lilypond (displacement will depend on the scaling!)
(translate-string (ly:format "~a ~a translate" (- (list-ref bbox 0)) (- (list-ref bbox 1))))
(clip-rect-string (ly:format
"~a ~a ~a ~a rectclip"
(list-ref bbox 0)
(list-ref bbox 1)
(- (list-ref bbox 2) (list-ref bbox 0))
(- (list-ref bbox 3) (list-ref bbox 1)))))
(if (not already-embedded) (set! eps-references
(acons (string->symbol file-name) (cons counter bbox) eps-references)))
(if bbox
(ly:make-stencil
(list
'embedded-ps
(string-append
(if (and already-embedded (not just-embed)) "" (ly:format
"
/~a
currentfile
<< /Filter /SubFileDecode
/DecodeParms << /EODCount 0 /EODString (*EOD*) >>
>> /ReusableStreamDecode filter
~a
*EOD*
def
/~a
<< /FormType 1
/BBox [~a ~a ~a ~a]
/Matrix [ 1 0 0 1 0 0]
/PaintProc
{ pop
/ostate save def
/showpage {} def
/setpagedevice /pop load def
~a 0 setfileposition ~a cvx exec
ostate restore
} bind
>> def
" data-name eps-content form-name (list-ref bbox 0) (list-ref bbox 1) (list-ref bbox 2) (list-ref bbox 3) data-name data-name))
(if just-embed "" (ly:format
"
gsave
currentpoint translate
BeginEPSF
~a dup scale
~a
~a
%%BeginDocument: ~a
~a
%%EndDocument
EndEPSF
grestore
" factor translate-string clip-rect-string file-name contents
))))
;; Stencil starts at (0,0), since we have shifted the eps, and its
;; size is exactly the size of the scaled bounding box
(if just-embed '(0 . 0) (cons 0 (- (list-ref scaled-bbox 2) (list-ref scaled-bbox 0))))
(if just-embed '(0 . 0) (cons 0 (- (list-ref scaled-bbox 3) (list-ref scaled-bbox 1)))))
(ly:make-stencil "" '(0 . 0) '(0 . 0)))
))
#(define-markup-command (epsfileref layout props axis size file-name)
(number? number? string?)
#:category graphic
"
@cindex inlining an Encapsulated PostScript image
Inline an EPS image. The image is scaled along @var{axis} to
@var{size}.
@lilypond[verbatim,quote]
\\markup {
\\general-align #Y #DOWN {
\\epsfile #X #20 #\"context-example.eps\"
\\epsfile #Y #20 #\"context-example.eps\"
}
}
@end lilypond"
(if (ly:get-option 'safe)
(interpret-markup layout props "not allowed in safe")
(eps-file-ref->stencil axis size #f file-name)
))
#(define-markup-command (epsfileembed layout props file-name)
(string?)
#:category graphic
"
@cindex inlining an Encapsulated PostScript image
Inline an EPS image. The image is scaled along @var{axis} to
@var{size}.
@lilypond[verbatim,quote]
\\markup {
\\general-align #Y #DOWN {
\\epsfile #X #20 #\"context-example.eps\"
\\epsfile #Y #20 #\"context-example.eps\"
}
}
@end lilypond"
(if (ly:get-option 'safe)
(interpret-markup layout props "not allowed in safe")
(eps-file-ref->stencil X 30 #t file-name)
))