From fdaeb38609a4971f9bf11cdba99e8eb62b11dce2 Mon Sep 17 00:00:00 2001 From: TEC Date: Wed, 10 Jan 2024 14:51:33 +0800 Subject: [PATCH] org-latex-preview: Skip blank fragments * lisp/org-latex-preview.el (org-latex-preview--construct-entries, org-latex-preview-auto--regenerate-overlay): Blank fragments produce invisible previews, which is at best silly and at worst confusing. It's much better to simply not produce a preview image of blank fragments. We also now avoid exessive re-numbering calculations when repeatadly generating live previews. --- lisp/org-latex-preview.el | 111 +++++++++++++++++++++++--------------- lisp/ox-html.el | 109 +++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 83 deletions(-) diff --git a/lisp/org-latex-preview.el b/lisp/org-latex-preview.el index 603475dc3..7f6349c03 100644 --- a/lisp/org-latex-preview.el +++ b/lisp/org-latex-preview.el @@ -789,21 +789,34 @@ image. The preview image is regenerated if necessary." (overlay-put ov 'display (overlay-get ov 'preview-image))) (run-hook-with-args 'org-latex-preview-overlay-close-functions ov)))) -(defun org-latex-preview-auto--regenerate-overlay (ov) - "Regenerate the LaTeX fragment under overlay OV." +(defun org-latex-preview-auto--regenerate-overlay (ov &optional inhibit-renumbering) + "Regenerate the LaTeX fragment under overlay OV. + +When `org-latex-preview-numbered' is non-nil, and the overlay +being updated is for an environment, other numbered environments +will be updated should their numbering change. + +Numbering checking and updating can be prevented by setting +INHIBIT-RENUMBERING to a non-nil value." (with-current-buffer (overlay-buffer ov) - (let* ((fragment (save-excursion - (goto-char (overlay-start ov)) + (let* ((start (overlay-start ov)) + (end (overlay-end ov)) + (fragment (save-excursion + (goto-char start) (org-element-context))) (others (and org-latex-preview-numbered + (not inhibit-renumbering) (eq (org-element-type fragment) 'latex-environment) - (org-latex-preview--get-numbered-environments - (overlay-end ov) nil)))) + (org-latex-preview--get-numbered-environments end nil)))) (if (memq (org-element-type fragment) '(latex-fragment latex-environment)) - (org-latex-preview--place-from-elements - org-latex-preview-process-default - (cons fragment others)) + (if (org-latex-preview--empty-fragment-p + (org-element-property :value fragment)) + (progn (delete-overlay ov) + (org-latex-preview--ensure-overlay start end)) + (org-latex-preview--place-from-elements + org-latex-preview-process-default + (cons fragment others))) (delete-overlay ov) (when others (org-latex-preview--place-from-elements @@ -1068,6 +1081,7 @@ BOX-FACE is the face to apply in addition." (let ((props (get-char-property-and-overlay (point) 'org-overlay-type))) (and (eq (car props) 'org-latex-overlay) (cdr props))))) + (image (overlay-get ov 'preview-image)) (end (overlay-end ov))) (let ((latex-env-p (progn @@ -1093,8 +1107,7 @@ BOX-FACE is the face to apply in addition." (concat (and latex-env-p "\n​") " ")) (overlay-put ov 'view-text t) (overlay-put ov 'after-string org-latex-preview-live--docstring))) - (org-latex-preview-live--update-props - (overlay-get ov 'preview-image) '(:box t)))))) + (org-latex-preview-live--update-props image '(:box t)))))) (defun org-latex-preview-live--update-overlay (ov) "Update the live LaTeX preview for overlay OV." @@ -1476,32 +1489,45 @@ protection against placing doubled up overlays." "Constuct a well formatted list of entries and (optinally) numbering offsets. This operates by processing ELEMENTS. When CONSTRUCT-NUMBERING-P is non-nil, the number offsets will also be calculated, using PARSE-TREE if given." - (let* ((numbering-table (and construct-numbering-p - (cl-find 'latex-environment elements - :key #'org-element-type :test #'eq) - (org-latex-preview--environment-numbering-table - parse-tree))) - (numbering-offsets - (and numbering-table - (mapcar - (lambda (element) - (and numbering-table - (eq (org-element-type element) 'latex-environment) - (gethash element numbering-table))) - elements))) - (entries - (mapcar - (lambda (element) - (list (or (org-element-property :post-affiliated element) - (org-element-property :begin element)) - (- (org-element-property :end element) - (or (org-element-property :post-blank element) 0) - (if (eq (char-before (org-element-property :end element)) - ?\n) - 1 0)) - (org-element-property :value element))) - elements))) - (list entries numbering-offsets))) + (let ((numbering-table (and construct-numbering-p + (cl-find 'latex-environment elements + :key #'org-element-type :test #'eq) + (org-latex-preview--environment-numbering-table + parse-tree))) + entries numbering-offsets) + (dolist (element elements) + (let ((beg (or (org-element-property :post-affiliated element) + (org-element-property :begin element))) + (end (- (org-element-property :end element) + (or (org-element-property :post-blank element) 0) + (if (eq (char-before (org-element-property :end element)) + ?\n) + 1 0))) + (content (org-element-property :value element))) + (push (list beg end content) entries) + (when numbering-table + (push (and (eq (org-element-type element) 'latex-environment) + (gethash element numbering-table)) + numbering-offsets)))) + (list (nreverse entries) (nreverse numbering-offsets)))) + +(defun org-latex-preview--empty-fragment-p (content) + "Test if the LaTeX string CONTENT is an empty LaTeX fragment (e.g. \\[\\])." + (let ((content-point 0) + (content-max (1- (length content))) + (only-blanks t)) + (cond + ((eq (aref content 0) ?$) + (if (eq (aref content 1) ?$) + (setq content-point 2 content-max (- content-max 2)) + (setq content-point 1 content-max (- content-max 1)))) + ((eq (aref content 0) ?\\) + (setq content-point 2 content-max (- content-max 2)))) + (while (and only-blanks (<= content-point content-max)) + (if (memq (aref content content-point) '(?\s ?\t ?\n ?\r)) + (cl-incf content-point) + (setq only-blanks nil))) + only-blanks)) (defun org-latex-preview--place-from-elements (processing-type elements) "Preview LaTeX math fragments ELEMENTS using PROCESSING-TYPE." @@ -1551,11 +1577,12 @@ is either the substring between BEG and END or (when provided) VALUE." (org-latex-preview--update-overlay (org-latex-preview--ensure-overlay beg end) path-info) - (push (list :string (org-latex-preview--tex-styled - processing-type value options) - :overlay (org-latex-preview--ensure-overlay beg end) - :key hash) - fragment-info)) + (unless (org-latex-preview--empty-fragment-p value) + (push (list :string (org-latex-preview--tex-styled + processing-type value options) + :overlay (org-latex-preview--ensure-overlay beg end) + :key hash) + fragment-info))) (setq prev-fg fg prev-bg bg)))) (when fragment-info (org-latex-preview--create-image-async diff --git a/lisp/ox-html.el b/lisp/ox-html.el index f5a06c33e..34234a252 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -1184,6 +1184,7 @@ This supports two extra properties, :inline images that should not be saved according to :image-dir, but instead inlined in the generated HTML. This can be: - t, to inline all images + - svg, to inline all images, using with SVGs - nil, to never inline images - an extension or list of extensions, for images that should be inline (e.g. \"svg\")" @@ -3207,54 +3208,80 @@ that an image for ELEMENT already exists within it." (path-info (or (org-latex-preview--get-cached hash) (error "Expected LaTeX preview %S to exist in the cache" hash))) (image-options (plist-get info :html-latex-image-options)) - (image-dir (plist-get image-options :image-dir)) - (inline-condition (plist-get image-options :inline)) (rescale-factor (if (eq (plist-get (cdr path-info) :image-type) 'svg) (plist-get image-options :scale) 1)) (image-source - (cond - ((or (eq inline-condition 't) - (member (file-name-extension (car path-info)) - (org-ensure-list inline-condition))) - (let ((coding-system-for-read 'utf-8) - (file-name-handler-alist nil)) - (with-temp-buffer - (insert-file-contents-literally (car path-info)) - (base64-encode-region (point-min) (point-max)) - (goto-char (point-min)) - (insert "data:image/svg+xml;base64,") - (buffer-string)))) - ((stringp image-dir) - (let* ((image-dir (expand-file-name image-dir)) - (image-path (file-name-with-extension - (file-name-concat image-dir (substring hash 0 11)) - (file-name-extension (car path-info))))) - (unless (file-directory-p image-dir) - (mkdir image-dir t)) - (unless (file-exists-p image-path) - (copy-file (car path-info) image-path)) - image-path)) - (t (car path-info))))) + (org-html-latex-image--data (car path-info) hash info))) (unless (and (plist-get (cdr path-info) :height) (plist-get (cdr path-info) :depth)) (error "Something went wrong during image generation")) - (org-html-close-tag - "img" - (org-html--make-attribute-string - (list :src image-source - :alt (org-html-encode-plain-text - (org-element-property :value element)) - :style (if (eq (org-element-type element) 'latex-environment) - (format "height: %.4fem" - (* rescale-factor (plist-get (cdr path-info) :height))) - (format "height: %.4fem; vertical-align: -%.4fem; display: inline-block" - (* rescale-factor (plist-get (cdr path-info) :height)) - (* rescale-factor (plist-get (cdr path-info) :depth)))) - :class (if (eq (org-element-type element) 'latex-environment) - "org-latex org-latex-environment" - "org-latex org-latex-fragment"))) - info))) + (if (and (eq (plist-get image-options :inline) 'svg) + (string= (file-name-extension (car path-info)) "svg")) + image-source + (org-html-close-tag + "img" + (org-html--make-attribute-string + (list :src image-source + :alt (org-html-encode-plain-text + (org-element-property :value element)) + :style (if (eq (org-element-type element) 'latex-environment) + (format "height: %.4fem" + (* rescale-factor (plist-get (cdr path-info) :height))) + (format "height: %.4fem; vertical-align: -%.4fem; display: inline-block" + (* rescale-factor (plist-get (cdr path-info) :height)) + (* rescale-factor (plist-get (cdr path-info) :depth)))) + :class (if (eq (org-element-type element) 'latex-environment) + "org-latex org-latex-environment" + "org-latex org-latex-fragment"))) + info)))) + +(defun org-html-latex-image--data (source-file hash info) + "Obtaine the image source for SOURCE-FILE as a string, based on HASH and INFO. +This can take the form of a path, data URI, or element." + (let* ((image-options (plist-get info :html-latex-image-options)) + (inline-condition (plist-get image-options :inline)) + (image-dir (plist-get image-options :image-dir))) + (cond + ((or inline-condition + (member (file-name-extension source-file) + (org-ensure-list inline-condition))) + (let ((coding-system-for-read 'utf-8) + (file-name-handler-alist nil)) + (with-temp-buffer + (insert-file-contents-literally source-file) + (cond + ((and (eq inline-condition 'svg) + (string= (file-name-extension source-file) "svg")) + (buffer-string)) + ((string= (file-name-extension source-file) "svg") + ;; Modelled after . + (concat "data:image/svg+xml," + (url-hexify-string + (subst-char-in-string ?\" ?\' (buffer-string)) + '(?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n + ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z ?A ?B + ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P + ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z ?0 ?1 ?2 ?3 + ?4 ?5 ?6 ?7 ?8 ?9 ?- ?_ ?. ?~ + ;;Special additions + ?\s ?= ?: ?/)))) + (t + (base64-encode-region (point-min) (point-max)) + (goto-char (point-min)) + (insert "data:image/" (file-name-extension source-file) ";base64,") + (buffer-string)))))) + ((stringp image-dir) + (let* ((image-dir (expand-file-name image-dir)) + (image-path (file-name-with-extension + (file-name-concat image-dir (substring hash 0 11)) + (file-name-extension source-file)))) + (unless (file-directory-p image-dir) + (mkdir image-dir t)) + (unless (file-exists-p image-path) + (copy-file source-file image-path)) + image-path)) + (t source-file)))) ;;;; Line Break