Exporting several variants from an org-beamer file
This is a follow up on my org beamer post, since I've changed the way I manage to export three variants of the same document. Recall what I want to do is to be able to export:
pres: the slides with speaker notes (obviously),handout: a handout version (no animations, several slides per page),slides: the slides without speaker notes (so that students might be able to re-see the slides exactly as they were shown during the lecture, including animations).
In the above post, I explained I wasn't really satisfied with the need
to maintain three setup files. At some point, I actually managed to
merge them in a single one by using org macros of the (eval form to
select amongst several values, but that was quite cumbersome.
I rethought about that and came up with the following solution:
- use a "setup" file in which settings to be used for each variant are defined,
- define a new export backend that will offer several export actions in the export dispatcher, in order to select the variant to output,
- use a
org-export-before-parsing-functionshook that will copy the appropriate settings from the setup file to the export buffer just before actual export is performed.
Here's the definition of the my-beamer export backend:
(require 'ox-beamer)
(org-export-define-derived-backend 'my-beamer 'beamer
:menu-entry '(?s "My beamer export"
((?p "Teacher presentation" my--beamer-export-pres)
(?s "Student slides" my--beamer-export-slides)
(?h "Student handout" my--beamer-export-handout)
(?a "All" my--beamer-export-all))))
The setup file is defined by the my-beamer-setup-file variable.
Here it's defined as a plain file name to it will be looked for in the
current directory, but a full pathname could be used to have a global
setup file:
(defvar my-beamer-setup-file "my-beamer-setup.org"
"The default setup file for the my-beamer exporter. This will be used if
the MY_BEAMER_SETUP key is not set in the file.")
Moreover the setup file to use can also be set in the org file with
the MY_BEAMER_SETUP option.
(defun my--beamer-get-setup-name ()
"Get the name of the setup file to use (and delete line from buffer)."
(or (and (search-forward "#+MY_BEAMER_SETUP: " nil t)
(prog1 (thing-at-point 'filename) (delete-line)))
my-beamer-setup-file))The export action functions are defined as
(defun my--beamer-export-pres (&rest options)
(let ((basename (file-name-sans-extension (buffer-name))))
(my--org-beamer-do-export basename "pres")))
(defun my--beamer-export-slides (&rest options)
(let ((basename (file-name-sans-extension (buffer-name))))
(my--org-beamer-do-export basename "slides")))
(defun my--beamer-export-handout (&rest options)
(let ((basename (file-name-sans-extension (buffer-name))))
(my--org-beamer-do-export basename "handout")))
(defun my--beamer-export-all (&rest options)
(my--beamer-export-pres)
(my--beamer-export-slides)
(my--beamer-export-handout))
As we can see, they all call my--org-beamer-do-export with the file
basename and the desired variant. This function sets a dynamic
variable to the desired variant, calls the org-beamer-export-to-pdf
function and then renames the output file by suffixing it with a dash
and the variant name (so that exporting eg lecture1.org as the
handout variant creates the lecture1-handout.pdf file).
(defvar my--beamer-variant nil
"Dynamic variable for the export hook to know the output variant (will be
nil if we don't use the my-beamer backend).")
(defun my--org-beamer-do-export (basename variant)
"Export, rename."
(setq my--beamer-variant variant)
(org-beamer-export-to-pdf)
(rename-file (concat basename ".pdf")
(format "%s-%s.pdf" basename variant) t))
The actual work is done by the my--beamer-hook function. It will
open the setup file and, according to the value of
my--beamer-variant, iterate on the headlines of the setup file. It
will copy the contents in the export buffer if the selected variant
name appears in the heading tags.
(defun my--beamer-hook (backend)
;; note: the backend here is 'beamer since we call org-beamer-export-to-pdf
(when my--beamer-variant ;; nil when another backend is used
(let ((buf (current-buffer))
(setup (my--beamer-get-setup-name)))
(with-temp-buffer
(insert-file-contents setup)
(goto-char (point-min))
;; org-next... returns 0 if found or point if no more (undocumented)
(while (= 0 (org-next-visible-heading 1))
(let ((elt (org-element-at-point)) text)
(when (member my--beamer-variant
(org-element-property :tags elt))
(setq text (buffer-substring-no-properties
(org-element-property :contents-begin elt)
(org-element-property :contents-end elt)))
(with-current-buffer buf (insert text)))))
))))
(add-hook 'org-export-before-parsing-functions #'my--beamer-hook)Finally, here's the setup file I currently use. Using the tags to select parts allows to easily factor parts that are common to several variants.
Enjoy!
setup file for my-beamer org exporter.
Heading content is inserted if the selected output variant appears
in the heading tags.
* This part is common to all output variants :pres:slides:handout:
#+latex_header: \usepackage[]{babel}\usepackage{alltt}\usepackage{version}
#+latex_header: \usepackage{beamer-mfig}
#+latex_header: \graphicspath{{figs}}
# in header for it to also apply to title and toc
#+latex_header: \setbeamertemplate{navigation symbols}{}
#+latex_header: \setbeamertemplate{footline}[frame number]{}
#+latex: \setbeamercolor{block title}{fg=black,bg=white}
#+latex: \newif\ifhandout
* This part is for pres and slides :pres:slides:
# - select a 16/9 format for slides (default 4/3 is better for printed handout)
# - include toc slides at each section and subsection
# - include poll slides
#+BEAMER_THEME: Rochester
#+BEAMER_COLOR_THEME: spruce
#+LATEX_CLASS_OPTIONS: [aspectratio=169]
#+LATEX_HEADER: \AtBeginSection[]{\begin{frame}<beamer>[noframenumbering]{Outline}\tableofcontents[currentsection]\end{frame}}
#+LATEX_HEADER: \AtBeginSubsection[]{\begin{frame}<beamer>[noframenumbering]{Outline}\tableofcontents[currentsection\,currentsubsection]\end{frame}}
#+LATEX_HEADER: \newenvironment{poll}[2][]{\begin{frame}[environment=poll\,noframenumbering\,#1]{Poll -- #2} \renewcommand{\theenumi}{\textbf{\Alph{enumi}}}\setbeamertemplate{enumerate item}[square]}{\end{frame}}
#+latex: \handoutfalse
* For pres only, show notes on second screen :pres:
#+LATEX_HEADER: \setbeameroption{show notes on second screen}
* For slides only, hide notes :slides:
#+LATEX_HEADER: \setbeameroption{hide notes}
* For handout :handout:
# What differs in handout:
# - sets the handout beamer class option (so no overlays)
# - select a different color theme
# - exclude slides with tag nohandout
# - use light footline with just the slide number
# - use pgfpages to print 8 slides per page
# - exclude polls
#+LATEX_CLASS_OPTIONS: [handout]
#+BEAMER_THEME: Rochester
#+BEAMER_COLOR_THEME: dove
#+EXCLUDE_TAGS: nohandout noexport
#+LATEX_HEADER: \setbeamertemplate{footline}{\hfill\insertframenumber}
#+LATEX_HEADER: \usepackage{pgfpages}\pgfpagesuselayout{8 on 1}[a4paper, border shrink=5mm]
#+LATEX_HEADER: \excludeversion{poll}
#+latex: \handouttrue