OK, not my cup of tea, but anyhow... Let's wrap code around code around code...
A new version of the patch. /PA On Mon, 13 Apr 2026 at 13:02, Pedro Andres Aranda Gutierrez < [email protected]> wrote: > Hi, > > let's 'reset' the exchange and cool down. > > My settings would be, ideally: > > (use-package org-mode > :pin gnu > :ensure t) > in my case with the development version of org-mode from savannah compiled > locally and included with a :load-path > > My findings: > > A. Things that are possible right now: > > 1.- In anything LaTeX, I need to pass options to packages that are > included in the document class: > Answer: use #+LATEX_CLASS_PRE > 2.- In a Beamer presentation, I need to set very specific things before > the theme: > Answer: use #+BEAMER_THEME_PRE > > B. Things that need further thought: > > 1.- It is advisable to put the sequence > BEAMER_THEME_PRE > BEAMER_THEME > BEAMER_THEME_POST > directly after the class declaration. This is what the Beamer authors do > in their examples. > 2.- It is advisable to put "all things font" ASAP. > Why? you may have LaTeX code in your header that will be stored with the > 'full state' to be called later. > An example would be using \savebox{} for certain parts in the header of a > document (in LATEX_HEADER). > If you change the fonts after declaring the boxes, this declaration has no > effect in the saved boxes > and you will get wrong fonts there. Moving the font packages out of the > default classes and > into font handling might be helpful.... even for pdflatex. > > My request: > 1. if you want to use templates, make sure there is room for these extra > things there to avoid needed patching the generated code with (sometimes > intricate) search/replace processes which may not be self-evident in the > code. It will be more readable and all (starting by you) will profit > > 2. Agree on a default sequence for Beamer/LaTeX (taking into account what > I mention above; promise it will help others...): > > For LaTeX > [CLASS-PRE] > \documentclass[options]{class} > [FONTS] > [PACKAGES] > [PACKAGES-EXTRA] > [HEADER] > [HEADER-EXTRA] > > And for BEAMER > [CLASS-PRE] > \documentclass[options]{beamer} > [BEAMER-THEME] > [FONTS] > [PACKAGES] > [PACKAGES-EXTRA] > [HEADER] > [HEADER-EXTRA] > > If more such definitions are needed, let's investigate... > > > Best, /PA > > PS: I'm sending a PR to https://github.com/fniessen/refcard-org-beamer to > investigate how that eval-after-load is helpful... > PPS: Maybe novice users don't want to have to manage that kind of > configuration and would be happier without it and with as few setq's as > possible. At least that's what my students and colleagues comment. > > > -- > Fragen sind nicht da, um beantwortet zu werden, > Fragen sind da um gestellt zu werden > Georg Kreisler > > "Sagen's Paradeiser" (ORF: Als Radiohören gefährlich war) => write BE! > Year 2 of the New Koprocracy > > -- Fragen sind nicht da, um beantwortet zu werden, Fragen sind da um gestellt zu werden Georg Kreisler "Sagen's Paradeiser" (ORF: Als Radiohören gefährlich war) => write BE! Year 2 of the New Koprocracy
From 85c1cac143d7ef67439d4419fbaa1d8965d68a23 Mon Sep 17 00:00:00 2001 From: "Pedro A. Aranda" <[email protected]> Date: Tue, 14 Apr 2026 13:58:36 +0200 Subject: [PATCH] ox-beamer: New LaTeX preamble sequence and new BEAMER_THEME_PRE lisp/ox-latex.el: Factor out the class template generation to (org-latex--make-class-template). Clean up and simplify the code. lisp/ox-beamer.el: New option BEAMER_THEME_PRE. (org-beamer-theme-settings): Factored out function to manage theme-related settings. (org-beamer--insert-theme-info): Function to force the insertion of the theme-related info just after the document class. (org-beamer-template): Wrap (org-beamer--insert-theme-info) around result to make sure the theme info is where it should be. testing/lisp/test-ox-beamer.el: New test for BEAMER_CLASS_PRE and the new Latex class template. doc/org-manual.org: Document BEAMER_THEME_PRE etc/ORG-NEWS: Announce new LaTeX preamble sequence in Beamer header and BEAMER_THEME_PRE. --- doc/org-manual.org | 13 ++ etc/ORG-NEWS | 13 ++ lisp/ox-beamer.el | 216 ++++++++++++++++++-------------- lisp/ox-latex.el | 51 +++++--- testing/lisp/test-ox-beamer.el | 16 +++ 7 files changed, 200 insertions(+), 113 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index baa1127a0..6658a7e51 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -13162,6 +13162,19 @@ settings (see [[*Export Settings]]). #+cindex: @samp{BEAMER_OUTER_THEME}, keyword The Beamer outer theme. +- =BEAMER_THEME_PRE= :: + + #+cindex: @samp{BEAMER_THEME_PRE}, keyword + Arbitrary lines inserted in the preamble, just before + =\usetheme{}=. For example: + + : #+BEAMER_THEME_PRE: \usepackage{geometry} + : #+BEAMER_THEME_PRE: \geometry{paperwidth=160mm, paperheight=90mm} + : #+BEAMER_THEME: Madrid + + Use the ~Madrid~ theme, setting the page size to 160mm by 90mm. This + can be used to help creating figures with TiKZ. + - =BEAMER_HEADER= :: #+cindex: @samp{BEAMER_HEADER}, keyword diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 93c108690..870f0c349 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -42,6 +42,14 @@ assume that the outer list has length one, which means ~(car (org-babel-tangle-single-block ...))~ is not guaranteed to return the correct ~(FILE . ...)~ cons. +*** ox-beamer: New LaTeX preamble sequence + +When exporting to Beamer, all theme-related configuration will be +generated right after the document class declaration in concordance +with the examples shipped with the Beamer class. Previously, the +theme configuration went after the whole LaTeX preamble and the beamer +frame definition, if it was needed. + ** New features # We list the most important features, and the features that may @@ -195,6 +203,11 @@ addition to being added to a data attribute, ~data-linenr~, on that highlight and copy/paste multiple lines from a code block without having the line numbers included. +*** New option BEAMER_THEME_PRE + +The new option BEAMER_THEME_PRE is used to add arbitrary LaTeX lines +that must be processed before the Beamer theme is loaded. + ** New functions and changes in function arguments # This also includes changes in function behavior from Elisp perspective. diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el index 72fe18acd..d5ab90eca 100644 --- a/lisp/ox-beamer.el +++ b/lisp/ox-beamer.el @@ -261,6 +261,7 @@ Return overlay specification, as a string, or nil." (:latex-class "LATEX_CLASS" nil "beamer" t) (:beamer-subtitle-format nil nil org-beamer-subtitle-format) (:beamer-column-view-format "COLUMNS" nil org-beamer-column-view-format) + (:beamer-theme-pre "BEAMER_THEME_PRE" nil nil newline) (:beamer-theme "BEAMER_THEME" nil org-beamer-theme) (:beamer-color-theme "BEAMER_COLOR_THEME" nil nil t) (:beamer-font-theme "BEAMER_FONT_THEME" nil nil t) @@ -856,111 +857,140 @@ contextual information." ;;;; Template ;; +;; Extract theme related information +;; +(defun org-beamer-theme-settings (info) + "Return the code related to the Beamer theme for the latex preamble. +Will be called with the latex class is \"beamer\". +INFO is a plist holding the export options." + (let ((pre-header (plist-get info :beamer-theme-pre)) + (format-theme + (lambda (prop command) + (let ((theme (plist-get info prop))) + (when theme + (concat command + (if (not (string-match "\\[.*\\]" theme)) + (format "{%s}\n" theme) + (format "%s{%s}\n" + (match-string 0 theme) + (org-trim + (replace-match "" nil nil theme)))))))))) + (concat + pre-header (and pre-header "\n") + (mapconcat (lambda (args) (apply format-theme args)) + '((:beamer-theme "\\usetheme") + (:beamer-color-theme "\\usecolortheme") + (:beamer-font-theme "\\usefonttheme") + (:beamer-inner-theme "\\useinnertheme") + (:beamer-outer-theme "\\useoutertheme")) + "")))) ;; Template used is similar to the one used in `latex' backend, ;; excepted for the table of contents and Beamer themes. +(defun org-beamer--insert-theme-info (template theme-info) + "Return the beamer TEMPLATE with the THEME-INFO inserted +directly after document class declaration" + (if theme-info + (progn + (setq theme-info + (replace-regexp-in-string (regexp-quote "\\") "\\\\" theme-info nil 'literal)) + (replace-regexp-in-string + "\\(\\documentclass.+{beamer}\\)" + #'(lambda(s) + (concat (match-string-no-properties 1 s) "\n" theme-info)) + template)) + template)) ;; just in case the theme information is nil + (defun org-beamer-template (contents info) "Return complete document string after Beamer conversion. CONTENTS is the transcoded contents string. INFO is a plist holding export options." (let ((title (org-export-data (plist-get info :title) info)) (subtitle (org-export-data (plist-get info :subtitle) info))) - (concat - ;; Timestamp. - (and (plist-get info :time-stamp-file) - (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) - ;; LaTeX compiler - (org-latex--insert-compiler info) - ;; Document class and packages. - (org-latex-make-preamble info) - ;; Define the alternative frame environment, if needed. - (when (plist-get info :beamer-define-frame) + ;; + ;; We use the :latex-class-post to inject the theme settings as + ;; the first thingin the LaTeX preamble after the class declaration + ;; + ;; (plist-put info :latex-class-post (org-beamer-theme-settings info)) + (org-beamer--insert-theme-info + (concat + ;; Timestamp. + (and (plist-get info :time-stamp-file) + (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) + ;; LaTeX compiler + (org-latex--insert-compiler info) + ;; Document class and packages. + (org-latex-make-preamble info) + ;; Define the alternative frame environment, if needed. + (when (plist-get info :beamer-define-frame) (format "\\newenvironment<>{%s}[1][]{\\begin{frame}#2[environment=%1$s,#1]}{\\end{frame}}\n" org-beamer-frame-environment)) - ;; Insert themes. - (let ((format-theme - (lambda (prop command) - (let ((theme (plist-get info prop))) - (when theme - (concat command - (if (not (string-match "\\[.*\\]" theme)) - (format "{%s}\n" theme) - (format "%s{%s}\n" - (match-string 0 theme) - (org-trim - (replace-match "" nil nil theme)))))))))) - (mapconcat (lambda (args) (apply format-theme args)) - '((:beamer-theme "\\usetheme") - (:beamer-color-theme "\\usecolortheme") - (:beamer-font-theme "\\usefonttheme") - (:beamer-inner-theme "\\useinnertheme") - (:beamer-outer-theme "\\useoutertheme")) - "")) - ;; Possibly limit depth for headline numbering. - (let ((sec-num (plist-get info :section-numbers))) - (when (integerp sec-num) - (format "\\setcounter{secnumdepth}{%d}\n" sec-num))) - ;; Author. - (let ((author (and (plist-get info :with-author) - (let ((auth (plist-get info :author))) - (and auth (org-export-data auth info))))) - (email (and (plist-get info :with-email) - (org-export-data (plist-get info :email) info)))) - (cond ((and author email (not (string= "" email))) - (format "\\author{%s\\thanks{%s}}\n" author email)) - ((or author email) (format "\\author{%s}\n" (or author email))))) - ;; Date. - (let ((date (and (plist-get info :with-date) (org-export-get-date info)))) - (format "\\date{%s}\n" (org-export-data date info))) - ;; Title - (format "\\title{%s}\n" title) - (when (org-string-nw-p subtitle) - (concat (format (plist-get info :beamer-subtitle-format) subtitle) "\n")) - ;; Beamer-header - (let ((beamer-header (plist-get info :beamer-header))) - (when beamer-header - (format "%s\n" (plist-get info :beamer-header)))) - ;; 9. Hyperref options. - (let ((template (plist-get info :latex-hyperref-template))) - (and (stringp template) - (format-spec template (org-latex--format-spec info)))) - ;; engrave-faces-latex preamble - (when (and (eq (plist-get info :latex-src-block-backend) 'engraved) - (org-element-map (plist-get info :parse-tree) - '(src-block inline-src-block) #'identity - info t)) - (org-latex-generate-engraved-preamble info)) - ;; Document start. - "\\begin{document}\n\n" - ;; Title command. - (org-element-normalize-string - (cond ((not (plist-get info :with-title)) nil) - ((string= "" title) nil) - ((not (stringp org-latex-title-command)) nil) - ((string-match "\\(?:[^%]\\|^\\)%s" - org-latex-title-command) - (format org-latex-title-command title)) - (t org-latex-title-command))) - ;; Table of contents. - (let ((depth (plist-get info :with-toc))) - (when depth - (concat - (format "\\begin{frame}%s{%s}\n" - (org-beamer--normalize-argument - (plist-get info :beamer-outline-frame-options) 'option) - (plist-get info :beamer-outline-frame-title)) - (when (wholenump depth) - (format "\\setcounter{tocdepth}{%d}\n" depth)) - "\\tableofcontents\n" - "\\end{frame}\n\n"))) - ;; Document's body. - contents - ;; Creator. - (if (plist-get info :with-creator) - (concat (plist-get info :creator) "\n") - "") + ;; Possibly limit depth for headline numbering. + (let ((sec-num (plist-get info :section-numbers))) + (when (integerp sec-num) + (format "\\setcounter{secnumdepth}{%d}\n" sec-num))) + ;; Author. + (let ((author (and (plist-get info :with-author) + (let ((auth (plist-get info :author))) + (and auth (org-export-data auth info))))) + (email (and (plist-get info :with-email) + (org-export-data (plist-get info :email) info)))) + (cond ((and author email (not (string= "" email))) + (format "\\author{%s\\thanks{%s}}\n" author email)) + ((or author email) (format "\\author{%s}\n" (or author email))))) + ;; Date. + (let ((date (and (plist-get info :with-date) (org-export-get-date info)))) + (format "\\date{%s}\n" (org-export-data date info))) + ;; Title + (format "\\title{%s}\n" title) + (when (org-string-nw-p subtitle) + (concat (format (plist-get info :beamer-subtitle-format) subtitle) "\n")) + ;; Beamer-header + (let ((beamer-header (plist-get info :beamer-header))) + (when beamer-header + (format "%s\n" (plist-get info :beamer-header)))) + ;; 9. Hyperref options. + (let ((template (plist-get info :latex-hyperref-template))) + (and (stringp template) + (format-spec template (org-latex--format-spec info)))) + ;; engrave-faces-latex preamble + (when (and (eq (plist-get info :latex-src-block-backend) 'engraved) + (org-element-map (plist-get info :parse-tree) + '(src-block inline-src-block) #'identity + info t)) + (org-latex-generate-engraved-preamble info)) + ;; Document start. + "\\begin{document}\n\n" + ;; Title command. + (org-element-normalize-string + (cond ((not (plist-get info :with-title)) nil) + ((string= "" title) nil) + ((not (stringp org-latex-title-command)) nil) + ((string-match "\\(?:[^%]\\|^\\)%s" + org-latex-title-command) + (format org-latex-title-command title)) + (t org-latex-title-command))) + ;; Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth + (concat + (format "\\begin{frame}%s{%s}\n" + (org-beamer--normalize-argument + (plist-get info :beamer-outline-frame-options) 'option) + (plist-get info :beamer-outline-frame-title)) + (when (wholenump depth) + (format "\\setcounter{tocdepth}{%d}\n" depth)) + "\\tableofcontents\n" + "\\end{frame}\n\n"))) + ;; Document's body. + contents + ;; Creator. + (if (plist-get info :with-creator) + (concat (plist-get info :creator) "\n") + "") ;; Document end. - "\\end{document}"))) + "\\end{document}") + (org-beamer-theme-settings info)))) diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index f39240cd8..b0b515335 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -2032,6 +2032,35 @@ If the square brackets are missing, return STR enclosed in square brackets." (replace-regexp-in-string ; remove excess ] at the end "]+\\'" "]" str)))))) +;;; LaTeX class declaration +(defun org-latex--make-class-template (info snippet?) + "Return the class template from INFO. +Factored out from `org-latex-make-preamble' to improve readibility" + (let* ((class (plist-get info :latex-class)) + (class-options (org-latex--mk-options (plist-get info :latex-class-options))) + (header (nth 1 (assoc class (plist-get info :latex-classes)))) + (pre-template ;; this may need further ammending + (if (stringp header) ;; when the class is defined in org-latex-classes + (mapconcat #'org-element-normalize-string + (list + (and (not snippet?) + (plist-get info :latex-class-pre)) + (if (not class-options) header + (replace-regexp-in-string + "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)" + class-options header t nil 1))) + nil) + ;; unknown to org-latex-classes -> error + (user-error "Unknown LaTeX class `%s'" class)))) + (mapconcat #'org-element-normalize-string + ;; + ;; Put all class related stuff here. + ;; + (list (and (not snippet?) + (plist-get info :latex-class-pre)) + pre-template) + ""))) + ;;;###autoload (defun org-latex-make-preamble (info &optional template snippet?) "Return a formatted LaTeX preamble. @@ -2041,22 +2070,11 @@ as expected by `org-splice-latex-header'. When SNIPPET? is non-nil, only includes packages relevant to image generation, as specified in `org-latex-default-packages-alist' or `org-latex-packages-alist'." - (let* ((class (plist-get info :latex-class)) - (class-template - (or template - (let* ((class-options (org-latex--mk-options (plist-get info :latex-class-options))) - (header (nth 1 (assoc class (plist-get info :latex-classes))))) - (and (stringp header) - (mapconcat #'org-element-normalize-string - (list - (and (not snippet?) - (plist-get info :latex-class-pre)) - (if (not class-options) header - (replace-regexp-in-string - "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)" - class-options header t nil 1))) - nil))) - (user-error "Unknown LaTeX class `%s'" class)))) + (let ((class-template + (or template + ;; Just in case someone needs to write + ;; (org-latex-make-preamble nil t) + (org-latex--make-class-template info snippet?)))) (org-latex-guess-polyglossia-language (org-latex-guess-babel-language (org-latex-guess-inputenc @@ -2073,7 +2091,6 @@ specified in `org-latex-default-packages-alist' or (and (not snippet?) (plist-get info :latex-use-sans) "\\renewcommand*\\familydefault{\\sfdefault}")) - "")))) info) info))) diff --git a/testing/lisp/test-ox-beamer.el b/testing/lisp/test-ox-beamer.el index 24550ec62..4c68fa33a 100644 --- a/testing/lisp/test-ox-beamer.el +++ b/testing/lisp/test-ox-beamer.el @@ -109,5 +109,21 @@ Here is a second example: (should (search-forward (concat "\\end{frame}") nil t)) (should (search-forward (concat "\\end{" org-beamer-frame-environment "}")))))) +(ert-deftest ox-beamer/org-beamer-pre-theme () + "Test that the theme is in its new place and beamer-pre is included." + (org-test-with-exported-text + 'beamer + "#+OPTIONS: toc:nil +#+LATEX_CLASS_OPTIONS: presentation,t +#+BEAMER_THEME_PRE: \\usepackage{geometry} +#+BEAMER_THEME: Boadilla +* A frame +Here is an example. +" + (goto-char (point-min)) + (should (search-forward "\\documentclass[presentation,t]{beamer} +\\usepackage{geometry} +\\usetheme{Boadilla}\n")))) + (provide 'test-ox-beamer) ;;; test-ox-beamer.el ends here -- 2.43.0
