diff --git a/private_includes/base/title_with_category_images.ily b/private_includes/base/title_with_category_images.ily index 8a4f87a..0c3b649 100644 --- a/private_includes/base/title_with_category_images.ily +++ b/private_includes/base/title_with_category_images.ily @@ -26,25 +26,49 @@ #(define pdf-encode (@@ (lily framework-ps) pdf-encode)) % 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) empty-stencil (ly:make-stencil (list 'embedded-ps (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 ;'(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?) (interpret-markup layout (prepend-alist-chain 'songfilename (chain-assoc-get 'header:songfilename props "") props) (let* ((title (chain-assoc-get 'header:title props "")) (starttext (chain-assoc-get 'header:starttext props #f)) (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)))) (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) ))) diff --git a/private_includes/book/book_include.ily b/private_includes/book/book_include.ily index cf20fad..87393e1 100644 --- a/private_includes/book/book_include.ily +++ b/private_includes/book/book_include.ily @@ -119,7 +119,7 @@ width may require additional tweaking.)" includeSong = -#(define-void-function (parser location filename) (string?) +#(define-void-function (filename) (string?) #{ \bookOutputName #filename #} @@ -130,32 +130,47 @@ includeSong = (acons label additional-page-numbers additional-page-switch-label-list)) (set! song-list (acons (string->symbol filename) - (acons 'label label - (acons 'header HEADER - (acons 'music MUSIC - (acons 'layout LAYOUT - (acons 'text-pages - (map (lambda (text) + `( + (label . ,label) + (header . ,HEADER) + (music . ,MUSIC) + (layout . ,LAYOUT) + (text-pages . ,(map (lambda (text) #{ \markuplist \setsongfilename $filename $text #}) - TEXT_PAGES) - '()))))) + TEXT_PAGES)) + ) song-list)) )) blankpage = -#(define-void-function (parser location) () +#(define-void-function () () (set! song-list (acons 'emptyPage '() song-list))) imagepage = -#(define-void-function (parser location xsize filename) (number? string?) +#(define-void-function (xsize filename) (number? string?) (set! song-list (acons 'imagePage - (acons 'xsize xsize (acons 'filename filename '())) + `((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)) @@ -164,42 +179,92 @@ imagepage = ))) 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)))))) + (index 0)) (for-each (lambda (songitems) - (ly:book-add-bookpart! (ly:parser-lookup '$current-book) - (let ((filename (car songitems)) - (songvars (cdr songitems))) - (if (eq? filename 'emptyPage) - #{ \bookpart { \markup { \null } } #} - (if (eq? filename 'imagePage) - (let ((xsize (assq-ref songvars 'xsize)) - (filename (ly:format "~a/~a" imagePagePath (assq-ref songvars '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 { \pagecenter { \epsfile #X #xsize #filename } } - } #} - ) - (let* ((newnumber (+ 1 song-number)) - (header #{ \bookpart { $(assq-ref songvars 'header) \header { - songfilename = $(symbol->string filename) - myindexlabel = #(assq-ref songvars 'label) - songnumber = #(number->string newnumber) - } } #}) - (music (assq-ref songvars 'music)) - (layout (assq-ref songvars 'layout)) - (text-pages (assq-ref songvars 'text-pages)) - (label (assq-ref songvars 'label))) + (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 { \null } } #} + (if (eq? filename 'markupPage) + #{ \bookpart { \markup { $(assq-ref songvars 'markup) } } #} + (if (eq? filename 'imagePage) + (let ((xsize (assq-ref songvars 'xsize)) + (filename (ly:format "~a/~a" imagePagePath (assq-ref songvars '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 { \pagecenter { \epsfile #X #xsize #filename } } + } #} + ) + ;; 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)) + (header #{ \bookpart { $(assq-ref songvars 'header) \header { + songfilename = $(symbol->string filename) + myindexlabel = #(assq-ref songvars 'label) + songnumber = #(number->string newnumber) + chaptertitle = #chaptertitle + chaptercount = #chaptercount + } } #}) + (music (assq-ref songvars 'music)) + (layout (assq-ref songvars 'layout)) + (text-pages (assq-ref songvars 'text-pages)) + (label (assq-ref songvars 'label))) + (if chaptertitle (set! pending (cdr pending))) (set! song-number newnumber) #{ \bookpart { @@ -208,9 +273,9 @@ songs = \score { $music \layout { $layout songfilename = $(symbol->string filename) } } $(add-text-pages text-pages) } - #})))))) - (reverse song-list) - )) + #})))))))) + ordered + ))) #(define (boernel-stats) (let ( diff --git a/private_includes/book/toc_include.ily b/private_includes/book/toc_include.ily index c8a02d9..d9c2611 100644 --- a/private_includes/book/toc_include.ily +++ b/private_includes/book/toc_include.ily @@ -366,7 +366,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s (let ((song-lines (map (lambda (song) (let* ((filename (symbol->string (car 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) '(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) @@ -413,7 +413,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 '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" (lambda (port) (csv-write (cons '( @@ -443,6 +443,6 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s ; we use a delayed stencil to have all the page references available (ly:make-stencil `(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 '())) empty-stencil)))))