ob-latex.el (11847B)
1 ;;; ob-latex.el --- Babel Functions for LaTeX -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2009-2024 Free Software Foundation, Inc. 4 5 ;; Author: Eric Schulte 6 ;; Keywords: literate programming, reproducible research 7 ;; URL: https://orgmode.org 8 9 ;; This file is part of GNU Emacs. 10 11 ;; GNU Emacs is free software: you can redistribute it and/or modify 12 ;; it under the terms of the GNU General Public License as published by 13 ;; the Free Software Foundation, either version 3 of the License, or 14 ;; (at your option) any later version. 15 16 ;; GNU Emacs is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 21 ;; You should have received a copy of the GNU General Public License 22 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 23 24 ;;; Commentary: 25 26 ;; Org-Babel support for evaluating LaTeX source code. 27 ;; 28 ;; Currently on evaluation this returns raw LaTeX code, unless a :file 29 ;; header argument is given in which case small png or pdf files will 30 ;; be created directly form the latex source code. 31 32 ;;; Code: 33 34 (require 'org-macs) 35 (org-assert-version) 36 37 (require 'ob) 38 (require 'org-macs) 39 40 (declare-function org-create-formula-image "org" (string tofile options buffer &optional type)) 41 (declare-function org-latex-compile "ox-latex" (texfile &optional snippet)) 42 (declare-function org-latex-guess-inputenc "ox-latex" (header)) 43 (declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra)) 44 (declare-function org-at-heading-p "org" (&optional _)) 45 (declare-function org-back-to-heading "org" (&optional invisible-ok)) 46 (declare-function org-next-visible-heading "org" (arg)) 47 48 (defvar org-babel-tangle-lang-exts) 49 (add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex")) 50 51 (defvar org-format-latex-header) ; From org.el 52 (defvar org-format-latex-options) ; From org.el 53 (defvar org-latex-default-packages-alist) ; From org.el 54 (defvar org-latex-packages-alist) ; From org.el 55 (defvar org-preview-latex-process-alist) ; From org.el 56 57 (defvar org-babel-default-header-args:latex 58 '((:results . "latex") (:exports . "results")) 59 "Default arguments to use when evaluating a LaTeX source block.") 60 61 (defconst org-babel-header-args:latex 62 '((border . :any) 63 (fit . :any) 64 (imagemagick . ((nil t))) 65 (iminoptions . :any) 66 (imoutoptions . :any) 67 (packages . :any) 68 (pdfheight . :any) 69 (pdfpng . :any) 70 (pdfwidth . :any) 71 (headers . :any) 72 (buffer . ((yes no)))) 73 "LaTeX-specific header arguments.") 74 75 (defcustom org-babel-latex-htlatex "htlatex" 76 "The htlatex command to enable conversion of LaTeX to SVG or HTML." 77 :group 'org-babel 78 :type 'string) 79 80 (defcustom org-babel-latex-preamble 81 (lambda (_) 82 "\\documentclass[preview]{standalone} 83 \\def\\pgfsysdriver{pgfsys-tex4ht.def} 84 ") 85 "Closure which evaluates at runtime to the LaTeX preamble. 86 87 It takes 1 argument which is the parameters of the source block." 88 :group 'org-babel 89 :type 'function) 90 91 (defcustom org-babel-latex-begin-env 92 (lambda (_) 93 "\\begin{document}") 94 "Function that evaluates to the begin part of the document environment. 95 96 It takes 1 argument which is the parameters of the source block. 97 This allows adding additional code that will be ignored when 98 exporting the literal LaTeX source." 99 :group 'org-babel 100 :type 'function) 101 102 (defcustom org-babel-latex-end-env 103 (lambda (_) 104 "\\end{document}") 105 "Closure which evaluates at runtime to the end part of the document environment. 106 107 It takes 1 argument which is the parameters of the source block. 108 This allows adding additional code that will be ignored when 109 exporting the literal LaTeX source." 110 :group 'org-babel 111 :type 'function) 112 113 (defcustom org-babel-latex-pdf-svg-process 114 "inkscape \ 115 --pdf-poppler \ 116 --export-area-drawing \ 117 --export-text-to-path \ 118 --export-plain-svg \ 119 --export-filename=%O \ 120 %f" 121 "Command to convert a PDF file to an SVG file." 122 :group 'org-babel 123 :type 'string 124 :package-version '(Org . "9.6")) 125 126 (defcustom org-babel-latex-htlatex-packages 127 '("[usenames]{color}" "{tikz}" "{color}" "{listings}" "{amsmath}") 128 "Packages to use for htlatex export." 129 :group 'org-babel 130 :type '(repeat (string))) 131 132 (defcustom org-babel-latex-process-alist 133 `(,(cons 'png (alist-get 'dvipng org-preview-latex-process-alist))) 134 "Definitions of external processes for LaTeX result generation. 135 See `org-preview-latex-process-alist' for more details. 136 137 The following process symbols are recognized: 138 - `png' :: Process used to produce .png output." 139 :group 'org-babel 140 :package-version '(Org . "9.7") 141 :type '(alist :tag "LaTeX to image backends" 142 :value-type (plist))) 143 144 (defun org-babel-expand-body:latex (body params) 145 "Expand BODY according to PARAMS, return the expanded body." 146 (mapc (lambda (pair) ;; replace variables 147 (setq body 148 (replace-regexp-in-string 149 (regexp-quote (format "%S" (car pair))) 150 (if (stringp (cdr pair)) 151 (cdr pair) (format "%S" (cdr pair))) 152 body t t))) 153 (org-babel--get-vars params)) 154 (let ((prologue (cdr (assq :prologue params))) 155 (epilogue (cdr (assq :epilogue params)))) 156 (org-trim 157 (concat 158 (and prologue (concat prologue "\n")) 159 body 160 (and epilogue (concat "\n" epilogue "\n")))))) 161 162 (defun org-babel-execute:latex (body params) 163 "Execute LaTeX BODY according to PARAMS. 164 This function is called by `org-babel-execute-src-block'." 165 (setq body (org-babel-expand-body:latex body params)) 166 (if (cdr (assq :file params)) 167 (let* ((out-file (cdr (assq :file params))) 168 (extension (file-name-extension out-file)) 169 (tex-file (org-babel-temp-file "latex-" ".tex")) 170 (border (cdr (assq :border params))) 171 (imagemagick (cdr (assq :imagemagick params))) 172 (im-in-options (cdr (assq :iminoptions params))) 173 (im-out-options (cdr (assq :imoutoptions params))) 174 (fit (or (cdr (assq :fit params)) border)) 175 (height (and fit (cdr (assq :pdfheight params)))) 176 (width (and fit (cdr (assq :pdfwidth params)))) 177 (headers (cdr (assq :headers params))) 178 (in-buffer (not (string= "no" (cdr (assq :buffer params))))) 179 (org-latex-packages-alist 180 (append (cdr (assq :packages params)) org-latex-packages-alist))) 181 (cond 182 ((and (string-suffix-p ".png" out-file) (not imagemagick)) 183 (let ((org-format-latex-header 184 (concat org-format-latex-header "\n" 185 (mapconcat #'identity headers "\n"))) 186 (org-preview-latex-process-alist org-babel-latex-process-alist)) 187 (org-create-formula-image 188 body out-file org-format-latex-options in-buffer 'png))) 189 ((string= "svg" extension) 190 (with-temp-file tex-file 191 (insert (concat (funcall org-babel-latex-preamble params) 192 (mapconcat #'identity headers "\n") 193 (funcall org-babel-latex-begin-env params) 194 body 195 (funcall org-babel-latex-end-env params)))) 196 (let ((tmp-pdf (org-babel-latex-tex-to-pdf tex-file))) 197 (let* ((log-buf (get-buffer-create "*Org Babel LaTeX Output*")) 198 (err-msg "org babel latex failed") 199 (img-out (org-compile-file 200 tmp-pdf 201 (list org-babel-latex-pdf-svg-process) 202 extension err-msg log-buf))) 203 (rename-file img-out out-file t)))) 204 ((string-suffix-p ".tikz" out-file) 205 (when (file-exists-p out-file) (delete-file out-file)) 206 (with-temp-file out-file 207 (insert body))) 208 ((and (string= "html" extension) 209 (executable-find org-babel-latex-htlatex)) 210 ;; TODO: this is a very different way of generating the 211 ;; frame latex document than in the pdf case. Ideally, both 212 ;; would be unified. This would prevent bugs creeping in 213 ;; such as the one fixed on Aug 16 2014 whereby :headers was 214 ;; not included in the SVG/HTML case. 215 (with-temp-file tex-file 216 (insert (concat 217 "\\documentclass[preview]{standalone} 218 \\def\\pgfsysdriver{pgfsys-tex4ht.def} 219 " 220 (mapconcat (lambda (pkg) 221 (concat "\\usepackage" pkg)) 222 org-babel-latex-htlatex-packages 223 "\n") 224 (if headers 225 (concat "\n" 226 (if (listp headers) 227 (mapconcat #'identity headers "\n") 228 headers) "\n") 229 "") 230 "\\begin{document}" 231 body 232 "\\end{document}"))) 233 (when (file-exists-p out-file) (delete-file out-file)) 234 (let ((default-directory (file-name-directory tex-file))) 235 (shell-command (format "%s %s" org-babel-latex-htlatex tex-file))) 236 (cond 237 ((file-exists-p (concat (file-name-sans-extension tex-file) "-1.svg")) 238 (if (string-suffix-p ".svg" out-file) 239 (progn 240 (shell-command "pwd") 241 (rename-file (concat (file-name-sans-extension tex-file) "-1.svg") 242 out-file t)) 243 (error "SVG file produced but HTML file requested"))) 244 ((file-exists-p (concat (file-name-sans-extension tex-file) ".html")) 245 (if (string-suffix-p ".html" out-file) 246 (rename-file (concat (file-name-sans-extension tex-file) ".html") 247 out-file t) 248 (error "HTML file produced but SVG file requested"))))) 249 ((or (string= "pdf" extension) imagemagick) 250 (with-temp-file tex-file 251 (require 'ox-latex) 252 (insert 253 (org-latex-guess-inputenc 254 (org-splice-latex-header 255 org-format-latex-header 256 (delq 257 nil 258 (mapcar 259 (lambda (el) 260 (unless (and (listp el) (string= "hyperref" (cadr el))) 261 el)) 262 org-latex-default-packages-alist)) 263 org-latex-packages-alist 264 nil)) 265 (if fit "\n\\usepackage[active, tightpage]{preview}\n" "") 266 (if border (format "\\setlength{\\PreviewBorder}{%s}" border) "") 267 (if height (concat "\n" (format "\\pdfpageheight %s" height)) "") 268 (if width (concat "\n" (format "\\pdfpagewidth %s" width)) "") 269 (if headers 270 (concat "\n" 271 (if (listp headers) 272 (mapconcat #'identity headers "\n") 273 headers) "\n") 274 "") 275 (if fit 276 (concat "\n\\begin{document}\n\\begin{preview}\n" body 277 "\n\\end{preview}\n\\end{document}\n") 278 (concat "\n\\begin{document}\n" body "\n\\end{document}\n")))) 279 (when (file-exists-p out-file) (delete-file out-file)) 280 (let ((transient-pdf-file (org-babel-latex-tex-to-pdf tex-file))) 281 (cond 282 ((string= "pdf" extension) 283 (rename-file transient-pdf-file out-file)) 284 (imagemagick 285 (org-babel-latex-convert-pdf 286 transient-pdf-file out-file im-in-options im-out-options) 287 (when (file-exists-p transient-pdf-file) 288 (delete-file transient-pdf-file))) 289 (t 290 (error "Can not create %s files, please specify a .png or .pdf file or try the :imagemagick header argument" 291 extension)))))) 292 nil) ;; signal that output has already been written to file 293 body)) 294 295 (defun org-babel-latex-convert-pdf (pdffile out-file im-in-options im-out-options) 296 "Generate OUT-FILE from PDFFILE using imagemagick. 297 IM-IN-OPTIONS are command line options for input file, as a string; 298 and IM-OUT-OPTIONS are the output file options." 299 (let ((cmd (concat "convert " im-in-options " " pdffile " " 300 im-out-options " " out-file))) 301 (message "Converting pdffile file %s..." cmd) 302 (shell-command cmd))) 303 304 (defun org-babel-latex-tex-to-pdf (file) 305 "Generate a pdf file according to the contents FILE." 306 (require 'ox-latex) 307 (org-latex-compile file)) 308 309 (defun org-babel-prep-session:latex (_session _params) 310 "Return an error because LaTeX doesn't support sessions." 311 (error "LaTeX does not support sessions")) 312 313 (provide 'ob-latex) 314 315 ;;; ob-latex.el ends here