1

I'm using org mode to write letters using a custom LaTeX class provided by my employer. With this class I have to define the title of the document in the LaTeX header like this:

\def\subject{This is the title of the document}

In org mode the title of the document is defined using the #+TITLE: keyword:

#+TITLE: This is the title of the document

I know that org mode provides the macro {{{title}}} which allows to use the title inside a document. However, it's not clear to me how I can use that to insert the title into the LaTeX header, it only works inside the document's body it seems. For illustration, I'd like to be able to do something like this:

#+LATEX_HEADER: \def\sender{ {{{title}}} }

but this obviously doesn't work because macros are not expanded in these contexts. Any ideas how I can solve this?

1 Answer 1

2

Some possibilities:

Allow macro expansion in a LaTeX header

You can try allowing macro expansion in #+LATEX_HEADER: keyword constructs. That can be done by changing line 113 (in my version - YMMV) of ox-latex.el from

    (:latex-header "LATEX_HEADER" nil nil newline)

to

    (:latex-header "LATEX_HEADER" nil nil parse)

However, in my experiment that was a dismal failure: when the exporter attempts to run org-latex-template it fails at that point, because the Org parser has parsed the \def\subject... construct as a LaTeX fragment, but somebody (I didn't bother to figure out who) is expecting a string and the export blows up.

Modify the org-latex-template function

You can modify org-latex-template to emit the \def\subject{This is the time of the document} construct immediately after it emits the similar \title{This is the title...} construct. That is a simple modification to lines 1739-1744 (again, in my version) of ox-latex.el from

       (concat
        (format "\\title{%s%s}\n" title
                 (if separate "" (or formatted-subtitle "")))
        (when (and separate subtitle)
          (concat formatted-subtitle "\n"))))

to

       (concat
        (format "\\title{%s%s}\n" title
                 (if separate "" (or formatted-subtitle "")))
        (format "\\def\\subject{%s}\n" title)
        (when (and separate subtitle)
          (concat formatted-subtitle "\n"))))

Although simple, it's pretty dirty: you are stuck with the added string in every LaTeX export and you have forked Org mode which means you'll have to maintain it for ever after, since such a change would certainly never be incorporated in the upstream project.

Write a derived exporter

You can write your own special-purpose exporter that is derived from the LaTeX exporter with one modification: the template expansion function is the modified org-latex-template from the previous section, rather than the original one. This is probably the simplest, relatively clean way to do what you want: it's not a fork, just your own add-on, which you can load at initialization and have it available; if you decide you don't need it any more, you change your init file not to load it and then you can throw it away.

Creating such a derived exporter is relatively easy, pretty much filling in some boilerplate stuff and creating the modified org-latex-template function as in the previous section:

(org-export-define-derived-backend 'tml 'latex
  :menu-entry
  '(?z "Export to TM-latex"
       ((?T "To temporary buffer" (lambda (a s v b) (org-tml-export-as-latex a s v)))
        (?t "To file" (lambda (a s v b) (org-tml-export-to-latex a s v)))
        (?o "To PDF file and open"
            (lambda (a s v b)
              (if a (org-tml-export-to-pdf t s v b)
            (org-open-file (org-tml-export-to-pdf nil s v b)))))))
  :translate-alist '((template . org-tml-template)))

(defun org-tml-export-as-latex (&optional async subtreep visible-only body-only ext-plist)
  (interactive)
  (org-export-to-buffer 'tml "*Org TML Export*"
    async subtreep visible-only nil nil (lambda () (LaTeX-mode))))

(defun org-tml-export-to-latex (&optional async subtreep visible-only body-only ext-plist)
  (interactive)
  (let ((outfile (org-export-output-file-name ".tex" subtreep)))
    (org-export-to-file 'tml outfile async subtreep visible-only ext-plist)))

(defun org-tml-export-to-pdf (&optional async subtreep visible-only body-only ext-plist)
  (interactive)
  (let ((outfile (org-export-output-file-name ".tex" subtreep)))
    (org-export-to-file 'tml outfile
      async subtreep visible-only body-only ext-plist
      #'org-latex-compile)))


(defun org-tml-template (contents info)
  ;; identical to org-latex-template from ox-latex.el
  ;; except for the one-line change in 2. above.
  ...
)

(provide 'ox-tml)

This defines a tml exporter, derived from the latex exporter, and identical to it, except for the template transcoder which is the modified one from the previous section. The three functions that are called from the z menu (sorry, the obvious choices t and m are already taken) are copies of the corresponding latex functions with obvious changes. Save this as ox-tml.el somewhere in your load-path and (require 'ox-tml) from your init file in order to use it.

Different organization for a derived exporter

There are other possible organizations for a derived exporter, e.g. adding export options that trigger "hook" calls at specific places in a modified org-latex-template (or some other function). That would add flexibility, but it's not fundamentally different from the example above.

A much better solution?

Maybe somebody will come up with something better but I haven't been able to think of a simpler or more general way to go.

3
  • NickD, thank you so much. This was maximally helpful and your solution works really well for me.
    – tmalsburg
    Commented May 19, 2022 at 9:23
  • Glad it helped.
    – NickD
    Commented May 19, 2022 at 17:22
  • 1
    I wanted to refer to this answer in the answer to another question and I just noticed that you didn't accept the answer, even though you say it works well for you. Any reason for that? Is something missing/wrong?
    – NickD
    Commented Aug 6 at 21:35

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.