Hi

On Mon, 6 Apr 2026 at 19:03, Ihor Radchenko <[email protected]> wrote:

> Pedro Andres Aranda Gutierrez <[email protected]> writes:
>
> >> That's a bad idea to call ox-beamer functions from inside ox-latex.
> >> This will lead to unmaintainable code if we mix different libraries.
> >
> > I was just trying to keep beamer code in ox-beamer.el
> > But there is nothing that would break if we moved the function from
> > ox-beamer.el to ox-latex.el. info will have the properties anyhow and
> > independently
> > of where the code is placed, or is there?
>
> Nothing will break, yes.
> But having beamer-related code in other files is simply a bad coding
> practice, especially if that's such a core functionality as defining
> themes. This is not about correctness, but about code quality.
>


> If you have library functionality split across multiple other libraries,
> it makes it difficult to understand how the code works. It also makes it
> potentially difficult to change ox-latex -- it will effectively gain an
> extra dependency to account for.


IMHO trying to compact code too much makes things worse.
There are ways to move code around when you understand what the code does...


> Unfortunately, we already have bad
> examples of such code style - org-agenda-prepare-buffers does
> agenda-specific things in org.el, making the already complicated
> org-agenda.el code even harder to parse.  I do not want such code to
> proliferate.
>
> >> If you want to modify how ox-latex generates the preamble, what about
> >> modifying
> >>
> >> (unless (assoc "beamer" org-latex-classes)
> >>   (add-to-list 'org-latex-classes
> >>                '("beamer"
> >>                  "\\documentclass[presentation]{beamer}"
> >>                  ("\\section{%s}" . "\\section*{%s}")
> >>                  ("\\subsection{%s}" . "\\subsection*{%s}")
> >>                  ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))))
> >>
> >> ?
> >>
> >> Or, alternatively, you can dynamically splice the theme into
> >> :latex-classes in the INFO inside `org-beamer-template' before calling
> >> `org-latex-make-preamble'.
> >>
> >
> > Isn't that a bit of an overkill? Since the information is already
> there....
> > This was just a way of keeping beamer stuff things together. But it
> really
> > doesn't have to.
> > I feel that moving the function from ox-beamer to ox-latex makes the
> trick
> > cleaner.
>
> I hope I explained the reasons clearly in the above.
> I am strongly against splitting code between libraries. We already have
> too much of such practice. That is just creating headaches for future
> contributors.
>

Maybe the current code is already a headache... Don't have time to to start
an argument on that.
Just look at the attached 0001-ox-latex-... patch. Came after I broke my
head over the "real" patch.

My suggestion may be an overkill, yes. I will be happy if you find
> something simpler.


Apparently there was... if the code had only been easier to read. See
0001-ox-beamer... patch


> But please do avoid putting ox-beamer code into
> ox-latex directly.
>


>
> >> > +    (concat
> >> > +     pre-header
> >> > +     (mapconcat (lambda (args) (apply format-theme args))
> >>
> >> I'd write (lambda (prop command) ...) explicitly. That's more readable
> >> IMHO.
> >>
> > I was trying to just factor out the original code and not touch it. And
> > that is how it was originally...
>
> Right. I did not notice that. I do not object keeping it then. Although
> an improvement would be welcome (while we are at it).
>
> --
> Ihor Radchenko // yantar92,
> Org mode maintainer,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>
>


-- 
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 86a7d41007d3215caeac90de2ac88c0745acde69 Mon Sep 17 00:00:00 2001
From: "Pedro A. Aranda" <[email protected]>
Date: Tue, 7 Apr 2026 08:57:33 +0200
Subject: [PATCH] ox-beamer: New LaTeX preamble sequence and new
 BEAMER_THEME_PRE

lisp/ox-beamer.el: New option BEAMER_THEME_PRE.
(org-beamer-theme-settings): new function to manage theme-related
settings.
(org-beamer-class-template): new function to generate the Beamer class
template.
(org-beamer-template): pass the generated Beamer class template
to (org-latex-make-preamble).
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                   | 11 ++++++
 lisp/ox-beamer.el              | 66 +++++++++++++++++++++++-----------
 testing/lisp/test-ox-beamer.el | 14 ++++++++
 4 files changed, 83 insertions(+), 21 deletions(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 357487c70..28fb068e9 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 c5792cf58..ef0479d51 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -42,6 +42,12 @@ 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.
+
 ** New features
 
 # We list the most important features, and the features that may
@@ -149,6 +155,11 @@ header argument will be updated. This allows for the cache feature to
 still work when a block is evaluated indirectly to resolve a reference
 in another block.
 
+*** 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..0e7dc5c6d 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,6 +857,48 @@ 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"))
+	        ""))))
+;;
+;; Generate the preamble up to the theme
+(defun org-beamer-class-template(info)
+  "Generate the class template from INFO.
+This will include class-pre, class and theme definitions."
+  (let* ((class-pre (plist-get info :latex-class-pre))
+         (class-options (concat "[" (plist-get info :latex-class-options) "]")))
+    ;; See org-latex--mk-options:
+    ;; Accept class options with and without square brackets
+    (setq class-options (replace-regexp-in-string "\\`\\[+" "[" class-options))
+    (setq class-options (replace-regexp-in-string "\\]+\\'" "]" class-options))
+    (concat class-pre (and class-pre "\n")
+            "\\documentclass" class-options "{beamer}\n"
+            (org-beamer-theme-settings info))))
+;;
 ;; Template used is similar to the one used in `latex' backend,
 ;; excepted for the table of contents and Beamer themes.
 
@@ -871,31 +914,12 @@ holding export options."
 	  (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)
+     ;; Document class, theme and packages.
+     (org-latex-make-preamble info (org-beamer-class-template 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)
diff --git a/testing/lisp/test-ox-beamer.el b/testing/lisp/test-ox-beamer.el
index 24550ec62..3e531db3f 100644
--- a/testing/lisp/test-ox-beamer.el
+++ b/testing/lisp/test-ox-beamer.el
@@ -109,5 +109,19 @@ 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}\n\\usepackage{geometry}\n\\usetheme{Boadilla}\n"))))
+
 (provide 'test-ox-beamer)
 ;;; test-ox-beamer.el ends here
-- 
2.43.0

From 87702bdafc9486541c3f08401ec9b410b630f22d Mon Sep 17 00:00:00 2001
From: "Pedro A. Aranda" <[email protected]>
Date: Wed, 8 Apr 2026 18:08:46 +0200
Subject: [PATCH] ox-latex.el: Improve readibility of org-latex-make-preamble

ox-latex.el: Factor out the class template generation
to (org-latex--make-class-template).  Clean up and simplify the code.
(org-latex-make-preamble): use new function and remove empty line.
---
 lisp/ox-latex.el | 41 +++++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 18 deletions(-)

diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index f39240cd8..3f019386f 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -1,4 +1,4 @@
-;;; ox-latex.el --- LaTeX Backend for Org Export Engine -*- lexical-binding: t; -*-
+;; ox-latex.el --- LaTeX Backend for Org Export Engine -*- lexical-binding: t; -*-
 
 ;; Copyright (C) 2011-2026 Free Software Foundation, Inc.
 
@@ -2032,6 +2032,26 @@ 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)))))
+    (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)
+      (user-error "Unknown LaTeX class `%s'" class))))
+
+
 ;;;###autoload
 (defun org-latex-make-preamble (info &optional template snippet?)
   "Return a formatted LaTeX preamble.
@@ -2041,22 +2061,8 @@ 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 (org-latex--make-class-template info snippet?)))
     (org-latex-guess-polyglossia-language
      (org-latex-guess-babel-language
       (org-latex-guess-inputenc
@@ -2073,7 +2079,6 @@ specified in `org-latex-default-packages-alist' or
                           (and (not snippet?)
                                (plist-get info :latex-use-sans)
                                "\\renewcommand*\\familydefault{\\sfdefault}"))
-
 		    ""))))
       info)
      info)))
-- 
2.43.0

Reply via email to