9 Commits
Author SHA1 Message Date
tux 7de31cf0dd chapter toc 2026-07-07 12:16:13 +02:00
tux ae72640b1a chapters for every pagetype 2026-07-07 11:05:47 +02:00
tux 7076b16b63 Use chapters for digital toc
* introduce \songsChapter and \markuppage
* have nested chapters in PDF toc
2026-07-05 14:09:44 +02:00
tux 50e08220c4 make customEps path resolution more robust 2026-07-01 17:58:10 +02:00
tux 40f483bc67 default to numeric time signatures 2026-06-21 16:44:49 +02:00
tux 9b1297f675 fix tag group usage in markups 2026-06-04 00:05:42 +02:00
zuk aed11a70a7 Add meloref to author system 2026-05-12 10:42:14 +02:00
zuk 92a129f346 Fix double melody year in songinfo 2026-05-03 22:22:59 +02:00
zuk bb940b2a3b Fix transposeGlobal: Make additional transpositions possible 2026-04-22 11:45:57 +02:00
9 changed files with 267 additions and 70 deletions
@@ -57,6 +57,7 @@ generalLayout = \layout {
\remove "Metronome_mark_engraver" \remove "Metronome_mark_engraver"
\RemoveEmptyStaves \RemoveEmptyStaves
\override VerticalAxisGroup.remove-first = ##t \override VerticalAxisGroup.remove-first = ##t
\numericTimeSignature
\overrideTimeSignatureSettings \overrideTimeSignatureSettings
4/4 % timeSignatureFraction 4/4 % timeSignatureFraction
1/4 % baseMomentFraction 1/4 % baseMomentFraction
@@ -7,6 +7,7 @@
poetAndComposerEqualPrefix = "Worte und Weise:" poetAndComposerEqualPrefix = "Worte und Weise:"
voicePrefix = "Stimme:" voicePrefix = "Stimme:"
versePrefix = "Strophe:" versePrefix = "Strophe:"
refPrefix = "Refrain:"
translationAuthorPrefix = "Übersetzung:" translationAuthorPrefix = "Übersetzung:"
translationPrefix = "Übersetzung:" translationPrefix = "Übersetzung:"
pronunciationPrefix = "Aussprache:" pronunciationPrefix = "Aussprache:"
@@ -39,6 +40,7 @@
(versePoetData '()) (versePoetData '())
(composerIds '()) (composerIds '())
(verseComposerData '()) (verseComposerData '())
(refComposerIds '())
(voiceComposerData '()) (voiceComposerData '())
(compositionIds '()) (compositionIds '())
(adaptionTextIds '()) (adaptionTextIds '())
@@ -48,6 +50,7 @@
(year_text #f) (year_text #f)
(year_translation #f) (year_translation #f)
(year_melody #f) (year_melody #f)
(year_melody_meloref #f)
(year_composition #f) (year_composition #f)
(year_adaption_text #f) (year_adaption_text #f)
(year_adaption_music #f) (year_adaption_music #f)
@@ -60,12 +63,14 @@
(adaptionTextPrefix "") (adaptionTextPrefix "")
(adaptionMusicPrefix "") (adaptionMusicPrefix "")
(bridgePrefix "") (bridgePrefix "")
(interludePrefix "")) (interludePrefix "")
(refPrefix ""))
(if (and (if (and
(equal? poetIds composerIds) (equal? poetIds composerIds)
(null? translatorIds) (null? translatorIds)
(null? versePoetData) (null? versePoetData)
(null? verseComposerData) (null? verseComposerData)
(null? refComposerIds)
(null? voiceComposerData) (null? voiceComposerData)
(null? compositionIds) (null? compositionIds)
(null? adaptionTextIds) (null? adaptionTextIds)
@@ -104,6 +109,7 @@
(null? compositionIds) (null? compositionIds)
(null? adaptionMusicIds) (null? adaptionMusicIds)
(null? verseComposerData) (null? verseComposerData)
(null? refComposerIds)
(null? voiceComposerData) (null? voiceComposerData)
(null? bridgeIds) (null? bridgeIds)
(null? interludeIds)) #f (null? interludeIds)) #f
@@ -116,6 +122,10 @@
year_melody year_melody
) ", ") ) ", ")
(render-partial-contribution-group 'versePrefix verseComposerData) (render-partial-contribution-group 'versePrefix verseComposerData)
(join-present (list
(render-contribution-group refPrefix refComposerIds)
year_melody_meloref
) ", ")
(render-partial-contribution-group 'voicePrefix voiceComposerData) (render-partial-contribution-group 'voicePrefix voiceComposerData)
(join-present (list (join-present (list
(render-contribution-group compositionPrefix compositionIds) (render-contribution-group compositionPrefix compositionIds)
@@ -6,10 +6,20 @@
#(define-markup-command (customEps layout props ysize filename)(number? string?) #(define-markup-command (customEps layout props ysize filename)(number? string?)
#:properties ((songfilename "")) #:properties ((songfilename ""))
(interpret-markup layout props (interpret-markup layout props
(let ((defaulttitlemarkup (ly:output-def-lookup layout 'defaultTitleMarkup)) (let* ((defaulttitlemarkup (ly:output-def-lookup layout 'defaultTitleMarkup))
(filepath (if (string-null? songfilename) ;; Determine the song directory to resolve the EPS relative to.
filename ;; In TEXT the name arrives via the markup props (\setsongfilename),
(ly:format "~a/~a/~a" songPath songfilename filename)))) ;; but inside MUSIC (e.g. \mark) the props do not carry it, so we
;; fall back to the value stored in the score's \layout block.
(layout-songfilename (ly:output-def-lookup layout 'songfilename #f))
(songdir (cond ((not (string-null? songfilename)) songfilename)
((and (string? layout-songfilename)
(not (string-null? layout-songfilename)))
layout-songfilename)
(else #f)))
(filepath (if songdir
(ly:format "~a/~a/~a" songPath songdir filename)
filename)))
(if (file-exists? filepath) (if (file-exists? filepath)
(make-epsfile-markup Y ysize filepath) (make-epsfile-markup Y ysize filepath)
(if defaulttitlemarkup (if defaulttitlemarkup
@@ -101,6 +101,7 @@
#:versePoetData (find-author-id-with-part-numbers 'verse authors) #:versePoetData (find-author-id-with-part-numbers 'verse authors)
#:composerIds (find-author-ids-by 'melody authors) #:composerIds (find-author-ids-by 'melody authors)
#:verseComposerData (find-author-id-with-part-numbers 'meloverse authors) #:verseComposerData (find-author-id-with-part-numbers 'meloverse authors)
#:refComposerIds (find-author-ids-by 'meloref authors)
#:voiceComposerData (find-author-id-with-part-numbers 'voice authors) #:voiceComposerData (find-author-id-with-part-numbers 'voice authors)
#:compositionIds (find-author-ids-by 'composition authors) #:compositionIds (find-author-ids-by 'composition authors)
#:adaptionTextIds (find-author-ids-by 'adaption_text authors) #:adaptionTextIds (find-author-ids-by 'adaption_text authors)
@@ -110,6 +111,7 @@
#:year_text (chain-assoc-get 'header:year_text props #f) #:year_text (chain-assoc-get 'header:year_text props #f)
#:year_translation (chain-assoc-get 'header:year_translation props #f) #:year_translation (chain-assoc-get 'header:year_translation props #f)
#:year_melody (chain-assoc-get 'header:year_melody props #f) #:year_melody (chain-assoc-get 'header:year_melody props #f)
#:year_melody_meloref (chain-assoc-get 'header:year_melody_meloref props #f)
#:year_composition (chain-assoc-get 'header:year_composition props #f) #:year_composition (chain-assoc-get 'header:year_composition props #f)
#:year_adaption_text (chain-assoc-get 'header:year_adaption_text props #f) #:year_adaption_text (chain-assoc-get 'header:year_adaption_text props #f)
#:year_adaption_music (chain-assoc-get 'header:year_adaption_music props #f) #:year_adaption_music (chain-assoc-get 'header:year_adaption_music props #f)
@@ -123,7 +125,7 @@
#:adaptionMusicPrefix (ly:output-def-lookup layout 'adaptionMusicPrefix) #:adaptionMusicPrefix (ly:output-def-lookup layout 'adaptionMusicPrefix)
#:bridgePrefix (ly:output-def-lookup layout 'bridgePrefix) #:bridgePrefix (ly:output-def-lookup layout 'bridgePrefix)
#:interludePrefix (ly:output-def-lookup layout 'interludePrefix) #:interludePrefix (ly:output-def-lookup layout 'interludePrefix)
) #:refPrefix (ly:output-def-lookup layout 'refPrefix))
(list #f #f) (list #f #f)
) )
) )
@@ -143,7 +145,7 @@
(translation (chain-assoc-get 'header:translation props #f)) (translation (chain-assoc-get 'header:translation props #f))
(pronunciation (chain-assoc-get 'header:pronunciation props #f)) (pronunciation (chain-assoc-get 'header:pronunciation props #f))
(year_text (if (string? (car poet-and-composers)) #f (chain-assoc-get 'header:year_text props #f))) (year_text (if (string? (car poet-and-composers)) #f (chain-assoc-get 'header:year_text props #f)))
(year_melody (if (string? (car poet-and-composers)) #f (chain-assoc-get 'header:year_melody props #f)))) (year_melody (if (string? (cadr poet-and-composers)) #f (chain-assoc-get 'header:year_melody props #f))))
(markup (markup
#:override (cons 'songinfo:poet-maybe-with-composer #:override (cons 'songinfo:poet-maybe-with-composer
(if (and poet-maybe-with-composer (not (and (string? poet-maybe-with-composer) (string-null? poet-maybe-with-composer)))) poet-maybe-with-composer #f)) (if (and poet-maybe-with-composer (not (and (string? poet-maybe-with-composer) (string-null? poet-maybe-with-composer)))) poet-maybe-with-composer #f))
@@ -9,5 +9,8 @@ tagGroup =
#(define-markup-command (handle-tag-groups layout props recorded-groups m) (list? markup?) #(define-markup-command (handle-tag-groups layout props recorded-groups m) (list? markup?)
(resetTagGroups) (resetTagGroups)
(every (lambda (group) (define-tag-group group)) recorded-groups) (for-each
(lambda (group)
(define-tag-group group))
recorded-groups)
(interpret-markup layout props m)) (interpret-markup layout props m))
@@ -26,25 +26,49 @@
#(define pdf-encode (@@ (lily framework-ps) pdf-encode)) #(define pdf-encode (@@ (lily framework-ps) pdf-encode))
% PDF tags % PDF tags
#(define-markup-command (title-to-pdf-toc layout props title) (string?) % Erzeugt ein PDF-Lesezeichen (Outline-Eintrag) auf der aktuellen Seite.
% count = Anzahl der direkten Unterpunkte, die (in Seitenreihenfolge) direkt
% nachfolgen. 0 = Blatt-Eintrag (kein /Count), >0 = aufgeklappt,
% <0 = zugeklappt (Betrag = Anzahl direkter Kinder).
#(define (make-pdf-toc-stencil title count)
(if (string-null? title) (if (string-null? title)
empty-stencil empty-stencil
(ly:make-stencil (ly:make-stencil
(list 'embedded-ps (list 'embedded-ps
(ly:format (ly:format
"[/Action /GoTo /View [/XYZ -4 currentpagedevice /PageSize get 1 get 4 add null] /Title (~a) /OUT pdfmark" (pdf-encode title))) "[~a/Action /GoTo /View [/XYZ -4 currentpagedevice /PageSize get 1 get 4 add null] /Title (~a) /OUT pdfmark"
(if (= count 0) "" (ly:format "/Count ~a " count))
(pdf-encode title)))
empty-interval empty-interval empty-interval empty-interval
;'(0 . 0) '(0 . 0) ;'(0 . 0) '(0 . 0)
))) )))
% Einzelner (Blatt-)Eintrag im PDF-Inhaltsverzeichnis.
#(define-markup-command (title-to-pdf-toc layout props title) (string?)
(make-pdf-toc-stencil title 0))
% Kapitel-Eintrag mit verschachtelten Unterpunkten.
% count = Anzahl der direkt nachfolgenden Einträge, die diesem Kapitel
% untergeordnet werden sollen (positiv = aufgeklappt, negativ = zugeklappt).
#(define-markup-command (chapter-to-pdf-toc layout props count title) (integer? string?)
(make-pdf-toc-stencil title count))
#(define-markup-command (build-full-title layout props right)(boolean?) #(define-markup-command (build-full-title layout props right)(boolean?)
(interpret-markup layout (prepend-alist-chain 'songfilename (chain-assoc-get 'header:songfilename props "") props) (interpret-markup layout (prepend-alist-chain 'songfilename (chain-assoc-get 'header:songfilename props "") props)
(let* ((title (chain-assoc-get 'header:title props "")) (let* ((title (chain-assoc-get 'header:title props ""))
(starttext (chain-assoc-get 'header:starttext props #f)) (starttext (chain-assoc-get 'header:starttext props #f))
(pdfbookmark (if starttext (string-append starttext " | " title) title)) (pdfbookmark (if starttext (string-append starttext " | " title) title))
(chaptertitle (chain-assoc-get 'header:chaptertitle props #f))
(chaptercount (chain-assoc-get 'header:chaptercount props 0))
(title-markup (ly:output-def-lookup layout (if right 'oddTitleLineMarkup 'evenTitleLineMarkup)))) (title-markup (ly:output-def-lookup layout (if right 'oddTitleLineMarkup 'evenTitleLineMarkup))))
(if title (if title
(markup #:title-to-pdf-toc pdfbookmark title-markup) ;; Ist dieses Lied das erste eines Kapitels, wird der Kapitel-Eintrag
;; unmittelbar vor dem Lied-Eintrag ausgegeben, damit die pdfmark-/OUT-
;; Reihenfolge (Elternteil vor Kindern) im PDF-Stream garantiert stimmt.
(if (and (string? chaptertitle) (not (string-null? chaptertitle)))
(markup #:chapter-to-pdf-toc chaptercount chaptertitle
#:title-to-pdf-toc pdfbookmark title-markup)
(markup #:title-to-pdf-toc pdfbookmark title-markup))
make-null-markup) make-null-markup)
))) )))
+6 -1
View File
@@ -2,7 +2,12 @@ TRANSPOSITION = #(cons #f #f)
transposeGlobal = transposeGlobal =
#(define-void-function (from to) (ly:pitch? ly:pitch?) #(define-void-function (from to) (ly:pitch? ly:pitch?)
(set! TRANSPOSITION (cons from to))) (if (not (car TRANSPOSITION))
(set! TRANSPOSITION (cons from to))
(let ((current_to (cdr TRANSPOSITION))
(interval (ly:pitch-diff to from)))
(set! TRANSPOSITION (cons (car TRANSPOSITION)
(ly:pitch-transpose current_to interval))))))
transposable = transposable =
#(define-music-function (fromto music) (pair? ly:music?) #(define-music-function (fromto music) (pair? ly:music?)
+122 -21
View File
@@ -119,7 +119,7 @@ width may require additional tweaking.)"
includeSong = includeSong =
#(define-void-function (parser location filename) (string?) #(define-void-function (filename) (string?)
#{ #{
\bookOutputName #filename \bookOutputName #filename
#} #}
@@ -130,32 +130,47 @@ includeSong =
(acons label additional-page-numbers additional-page-switch-label-list)) (acons label additional-page-numbers additional-page-switch-label-list))
(set! song-list (set! song-list
(acons (string->symbol filename) (acons (string->symbol filename)
(acons 'label label `(
(acons 'header HEADER (label . ,label)
(acons 'music MUSIC (header . ,HEADER)
(acons 'layout LAYOUT (music . ,MUSIC)
(acons 'text-pages (layout . ,LAYOUT)
(map (lambda (text) (text-pages . ,(map (lambda (text)
#{ \markuplist \setsongfilename $filename $text #}) #{ \markuplist \setsongfilename $filename $text #})
TEXT_PAGES) TEXT_PAGES))
'()))))) )
song-list)) song-list))
)) ))
blankpage = blankpage =
#(define-void-function (parser location) () #(define-void-function () ()
(set! song-list (set! song-list
(acons 'emptyPage (acons 'emptyPage
'() '()
song-list))) song-list)))
imagepage = imagepage =
#(define-void-function (parser location xsize filename) (number? string?) #(define-void-function (xsize filename) (number? string?)
(set! song-list (set! song-list
(acons 'imagePage (acons 'imagePage
(acons 'xsize xsize (acons 'filename filename '())) `((xsize . ,xsize)
(filename . ,filename))
song-list))) 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?) #(define-markup-command (pagecenter layout props stuff)(markup?)
(interpret-markup layout props (interpret-markup layout props
(let ((halfpaperheight (/ (ly:output-def-lookup layout 'paper-height) 2)) (let ((halfpaperheight (/ (ly:output-def-lookup layout 'paper-height) 2))
@@ -164,16 +179,86 @@ imagepage =
))) )))
songs = songs =
#(define-void-function (parser location) () #(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) (for-each (lambda (songitems)
(let ((this-index index))
(set! index (+ index 1))
(ly:book-add-bookpart! (ly:parser-lookup '$current-book) (ly:book-add-bookpart! (ly:parser-lookup '$current-book)
(let ((filename (car songitems)) (let ((filename (car songitems))
(songvars (cdr songitems))) (songvars (cdr songitems)))
(if (eq? filename 'emptyPage) (if (eq? filename 'emptyPage)
#{ \bookpart { \markup { \null } } #} #{ \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) (if (eq? filename 'imagePage)
(let ((xsize (assq-ref songvars 'xsize)) (let* ((xsize (assq-ref songvars 'xsize))
(filename (ly:format "~a/~a" imagePagePath (assq-ref songvars 'filename)))) (filename (ly:format "~a/~a" imagePagePath (assq-ref songvars 'filename)))
(img-markup #{ \markup { \pagecenter { \epsfile #X #xsize #filename } } #}))
#{ \bookpart { #{ \bookpart {
\paper { \paper {
%{ %{
@@ -187,14 +272,30 @@ songs =
last-bottom-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0)) last-bottom-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0))
page-count = 1 page-count = 1
} }
\markup { \pagecenter { \epsfile #X #xsize #filename } } \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)) (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 { (header #{ \bookpart { $(assq-ref songvars 'header) \header {
songfilename = $(symbol->string filename) songfilename = $(symbol->string filename)
myindexlabel = #(assq-ref songvars 'label) myindexlabel = #(assq-ref songvars 'label)
songnumber = #(number->string newnumber) songnumber = #(number->string newnumber)
chaptertitle = #chaptertitle
chaptercount = #chaptercount
chapterref = #current-chapter
} } #}) } } #})
(music (assq-ref songvars 'music)) (music (assq-ref songvars 'music))
(layout (assq-ref songvars 'layout)) (layout (assq-ref songvars 'layout))
@@ -205,12 +306,12 @@ songs =
\bookpart { \bookpart {
$header $header
\headerToTOC #header #label \headerToTOC #header #label
\score { $music \layout { $layout } } \score { $music \layout { $layout songfilename = $(symbol->string filename) } }
$(add-text-pages text-pages) $(add-text-pages text-pages)
} }
#})))))) #}))))))))
(reverse song-list) ordered
)) )))
#(define (boernel-stats) #(define (boernel-stats)
(let ( (let (
+48 -7
View File
@@ -132,6 +132,37 @@
author-index-hash) author-index-hash)
(lambda (a b) (string-ci<? (car a) (car b)))))))) (lambda (a b) (string-ci<? (car a) (car b))))))))
% code for chapter index
% Anders als bei Kategorien/Autoren ergibt sich die Kapitelzugehoerigkeit nicht
% aus dem Song-Header, sondern aus dem \songsChapter, das dem Song vorangeht
% (in book_include.ily als Header-Feld chapterref an jeden Song gehaengt).
% Die Kapitel behalten hier ihre Auftritts-Reihenfolge (nicht alphabetisch),
% ebenso die Songs innerhalb eines Kapitels ihre Song-Reihenfolge.
#(define*-public (add-chapter-index-item! chapter markup-symbol textoptions #:optional label) #f)
#(define-public (chapter-index-items) #f)
#(let ((chapter-index-hash (make-hash-table))
(chapter-order '()))
(set! add-chapter-index-item!
(lambda* (chapter markup-symbol textoptions #:optional (label (gensym "index")))
(let* ((chaptersym (string->symbol chapter))
(existing (hashq-ref chapter-index-hash chaptersym #f))
(chapterlist (or existing
(begin
(set! chapter-order (cons chaptersym chapter-order))
(list (list label 'indexChapterMarkup `(((combine-with-next . #t) (rawtext . ,chapter))))))))
)
(hashq-set! chapter-index-hash chaptersym
(cons (list label markup-symbol textoptions) chapterlist)))
(make-music 'EventChord
'page-marker #t
'page-label label
'elements (list (make-music 'LabelEvent
'page-label label)))))
(set! chapter-index-items (lambda ()
(append-map (lambda (chaptersym) (reverse (hashq-ref chapter-index-hash chaptersym (list))))
(reverse chapter-order)))))
#(define-markup-command (with-link-symbol-ref layout props symbol arg) #(define-markup-command (with-link-symbol-ref layout props symbol arg)
(symbol? markup?) (symbol? markup?)
@@ -215,6 +246,11 @@
(make-null-markup)) (make-null-markup))
\vspace #.4 \vspace #.4
} }
indexChapterMarkup = \markup \override #'(baseline-skip . 1.5) \left-column {
\vspace #1
\sans \bold \fontsize #3 \fromproperty #'index:text
\vspace #.4
}
} }
#(define (prepare-item-markup items layout) #(define (prepare-item-markup items layout)
@@ -247,7 +283,8 @@
(let ((items (case index-type (let ((items (case index-type
((alphabetical) index-items) ((alphabetical) index-items)
((categories) category-index-items) ((categories) category-index-items)
((authors) author-index-items))) ((authors) author-index-items)
((chapters) chapter-index-items)))
(title (interpret-markup layout props title-markup))) (title (interpret-markup layout props title-markup)))
(cons title (cons title
(interpret-markup-list layout props (interpret-markup-list layout props
@@ -283,17 +320,18 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(versePoetData (find-author-id-with-part-numbers 'verse authors)) (versePoetData (find-author-id-with-part-numbers 'verse authors))
(composerIds (find-author-ids-by 'melody authors)) (composerIds (find-author-ids-by 'melody authors))
(verseComposerData (find-author-id-with-part-numbers 'meloverse authors)) (verseComposerData (find-author-id-with-part-numbers 'meloverse authors))
(refComposerIds (find-author-ids-by 'meloref authors))
(voiceComposerData (find-author-id-with-part-numbers 'voice authors)) (voiceComposerData (find-author-id-with-part-numbers 'voice authors))
(compositionIds (find-author-ids-by 'composition authors)) (compositionIds (find-author-ids-by 'composition authors))
(adaptionIds (find-author-ids-by 'adaption authors)) (adaptionIds (find-author-ids-by 'adaption authors))
(bridgeIds (find-author-ids-by 'bridge authors)) (bridgeIds (find-author-ids-by 'bridge authors))
(interludeIds (find-author-ids-by 'interlude authors))) (interludeIds (find-author-ids-by 'interlude authors)))
(delete-duplicates (delete-duplicates
(append poetIds translatorIds (map car versePoetData) composerIds (map car verseComposerData) (map car voiceComposerData) compositionIds adaptionIds bridgeIds interludeIds)) (append poetIds translatorIds (map car versePoetData) composerIds (map car verseComposerData) refComposerIds (map car voiceComposerData) compositionIds adaptionIds bridgeIds interludeIds))
)) ))
(let* (let*
( (
(extractedheadervars (extract-and-check-vars-from-header header '(title starttext alttitle categorytitle categories authors songnumber))) (extractedheadervars (extract-and-check-vars-from-header header '(title starttext alttitle categorytitle categories authors songnumber chapterref)))
(title (assq-ref extractedheadervars 'title)) (title (assq-ref extractedheadervars 'title))
(starttext (assq-ref extractedheadervars 'starttext)) (starttext (assq-ref extractedheadervars 'starttext))
(alttitle (assq-ref extractedheadervars 'alttitle)) (alttitle (assq-ref extractedheadervars 'alttitle))
@@ -301,12 +339,14 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(categories (assq-ref extractedheadervars 'categories)) (categories (assq-ref extractedheadervars 'categories))
(authors (assq-ref extractedheadervars 'authors)) (authors (assq-ref extractedheadervars 'authors))
(songnumber (assq-ref extractedheadervars 'songnumber)) (songnumber (assq-ref extractedheadervars 'songnumber))
(chapterref (assq-ref extractedheadervars 'chapterref))
(textoptions (lambda (text alternative) `(((rawtext . ,text) (alternative . ,alternative) (songnumber . ,songnumber))))) (textoptions (lambda (text alternative) `(((rawtext . ,text) (alternative . ,alternative) (songnumber . ,songnumber)))))
(add-to-toc! (lambda (toctitle alternative) (add-to-toc! (lambda (toctitle alternative)
(add-index-item! 'indexItemMarkup (textoptions toctitle alternative) toctitle label))) (add-index-item! 'indexItemMarkup (textoptions toctitle alternative) toctitle label)))
) )
(if categories (add-category-index-item! (string-tokenize categories) 'indexItemMarkup (textoptions (if categorytitle categorytitle title) #f) label)) (if categories (add-category-index-item! (string-tokenize categories) 'indexItemMarkup (textoptions (if categorytitle categorytitle title) #f) label))
(if authors (add-author-index-item! (all-author-ids authors) 'indexItemMarkup (textoptions (if categorytitle categorytitle title) #f) label)) (if authors (add-author-index-item! (all-author-ids authors) 'indexItemMarkup (textoptions (if categorytitle categorytitle title) #f) label))
(if chapterref (add-chapter-index-item! chapterref 'indexItemMarkup (textoptions (if categorytitle categorytitle title) #f) label))
(if starttext (add-to-toc! starttext #t)) (if starttext (add-to-toc! starttext #t))
(if alttitle (if alttitle
(if (list? alttitle) (if (list? alttitle)
@@ -365,7 +405,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(let ((song-lines (map (lambda (song) (let ((song-lines (map (lambda (song)
(let* ((filename (symbol->string (car song))) (let* ((filename (symbol->string (car song)))
(songvars (cdr song)) (songvars (cdr song))
(page-number (number->string (assoc-get (assq-ref songvars 'label) labelPageTable))) (page-number (assoc-get (assq-ref songvars 'label) labelPageTable))
(extractedheadervars (extract-and-check-vars-from-header (assq-ref songvars 'header) (extractedheadervars (extract-and-check-vars-from-header (assq-ref songvars 'header)
'(title starttext alttitle categorytitle categories authors year_text year_melody year_translation year_composition year_adaption infotext translation pronunciation copyright source))) '(title starttext alttitle categorytitle categories authors year_text year_melody year_translation year_composition year_adaption infotext translation pronunciation copyright source)))
(headervar-or-empty (lambda (varsym) (headervar-or-empty (lambda (varsym)
@@ -377,6 +417,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(versePoetData (find-author-id-with-part-numbers 'verse authors)) (versePoetData (find-author-id-with-part-numbers 'verse authors))
(composerIds (find-author-ids-by 'melody authors)) (composerIds (find-author-ids-by 'melody authors))
(verseComposerData (find-author-id-with-part-numbers 'meloverse authors)) (verseComposerData (find-author-id-with-part-numbers 'meloverse authors))
(refComposerIds (find-author-ids-by 'meloref authors))
(voiceComposerData (find-author-id-with-part-numbers 'voice authors)) (voiceComposerData (find-author-id-with-part-numbers 'voice authors))
(compositionIds (find-author-ids-by 'composition authors)) (compositionIds (find-author-ids-by 'composition authors))
(adaptionTextIds (find-author-ids-by 'adaption_text authors)) (adaptionTextIds (find-author-ids-by 'adaption_text authors))
@@ -398,7 +439,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(headervar-or-empty 'categories) (headervar-or-empty 'categories)
(format-authors (append poetIds adaptionTextIds (map car versePoetData))) (format-authors (append poetIds adaptionTextIds (map car versePoetData)))
(format-authors translatorIds) (format-authors translatorIds)
(format-authors (append composerIds compositionIds adaptionMusicIds bridgeIds interludeIds (map car voiceComposerData) (map car verseComposerData))) (format-authors (append composerIds compositionIds adaptionMusicIds bridgeIds interludeIds (map car voiceComposerData) (map car verseComposerData) refComposerIds))
(headervar-or-empty 'year_text) (headervar-or-empty 'year_text)
(headervar-or-empty 'year_melody) (headervar-or-empty 'year_melody)
(headervar-or-empty 'year_translation) (headervar-or-empty 'year_translation)
@@ -411,7 +452,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
(format-info-paragraphs (headervar-or-empty 'translation)) (format-info-paragraphs (headervar-or-empty 'translation))
(format-info-paragraphs (headervar-or-empty 'pronunciation)) (format-info-paragraphs (headervar-or-empty 'pronunciation))
)))) ))))
(alist-delete 'imagePage (alist-delete 'emptyPage song-list))))) (alist-delete 'markupPage (alist-delete 'imagePage (alist-delete 'emptyPage song-list))))))
(call-with-output-file "toc.csv" (call-with-output-file "toc.csv"
(lambda (port) (lambda (port)
(csv-write (cons '( (csv-write (cons '(
@@ -441,6 +482,6 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
; we use a delayed stencil to have all the page references available ; we use a delayed stencil to have all the page references available
(ly:make-stencil (ly:make-stencil
`(delay-stencil-evaluation `(delay-stencil-evaluation
,(delay (let* ((table (ly:output-def-lookup layout 'label-absolute-page-table))) ,(delay (let* ((table (ly:output-def-lookup layout 'label-page-string-table)))
(generate-toc-csv (if (list? table) table '())) (generate-toc-csv (if (list? table) table '()))
empty-stencil))))) empty-stencil)))))