ob-latex.el (10918B)
1 ;;; ob-latex.el --- Babel Functions for LaTeX -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2009-2023 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 56 (defvar org-babel-default-header-args:latex 57 '((:results . "latex") (:exports . "results")) 58 "Default arguments to use when evaluating a LaTeX source block.") 59 60 (defconst org-babel-header-args:latex 61 '((border . :any) 62 (fit . :any) 63 (imagemagick . ((nil t))) 64 (iminoptions . :any) 65 (imoutoptions . :any) 66 (packages . :any) 67 (pdfheight . :any) 68 (pdfpng . :any) 69 (pdfwidth . :any) 70 (headers . :any) 71 (buffer . ((yes no)))) 72 "LaTeX-specific header arguments.") 73 74 (defcustom org-babel-latex-htlatex "htlatex" 75 "The htlatex command to enable conversion of LaTeX to SVG or HTML." 76 :group 'org-babel 77 :type 'string) 78 79 (defcustom org-babel-latex-preamble 80 (lambda (_) 81 "\\documentclass[preview]{standalone} 82 \\def\\pgfsysdriver{pgfsys-tex4ht.def} 83 ") 84 "Closure which evaluates at runtime to the LaTeX preamble. 85 86 It takes 1 argument which is the parameters of the source block." 87 :group 'org-babel 88 :type 'function) 89 90 (defcustom org-babel-latex-begin-env 91 (lambda (_) 92 "\\begin{document}") 93 "Function that evaluates to the begin part of the document environment. 94 95 It takes 1 argument which is the parameters of the source block. 96 This allows adding additional code that will be ignored when 97 exporting the literal LaTeX source." 98 :group 'org-babel 99 :type 'function) 100 101 (defcustom org-babel-latex-end-env 102 (lambda (_) 103 "\\end{document}") 104 "Closure which evaluates at runtime to the end part of the document environment. 105 106 It takes 1 argument which is the parameters of the source block. 107 This allows adding additional code that will be ignored when 108 exporting the literal LaTeX source." 109 :group 'org-babel 110 :type 'function) 111 112 (defcustom org-babel-latex-pdf-svg-process 113 "inkscape \ 114 --pdf-poppler \ 115 --export-area-drawing \ 116 --export-text-to-path \ 117 --export-plain-svg \ 118 --export-filename=%O \ 119 %f" 120 "Command to convert a PDF file to an SVG file." 121 :group 'org-babel 122 :type 'string 123 :package-version '(Org . "9.6")) 124 125 (defcustom org-babel-latex-htlatex-packages 126 '("[usenames]{color}" "{tikz}" "{color}" "{listings}" "{amsmath}") 127 "Packages to use for htlatex export." 128 :group 'org-babel 129 :type '(repeat (string))) 130 131 (defun org-babel-expand-body:latex (body params) 132 "Expand BODY according to PARAMS, return the expanded body." 133 (mapc (lambda (pair) ;; replace variables 134 (setq body 135 (replace-regexp-in-string 136 (regexp-quote (format "%S" (car pair))) 137 (if (stringp (cdr pair)) 138 (cdr pair) (format "%S" (cdr pair))) 139 body))) 140 (org-babel--get-vars params)) 141 (org-trim body)) 142 143 (defun org-babel-execute:latex (body params) 144 "Execute a block of Latex code with Babel. 145 This function is called by `org-babel-execute-src-block'." 146 (setq body (org-babel-expand-body:latex body params)) 147 (if (cdr (assq :file params)) 148 (let* ((out-file (cdr (assq :file params))) 149 (extension (file-name-extension out-file)) 150 (tex-file (org-babel-temp-file "latex-" ".tex")) 151 (border (cdr (assq :border params))) 152 (imagemagick (cdr (assq :imagemagick params))) 153 (im-in-options (cdr (assq :iminoptions params))) 154 (im-out-options (cdr (assq :imoutoptions params))) 155 (fit (or (cdr (assq :fit params)) border)) 156 (height (and fit (cdr (assq :pdfheight params)))) 157 (width (and fit (cdr (assq :pdfwidth params)))) 158 (headers (cdr (assq :headers params))) 159 (in-buffer (not (string= "no" (cdr (assq :buffer params))))) 160 (org-latex-packages-alist 161 (append (cdr (assq :packages params)) org-latex-packages-alist))) 162 (cond 163 ((and (string-suffix-p ".png" out-file) (not imagemagick)) 164 (let ((org-format-latex-header 165 (concat org-format-latex-header "\n" 166 (mapconcat #'identity headers "\n")))) 167 (org-create-formula-image 168 body out-file org-format-latex-options in-buffer))) 169 ((string= "svg" extension) 170 (with-temp-file tex-file 171 (insert (concat (funcall org-babel-latex-preamble params) 172 (mapconcat #'identity headers "\n") 173 (funcall org-babel-latex-begin-env params) 174 body 175 (funcall org-babel-latex-end-env params)))) 176 (let ((tmp-pdf (org-babel-latex-tex-to-pdf tex-file))) 177 (let* ((log-buf (get-buffer-create "*Org Babel LaTeX Output*")) 178 (err-msg "org babel latex failed") 179 (img-out (org-compile-file 180 tmp-pdf 181 (list org-babel-latex-pdf-svg-process) 182 extension err-msg log-buf))) 183 (shell-command (format "mv %s %s" img-out out-file))))) 184 ((string-suffix-p ".tikz" out-file) 185 (when (file-exists-p out-file) (delete-file out-file)) 186 (with-temp-file out-file 187 (insert body))) 188 ((and (string= "html" extension) 189 (executable-find org-babel-latex-htlatex)) 190 ;; TODO: this is a very different way of generating the 191 ;; frame latex document than in the pdf case. Ideally, both 192 ;; would be unified. This would prevent bugs creeping in 193 ;; such as the one fixed on Aug 16 2014 whereby :headers was 194 ;; not included in the SVG/HTML case. 195 (with-temp-file tex-file 196 (insert (concat 197 "\\documentclass[preview]{standalone} 198 \\def\\pgfsysdriver{pgfsys-tex4ht.def} 199 " 200 (mapconcat (lambda (pkg) 201 (concat "\\usepackage" pkg)) 202 org-babel-latex-htlatex-packages 203 "\n") 204 (if headers 205 (concat "\n" 206 (if (listp headers) 207 (mapconcat #'identity headers "\n") 208 headers) "\n") 209 "") 210 "\\begin{document}" 211 body 212 "\\end{document}"))) 213 (when (file-exists-p out-file) (delete-file out-file)) 214 (let ((default-directory (file-name-directory tex-file))) 215 (shell-command (format "%s %s" org-babel-latex-htlatex tex-file))) 216 (cond 217 ((file-exists-p (concat (file-name-sans-extension tex-file) "-1.svg")) 218 (if (string-suffix-p ".svg" out-file) 219 (progn 220 (shell-command "pwd") 221 (shell-command (format "mv %s %s" 222 (concat (file-name-sans-extension tex-file) "-1.svg") 223 out-file))) 224 (error "SVG file produced but HTML file requested"))) 225 ((file-exists-p (concat (file-name-sans-extension tex-file) ".html")) 226 (if (string-suffix-p ".html" out-file) 227 (shell-command "mv %s %s" 228 (concat (file-name-sans-extension tex-file) 229 ".html") 230 out-file) 231 (error "HTML file produced but SVG file requested"))))) 232 ((or (string= "pdf" extension) imagemagick) 233 (with-temp-file tex-file 234 (require 'ox-latex) 235 (insert 236 (org-latex-guess-inputenc 237 (org-splice-latex-header 238 org-format-latex-header 239 (delq 240 nil 241 (mapcar 242 (lambda (el) 243 (unless (and (listp el) (string= "hyperref" (cadr el))) 244 el)) 245 org-latex-default-packages-alist)) 246 org-latex-packages-alist 247 nil)) 248 (if fit "\n\\usepackage[active, tightpage]{preview}\n" "") 249 (if border (format "\\setlength{\\PreviewBorder}{%s}" border) "") 250 (if height (concat "\n" (format "\\pdfpageheight %s" height)) "") 251 (if width (concat "\n" (format "\\pdfpagewidth %s" width)) "") 252 (if headers 253 (concat "\n" 254 (if (listp headers) 255 (mapconcat #'identity headers "\n") 256 headers) "\n") 257 "") 258 (if fit 259 (concat "\n\\begin{document}\n\\begin{preview}\n" body 260 "\n\\end{preview}\n\\end{document}\n") 261 (concat "\n\\begin{document}\n" body "\n\\end{document}\n")))) 262 (when (file-exists-p out-file) (delete-file out-file)) 263 (let ((transient-pdf-file (org-babel-latex-tex-to-pdf tex-file))) 264 (cond 265 ((string= "pdf" extension) 266 (rename-file transient-pdf-file out-file)) 267 (imagemagick 268 (org-babel-latex-convert-pdf 269 transient-pdf-file out-file im-in-options im-out-options) 270 (when (file-exists-p transient-pdf-file) 271 (delete-file transient-pdf-file))) 272 (t 273 (error "Can not create %s files, please specify a .png or .pdf file or try the :imagemagick header argument" 274 extension)))))) 275 nil) ;; signal that output has already been written to file 276 body)) 277 278 (defun org-babel-latex-convert-pdf (pdffile out-file im-in-options im-out-options) 279 "Generate a file from a pdf file using imagemagick." 280 (let ((cmd (concat "convert " im-in-options " " pdffile " " 281 im-out-options " " out-file))) 282 (message "Converting pdffile file %s..." cmd) 283 (shell-command cmd))) 284 285 (defun org-babel-latex-tex-to-pdf (file) 286 "Generate a pdf file according to the contents FILE." 287 (require 'ox-latex) 288 (org-latex-compile file)) 289 290 (defun org-babel-prep-session:latex (_session _params) 291 "Return an error because LaTeX doesn't support sessions." 292 (error "LaTeX does not support sessions")) 293 294 (provide 'ob-latex) 295 296 ;;; ob-latex.el ends here