# HG changeset patch # User Arne Babenhauserheide # Date 1690264895 -7200 # Tue Jul 25 08:01:35 2023 +0200 # Node ID d4f9b0820bb31bc8e70ac3e935bfb436e4c25229 # Parent 1019c0eb0da0bf26b8042627e731c3abc3b43f89 add filktex.el diff --git a/build.sh b/build.sh --- a/build.sh +++ b/build.sh @@ -9,7 +9,7 @@ if ! test -e Makefile; then ln -s ../MakeStuff/Makefile; fi for i in *ly; do - lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts --pdf -o "$(basename "$i" .ly)"-lily $i + lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts --pdf -o "$(basename "$i" .ly)" $i # cannot use pdf as backend with epubli # gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.3 -dPDFSETTINGS=/printer -dColorImageResolution=600 -dEmbedAllFonts=true -sOutputFile="$(basename "$i" .ly)".pdf "$(basename "$i" .ly)"-lily.pdf # rm "$(basename "$i" .ly)".pdf diff --git a/delfini.flk b/delfini.flk --- a/delfini.flk +++ b/delfini.flk @@ -4,7 +4,7 @@ \subtitle{Für A} \catcode`\[=12%allow using [ as parameter -\includegraphics[width=0.96\linewidth, keepaspectratio, trim=0 160cm 0 0]{delfini-tune}% trim, because lilypond creates full-page pngs +\includegraphics[width=0.96\linewidth, keepaspectratio]{delfini-tune}% trim, because lilypond creates full-page pngs \catcode`\[=13%force [ to start chords again \Large diff --git a/der-falke.flk b/der-falke.flk --- a/der-falke.flk +++ b/der-falke.flk @@ -3,7 +3,7 @@ \index{Ich zog mir einen Falken} \subtitle{Für A, wohl von 1160} \catcode`\[=12%allow using [ as parameter -\includegraphics[width=0.96\linewidth, keepaspectratio, trim=0 160cm 0 0]{der-falke-tune} +\includegraphics[width=0.96\linewidth, keepaspectratio]{der-falke-tune} \catcode`\[=13%force [ to start chords again \Large diff --git a/filktex.el b/filktex.el new file mode 100644 --- /dev/null +++ b/filktex.el @@ -0,0 +1,244 @@ +;;; filktex -- support for editing filktex files + +;;; Commentary: + +;; TODO: adapt chords-above-text conversion from https://github.com/ssavitzky/Honu/blob/master/emacs/flktex-mode.el#L124 + +;;; Code: + +; How to write a good font=lock-mode http://ergoemacs.org/emacs/elisp_syntax_coloring.html (TODO…) + +(defun replace-regexp-buffer (reg to) + (replace-regexp reg to nil (point-min) (point-max))) + +(defun songbook->filktex () + "Convert a buffer from songbook to filktex." + (interactive) + (save-excursion + (replace-string "{soc}" "\\begin{refrain}" nil (point-min) (point-max)) + (replace-string "{eoc}" "\\end{refrain}" nil (point-min) (point-max)) + (replace-regexp-buffer "{c:Lyrics:\\(.*\\)}" "\\\\lyrics{\\1}") + (replace-regexp-buffer "{c:Music:\\(.*\\)}" "\\\\music{\\1}") + (replace-regexp-buffer "{c:\\(.*\\):}" "\\\\inset{\\1}") + (replace-regexp-buffer "{c:\\(.*\\)}" "\\\\inset{\\1}") + (replace-regexp-buffer "{\\(title\\):\\(.*\\)}" "\\\\begin{song}{\\2}") + (replace-regexp-buffer "{\\(t\\):\\(.*\\)}" "\\\\begin{song}{\\2}") + (replace-regexp-buffer "{\\(subtitle\\):\\(.*\\)}" "\\\\subtitle{\\2}") + (replace-regexp-buffer "{\\(st\\):\\(.*\\)}" "\\\\subtitle{\\2}") + (replace-regexp-buffer "{\\(tag\\):\\(.*\\)}" "\\\\tags{\\2}") + (replace-regexp-buffer "{\\(time\\):\\(.*\\)}" "\\\\timing{\\2}") + ;; add verse breaks + (replace-regexp-buffer "\n *\n" "\n\\\\\\\\\n") + ;; comment out unknown commands on their own lines + (replace-regexp-buffer "^ *{\\(.*\\)} *$" "% \\1") + ;; fix literal & + (replace-string "&" "\\&" nil (point-min) (point-max)) + ;; add potentially missing begin song + (goto-char (point-min)) + (when (not (search-forward "\\begin{song}")) + (insert "\\begin{song}{TITLE}")) + (goto-char (point-max)) + (insert "\n\\end{song}"))) + +(defun filktex-mode-previous-definition () + "Go to the previous verse, or to the start of the file." + (interactive) + (beginning-of-line) + (if (search-backward-regexp (rx (sequence (or "\n" "\\" "}") (* whitespace) "\n" (* whitespace) (or word-start "["))) nil t) + (when (match-end 0) (goto-char (match-end 0))) + ;; if the search fails, go to the beginning of the file + (goto-char (point-min))) + (when (equal (char-before (point)) (string-to-char "[")) + (forward-char -1))) + +(defun filktex-mode-next-definition () + "Go to the next verse, or to the end of the file." + (interactive) + (when (not + (search-forward-regexp (rx (sequence (or "\n" "\\" "}") (* whitespace) "\n" (* whitespace) (or word-start "["))) nil t)) + ;; if the search fails, go to the end of the file + (goto-char (point-max))) + (when (equal (char-before (point)) (string-to-char "[")) + (forward-char -1))) + + +(defun filktex-mode-control-activate () + (local-set-key "\M-n" 'filktex-mode-next-definition) + (local-set-key "\M-p" 'filktex-mode-previous-definition)) + +;; TODO: use tex-mode for keywords (require 'tex-mode) + +(define-generic-mode 'filktex-mode ;; note: see customize group font lock faces to select faces + '(("%" . nil)) ; comments + '("song" "subtitle" "tags" "timing" "tailnotice" "tailnote" "begin" "end" "pagebreak" "twocolumn") ; keywords + '(("{\\|}" 0 'font-lock-builtin-face t) ;; any curly brace: { or } + ("\\[\\([^\]]+\\)\\]" 1 'font-lock-constant-face t) ;; chords, i.e. [Dm] + ("\\\\\\([^{ ]*\\){\\([^}]+\\)}" 1 'font-lock-function-face) ;; i.e. \subtitle{some song} + ("\\\\\\([^{ ]*\\){\\([^}]+\\)}" 2 'font-lock-string-face) ;; i.e. \subtitle{some song} + ) + '(".*\\.flk") ; auto-mode-alist + '(filktex-mode-control-activate) + "Generic mode for filktex files.") + + +;;; Convert lyrics from "chords above text" to chords inline in brackets. +;;; by Steve Savitzky: https://github.com/ssavitzky/Honu/blob/master/emacs/flktex-mode.el#L124 +(defun flk-inline-chords-at-point () + "Convert the two lines at point from chords-over-lyrics to chords-inline. +If executed on an empty line, it inserts two backslashes to mark the end of a verse." + (interactive) + (beginning-of-line) + (if (looking-at "^[:space:]*$") + (progn (flk-gobble-line) (insert "\\\\")) + ;; this would be simpler in Haskell, where a string is just a list of characters. + ;; as it is, we use lists to avoid creating unnecessary intermediate strings. + (mapc 'insert (flk-inline-chords (flk-gobble-line) (flk-gobble-line)))) + (insert "\n")) + +(defun flk-gobble-line () + "Remove the line at point, and return it as a list of 1-character strings." + (let ((c (char-after))) + (if (or (null c) (char-equal ?\n c)) + (progn (delete-char 1) nil) + (cons (string c) (progn (delete-char 1) (flk-gobble-line))) + ))) + +(defun flk-inline-chords (chords lyrics) + "This function takes a line of chords and a line of lyrics, passed as lists of +single-character strings, and converts them to a single line with chords inlined +in brackets, returned as a list of strings. + If the lyrics (second) line is empty, return both lines unchanged." + (if (null lyrics) + (reverse (cons "\n" (reverse chords))) + (reverse (flk-convert-chords chords lyrics nil)))) + +(defun flk-convert-chords (chords lyrics result) + "This function converts a line of chords and a line of lyrics into a list of strings +in reverse order. We do it this way to take advantage of tail recursion." + (cond ((and (null chords) (null lyrics)) + result) ; we're done + ((null chords) + ;; nothing left in chords; this just pushes the remaining lyrics onto result + (flk-convert-chords nil (cdr lyrics) (cons (car lyrics) result))) + ((equal " " (car chords)) + (if (null lyrics) + (flk-convert-chords (cdr chords) nil (cons " " result)) + (flk-convert-chords (cdr chords) (cdr lyrics) (cons (car lyrics) result)))) + ((flk-convert-chord chords lyrics "" (cons "[" result))) + )) + +(defun flk-convert-chord (chords lyrics lyrics-under-chord result) + "Convert a chord at the front of lyrics, and follow it with the remaining lyrics. +The lyrics under the chord are accumulated in a string." + (cond ((or (null chords) (equal " " (car chords))) + ;; we're done with this chord. + (flk-convert-chords chords lyrics (cons (concat "]" lyrics-under-chord) result))) + ((null lyrics) + (flk-convert-chord (cdr chords) nil + lyrics-under-chord + (cons (flk-convert-chord-char (car chords) (cdr chords)) result))) + ((flk-convert-chord (cdr chords) (cdr lyrics) + (concat lyrics-under-chord (car lyrics)) + (cons (flk-convert-chord-char (car chords) (cdr chords)) result))) + )) + +;;; Convert a single character in a chord. +;;; # -> \sharp +;;; b -> \flat +;;; m -> \min -- just \m if followed by a or i +;;; \s -> \s if followed by u, else s +;;; +(defun flk-convert-chord-char (char rest) + (cond ((equal "#" char) "\\sharp") + ((equal "b" char) "\\flat") + ((equal "m" char) (cond ((null rest) "\\min") + ((or (equal "i" (car rest)) + (equal "a" (car rest))) + "\\m") + ("\\min"))) + ((equal "s" char) (cond ((and (listp rest) + (equal "u" (car rest))) + "\\s") + ("s"))) + (char))) + +; for debugging -- really ought to make a test suite +;(setq ch '("a" " " "b" " " "c")) +;(setq ly '("x" "y" " " "z")) +;(flk-inline-chords ch ly) + + +(defun filktex--new-song () + "Choose a new file in folder CATEGORY." + (interactive) + (let* ((default-directory (expand-file-name "~/Schreibtisch/Liederbuch/")) + (read-file-name-function 'read-file-name-default) + (filename ".flk")) + (filktex--create-song-file + (read-file-name "Open new song file: " default-directory nil nil filename)))) + + +(defcustom filktex--song-template +"\\begin{song}[long]{BASENAME} % long for double-page songs +% to add a tune, see child-of-the-library.flk +\\index{BASENAME} % shown in the first-line index +% \\subtitle{} +% \\begin{twocolumns} + +\\begin{refrain} + +\\end{refrain} + +Songtext +\\\\ +Songtext + +\\begin{refrain} + \\inset{REFRAIN} +\\end{refrain} +\\begin{bridge} + \\inset{BRIDGE} +\\end{bridge} +\\begin{note} +\\end{note} +\\begin{quotation} +\\end{quotation} +% \\columnbreak % break column for twocolumn-mode +% \\pagebreak % break page for twocolumn-mode +\\\\ +% \\end{twocolumns} +% \\tags{funny} +% \\category{english} +% \\key{em} +% \\timing{6:00} +% \\music{} % copyright +% \\lyrics{} % copyright +% \\arranger{} % copyright +% \\description{} +% \\dedication{} +% \\notice{} % copyright notice and other notes +% \\tailnote{} % note at the end +\\end{song} +" "Song template for new songs." +:type 'string :group 'filktex) + +(defun filktex--create-song-file (filename) + "Create the new article in the CATEGORY with the FILENAME, adding the required link in configure.ac." + (let* ((read-file-name-function 'read-file-name-default) + (nondirectory (file-name-nondirectory filename)) + (basename (file-name-base filename))) + (save-mark-and-excursion + (save-window-excursion + (find-file-existing "zongbook.tex") + (goto-char (point-max)) + (search-backward "\\file{") + (goto-char (point-at-eol)) + (insert (concat "\n\\file{" nondirectory "}\n")) + (save-buffer))) + + (find-file filename) + (insert (string-replace "BASENAME" basename filktex--song-template)) + (goto-char (point-min)))) + +(provide 'filktex) +;;; filktex.el ends here diff --git a/zongbook.tex b/zongbook.tex --- a/zongbook.tex +++ b/zongbook.tex @@ -18,10 +18,10 @@ \begin{document} \maketitle +\thispagestyle{empty} \ThisULCornerWallPaper{0.38}{draketo-dragon} \ThisURCornerWallPaper{0.38}{draketo-dragon-mirrored} \ThisCenterWallPaper{0.5}{SDOeclipse-bgwhite-flipped} - \label{fig:draketo-dragon}\label{fig:sdoeclipse-bgwhite}\label{fig:lisar}\label{fig:lisar-new}\label{fig:glider-new}\label{fig:hurricane}\label{fig:runemaster}\label{fig:thursagan}\label{fig:fencer}\label{fig:alanin-older}\label{fig:mage-red-female}\label{fig:mage-arch-female} \newpage