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

Reply via email to