Use chapters for digital toc
* introduce \songsChapter and \markuppage * have nested chapters in PDF toc
This commit is contained in:
@@ -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)
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|||||||
@@ -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,42 +179,92 @@ 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))))))
|
||||||
|
(index 0))
|
||||||
(for-each (lambda (songitems)
|
(for-each (lambda (songitems)
|
||||||
(ly:book-add-bookpart! (ly:parser-lookup '$current-book)
|
(let ((this-index index))
|
||||||
(let ((filename (car songitems))
|
(set! index (+ index 1))
|
||||||
(songvars (cdr songitems)))
|
(ly:book-add-bookpart! (ly:parser-lookup '$current-book)
|
||||||
(if (eq? filename 'emptyPage)
|
(let ((filename (car songitems))
|
||||||
#{ \bookpart { \markup { \null } } #}
|
(songvars (cdr songitems)))
|
||||||
(if (eq? filename 'imagePage)
|
(if (eq? filename 'emptyPage)
|
||||||
(let ((xsize (assq-ref songvars 'xsize))
|
#{ \bookpart { \markup { \null } } #}
|
||||||
(filename (ly:format "~a/~a" imagePagePath (assq-ref songvars 'filename))))
|
(if (eq? filename 'markupPage)
|
||||||
#{ \bookpart {
|
#{ \bookpart { \markup { $(assq-ref songvars 'markup) } } #}
|
||||||
\paper {
|
(if (eq? filename 'imagePage)
|
||||||
%{
|
(let ((xsize (assq-ref songvars 'xsize))
|
||||||
inner-margin = 0
|
(filename (ly:format "~a/~a" imagePagePath (assq-ref songvars 'filename))))
|
||||||
outer-margin = 0
|
#{ \bookpart {
|
||||||
binding-offset = 0
|
\paper {
|
||||||
top-margin = 0
|
%{
|
||||||
bottom-margin = 0
|
inner-margin = 0
|
||||||
%}
|
outer-margin = 0
|
||||||
print-page-number = ##f
|
binding-offset = 0
|
||||||
last-bottom-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0))
|
top-margin = 0
|
||||||
page-count = 1
|
bottom-margin = 0
|
||||||
}
|
%}
|
||||||
\markup { \pagecenter { \epsfile #X #xsize #filename } }
|
print-page-number = ##f
|
||||||
} #}
|
last-bottom-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0))
|
||||||
)
|
page-count = 1
|
||||||
(let* ((newnumber (+ 1 song-number))
|
}
|
||||||
(header #{ \bookpart { $(assq-ref songvars 'header) \header {
|
\markup { \pagecenter { \epsfile #X #xsize #filename } }
|
||||||
songfilename = $(symbol->string filename)
|
} #}
|
||||||
myindexlabel = #(assq-ref songvars 'label)
|
)
|
||||||
songnumber = #(number->string newnumber)
|
;; Ist ein Kapitel faellig (Start <= aktuelle Position) und dies ein
|
||||||
} } #})
|
;; echtes Lied, wird die Kapitelinfo an dieses Lied gehaengt und aus
|
||||||
(music (assq-ref songvars 'music))
|
;; `pending` entfernt.
|
||||||
(layout (assq-ref songvars 'layout))
|
(let* ((newnumber (+ 1 song-number))
|
||||||
(text-pages (assq-ref songvars 'text-pages))
|
(chaptertitle (if (and (pair? pending) (<= (car (car pending)) this-index))
|
||||||
(label (assq-ref songvars 'label)))
|
(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)
|
(set! song-number newnumber)
|
||||||
#{
|
#{
|
||||||
\bookpart {
|
\bookpart {
|
||||||
@@ -208,9 +273,9 @@ songs =
|
|||||||
\score { $music \layout { $layout songfilename = $(symbol->string filename) } }
|
\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 (
|
||||||
|
|||||||
@@ -366,7 +366,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)
|
||||||
@@ -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 '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 '(
|
||||||
@@ -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
|
; 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)))))
|
||||||
|
|||||||
Reference in New Issue
Block a user