From 5c50a75c9014d8712a239bd48293114d6520384a Mon Sep 17 00:00:00 2001 Message-ID: <5c50a75c9014d8712a239bd48293114d6520384a.1735118328.git.yantar92@posteo.net> From: Ihor Radchenko Date: Wed, 25 Dec 2024 10:15:39 +0100 Subject: [PATCH] ox-latex: Put footnotes near their first reference in verses * lisp/ox-latex.el (org-latex-footnote-reference): Do not push footnote definition to the end of the verse. When verse spans multiple pages, this makes all the footnotes appear at the last page rather than near the page where a footnote is defined/references. (org-latex--plain-text-verse-block): New helper function preserving special verse formatting. Extracted from `org-latex-verse-block'. (org-latex-plain-text): Limit special formatting of verse block contents to plain text only, and only for plain text nodes that are inside verse block, but not inside (inline) footnote rereferences. (org-latex-verse-block): Rely upon CONTENTS being properly formatted already via plain-text transcoder. Except trailing and leading blank lines that may not be reliably detected before we assemble to full CONTENTS. This implements an alternative fix to https://list.orgmode.org/orgmode/87sg8prant.fsf@posteo.net/ that does not have a downside of putting all the references to the last page of the verse. Reported-by: 8dcc <8dcc.git@gmail.com> Link: https://orgmode.org/list/87bk0u9k6l.fsf@gmail.com --- lisp/ox-latex.el | 115 +++++++++++++++++++--------------- testing/lisp/test-ox-latex.el | 27 ++++++++ 2 files changed, 91 insertions(+), 51 deletions(-) diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index 4b2e797d08..743c6340d4 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -2237,11 +2237,10 @@ (defun org-latex-footnote-reference (footnote-reference _contents info) (org-export-get-footnote-definition footnote-reference info) info t))) ;; Use \footnotemark if reference is within another footnote - ;; reference, footnote definition, table cell, verse block, or - ;; item's tag. + ;; reference, footnote definition, table cell, or item's tag. ((or (org-element-lineage footnote-reference - '(footnote-reference footnote-definition - table-cell verse-block)) + '( footnote-reference footnote-definition + table-cell)) (org-element-type-p (org-element-parent-element footnote-reference) 'item)) "\\footnotemark") @@ -3119,6 +3118,8 @@ (defun org-latex-plain-text (text info) "{[}" output nil nil 1)) + ;; When inside verse block, use special rules. + (setq output (org-latex--plain-text-verse-block output text)) ;; Return value. output)) @@ -4188,6 +4189,48 @@ (defun org-latex-verbatim (verbatim _contents info) ;;;; Verse Block +(defun org-latex--plain-text-verse-block (contents plain-text) + "Format CONTENTS if PLAIN-TEXT is inside verse environment. +INFO is the communication plist. +Return CONTENTS unchanged when TEXT is not inside verse environment or +when TEXT is a part of footnote reference. + +In a verse environment, add a line break to each newline character and +change each white space at beginning of a line into a normal space, +calculated with `\\fontdimen2\\font'. One or more blank lines between +lines are exported as a single blank line. If the `:lines' attribute +is used, the last verse of each stanza ends with the string `\\!', +according to the syntax of the `verse' package. The separation between +stanzas can be controlled with the length `\\stanzaskip', of the +aforementioned package. If the `:literal' attribute is used, all +blank lines are preserved and exported as `\\vspace*{\\baselineskip}', +including the blank lines before or after CONTENTS." + (if-let* ((verse-block (org-element-lineage plain-text 'verse-block)) + ;; VALUEFORM + ((not (org-element-lineage plain-text 'footnote-reference)))) + (let* ((lin (org-export-read-attribute :attr_latex verse-block :lines)) + (lit (org-export-read-attribute :attr_latex verse-block :literal))) + (replace-regexp-in-string + "^[ \t]+" (lambda (m) (format "\\hspace*{%d\\fontdimen2\\font}" (length m))) + (replace-regexp-in-string + (if (not lit) + (rx-to-string + `(seq (group "\\\\\n") + (1+ (group line-start (0+ space) "\\\\\n")))) + "^[ \t]*\\\\$") + (if (not lit) + (if lin "\\\\!\n\n" "\n\n") + "\\vspace*{\\baselineskip}") + (replace-regexp-in-string + "\\([ \t]*\\\\\\\\\\)?[ \t]*\n" + "\\\\\n" + contents + nil t) + nil t) + nil t)) + ;; Not in verse block, return CONTENTS unchanged. + contents)) + (defun org-latex-verse-block (verse-block contents info) "Transcode a VERSE-BLOCK element from Org to LaTeX. CONTENTS is verse block contents. INFO is a plist holding @@ -4195,57 +4238,27 @@ (defun org-latex-verse-block (verse-block contents info) (let* ((lin (org-export-read-attribute :attr_latex verse-block :lines)) (latcode (org-export-read-attribute :attr_latex verse-block :latexcode)) (cent (org-export-read-attribute :attr_latex verse-block :center)) - (lit (org-export-read-attribute :attr_latex verse-block :literal)) (attr (concat - (if cent "[\\versewidth]" "") - (if lin (format "\n\\poemlines{%s}" lin) "") - (if latcode (format "\n%s" latcode) ""))) + (if cent "[\\versewidth]" "") + (if lin (format "\n\\poemlines{%s}" lin) "") + (if latcode (format "\n%s" latcode) ""))) + (lit (org-export-read-attribute :attr_latex verse-block :literal)) (versewidth (org-export-read-attribute :attr_latex verse-block :versewidth)) (vwidth (if versewidth (format "\\settowidth{\\versewidth}{%s}\n" versewidth) "")) (linreset (if lin "\n\\poemlines{0}" ""))) - (concat - (org-latex--wrap-label - verse-block - ;; In a verse environment, add a line break to each newline - ;; character and change each white space at beginning of a line - ;; into a normal space, calculated with `\fontdimen2\font'. One - ;; or more blank lines between lines are exported as a single - ;; blank line. If the `:lines' attribute is used, the last - ;; verse of each stanza ends with the string `\\!', according to - ;; the syntax of the `verse' package. The separation between - ;; stanzas can be controlled with the length `\stanzaskip', of - ;; the aforementioned package. If the `:literal' attribute is - ;; used, all blank lines are preserved and exported as - ;; `\vspace*{\baselineskip}', including the blank lines before - ;; or after CONTENTS. - (format "%s\\begin{verse}%s\n%s\\end{verse}%s" - vwidth - attr - (replace-regexp-in-string - "^[ \t]+" (lambda (m) (format "\\hspace*{%d\\fontdimen2\\font}" (length m))) - (replace-regexp-in-string - (if (not lit) - (rx-to-string - `(seq (group "\\\\\n") - (1+ (group line-start (0+ space) "\\\\\n")))) - "^[ \t]*\\\\$") - (if (not lit) - (if lin "\\\\!\n\n" "\n\n") - "\\vspace*{\\baselineskip}") - (replace-regexp-in-string - "\\([ \t]*\\\\\\\\\\)?[ \t]*\n" - "\\\\\n" - (if (not lit) - (concat (org-trim contents t) "\n") - contents) - nil t) - nil t) - nil t) - linreset) - info) - ;; Insert footnote definitions, if any, after the environment, so - ;; the special formatting above is not applied to them. - (org-latex--delayed-footnotes-definitions verse-block info)))) + (org-latex--wrap-label + verse-block + (format "%s\\begin{verse}%s\n%s\\end{verse}%s" + vwidth attr + ;; If the `:literal' attribute is used, all blank lines + ;; are preserved and exported as + ;; `\\vspace*{\\baselineskip}', including the blank lines + ;; before or after CONTENTS. + (if (not lit) + (concat (org-trim contents t) "\n") + contents) + linreset) + info))) ;;; End-user functions diff --git a/testing/lisp/test-ox-latex.el b/testing/lisp/test-ox-latex.el index 892ac44374..b75921ae78 100644 --- a/testing/lisp/test-ox-latex.el +++ b/testing/lisp/test-ox-latex.el @@ -79,6 +79,33 @@ (ert-deftest test-ox-latex/verse () lorem ipsum dolor\\\\ lorem ipsum dolor\\\\ +\\end{verse}"))) + ;; Footnotes inside verse blocks + (org-test-with-exported-text + 'latex + "#+begin_verse +lorem +ipsum[fn::Foo + +bar] +dolor +#+end_verse + +[fn:1] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Donec hendrerit tempor. + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec +hendrerit tempor tellus. +" + (goto-char (point-min)) + (should + (search-forward + "\\begin{verse} +lorem\\\\ +ipsum\\footnote{Foo + +bar}\\\\ +dolor\\\\ \\end{verse}")))) (ert-deftest test-ox-latex/longtable () -- 2.47.1