forked from mirrors/org-mode
756 lines
32 KiB
EmacsLisp
756 lines
32 KiB
EmacsLisp
;;; org-latex-preview.el --- LaTeX previews for Org -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2022 Free Software Foundation, Inc.
|
|
|
|
;; Keywords: tex, extensions, tools
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
;;
|
|
;; LaTeX previews for Org
|
|
|
|
;;; Code:
|
|
|
|
(defgroup org-latex nil
|
|
"Options for embedding LaTeX code into Org mode."
|
|
:tag "Org LaTeX"
|
|
:group 'org)
|
|
|
|
(defcustom org-format-latex-options
|
|
'(:foreground default :background default :scale 1.0
|
|
:html-foreground "Black" :html-background "Transparent"
|
|
:html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\["))
|
|
"Options for creating images from LaTeX fragments.
|
|
This is a property list with the following properties:
|
|
:foreground the foreground color for images embedded in Emacs, e.g. \"Black\".
|
|
`default' means use the foreground of the default face.
|
|
`auto' means use the foreground from the text face.
|
|
:background the background color, or \"Transparent\".
|
|
`default' means use the background of the default face.
|
|
`auto' means use the background from the text face.
|
|
:scale a scaling factor for the size of the images, to get more pixels
|
|
:html-foreground, :html-background, :html-scale
|
|
the same numbers for HTML export.
|
|
:matchers a list indicating which matchers should be used to
|
|
find LaTeX fragments. Valid members of this list are:
|
|
\"begin\" find environments
|
|
\"$1\" find single characters surrounded by $.$
|
|
\"$\" find math expressions surrounded by $...$
|
|
\"$$\" find math expressions surrounded by $$....$$
|
|
\"\\(\" find math expressions surrounded by \\(...\\)
|
|
\"\\=\\[\" find math expressions surrounded by \\=\\[...\\]"
|
|
:group 'org-latex
|
|
:type 'plist)
|
|
|
|
(defcustom org-format-latex-signal-error t
|
|
"Non-nil means signal an error when image creation of LaTeX snippets fails.
|
|
When nil, just push out a message."
|
|
:group 'org-latex
|
|
:version "24.1"
|
|
:type 'boolean)
|
|
|
|
(defcustom org-latex-to-mathml-jar-file nil
|
|
"Value of\"%j\" in `org-latex-to-mathml-convert-command'.
|
|
Use this to specify additional executable file say a jar file.
|
|
|
|
When using MathToWeb as the converter, specify the full-path to
|
|
your mathtoweb.jar file."
|
|
:group 'org-latex
|
|
:version "24.1"
|
|
:type '(choice
|
|
(const :tag "None" nil)
|
|
(file :tag "JAR file" :must-match t)))
|
|
|
|
(defcustom org-latex-to-mathml-convert-command nil
|
|
"Command to convert LaTeX fragments to MathML.
|
|
Replace format-specifiers in the command as noted below and use
|
|
`shell-command' to convert LaTeX to MathML.
|
|
%j: Executable file in fully expanded form as specified by
|
|
`org-latex-to-mathml-jar-file'.
|
|
%I: Input LaTeX file in fully expanded form.
|
|
%i: Shell-escaped LaTeX fragment to be converted.
|
|
It must not be used inside a quoted argument, the result of %i
|
|
expansion inside a quoted argument is undefined.
|
|
%o: Output MathML file.
|
|
|
|
This command is used by `org-create-math-formula'.
|
|
|
|
When using MathToWeb as the converter, set this option to
|
|
\"java -jar %j -unicode -force -df %o %I\".
|
|
|
|
When using LaTeXML set this option to
|
|
\"latexmlmath %i --presentationmathml=%o\"."
|
|
:group 'org-latex
|
|
:version "24.1"
|
|
:type '(choice
|
|
(const :tag "None" nil)
|
|
(string :tag "\nShell command")))
|
|
|
|
(defcustom org-latex-to-html-convert-command nil
|
|
"Shell command to convert LaTeX fragments to HTML.
|
|
This command is very open-ended: the output of the command will
|
|
directly replace the LaTeX fragment in the resulting HTML.
|
|
Replace format-specifiers in the command as noted below and use
|
|
`shell-command' to convert LaTeX to HTML.
|
|
%i: The LaTeX fragment to be converted (shell-escaped).
|
|
It must not be used inside a quoted argument, the result of %i
|
|
expansion inside a quoted argument is undefined.
|
|
|
|
For example, this could be used with LaTeXML as
|
|
\"latexmlc literal:%i --profile=math --preload=siunitx.sty 2>/dev/null\"."
|
|
:group 'org-latex
|
|
:package-version '(Org . "9.4")
|
|
:type '(choice
|
|
(const :tag "None" nil)
|
|
(string :tag "Shell command")))
|
|
|
|
(defcustom org-preview-latex-default-process 'dvipng
|
|
"The default process to convert LaTeX fragments to image files.
|
|
All available processes and theirs documents can be found in
|
|
`org-preview-latex-process-alist', which see."
|
|
:group 'org-latex
|
|
:version "26.1"
|
|
:package-version '(Org . "9.0")
|
|
:type 'symbol)
|
|
|
|
(defcustom org-preview-latex-process-alist
|
|
'((dvipng
|
|
:programs ("latex" "dvipng")
|
|
:description "dvi > png"
|
|
:message "you need to install the programs: latex and dvipng."
|
|
:image-input-type "dvi"
|
|
:image-output-type "png"
|
|
:image-size-adjust (1.0 . 1.0)
|
|
:latex-compiler ("latex -interaction nonstopmode -output-directory %o %f")
|
|
:image-converter ("dvipng -D %D -T tight -o %O %f")
|
|
:transparent-image-converter
|
|
("dvipng -D %D -T tight -bg Transparent -o %O %f"))
|
|
(dvisvgm
|
|
:programs ("latex" "dvisvgm")
|
|
:description "dvi > svg"
|
|
:message "you need to install the programs: latex and dvisvgm."
|
|
:image-input-type "dvi"
|
|
:image-output-type "svg"
|
|
:image-size-adjust (1.7 . 1.5)
|
|
:latex-compiler ("latex -interaction nonstopmode -output-directory %o %f")
|
|
:image-converter ("dvisvgm %f --no-fonts --exact-bbox --scale=%S --output=%O"))
|
|
(imagemagick
|
|
:programs ("latex" "convert")
|
|
:description "pdf > png"
|
|
:message "you need to install the programs: latex and imagemagick."
|
|
:image-input-type "pdf"
|
|
:image-output-type "png"
|
|
:image-size-adjust (1.0 . 1.0)
|
|
:latex-compiler ("pdflatex -interaction nonstopmode -output-directory %o %f")
|
|
:image-converter
|
|
("convert -density %D -trim -antialias %f -quality 100 %O")))
|
|
"Definitions of external processes for LaTeX previewing.
|
|
Org mode can use some external commands to generate TeX snippet's images for
|
|
previewing or inserting into HTML files, e.g., \"dvipng\". This variable tells
|
|
`org-create-formula-image' how to call them.
|
|
|
|
The value is an alist with the pattern (NAME . PROPERTIES). NAME is a symbol.
|
|
PROPERTIES accepts the following attributes:
|
|
|
|
:programs list of strings, required programs.
|
|
:description string, describe the process.
|
|
:message string, message it when required programs cannot be found.
|
|
:image-input-type string, input file type of image converter (e.g., \"dvi\").
|
|
:image-output-type string, output file type of image converter (e.g., \"png\").
|
|
:image-size-adjust cons of numbers, the car element is used to adjust LaTeX
|
|
image size showed in buffer and the cdr element is for
|
|
HTML file. This option is only useful for process
|
|
developers, users should use variable
|
|
`org-format-latex-options' instead.
|
|
:post-clean list of strings, files matched are to be cleaned up once
|
|
the image is generated. When nil, the files with \".dvi\",
|
|
\".xdv\", \".pdf\", \".tex\", \".aux\", \".log\", \".svg\",
|
|
\".png\", \".jpg\", \".jpeg\" or \".out\" extension will
|
|
be cleaned up.
|
|
:latex-header list of strings, the LaTeX header of the snippet file.
|
|
When nil, the fallback value is used instead, which is
|
|
controlled by `org-format-latex-header',
|
|
`org-latex-default-packages-alist' and
|
|
`org-latex-packages-alist', which see.
|
|
:latex-compiler list of LaTeX commands, as strings. Each of them is given
|
|
to the shell. Place-holders \"%t\", \"%b\" and \"%o\" are
|
|
replaced with values defined below.
|
|
:image-converter list of image converter commands strings. Each of them is
|
|
given to the shell and supports any of the following
|
|
place-holders defined below.
|
|
|
|
If set, :transparent-image-converter is used instead of :image-converter to
|
|
convert an image when the background color is nil or \"Transparent\".
|
|
|
|
Place-holders used by `:image-converter' and `:latex-compiler':
|
|
|
|
%f input file name
|
|
%b base name of input file
|
|
%o base directory of input file
|
|
%O absolute output file name
|
|
|
|
Place-holders only used by `:image-converter':
|
|
|
|
%D dpi, which is used to adjust image size by some processing commands.
|
|
%S the image size scale ratio, which is used to adjust image size by some
|
|
processing commands."
|
|
:group 'org-latex
|
|
:package-version '(Org . "9.6")
|
|
:type '(alist :tag "LaTeX to image backends"
|
|
:value-type (plist)))
|
|
|
|
(defcustom org-preview-latex-image-directory "ltximg/"
|
|
"Path to store latex preview images.
|
|
A relative path here creates many directories relative to the
|
|
processed Org files paths. An absolute path puts all preview
|
|
images at the same place."
|
|
:group 'org-latex
|
|
:version "26.1"
|
|
:package-version '(Org . "9.0")
|
|
:type 'string)
|
|
|
|
(defun org-format-latex-mathml-available-p ()
|
|
"Return t if `org-latex-to-mathml-convert-command' is usable."
|
|
(save-match-data
|
|
(when (and (boundp 'org-latex-to-mathml-convert-command)
|
|
org-latex-to-mathml-convert-command)
|
|
(let ((executable (car (split-string
|
|
org-latex-to-mathml-convert-command))))
|
|
(when (executable-find executable)
|
|
(if (string-match
|
|
"%j" org-latex-to-mathml-convert-command)
|
|
(file-readable-p org-latex-to-mathml-jar-file)
|
|
t))))))
|
|
|
|
(defcustom org-format-latex-header "\\documentclass{article}
|
|
\\usepackage[usenames]{color}
|
|
\[DEFAULT-PACKAGES]
|
|
\[PACKAGES]
|
|
\\pagestyle{empty} % do not remove
|
|
% The settings below are copied from fullpage.sty
|
|
\\setlength{\\textwidth}{\\paperwidth}
|
|
\\addtolength{\\textwidth}{-3cm}
|
|
\\setlength{\\oddsidemargin}{1.5cm}
|
|
\\addtolength{\\oddsidemargin}{-2.54cm}
|
|
\\setlength{\\evensidemargin}{\\oddsidemargin}
|
|
\\setlength{\\textheight}{\\paperheight}
|
|
\\addtolength{\\textheight}{-\\headheight}
|
|
\\addtolength{\\textheight}{-\\headsep}
|
|
\\addtolength{\\textheight}{-\\footskip}
|
|
\\addtolength{\\textheight}{-3cm}
|
|
\\setlength{\\topmargin}{1.5cm}
|
|
\\addtolength{\\topmargin}{-2.54cm}"
|
|
"The document header used for processing LaTeX fragments.
|
|
It is imperative that this header make sure that no page number
|
|
appears on the page. The package defined in the variables
|
|
`org-latex-default-packages-alist' and `org-latex-packages-alist'
|
|
will either replace the placeholder \"[PACKAGES]\" in this
|
|
header, or they will be appended."
|
|
:group 'org-latex
|
|
:type 'string)
|
|
|
|
(defun org--make-preview-overlay (beg end image &optional imagetype)
|
|
"Build an overlay between BEG and END using IMAGE file.
|
|
Argument IMAGETYPE is the extension of the displayed image,
|
|
as a string. It defaults to \"png\"."
|
|
(let ((ov (make-overlay beg end))
|
|
(imagetype (or (intern imagetype) 'png)))
|
|
(overlay-put ov 'org-overlay-type 'org-latex-overlay)
|
|
(overlay-put ov 'evaporate t)
|
|
(overlay-put ov
|
|
'modification-hooks
|
|
(list (lambda (o _flag _beg _end &optional _l)
|
|
(delete-overlay o))))
|
|
(overlay-put ov
|
|
'display
|
|
(list 'image :type imagetype :file image :ascent 'center))))
|
|
|
|
(defun org-clear-latex-preview (&optional beg end)
|
|
"Remove all overlays with LaTeX fragment images in current buffer.
|
|
When optional arguments BEG and END are non-nil, remove all
|
|
overlays between them instead. Return a non-nil value when some
|
|
overlays were removed, nil otherwise."
|
|
(let ((overlays
|
|
(cl-remove-if-not
|
|
(lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay))
|
|
(overlays-in (or beg (point-min)) (or end (point-max))))))
|
|
(mapc #'delete-overlay overlays)
|
|
overlays))
|
|
|
|
(defun org--latex-preview-region (beg end)
|
|
"Preview LaTeX fragments between BEG and END.
|
|
BEG and END are buffer positions."
|
|
(let ((file (buffer-file-name (buffer-base-buffer))))
|
|
(save-excursion
|
|
(org-format-latex
|
|
(concat org-preview-latex-image-directory "org-ltximg")
|
|
beg end
|
|
;; Emacs cannot overlay images from remote hosts. Create it in
|
|
;; `temporary-file-directory' instead.
|
|
(if (or (not file) (file-remote-p file))
|
|
temporary-file-directory
|
|
default-directory)
|
|
'overlays nil 'forbuffer org-preview-latex-default-process))))
|
|
|
|
(defun org-latex-preview (&optional arg)
|
|
"Toggle preview of the LaTeX fragment at point.
|
|
|
|
If the cursor is on a LaTeX fragment, create the image and
|
|
overlay it over the source code, if there is none. Remove it
|
|
otherwise. If there is no fragment at point, display images for
|
|
all fragments in the current section. With an active region,
|
|
display images for all fragments in the region.
|
|
|
|
With a `\\[universal-argument]' prefix argument ARG, clear images \
|
|
for all fragments
|
|
in the current section.
|
|
|
|
With a `\\[universal-argument] \\[universal-argument]' prefix \
|
|
argument ARG, display image for all
|
|
fragments in the buffer.
|
|
|
|
With a `\\[universal-argument] \\[universal-argument] \
|
|
\\[universal-argument]' prefix argument ARG, clear image for all
|
|
fragments in the buffer."
|
|
(interactive "P")
|
|
(cond
|
|
((not (display-graphic-p)) nil)
|
|
;; Clear whole buffer.
|
|
((equal arg '(64))
|
|
(org-clear-latex-preview (point-min) (point-max))
|
|
(message "LaTeX previews removed from buffer"))
|
|
;; Preview whole buffer.
|
|
((equal arg '(16))
|
|
(message "Creating LaTeX previews in buffer...")
|
|
(org--latex-preview-region (point-min) (point-max))
|
|
(message "Creating LaTeX previews in buffer... done."))
|
|
;; Clear current section.
|
|
((equal arg '(4))
|
|
(org-clear-latex-preview
|
|
(if (use-region-p)
|
|
(region-beginning)
|
|
(if (org-before-first-heading-p) (point-min)
|
|
(save-excursion
|
|
(org-with-limited-levels (org-back-to-heading t) (point)))))
|
|
(if (use-region-p)
|
|
(region-end)
|
|
(org-with-limited-levels (org-entry-end-position)))))
|
|
((use-region-p)
|
|
(message "Creating LaTeX previews in region...")
|
|
(org--latex-preview-region (region-beginning) (region-end))
|
|
(message "Creating LaTeX previews in region... done."))
|
|
;; Toggle preview on LaTeX code at point.
|
|
((let ((datum (org-element-context)))
|
|
(and (memq (org-element-type datum) '(latex-environment latex-fragment))
|
|
(let ((beg (org-element-property :begin datum))
|
|
(end (org-element-property :end datum)))
|
|
(if (org-clear-latex-preview beg end)
|
|
(message "LaTeX preview removed")
|
|
(message "Creating LaTeX preview...")
|
|
(org--latex-preview-region beg end)
|
|
(message "Creating LaTeX preview... done."))
|
|
t))))
|
|
;; Preview current section.
|
|
(t
|
|
(let ((beg (if (org-before-first-heading-p) (point-min)
|
|
(save-excursion
|
|
(org-with-limited-levels (org-back-to-heading t) (point)))))
|
|
(end (org-with-limited-levels (org-entry-end-position))))
|
|
(message "Creating LaTeX previews in section...")
|
|
(org--latex-preview-region beg end)
|
|
(message "Creating LaTeX previews in section... done.")))))
|
|
|
|
(defun org-format-latex
|
|
(prefix &optional beg end dir overlays msg forbuffer processing-type)
|
|
"Replace LaTeX fragments with links to an image.
|
|
|
|
The function takes care of creating the replacement image.
|
|
|
|
Only consider fragments between BEG and END when those are
|
|
provided.
|
|
|
|
When optional argument OVERLAYS is non-nil, display the image on
|
|
top of the fragment instead of replacing it.
|
|
|
|
PROCESSING-TYPE is the conversion method to use, as a symbol.
|
|
|
|
Some of the options can be changed using the variable
|
|
`org-format-latex-options', which see."
|
|
(when (and overlays (fboundp 'clear-image-cache)) (clear-image-cache))
|
|
(unless (eq processing-type 'verbatim)
|
|
(let* ((math-regexp "\\$\\|\\\\[([]\\|^[ \t]*\\\\begin{[A-Za-z0-9*]+}")
|
|
(cnt 0)
|
|
checkdir-flag)
|
|
(goto-char (or beg (point-min)))
|
|
;; Optimize overlay creation: (info "(elisp) Managing Overlays").
|
|
(when (and overlays (memq processing-type '(dvipng imagemagick)))
|
|
(overlay-recenter (or end (point-max))))
|
|
(while (re-search-forward math-regexp end t)
|
|
(unless (and overlays
|
|
(eq (get-char-property (point) 'org-overlay-type)
|
|
'org-latex-overlay))
|
|
(let* ((context (org-element-context))
|
|
(type (org-element-type context)))
|
|
(when (memq type '(latex-environment latex-fragment))
|
|
(let ((block-type (eq type 'latex-environment))
|
|
(value (org-element-property :value context))
|
|
(beg (org-element-property :begin context))
|
|
(end (save-excursion
|
|
(goto-char (org-element-property :end context))
|
|
(skip-chars-backward " \r\t\n")
|
|
(point))))
|
|
(cond
|
|
((eq processing-type 'mathjax)
|
|
;; Prepare for MathJax processing.
|
|
(if (not (string-match "\\`\\$\\$?" value))
|
|
(goto-char end)
|
|
(delete-region beg end)
|
|
(if (string= (match-string 0 value) "$$")
|
|
(insert "\\[" (substring value 2 -2) "\\]")
|
|
(insert "\\(" (substring value 1 -1) "\\)"))))
|
|
((eq processing-type 'html)
|
|
(goto-char beg)
|
|
(delete-region beg end)
|
|
(insert (org-format-latex-as-html value)))
|
|
((assq processing-type org-preview-latex-process-alist)
|
|
;; Process to an image.
|
|
(cl-incf cnt)
|
|
(org-create-latex-preview
|
|
prefix beg end dir overlays msg forbuffer processing-type
|
|
value cnt block-type checkdir-flag))
|
|
((eq processing-type 'mathml)
|
|
;; Process to MathML.
|
|
(unless (org-format-latex-mathml-available-p)
|
|
(user-error "LaTeX to MathML converter not configured"))
|
|
(cl-incf cnt)
|
|
(when msg (message msg cnt))
|
|
(goto-char beg)
|
|
(delete-region beg end)
|
|
(insert (org-format-latex-as-mathml
|
|
value block-type prefix dir)))
|
|
(t
|
|
(error "Unknown conversion process %s for LaTeX fragments"
|
|
processing-type)))))))))))
|
|
|
|
(defun org-create-latex-preview (prefix beg end dir overlays msg forbuffer processing-type
|
|
value cnt block-type checkdir-flag)
|
|
"The `org-preview-latex-process-alist' branch of `org-format-latex'."
|
|
(goto-char beg)
|
|
(let* ((processing-info
|
|
(cdr (assq processing-type org-preview-latex-process-alist)))
|
|
(face (face-at-point))
|
|
;; Get the colors from the face at point.
|
|
(fg
|
|
(let ((color (plist-get org-format-latex-options
|
|
:foreground)))
|
|
(if forbuffer
|
|
(cond
|
|
((eq color 'auto)
|
|
(face-attribute face :foreground nil 'default))
|
|
((eq color 'default)
|
|
(face-attribute 'default :foreground nil))
|
|
(t color))
|
|
color)))
|
|
(bg
|
|
(let ((color (plist-get org-format-latex-options
|
|
:background)))
|
|
(if forbuffer
|
|
(cond
|
|
((eq color 'auto)
|
|
(face-attribute face :background nil 'default))
|
|
((eq color 'default)
|
|
(face-attribute 'default :background nil))
|
|
(t color))
|
|
color)))
|
|
(hash (sha1 (prin1-to-string
|
|
(list org-format-latex-header
|
|
org-latex-default-packages-alist
|
|
org-latex-packages-alist
|
|
org-format-latex-options
|
|
forbuffer value fg bg))))
|
|
(imagetype (or (plist-get processing-info :image-output-type) "png"))
|
|
(absprefix (expand-file-name prefix dir))
|
|
(linkfile (format "%s_%s.%s" prefix hash imagetype))
|
|
(movefile (format "%s_%s.%s" absprefix hash imagetype))
|
|
(sep (and block-type "\n\n"))
|
|
(link (concat sep "[[file:" linkfile "]]" sep))
|
|
(options
|
|
(org-combine-plists
|
|
org-format-latex-options
|
|
`(:foreground ,fg :background ,bg))))
|
|
(when msg (message msg cnt))
|
|
(unless checkdir-flag ; Ensure the directory exists.
|
|
(setq checkdir-flag t)
|
|
(let ((todir (file-name-directory absprefix)))
|
|
(unless (file-directory-p todir)
|
|
(make-directory todir t))))
|
|
(unless (file-exists-p movefile)
|
|
(org-create-formula-image
|
|
value movefile options forbuffer processing-type))
|
|
(org-place-formula-image
|
|
link block-type beg end value overlays movefile imagetype)))
|
|
|
|
(defun org-place-formula-image (link block-type beg end value overlays movefile imagetype)
|
|
"Place an overlay from BEG to END showing MOVEFILE.
|
|
The overlay will be above BEG if OVERLAYS is non-nil."
|
|
(if overlays
|
|
(progn
|
|
(dolist (o (overlays-in beg end))
|
|
(when (eq (overlay-get o 'org-overlay-type)
|
|
'org-latex-overlay)
|
|
(delete-overlay o)))
|
|
(org--make-preview-overlay beg end movefile imagetype)
|
|
(goto-char end))
|
|
(delete-region beg end)
|
|
(insert
|
|
(org-add-props link
|
|
(list 'org-latex-src
|
|
(replace-regexp-in-string "\"" "" value)
|
|
'org-latex-src-embed-type
|
|
(if block-type 'paragraph 'character))))))
|
|
|
|
(defun org-create-math-formula (latex-frag &optional mathml-file)
|
|
"Convert LATEX-FRAG to MathML and store it in MATHML-FILE.
|
|
Use `org-latex-to-mathml-convert-command'. If the conversion is
|
|
successful, return the portion between \"<math...> </math>\"
|
|
elements otherwise return nil. When MATHML-FILE is specified,
|
|
write the results in to that file. When invoked as an
|
|
interactive command, prompt for LATEX-FRAG, with initial value
|
|
set to the current active region and echo the results for user
|
|
inspection."
|
|
(interactive (list (let ((frag (when (org-region-active-p)
|
|
(buffer-substring-no-properties
|
|
(region-beginning) (region-end)))))
|
|
(read-string "LaTeX Fragment: " frag nil frag))))
|
|
(unless latex-frag (user-error "Invalid LaTeX fragment"))
|
|
(let* ((tmp-in-file
|
|
(let ((file (file-relative-name
|
|
(make-temp-name (expand-file-name "ltxmathml-in")))))
|
|
(write-region latex-frag nil file)
|
|
file))
|
|
(tmp-out-file (file-relative-name
|
|
(make-temp-name (expand-file-name "ltxmathml-out"))))
|
|
(cmd (format-spec
|
|
org-latex-to-mathml-convert-command
|
|
`((?j . ,(and org-latex-to-mathml-jar-file
|
|
(shell-quote-argument
|
|
(expand-file-name
|
|
org-latex-to-mathml-jar-file))))
|
|
(?I . ,(shell-quote-argument tmp-in-file))
|
|
(?i . ,latex-frag)
|
|
(?o . ,(shell-quote-argument tmp-out-file)))))
|
|
mathml shell-command-output)
|
|
(when (called-interactively-p 'any)
|
|
(unless (org-format-latex-mathml-available-p)
|
|
(user-error "LaTeX to MathML converter not configured")))
|
|
(message "Running %s" cmd)
|
|
(setq shell-command-output (shell-command-to-string cmd))
|
|
(setq mathml
|
|
(when (file-readable-p tmp-out-file)
|
|
(with-current-buffer (find-file-noselect tmp-out-file t)
|
|
(goto-char (point-min))
|
|
(when (re-search-forward
|
|
(format "<math[^>]*?%s[^>]*?>\\(.\\|\n\\)*</math>"
|
|
(regexp-quote
|
|
"xmlns=\"http://www.w3.org/1998/Math/MathML\""))
|
|
nil t)
|
|
(prog1 (match-string 0) (kill-buffer))))))
|
|
(cond
|
|
(mathml
|
|
(setq mathml
|
|
(concat "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" mathml))
|
|
(when mathml-file
|
|
(write-region mathml nil mathml-file))
|
|
(when (called-interactively-p 'any)
|
|
(message mathml)))
|
|
((warn "LaTeX to MathML conversion failed")
|
|
(message shell-command-output)))
|
|
(delete-file tmp-in-file)
|
|
(when (file-exists-p tmp-out-file)
|
|
(delete-file tmp-out-file))
|
|
mathml))
|
|
|
|
(defun org-format-latex-as-mathml (latex-frag latex-frag-type
|
|
prefix &optional dir)
|
|
"Use `org-create-math-formula' but check local cache first."
|
|
(let* ((absprefix (expand-file-name prefix dir))
|
|
(print-length nil) (print-level nil)
|
|
(formula-id (concat
|
|
"formula-"
|
|
(sha1
|
|
(prin1-to-string
|
|
(list latex-frag
|
|
org-latex-to-mathml-convert-command)))))
|
|
(formula-cache (format "%s-%s.mathml" absprefix formula-id))
|
|
(formula-cache-dir (file-name-directory formula-cache)))
|
|
|
|
(unless (file-directory-p formula-cache-dir)
|
|
(make-directory formula-cache-dir t))
|
|
|
|
(unless (file-exists-p formula-cache)
|
|
(org-create-math-formula latex-frag formula-cache))
|
|
|
|
(if (file-exists-p formula-cache)
|
|
;; Successful conversion. Return the link to MathML file.
|
|
(org-add-props
|
|
(format "[[file:%s]]" (file-relative-name formula-cache dir))
|
|
(list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
|
|
'org-latex-src-embed-type (if latex-frag-type
|
|
'paragraph 'character)))
|
|
;; Failed conversion. Return the LaTeX fragment verbatim
|
|
latex-frag)))
|
|
|
|
(defun org-format-latex-as-html (latex-fragment)
|
|
"Convert LATEX-FRAGMENT to HTML.
|
|
This uses `org-latex-to-html-convert-command', which see."
|
|
(let ((cmd (format-spec org-latex-to-html-convert-command
|
|
`((?i . ,latex-fragment)))))
|
|
(message "Running %s" cmd)
|
|
(shell-command-to-string cmd)))
|
|
|
|
(defun org--get-display-dpi ()
|
|
"Get the DPI of the display.
|
|
The function assumes that the display has the same pixel width in
|
|
the horizontal and vertical directions."
|
|
(if (display-graphic-p)
|
|
(round (/ (display-pixel-height)
|
|
(/ (display-mm-height) 25.4)))
|
|
(error "Attempt to calculate the dpi of a non-graphic display")))
|
|
|
|
(defun org-create-formula-image
|
|
(string tofile options buffer &optional processing-type)
|
|
"Create an image from LaTeX source using external processes.
|
|
|
|
The LaTeX STRING is saved to a temporary LaTeX file, then
|
|
converted to an image file by process PROCESSING-TYPE defined in
|
|
`org-preview-latex-process-alist'. A nil value defaults to
|
|
`org-preview-latex-default-process'.
|
|
|
|
The generated image file is eventually moved to TOFILE.
|
|
|
|
The OPTIONS argument controls the size, foreground color and
|
|
background color of the generated image.
|
|
|
|
When BUFFER non-nil, this function is used for LaTeX previewing.
|
|
Otherwise, it is used to deal with LaTeX snippets showed in
|
|
a HTML file."
|
|
(let* ((processing-type (or processing-type
|
|
org-preview-latex-default-process))
|
|
(processing-info
|
|
(cdr (assq processing-type org-preview-latex-process-alist)))
|
|
(programs (plist-get processing-info :programs))
|
|
(error-message (or (plist-get processing-info :message) ""))
|
|
(image-input-type (plist-get processing-info :image-input-type))
|
|
(image-output-type (plist-get processing-info :image-output-type))
|
|
(post-clean (or (plist-get processing-info :post-clean)
|
|
'(".dvi" ".xdv" ".pdf" ".tex" ".aux" ".log"
|
|
".svg" ".png" ".jpg" ".jpeg" ".out")))
|
|
(latex-header
|
|
(or (plist-get processing-info :latex-header)
|
|
(org-latex-make-preamble
|
|
(org-export-get-environment (org-export-get-backend 'latex))
|
|
org-format-latex-header
|
|
'snippet)))
|
|
(latex-compiler (plist-get processing-info :latex-compiler))
|
|
(tmpdir temporary-file-directory)
|
|
(texfilebase (make-temp-name
|
|
(expand-file-name "orgtex" tmpdir)))
|
|
(texfile (concat texfilebase ".tex"))
|
|
(image-size-adjust (or (plist-get processing-info :image-size-adjust)
|
|
'(1.0 . 1.0)))
|
|
(scale (* (if buffer (car image-size-adjust) (cdr image-size-adjust))
|
|
(or (plist-get options (if buffer :scale :html-scale)) 1.0)))
|
|
(dpi (* scale (if (and buffer (display-graphic-p)) (org--get-display-dpi) 140.0)))
|
|
(fg (or (plist-get options (if buffer :foreground :html-foreground))
|
|
"Black"))
|
|
(bg (or (plist-get options (if buffer :background :html-background))
|
|
"Transparent"))
|
|
(image-converter
|
|
(or (and (string= bg "Transparent")
|
|
(plist-get processing-info :transparent-image-converter))
|
|
(plist-get processing-info :image-converter)))
|
|
(log-buf (get-buffer-create "*Org Preview LaTeX Output*"))
|
|
(resize-mini-windows nil)) ;Fix Emacs flicker when creating image.
|
|
(dolist (program programs)
|
|
(org-check-external-command program error-message))
|
|
(if (eq fg 'default)
|
|
(setq fg (org-latex-color :foreground))
|
|
(setq fg (org-latex-color-format fg)))
|
|
(setq bg (cond
|
|
((eq bg 'default) (org-latex-color :background))
|
|
((string= bg "Transparent") nil)
|
|
(t (org-latex-color-format bg))))
|
|
;; Remove TeX \par at end of snippet to avoid trailing space.
|
|
(if (string-suffix-p string "\n")
|
|
(aset string (1- (length string)) ?%)
|
|
(setq string (concat string "%")))
|
|
(with-temp-file texfile
|
|
(insert latex-header)
|
|
(insert "\n\\begin{document}\n"
|
|
"\\definecolor{fg}{rgb}{" fg "}%\n"
|
|
(if bg
|
|
(concat "\\definecolor{bg}{rgb}{" bg "}%\n"
|
|
"\n\\pagecolor{bg}%\n")
|
|
"")
|
|
"\n{\\color{fg}\n"
|
|
string
|
|
"\n}\n"
|
|
"\n\\end{document}\n"))
|
|
(let* ((err-msg (format "Please adjust `%s' part of \
|
|
`org-preview-latex-process-alist'."
|
|
processing-type))
|
|
(image-input-file
|
|
(org-compile-file
|
|
texfile latex-compiler image-input-type err-msg log-buf))
|
|
(image-output-file
|
|
(org-compile-file
|
|
image-input-file image-converter image-output-type err-msg log-buf
|
|
`((?D . ,(shell-quote-argument (format "%s" dpi)))
|
|
(?S . ,(shell-quote-argument (format "%s" (/ dpi 140.0))))))))
|
|
(copy-file image-output-file tofile 'replace)
|
|
(dolist (e post-clean)
|
|
(when (file-exists-p (concat texfilebase e))
|
|
(delete-file (concat texfilebase e))))
|
|
image-output-file)))
|
|
|
|
(defun org-dvipng-color (attr)
|
|
"Return a RGB color specification for dvipng."
|
|
(org-dvipng-color-format (face-attribute 'default attr nil)))
|
|
|
|
(defun org-dvipng-color-format (color-name)
|
|
"Convert COLOR-NAME to a RGB color value for dvipng."
|
|
(apply #'format "rgb %s %s %s"
|
|
(mapcar 'org-normalize-color
|
|
(color-values color-name))))
|
|
|
|
(defun org-latex-color (attr)
|
|
"Return a RGB color for the LaTeX color package."
|
|
(org-latex-color-format (face-attribute 'default attr nil)))
|
|
|
|
(defun org-latex-color-format (color-name)
|
|
"Convert COLOR-NAME to a RGB color value."
|
|
(apply #'format "%s,%s,%s"
|
|
(mapcar 'org-normalize-color
|
|
(color-values color-name))))
|
|
|
|
(defun org-normalize-color (value)
|
|
"Return string to be used as color value for an RGB component."
|
|
(format "%g" (/ value 65535.0)))
|
|
|
|
(provide 'org-latex-preview)
|
|
;;; org-latex-preview.el ends here
|