config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

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