Skip to content

Instantly share code, notes, and snippets.

@bigodel
Last active July 30, 2024 00:47
Show Gist options
  • Save bigodel/56a4627afdfe9ad28f6dcc68b89a97f8 to your computer and use it in GitHub Desktop.
Save bigodel/56a4627afdfe9ad28f6dcc68b89a97f8 to your computer and use it in GitHub Desktop.
Contrived (La)TeX templates for Org-mode

Contrived (La)TeX templates for Org-mode

In my for ever long task of achieving Emacs and Org-mode configuration nirvana I stumbled upon the problem of trying to reuse a couple of .tex files I keep as templates for my (La)TeX exports for Org-mode. As I started to collaborate more and more in my TeX files, I realized that this setup would also need to be TeX-wise independent, that is, the exported .tex file should be self contained and self sufficient, so that I can keep collaborating and other people can see my work and (hopefully) reproduce whatever I have written without my contrived and very personal Emacs/Org-mode setup.

I guess I should also add that one of my goals was to have it as literate programming files, this beautiful, marvelous and humble programming paradigm that one of the gods of the Computer Science pantheon, Donald Knuth, has left us mere mortals (mostly because I tend to forget what I wrote in about a day’s time).

Also keep in mind that I am describing a specific setup that I use for TeX templates, but this seems to be generic enough to be adapted to almost any type of Org-mode export, although some with a bit more work, or with a bit more value from using this approach, than others.

A rough description

Currently my setup consists of two Org-mode files, one with common TeX macros and commands (all written in Plain TeX), and the other with the actual templates for the final output. Both the macros and the templates files make usage of Org-mode’s noweb, allowing me to leave comments and paragraphs of text explaining the reasoning for adding each bit of code.

I also make use of Directory Local Variables to specify behavior that should be only contained within my Org-mode templates folder, and the Library of Babel, which allows me to store bits of source code blocks and functions to manipulate its results to be used in whichever Org-mode file I want to.

While most of this is heavily tied to Org-mode and Emacs, the resulting tangled files and the resulting exported files are not. They contain exactly the source code that is necessary for it to be run in an environment to which these tools may be absent entirely.

If this were a text book I guess this should be a good time to ask the reader to try and setup their own system using the rough description above, so feel free to try it by yourself or just take a look in the next section and most probably maybe improve upon my work, in which case please let me know (u/heartb1t on Reddit or send me an email on jpedrodeamorim@gmail.com).

The more detailed explanation

All right, first and foremost I prefer to have purely Plain TeX macros and commands separate in a different file, so that I can separate cleanly it from LaTeX code. That said, lets dissect what my macros.org roughly looks like and then I’ll show the “whole file”.

Plain TeX macros to be used across templates

As a first little detail for it to work, I have it so that I ingest this file to the Library of Babel every time I open it (literally line ), so that I can use the source code blocks inside this file anywhere, and particularly in my LaTeX Org-mode template file.

# -*- eval: (org-babel-lob-ingest "./macros.org"); -*-
#+title: Collection of TeX macros

The second part for the setup is to have a heading as a wrapper for us to refer to, and have it in the export, our macros and all configuration done in this file in other templates. Note that I am already tangling this to a file on its own, because I like to have it separate, but whatever is put in here is going to be put on the final templates for LaTeX as well.

* Wrapper
:PROPERTIES:
:header-args: :tangle ~/path/to/templates/macros.tex :exports code :results none    (ref:wrapper-args)
:END:

This wrapper is just so that I can access these macros from other Org-mode
files.

#+name: tex-macros
#+begin_src latex :noweb no-export
<<mathmac>>                                                                         (ref:mathmac-noweb)

<<miscmac>>
#+end_src

Notice that on line (wrapper-args) I am setting some header arguments for how to treat exporting and the results of all code blocks in this heading. Refer to Org-mode’s manual on header arguments for more information on each of the arguments set here.

Next is the actual templating. I separate my macros by field, but this is not really what matters here (although I could share them as well), the crux of the setup is in line (macro-args), where I set this heading’s :noweb-ref, that is, the name which I will refer to it in the wrapper mentioned above (see (mathmac-noweb))

* Math
:PROPERTIES:
:header-args: :noweb-ref mathmac :exports no              (ref:macro-args)
:END:

This pertains to configuration and macros for writing mathematics and computer
science.

** Sets and set theory

Macros for sets and set theory symbols.

First the standard sets like natural numbers, integers, etc.

#+begin_src latex
% standard sets
\def\nats{\mathbb{N}}
\def\rats{\mathbb{Q}}
\def\ints{\mathbb{Z}}
\def\reals{\mathbb{R}}
\def\complex{\mathbb{C}}
\def\bools{\mathbb{B}}

#+end_src

** Logic

Macros for dealing with logic.

#+begin_src latex
% logic
\let\satisfies=\Vdash%
\let\consequence=\vdash%
\let\follows=\consequence%
\let\entails=\models%
\let\land=\wedge%
\let\lor=\vee%
\let\limplies=\rightarrow%
\let\lisimplied=\leftarrow%

#+end_src

And this is just to show how you can use this principle to have other headings, that don’t even need to be top-level headings.

* Misc
:PROPERTIES:
:header-args: :noweb-ref miscmac :exports no
:END:

...

As you can imagine, this can be tweaked and changed to suit each person’s workflow and organization of files and headings. Header arguments could be file-wise, heading-wise or source-block-wise. Here is the macros.org file in “full” (I have simplified and obfuscated some things for the sake of privacy).

# -*- eval: (org-babel-lob-ingest "./macros.org"); -*-
#+title: Collection of TeX macros

* Wrapper
:PROPERTIES:
:header-args: :tangle ~/path/to/templates/macros.tex :exports code :results none
:END:

This wrapper is just so that I can access these macros from other Org-mode
files.

#+name: tex-macros
#+begin_src latex :noweb no-export
<<mathmac>>

<<miscmac>>
#+end_src

* Math
:PROPERTIES:
:header-args: :noweb-ref mathmac :exports no
:END:

This pertains to configuration and macros for writing mathematics and computer
science.

** Sets and set theory

Macros for sets and set theory symbols.

First the standard sets like natural numbers, integers, etc.

#+begin_src latex
% standard sets
\def\nats{\mathbb{N}}
\def\rats{\mathbb{Q}}
\def\ints{\mathbb{Z}}
\def\reals{\mathbb{R}}
\def\complex{\mathbb{C}}
\def\bools{\mathbb{B}}

#+end_src

** Logic

Macros for dealing with logic.

#+begin_src latex
% logic
\let\satisfies=\Vdash%
\let\consequence=\vdash%
\let\follows=\consequence%
\let\entails=\models%
\let\land=\wedge%
\let\lor=\vee%
\let\limplies=\rightarrow%
\let\lisimplied=\leftarrow%

#+end_src

* Misc
:PROPERTIES:
:header-args: :noweb-ref mathmac :exports no
:END:

...

LaTeX templates

And finally, I have another file, latex.org, in the same folder as the previous one, where I keep templates for various LaTeX document classes and some miscellaneous things.

As you can see below, the first line is the same as the previous one, just to ensure that I have my Plain TeX macros available in my Library of Babel when dealing with the files with the templates.

# -*- eval: (org-babel-lob-ingest "./macros.org"); -*-
#+title: Templates for LaTeX files

I keep all of my packages that I need accross all my LaTeX files in a single heading with a =:noweb-ref= so that I can refer to it in multiple places.

* Packages
:PROPERTIES:
:header-args: :noweb-ref packages :exports code :results none (ref:packages-args)
:END:

Packages that I usually need.

#+begin_src latex
%%% --- packages ---
\usepackage[utf8]{inputenc}     % encoding of the document (not needed but kept)
\usepackage[T1]{fontenc}        % include the fonts with accents
% the last language is the default one
\usepackage[greek,brazilian,english]{babel} % use language specific commands
\usepackage{graphicx}           % utilities for images
\usepackage{hyperref}           % add hyperlinks and some new reference macros

#+end_src

** Mathematics related packages.

#+begin_src latex
\usepackage{amsmath}            % base math package
\usepackage{amsfonts}           % math fonts like \mathbb
\usepackage{amssymb}            % more maths symbols
\usepackage{amsthm}             % theorem environment
\usepackage{mathtools}          % fix some amsmath bugs and more tools for math
\usepackage{mathrsfs}           % ralph smith's formal script font for math

#+end_src

And some definitions of =amsthm= environments.

#+begin_src latex
% amsthm
% use the definition style for everything
\theoremstyle{definition}
\newtheorem{definition}{Definition}[section]
\newtheorem{theorem}[definition]{Theorem}
\newtheorem{corollary}[definition]{Corollary}
\newtheorem{lemma}[definition]{Lemma}
\def\qedsymbol{$\blacksquare$}  % change the QED symbol to be a black box

#+end_src

This is where I tie my Plain TeX macros to my LaTeX templates. I give it a :noweb-ref as well for me to refer to it later on, and some options for exporting so that it exports how I want it to, showing code and not resolving noweb references. You could also add your custom LaTeX macros in here.

* Macros
:PROPERTIES:
:header-args: :noweb-ref macros :noweb no-export :exports code :results none (ref:latex-macros-args)
:END:

Here I include my default TeX macros, which can be found at my macros
literate file.

#+begin_src latex
<<tex-macros>>
#+end_src

And we’ve finally arrived on an actual template for a functioning document. Just like before, I have a couple of export settings for the whole heading, but most importantly I tangle this to a file that I use as my template. Notice the usage of the noweb references of previous sections.

* Article
:PROPERTIES:
:header-args: :exports code :results none :tangle ~/path/to/templates/article.tex (ref:article-args)
:END:

Here is the actual template for the =article= document class.

#+begin_src latex :noweb no-export
\documentclass[11pt]{article}

<<packages>>

<<options>>

<<macros>>
#+end_src

Here is what the Beamer template looks like.

* Beamer
:PROPERTIES:
:header-args: :exports code :results none :tangle ~/path/to/templates/beamer.tex
:END:

Here is the actual template for the =beamer= document class.

#+begin_src latex :noweb no-export
\documentclass[presentation]{beamer}

<<beamer-theme>>

<<packages>>

<<options>>

<<macros>>
#+end_src

** Theme

Just use Singapore.

#+name: beamer-theme
#+begin_src latex
% -- theme --
\usethem{Singapore}             % use Singapore theme
#+end_src

A little bit of Elisp

While the setup so far works all by itself as organization and creation of .tex template files (just call org-babel-tangle (C-c C-v C-t)), the Org-mode LaTeX export is not using it yet.

For that I have a function to get the content of a file as a string and use that on org-latex-classes and org-format-latex-header. The former is used when exporting and the latter is used when previewing LaTeX in a Org-mode buffer.

(defun file-content-as-string (filename)
  "Return the contents of FILENAME as string."
  (with-temp-buffer
    (insert-file-contents filename)
    (buffer-string)))

(defvar org-tex-macros-template
  (file-content-as-string "~/path/to/templates/macros.tex")
  "The contents of my macros TeX file as a string.")

(defvar org-latex-article-template
  (file-content-as-string "~/path/to/templates/article.tex")
  "The contents of my article LaTeX template as a string.")

(defvar org-latex-beamer-template
  (utils-file-content-as-string "~/docs/tex/templates/beamer.tex")
  "The contents of my beamer LaTeX template as a string.")

I store it in variables because I feel like that is a bit more organized and clear.

After that, I wrap updating the Org-mode variables in a function to make it easier to update it on the fly.

(defun org-update-tex-templates ()
  "Update `org-latex-classes' and `org-format-latex-header'.
I wrap this into a function so I can call it in the hook when
saving the files."
  (interactive)

  ;; reset the variables values
  (setq org-tex-macros-template
        (utils-file-content-as-string "~/docs/tex/templates/macros.tex")

        org-latex-article-template
        (utils-file-content-as-string "~/docs/tex/templates/article.tex")

        org-latex-beamer-template
        (utils-file-content-as-string "~/docs/tex/templates/beamer.tex"))

  ;; set the org-variables
  (setq org-format-latex-header
        (format "%s
\\usepackage[usenames]{color}
\\pagestyle{empty}             %% do not remove
%% The settings below are copied from fullpage.sty
\\setlength{\\textwidth}{\\paperwidth}
\\addtolength{\\textwidth}{-3cm}
\\setlength{\\oddsidemargin}{1.5cm}
\\addtolength{\\oddsidemargin}{-2.54cm}
\\setlength{\\evensidemargin}{\\oddsidemargin}
\\setlength{\\textheight}{\\paperheight}
\\addtolength{\\textheight}{-\\headheight}
\\addtolength{\\textheight}{-\\headsep}
\\addtolength{\\textheight}{-\\footskip}
\\addtolength{\\textheight}{-3cm}
\\setlength{\\topmargin}{1.5cm}
\\addtolength{\\topmargin}{-2.54cm}"
                org-latex-article-template))

  (setq org-latex-classes              ; options for exporting latex class
        `(("article" ,(format "%s
[NO-DEFAULT-PACKAGES]
[NO-PACKAGES]
[NO-EXTRA]" org-latex-article-template)
           ("\\section{%s}" . "\\section*{%s}")
           ("\\subsection{%s}" . "\\subsection*{%s}")
           ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
           ("\\paragraph{%s}" . "\\paragraph*{%s}")
           ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
          ("beamer" ,(format "%s
[NO-DEFAULT-PACKAGES]
[NO-PACKAGES]
[NO-EXTRA]" org-latex-beamer-template)
           ("\\section{%s}" . "\\section*{%s}")
           ("\\subsection{%s}" . "\\subsection*{%s}")
           ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
          ("report" "\\documentclass[11pt]{report}"
           ("\\part{%s}" . "\\part*{%s}")
           ("\\chapter{%s}" . "\\chapter*{%s}")
           ("\\section{%s}" . "\\section*{%s}")
           ("\\subsection{%s}" . "\\subsection*{%s}")
           ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
          ("book" "\\documentclass[11pt]{book}"
           ("\\part{%s}" . "\\part*{%s}")
           ("\\chapter{%s}" . "\\chapter*{%s}")
           ("\\section{%s}" . "\\section*{%s}")
           ("\\subsection{%s}" . "\\subsection*{%s}")
           ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))))
;; call the function to populate the variables
(org-update-tex-templates)

There are other ways to configure these varibles. One can change org-latex-packages-alist or org-latex-default-packages-alist, which would be placed in place of [PACKAGES] and [DEFAULT-PACKAGES] respectively. I’d rather have a full file as my template and use that instead, that’s why I went to the lengths I did to come up with this absolutely unnecessary configuration.

The variable org-format-latex-header controls how the LaTeX preview fragments are processed. I just left some customizations that Org-mode has in this variable by default and used my template for articles.

A final little trick

As a final little trick to get everything working as I wanted to, I create a Directory Local Variable file in the same folder as the Org files with the templates. The contents of this file are pretty simple:

((org-mode . ((eval . (add-hook 'after-save-hook
                                (lambda ()
                                  "Tangle the files in
org/templates and call `org-update-tex-templates'. Ingest any
files necessary to `org-babel-library-of-babel' using
`org-babel-lob-ingest'."
                                  (org-babel-lob-ingest "./macros.org")
                                  (dolist (template-file
                                           ;; i set them manually so i can chose
                                           ;; the order in which to tangle
                                           '("macros.org" "latex.org"))
                                    (org-babel-tangle-file template-file))
                                  (when (functionp 'org-update-tex-templates)
                                    (org-update-tex-templates)))
                                nil 'local)))))

All it does is add a local hook on all the files in the current directory that calls (just to be sure) org-babel-lob-ingest on my macros file, and then I tangle each file in the order I set up. I set the order manually just because the macros file has to be tangled before the LaTeX templates file. Lastly, it updates the variables using my function so that after saving one of these files I can immediately start using whatever I changed on my templates.

Conclusion

I know it’s probably a lot, and I might have over complicated more than necessary, but this setup makes me happy. I love being able to modularize and write about my templates, so that I can remember later why I added each piece of it. I love making use of Org-mode’s simple syntax, Library of Babel and everything else that come with Org. This makes me wonder what else I could let Emacs and Org-mode swallow from my life. Please feel free to ask any questions and I’ll try to answer whenever possible.

@lccambiaghi
Copy link

Your setup is absolutely amazing! Thanks for sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment