2

At $dayjob I take notes using org-mode and from time to time end up sharing these as exported PDF's or HTML files. When I export a subtree these notes end up overwriting the same file over and over again, the file workbook-2021.pdf when using i.e. C-c C-e C-s l p. I would like to have subtrees export to a file named workbook-2021-headline.pdf.

Using code shared by Melioratus on the Emacs Stack Exchange I was able to write a function that finds the current headline and removes non-alphanumeric characters.

(defun headline-title()
    (let* ((headline
                    (save-mark-and-excursion
                        (outline-previous-heading)
                        (org-element-property :title (org-element-at-point)))))
        (replace-regexp-in-string "[^[:alnum:]-_]" "-" headline)))

I then want for this string to be appended to the filename of the current org-file when exporting the subtree. So given the following file named export-subtree.org and that the cursor is standing under the heading of "Works of wizards" then the file name would be export-subtree-Works-of-wizards.pdf:

* Week 1
** 2021-01-01
*** Concerning Hobbits
A word or two about their lore.
*** Works of wizards
Many more about wandering| wizards.

But this is where my Emacs foo comes short. I really don't know where to start. Because I do not want to be adding a property to every headline I want to export with a different name as many other answers on this network suggests.

Rovanion
  • 975
  • 7
  • 20

1 Answers1

2

Try this function:

(defun my/org-export-headline (&optional backend async subtreep visible-only body-only ext-plist)
  "Export the current Org headline using BACKEND.

The available backends are the ones of `org-export-backends' and
'pdf.

When optional argument SUBTREEP is non-nil, transcode the
sub-tree at point, extracting information from the headline
properties first.

When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.

When optional argument BODY-ONLY is non-nil, only return body
code, without surrounding template.

Optional argument EXT-PLIST, when provided, is a property list
with external parameters overriding Org default settings, but
still inferior to file-local settings."
  (interactive)
  (let* ((backend (unless backend
                    (intern
                     (completing-read "Available backends: "
                                      (append org-export-backends '(pdf))))))
         (headline (car (last (org-get-outline-path t))))
         (headline-alnum (replace-regexp-in-string "[^[:alnum:]-_]" "-" headline))
         (file-prefix (file-name-sans-extension (buffer-file-name)))
         (filename (format "%s-%s.%s" file-prefix headline-alnum
                           (if (eq backend 'pdf) "tex" backend))))
    (org-narrow-to-subtree)
    (org-export-to-file
       (if (eq backend 'pdf) 'latex backend)
        filename async subtreep visible-only body-only ext-plist
      (when (eq backend 'pdf)
        (lambda (file) (org-latex-compile file))))
    (widen)))

Usage: M-x my/org-export-headline and select a backend (e.g. pdf, html, etc.).

It may look over-complicated, but it is in fact really simple. To let it support pdf export, I inspired myself from org-latex-export-to-pdf which unfortunately force you to use its filename. I also make it support org-export-to-file extra parameters as org-latex-export-to-pdf does and copy-pasted its docstring. If you ignore the conditional (if (eq backend 'pdf) ...), it's really a matter of narrowing, org-export-to-file, then widening.

Firmin Martin
  • 1,265
  • 7
  • 23
  • 1
    Thanks, I got it working. A nitpick: if the buffer is always narrowed, ``(org-narrow-to-subtree) (org-export-to-file ...) (org-widen)`` forgets this narrowing; to avoid this, I changed it to ``(save-restriction (org-export-to-file ...))``. – toomas Oct 13 '22 at 11:05