diff --git a/annotate/README.md b/annotate/README.md index 0db1993..d70f31c 100644 --- a/annotate/README.md +++ b/annotate/README.md @@ -12,12 +12,12 @@ annotations are printed* (see `scholarly/annotate/config.ily`). At the beginning the music document, we can tell *scholarLY* which types of documents to export: ```lilypond -\setOption scholarly.annotate.export-targets #'(plaintext latex) +\setOption scholarly.annotate.export-targets #'(plaintext html latex) ``` Those output files will automatically format annotations to be further processed by the -relevant programs. Currently, *plaintext* and *latex* are available, while other types -are on the wishlist (such as *scheme* and *markdown*). +relevant programs. Currently, *plaintext*, *latex* and *html* are available, while other types +are on the wishlist (such as *scheme*, *json* and *markdown*). ## Basic Syntax diff --git a/annotate/config.ily b/annotate/config.ily index 82a1853..2e5e930 100644 --- a/annotate/config.ily +++ b/annotate/config.ily @@ -94,6 +94,25 @@ \registerOption scholarly.colorize ##t + + +%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Filenames to Export +%%%%%%%%%%%%%%%%%%%%%%%% +% default is .annotations. +\registerOption scholarly.annotate.export.filenames +% +#`((html . "index.html") ;; html + (latex . default) ;; latex + (scheme . default) ;; scheme + (plaintext . default) ;; plaintext + ) + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Handling of annotation types for plain text output %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -111,6 +130,91 @@ (question . "Question:") (todo . "TODO:")) + + +%%%%%%%%%%%%%%%%% +%%%% HTML options +%%%%%%%%%%%%%%%%% + +% Annotation types for html text output +\registerOption scholarly.annotate.export.html.labels +#`((critical-remark . "Critical Remark") + (musical-issue . "Musical Issue") + (lilypond-issue . "Lilypond Issue") + (question . "Question") + (todo . "TODO")) + +% Print full document with header (including CSS link) and body, or just +% annotations div +\registerOption scholarly.annotate.export.html.full-document ##t + + +% Annotation div types (can technically be anything, since they get directly +% converted to string; so even a new type of div, or `a`, or whatever else.) +\registerOption scholarly.annotate.export.html.divs +#`((full-ann-list . ol) ;; the div containing all annotations + (each-ann-outer . li) ;; the outer shell of an annotation + (each-ann-inner . ul) ;; the inner shell of an annotation + (each-ann-props . li) ;; each prop + ) + +\registerOption scholarly.annotate.export.html.annotations-div-tags + #`((class . "my-annotations") + (id . #f)) + +% Which props to print to html +\registerOption scholarly.annotate.export.html.props + #`(type grob-location grob-type message) + +% Which labels to print for props (only affect props included in previous list) +\registerOption scholarly.annotate.export.html.prop-labels + #`((type . "Type: ") + (grob-location . #f) + (grob-type . #f) + (message . #f)) + + +% Which stylesheet to link, or print in header, or generate +% if the `use-css` is set to default, it will ignore whatever is here +% and point to the default stylesheet. We happen to refer to that here +% by default anyway. But the name/location of that default styles should +% be hardcoded into export-html.ily presumably. +\registerOption scholarly.annotate.export.html.external-css-name + #"my-external-styles.css" + +\registerOption scholarly.annotate.export.html.generate-css-name + #"my-generated-styles.css" + + +% How to handle CSS upon html export +% #`inline = embed css inline (very much not yet implemented) +% #`header = print in header; +% #`linked = link in header, which also means export css (default or generated) +\registerOption scholarly.annotate.export.html.with-css + #`linked + + +% Which CSS to use when used at all (default is the fallback if not one of the other two) +% #`default = handle the default CSS included in the repository +% #`generate = generate a new CSS (using the name from `css-name` option) +% #`external = (link to) external stylesheet (does not support *importing* yet) +\registerOption scholarly.annotate.export.html.use-css + #`default + + + + +% css settings for a generated file +% for linking, scholarLY converts this all into a css file and links to it +% for printing in header, we do the same but print into the header +% for embedded/inline css, scholarly sorts the option as best it can; if the +% options don't match the divs (say, we have a setting for "foo" class, but there +% is no "foo" class in the document), it is ignored. +\registerOption scholarly.annotate.export.html.generate-css-settings #`() + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Handling of annotation types for LaTeX output %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/annotate/export-html.ily b/annotate/export-html.ily new file mode 100644 index 0000000..a484fe2 --- /dev/null +++ b/annotate/export-html.ily @@ -0,0 +1,277 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% This file is part of ScholarLY, % +% ========= % +% a toolkit library for scholarly work with GNU LilyPond and LaTeX, % +% belonging to openLilyLib (https://github.com/openlilylib/openlilylib % +% ----------- % +% % +% ScholarLY 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. % +% % +% ScholarLY 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 Lesser General Public License for more details. % +% % +% You should have received a copy of the GNU General Public License % +% along with ScholarLY. If not, see . % +% % +% ScholarLY is maintained by Urs Liska, ul@openlilylib.org % +% Copyright Urs Liska, 2015-17 % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Export annotations to html file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#(use-modules (ice-9 rdelim)) +#(use-modules (ice-9 regex)) + + +% Default CSS Settings: +% ~~~~~~~~~~~~~~~~~~~~ +% If the class/id name matches a builtin (such as annotations, annotation, etc.) +% then it is applied to that div (even if the user has distinguished a new class/id +% name for that particular div). This allows default css to still be toggled +% without the user needing to manually rename their stuff to match these. +% OTHERWISE, if it isn't a builtin, scholarLY turns that name (such as +% `my-id-for-something` below) into a string, and applies it to the matching +% class/id at the time of processing. + +#(define default-css-settings + `((class . ((full-ann-list . ("background: gray" + "margin: 0.5em" + "line-height: 1.2" + (ul . "list-style-type: none"))) + (annotation . ((ul . "background: lightgray") + (ul_li . "margin: 0.25em"))) + (todo . ("background: red")) + (question . ("background: green")))) + (id . ((my-unique-annotations-list . ("foo: bar")))))) + +% nicely formatted css for header or exported +#(define (formatted-css css-type) + (let* ((css-settings (if (eq? css-type 'default) + default-css-settings + (getOption `(scholarly annotate export html + generate-css-settings)))) + (pretty-css "")) + (for-each + ;; family = a group named .class, #id, or anything else + (lambda (family) + (for-each + ;; member = each .class, #id, or anything else + (lambda (member) + (set! pretty-css (string-append pretty-css + (format (cond ((eq? (car family) 'class) + "\n.~a {~a \n}\n") + ((eq? (car family) 'id) + "\n#~a {~a \n}\n") + (else "\n~a {~a \n}\n")) + (if (eq? (car member) 'full-ann-list) + (let ((anns-tag-redefined + (getChildOption + `(scholarly annotate export html annotations-div-tags) + (car family)))) + (if anns-tag-redefined + anns-tag-redefined + (car member))) + (car member)) + ;; sub-mem-styles = a list of everything in each sub-member: + ;; sub-member { sub-mem-styles } + (let ((sub-mem-styles "")) + (for-each + ;; sub-member = list of strings and/or pairs + (lambda (sub-member) + (set! sub-mem-styles + (if (string? sub-member) + ;; string gets printed literally + (format "~a\n ~a;" sub-mem-styles sub-member) + ;; pairs are formatted like members and squeezed + ;; between the first and last delimiters + (format "~a\n} ~a {~a" + sub-mem-styles + (let ((multi-segs (string-match "(-|_)" + (symbol->string (car sub-member))))) + (if (not multi-segs) + (car sub-member) + ;; turn "ul_li" or "ul-li" into "ul li" + (regexp-substitute #f + multi-segs + 'pre " " 'post))) + (if (string? (cdr sub-member)) + (format "\n ~a;" (cdr sub-member)) + ;; it is a list of strings + (let ((sub-lst "")) + (for-each + (lambda (mem) + (set! sub-lst + (format "~a\n ~a;" + sub-lst mem))) + (cdr sub-member)) + sub-lst)))))) + (cdr member)) + sub-mem-styles))))) + (cdr family))) + css-settings) + pretty-css)) + + +#(define (indent inpt num) + (string-append (make-string num #\space) inpt)) + +% convenience functions +#(define (classify-html-tag class) + (format "class=\"~a\"" class)) +#(define (idify-html-tag id) + (format "id=\"~a\"" id)) +#(define (delimit-html-tags tags) + (format "<~a>" (string-join tags " "))) + +% open div with unique tags +#(define (div-open type ann-or-string nest-level) + ;; if class = string, don't check for an id. otherwise it + ;; is an ann props list, so check for an id and apply if necessary + (let* ((trailing-line (if (= nest-level 0) "\n" "")) + (div-type (symbol->string + (getChildOption `(scholarly annotate export html divs) type))) + (class (classify-html-tag (if (string? ann-or-string) + (if (equal? ann-or-string "annotations") + (let ((anns-class-renamed (assq-ref (getOption + `(scholarly annotate export html annotations-div-tags)) + 'class))) + (if anns-class-renamed + anns-class-renamed + ann-or-string)) + ann-or-string) + "annotation"))) + (id + (if (equal? ann-or-string "annotations") + (let ((anns-id-renamed (assq-ref (getOption + `(scholarly annotate export html annotations-div-tags)) + 'id))) + (if anns-id-renamed + (idify-html-tag anns-id-renamed) "")) + (let ((html-id (and (not (string? ann-or-string)) + (assq-ref ann-or-string 'html-id)))) + (if html-id (idify-html-tag html-id) "")))) + (div-tags (delimit-html-tags (list div-type class id))) + (div-begin (format "~a~a" (indent div-tags nest-level) trailing-line))) + (append-to-output-stringlist div-begin))) + +% close any div +#(define (div-close type nest-level) + (let ((div-type (symbol->string (getChildOption + `(scholarly annotate export html divs) + type))) + (trailing-line (if (= nest-level 1) "\n" ""))) + (append-to-output-stringlist + (indent (format "~a" div-type trailing-line) nest-level)))) + +% get all the props we want exported from the option +#(define (html-process-props ann) + (let ((props (getOption `(scholarly annotate export html props)))) + (for-each + (lambda (prop) + (let* ((key (assq-ref (getOption + `(scholarly annotate export html prop-labels)) + prop)) + (val (cond ((equal? prop 'grob-location) + (format-location ann)) + ((equal? prop 'type) + (getChildOption + `(scholarly annotate export html labels) + (assq-ref ann 'type))) + (else (assq-ref ann prop))))) + (begin + (if (symbol? val) + (set! val (symbol->string val))) + (div-open 'each-ann-props (symbol->string prop) 3) + (append-to-output-stringlist + (indent (if key (string-append key val) val) 4)) + (div-close 'each-ann-props 3)))) + props))) + + + +\register-export-routine html +#(lambda () + + (let* ((full-doc (getOption `(scholarly annotate export html full-document))) + (css-method (getOption `(scholarly annotate export html with-css))) + (css-type (getOption `(scholarly annotate export html use-css))) + (css-name (cond ((eq? css-type 'generate) + (getOption `(scholarly annotate export html generate-css-name))) + ((eq? css-type 'external) + (getOption `(scholarly annotate export html external-css-name))) + (else "default-stylesheet.css")))) + + ;; If option is True, add the header and body + (if full-doc + (begin + (append-to-output-stringlist (format +" + + + + + ~a + + + +" + ;; insert link to css, or directly embed css in header. + (cond ((eq? css-method 'linked) + (format "" + css-name)) + ((eq? css-method 'header) + (format "\n\n" (formatted-css css-type)))))) + ;; if we want to generate and link to a seperate .css file + (if (and (eq? css-method 'linked) + (or (eq? css-type 'generate) + (eq? css-type 'default))) + (with-output-to-file + css-name ; css-name + (lambda () (write-line (formatted-css css-type))))))) + + + + ;; wrap everything in the annotations div. this is sort of redundant, but + ;; could be useful if projects have multiple bookparts with annotation lists. + (div-open 'full-ann-list "annotations" 0) + + (for-each + (lambda (ann) + + ;; wrap each annotation in the common annotation class + ;; add div ID tag if available + (div-open 'each-ann-outer ann 1) + + ;; type as a class - maybe we want different types to have some different styles + ;; this also lets us make each *ann* a list itself if we want + (div-open 'each-ann-inner (symbol->string (assq-ref ann 'type)) 2) + + ;; add the rest of the props to output + (html-process-props ann) ;; indents x 3 + + (div-close 'each-ann-inner 2) + + (div-close 'each-ann-outer 1)) + + annotations) + + ;; close "annotations" div + (div-close 'full-ann-list 0) + + (if full-doc + (begin + (append-to-output-stringlist " + +"))) + + ;; write to output file + (write-output-file 'html))) diff --git a/annotate/export-latex.ily b/annotate/export-latex.ily index aa2cd54..d6efd4b 100644 --- a/annotate/export-latex.ily +++ b/annotate/export-latex.ily @@ -218,5 +218,4 @@ annotations) ;; write to output file - (write-output-file "inp")) - + (write-output-file 'latex)) diff --git a/annotate/export-plaintext.ily b/annotate/export-plaintext.ily index b7a5f32..cb6b69d 100644 --- a/annotate/export-plaintext.ily +++ b/annotate/export-plaintext.ily @@ -73,5 +73,4 @@ annotations) ;; write to output file - (write-output-file "log")) - + (write-output-file 'plaintext)) diff --git a/annotate/export.ily b/annotate/export.ily index 60b2286..dfc510e 100644 --- a/annotate/export.ily +++ b/annotate/export.ily @@ -86,15 +86,22 @@ setAnnotationOutputBasename = % Take the stringlist 'annotate-export-stringlist % and write it out to a file -#(define (write-output-file ext) +#(define (write-output-file type) ; ; TODO ; remove "messages" here and directly use the global object ; - ; TODO - ; Make the file name configurable and let it respect the target format - ; - (let* ((logfile (format "~a.annotations.~a" annotation-out-basename ext))) + (let* ((default-exts '((html . "html") + (latex . "inp") + (scheme . "scm") + (plaintext . "log"))) + (logfile (if (equal? (getChildOption + '(scholarly annotate export filenames) type) + 'default) + (format "~a.annotations.~a" annotation-out-basename + (assoc-ref default-exts type)) + (getChildOption + '(scholarly annotate export filenames) type)))) (ly:message "writing '~a' ..." logfile) (with-output-to-file logfile (lambda () @@ -140,5 +147,3 @@ setAnnotationOutputBasename = ly:message) (ly:message ""))) annotations)) - - diff --git a/annotate/module.ily b/annotate/module.ily index a6f947a..bcc0303 100644 --- a/annotate/module.ily +++ b/annotate/module.ily @@ -54,6 +54,7 @@ \include "export.ily" \include "export-latex.ily" \include "export-plaintext.ily" +\include "export-html.ily" \include "engraver.ily" % Include `editorial-functions` module diff --git a/usage-examples/annotate.ly b/usage-examples/annotate.ly index 2474020..ac5a0a7 100644 --- a/usage-examples/annotate.ly +++ b/usage-examples/annotate.ly @@ -8,7 +8,36 @@ \markup \vspace #1 -\setOption scholarly.annotate.export-targets #'(plaintext latex) +\setOption scholarly.annotate.export-targets #'(plaintext latex html) + +\setOption scholarly.annotate.export.html.props + #`(type grob-type message) + +\setOption scholarly.annotate.export.html.generate-css-settings + #`((class . ((full-ann-list . ("margin: 1em" + "padding-left: 0" + (ul . "list-style-type: none"))) + (annotation . ("margin-top: 1em" + "margin-left: 0.25" + "background: #b0b0bb" + (ul . "padding-left: 0"))) + (todo . ("background: #caa8a8" + "color: darkred")) + (type . ("color: white" + "background: #444444")))) + (id . ((my-id-for-something . ("foo: bar")))) + ;; "tag" is just a generic key here.. anything other than + ;; "class" or "id" just means that the elements within it + ;; will be printed without "." or "#". + (tag . ((body . ("width: 50%" + "min-width: 400px"))))) + +\setOption scholarly.annotate.export.html.use-css + #`generate +\setOption scholarly.annotate.export.html.with-css + #`linked + +%\displayOptions music = \relative c'{ c4 d e @@ -32,13 +61,14 @@ music = \relative c'{ { \voiceOne \criticalRemark \with { message = "An annotation for the top voice." + html-id = "my-unique-id" } NoteHead cis d } \new Voice = "voice two" { \voiceTwo - \criticalRemark \with { + \todo \with { message="A note about the second voice." } NoteHead