ox-odt: Update to use org-latex-preview

* lisp/ox-odt.el (org-odt-latex-image-options,
org-odt-link--inline-image, org-odt--translate-latex-fragments):
Use the org-latex-preview library to generate LaTeX preview images
for ODT exports when exporting with an appropriate method (dvipng,
dvisvgm or imagemagick).  The new user option
`org-odt-latex-image-options' adds LaTeX preview image processing
options for ODT exports similar to
`org-latex-preview-appearance-options' for in-buffer previews.
Add support for svg images in ODT exports, as well as LaTeX images
scaled to match the surrounding text.  Baseline alignment for
images is not implemented.
This commit is contained in:
Karthik Chikmagalur 2024-04-06 18:23:40 -07:00 committed by TEC
parent 2143cf5f09
commit b3ed738b26
Signed by: tec
SSH Key Fingerprint: SHA256:eobz41Mnm0/iYWBvWThftS0ElEs1ftBr6jamutnXc/A
1 changed files with 92 additions and 81 deletions

View File

@ -35,6 +35,7 @@
(require 'ox) (require 'ox)
(require 'table nil 'noerror) (require 'table nil 'noerror)
(require 'ox-mathml) (require 'ox-mathml)
(require 'org-latex-preview)
(declare-function org-at-heading-p "org" (&optional _)) (declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok)) (declare-function org-back-to-heading "org" (&optional invisible-ok))
@ -120,6 +121,7 @@
(:odt-pixels-per-inch nil nil org-odt-pixels-per-inch) (:odt-pixels-per-inch nil nil org-odt-pixels-per-inch)
(:odt-table-styles nil nil org-odt-table-styles) (:odt-table-styles nil nil org-odt-table-styles)
(:odt-use-date-fields nil nil org-odt-use-date-fields) (:odt-use-date-fields nil nil org-odt-use-date-fields)
(:odt-latex-image-options nil nil org-odt-latex-image-options)
;; Redefine regular option. ;; Redefine regular option.
(:with-latex nil "tex" org-odt-with-latex) (:with-latex nil "tex" org-odt-with-latex)
;; Retrieve LaTeX header for fragments. ;; Retrieve LaTeX header for fragments.
@ -721,6 +723,15 @@ Any other symbol is a synonym for `mathjax'."
(const :tag "Use imagemagick to make images" imagemagick) (const :tag "Use imagemagick to make images" imagemagick)
(other :tag "Use MathJax to display math" mathjax))) (other :tag "Use MathJax to display math" mathjax)))
(defcustom org-odt-latex-image-options
'(:foreground "Black" :background "Transparent"
:page-width 1.0 :scale 1.0 :inline nil)
"LaTeX preview options that apply to generated images.
This is a ODT-specific counterpart to
`org-latex-preview-appearance-options', which see."
:group 'org-export-odt
:package-version '(Org . "9.7")
:type 'plist)
;;;; Links ;;;; Links
@ -2285,12 +2296,24 @@ used as a communication channel."
;; ;;
;; Handle `:width', `:height' and `:scale' properties. Read ;; Handle `:width', `:height' and `:scale' properties. Read
;; them as numbers since we need them for computations. ;; them as numbers since we need them for computations.
(size (org-odt--image-size (--em-to-cm
src-expanded info ;; FIXME: Hardcoded default font-size to 12 according to the
(let ((width (plist-get attr-plist :width))) ;; default value of styles.xml in org-odt-styles-dir. I
(and width (read width))) ;; don't know how how to determine this dynamically.
(let ((length (plist-get attr-plist :length))) (lambda (size) (and size (* 12 0.0352778 size))))
(and length (read length))) (em-geometry
(cdr-safe
(gethash element (plist-get info :odt-latex-preview-hash-table))))
(width (or (funcall --em-to-cm (plist-get em-geometry :width)) ;latex image size
(and-let* ((w (plist-get attr-plist :width)) ;ATTR_ODT specified size
((stringp w)))
(read w))))
(height (or (funcall --em-to-cm (plist-get em-geometry :height)) ;latex image size
(and-let* ((l (plist-get attr-plist :length)) ;ATTR_ODT specified size
((stringp l)))
(read l))))
(size (org-odt--image-size
src-expanded info width height
(let ((scale (plist-get attr-plist :scale))) (let ((scale (plist-get attr-plist :scale)))
(and scale (read scale))) (and scale (read scale)))
nil ; embed-as nil ; embed-as
@ -3734,8 +3757,8 @@ contextual information."
(warning nil)) (warning nil))
;; MathML will be handled seperately. ;; MathML will be handled seperately.
(if (and (memq processing-type '(t mathml)) (if (and (memq processing-type '(t mathml))
(fboundp 'org-format-latex-mathml-available-p) (fboundp 'org-mathml-converter-available-p)
(org-format-latex-mathml-available-p) (org-mathml-converter-available-p)
(plist-put info :with-latex 'mathml)) (plist-put info :with-latex 'mathml))
(org-element-map tree '(latex-fragment latex-environment) (org-element-map tree '(latex-fragment latex-environment)
(lambda (latex) (lambda (latex)
@ -3783,12 +3806,14 @@ contextual information."
((t mathml) ((t mathml)
(setq warning "LaTeX to MathML converter not available. Falling back to verbatim." (setq warning "LaTeX to MathML converter not available. Falling back to verbatim."
processing-type 'verbatim)) processing-type 'verbatim))
((dvipng imagemagick) ((dvipng imagemagick dvisvgm)
(unless (and (org-check-external-command "latex" "" t) (let ((programs
(org-check-external-command (thread-first processing-type
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t)) (alist-get org-latex-preview-process-alist)
(setq warning "LaTeX to PNG converter not available. Falling back to verbatim." (plist-get :programs))))
processing-type 'verbatim))) (unless (cl-every (lambda (p) (org-check-external-command p "" 'no-error)) programs)
(setq warning "LaTeX or image converter not available. Falling back to verbatim."
processing-type 'verbatim))))
(otherwise (otherwise
(setq warning "Unknown LaTeX option. Forcing verbatim." (setq warning "Unknown LaTeX option. Forcing verbatim."
processing-type 'verbatim))) processing-type 'verbatim)))
@ -3796,81 +3821,67 @@ contextual information."
;; available, but there are fragments to be converted. ;; available, but there are fragments to be converted.
(when warning (when warning
(org-element-map tree '(latex-fragment latex-environment) (org-element-map tree '(latex-fragment latex-environment)
(lambda (_) (warn warning)) (lambda (_) (org-display-warning warning))
info 'first-match nil t)) info 'first-match nil t))
;; Store normalized value for later use. ;; Store normalized value for later use.
(when (plist-get info :with-latex) (when (plist-get info :with-latex)
(plist-put info :with-latex processing-type)) (plist-put info :with-latex processing-type))
(message "Formatting LaTeX using %s" processing-type) (message "Formatting LaTeX using %s" processing-type)
;; Convert `latex-fragment's and `latex-environment's. ;; Convert `latex-fragment's and `latex-environment's.
(when (memq processing-type '(dvipng imagemagick)) (when (memq processing-type '(dvipng imagemagick dvisvgm))
;; Prepare hash table with image file data
(plist-put info :odt-latex-preview-hash-table
(apply #'org-latex-preview-cache-images tree info
org-odt-latex-image-options))
;; Map over the parse tree again and replace LaTeX
;; fragments with links. If an image doesn't exist for the
;; fragment, leave it in verbatim.
(org-element-map tree '(latex-fragment latex-environment) (org-element-map tree '(latex-fragment latex-environment)
(lambda (latex-*) (lambda (latex-*)
(cl-incf count) (when-let*
(let* ((latex-frag (org-element-property :value latex-*)) ((latex-preview-hash-table (plist-get info :odt-latex-preview-hash-table))
(input-file (plist-get info :input-file)) (latex-frag (org-element-property :value latex-*))
(cache-dir (file-name-directory input-file)) (path-info
(cache-subdir (concat (or (gethash latex-* latex-preview-hash-table)
(cl-case processing-type (prog1 nil (org-display-warning
((dvipng imagemagick) (format "Failed to generate preview image for element: %s" latex-frag)))))
org-preview-latex-image-directory)) (source-file (car path-info))
(file-name-sans-extension (link (list 'link (list :type "file"
(file-name-nondirectory input-file)))) :path source-file
(display-msg :format 'bracket
(cl-case processing-type :raw-link (format "file:%s" source-file)))))
((dvipng imagemagick) (let ((replacement
(format "Creating LaTeX Image %d..." count)))) (cl-case (org-element-type latex-*)
;; Get an Org-style link to PNG image. ;;LaTeX environment. Mimic a "standalone image
(link ;; or formula" by enclosing the `link' in
(with-temp-buffer ;; a `paragraph'. Copy over original
(insert latex-frag) ;; attributes, captions to the enclosing
(delay-mode-hooks (let ((org-inhibit-startup t)) (org-mode))) ;; paragraph.
;; When converting to a PNG image, make sure to (latex-environment
;; copy all LaTeX header specifications from the (org-element-adopt-elements
;; Org source. (list 'paragraph
(let ((h (plist-get info :latex-header))) (list :style "OrgFormula"
(when h :name
(insert "\n" (org-element-property :name latex-*)
(replace-regexp-in-string :caption
"^" "#+LATEX_HEADER: " h)))) (org-element-property :caption latex-*)))
(org-latex-preview-replace-fragments link))
cache-subdir processing-type cache-dir display-msg) ;; LaTeX fragment. No special action.
(goto-char (point-min)) (latex-fragment link))))
(skip-chars-forward " \t\n") ;; Note down the object that link replaces.
(org-element-link-parser)))) (org-element-put-property replacement :replaces
(if (not (eq 'link (org-element-type link))) (list (org-element-type latex-*)
(message "LaTeX Conversion failed.") (list :value latex-frag)))
;; Conversion succeeded. Parse above Org-style link to ;; Restore blank after initial element or object.
;; a `link' object. (org-element-put-property
(let ((replacement replacement :post-blank
(cl-case (org-element-type latex-*) (org-element-property :post-blank latex-*))
;;LaTeX environment. Mimic a "standalone image ;; Replace now.
;; or formula" by enclosing the `link' in (org-element-set-element latex-* replacement)
;; a `paragraph'. Copy over original ;; Also replace in the latex preview table
;; attributes, captions to the enclosing (puthash replacement (gethash latex-* latex-preview-hash-table)
;; paragraph. latex-preview-hash-table))))
(latex-environment info))))
(org-element-adopt-elements
(list 'paragraph
(list :style "OrgFormula"
:name
(org-element-property :name latex-*)
:caption
(org-element-property :caption latex-*)))
link))
;; LaTeX fragment. No special action.
(latex-fragment link))))
;; Note down the object that link replaces.
(org-element-put-property replacement :replaces
(list (org-element-type latex-*)
(list :value latex-frag)))
;; Restore blank after initial element or object.
(org-element-put-property
replacement :post-blank
(org-element-property :post-blank latex-*))
;; Replace now.
(org-element-set-element latex-* replacement)))))
info nil nil t))))
tree) tree)