I'm interested in modifying the functionality for how org exports to markdown/html. What I want to achieve is hanging indents in exported poetry, and a decent way to achieve this is with <span></span> tags. so i would like to be able to export a verse block to something like:

<p class="verse-hanging">
<span class="hanging">poem verse 1</span><br />
<span class="hanging">poem verse 2</span><br />

rather than

<p class="verse">
poem verse 1<br />
poemverse 2<br />

[what ever the class names have to be, the only need i think is for them to not be verse, so the two can be styled differently.]

i have seen org-html-verse-block, i could rewrite it to this end, but i'm not sure how i would go about using it in an export.

the tags should be optional for verse blocks.

can i modify export functions or might this be more of a dangerous rabbit hole that i would never emerge out of?

  • Rewriting org-html-verse-block is the way to go, but you need to define a derived exporter. See emacs.stackexchange.com/questions/71803/… for an example - although the context is different, the implemenation is going to be very similar.
    – NickD
    Jul 6, 2022 at 21:56

1 Answer 1


With this org snippet


Nulla exercitation incididunt ullamco
        Eiusmod commodo eu labore occaecat ut non
Voluptate elit, eiusmod do
        Est dolor ullamco lorem pariatur esse duis


to get this on html export

<p class="verse">
<span class="verse-line"><br /></span>
<span class="verse-line">Nulla exercitation incididunt ullamco<br /></span>
<span class="verse-line">&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;Eiusmod commodo eu labore occaecat ut non<br /></span>
<span class="verse-line">Voluptate elit, eiusmod do<br /></span>
<span class="verse-line">&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;&#xa0;Est dolor ullamco lorem pariatur esse duis<br /></span>
<span class="verse-line"><br /></span>

install this

(require 'rx)

(add-to-list 'org-export-filter-verse-block-functions

(defun my-org-export-filter-verse-block-functions (text backend _info)
  (cl-case backend
     ;; Special processing for HTML
       (insert text)
       (pop-to-buffer (current-buffer))
       (let* ((start (progn
                       (goto-char (point-min))
                       (when (re-search-forward (rx (and "<p" (one-or-more (not ">")) ">")))
                         (insert "\n")
              (end (progn
                     (goto-char (point-max))
                     (when (re-search-backward (rx "</p>"))
                       (prog1 (point)
                         (insert "\n"))))))
         (narrow-to-region start end)
         ;; Remove leading empty lines
         (goto-char (point-min))
         (skip-chars-forward "\r\n")
         (delete-region (point-min) (point))
         ;; Remove Trailing empty lines
         (goto-char (point-max))
         (skip-chars-backward "\r\n")
         (delete-region (point-max) (point))
         ;; Surround each line with `<span class="verse-line"> </span>'
         (goto-char (point-min))
         (while (not (eobp))
           (insert (format "<span class=\"%s\">" "verse-line"))
           (goto-char (point-at-eol))
           (insert "</span>")
           (forward-line 1))
         (buffer-substring-no-properties (point-min) (point-max)))))
     ;; No special processing for non-HTML backends

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.