From eb1b287c009c2f7eb83e7e31d64980ba79f44527 Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur Date: Mon, 18 Dec 2023 12:56:33 -0800 Subject: [PATCH] org: Add image alignment * lisp/org.el (org-image--align, org-image-align, org-toggle-inline-images): Add the ability to left-align, center or right-align inline image previews in the Emacs window. This is controlled globally using the new user option `org-image-align'. Alignment can be specified per image using the `#+ATTR.*' affiliated keywords. The function `org-image--align' determines the kind of alignment for its argument link. * lisp/org-lint.el (org-lint-invalid-image-alignment): Add an org-lint checker for the pattern ":align nil" in `#+ATTR.*' keywords. This alignment specification is not supported. * doc/org-manual.org: Document the new feature under the Images section. --- doc/org-manual.org | 34 +++++++++++++++++ lisp/org-lint.el | 17 +++++++++ lisp/org.el | 91 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 137 insertions(+), 5 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index 5217e647d..0df730f2b 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -11501,6 +11501,40 @@ command: and fall back on the original width if none is found. + #+vindex: org-image-align + Org mode can left-align, center or right-align the display of inline + images. This setting is controlled (globally) by ~org-image-align~. + Only standalone links, /i.e/ links with no surrounding text in their + paragraphs (except whitespace) are affected. Its value can be the + following: + - (default) nil, insert the image where the link appears in the + buffer. + - The symbol ~left~, which is the same as nil. + - The symbol ~center~, which will preview standalone links centered + in the Emacs window. + - The symbol ~right~, which will preview standalone links + right-aligned in the Emacs window. + + Inline image alignment can be specified for each link using the + =#+ATTR.*= keyword if it matches an alignment specification like: + #+begin_example + ,#+ATTR_HTML: :align center + #+end_example + Supported values for =:align= are =left=, =center= and =right=. + Inline image display can also be centered using =:center=, as in + #+begin_example + ,#+ATTR_HTML: :center t + #+end_example + Org will use the alignment specification from any =#+ATTR.*= + keyword, such as =#+ATTR_HTML= or =#+ATTR_LATEX=, but =#+ATTR_ORG= + (if present) will override the others. For instance, this link + #+begin_example + ,#+ATTR_HTML: :align right + ,#+ATTR_ORG: :align center + [[/path/to/image/file.png]] + #+end_example + will be displayed centered. + #+vindex: org-cycle-inline-images-display Inline images can also be displayed when cycling the folding state. When custom option ~org-cycle-inline-images-display~ is set, the diff --git a/lisp/org-lint.el b/lisp/org-lint.el index 0e2967b6c..84bca9f48 100644 --- a/lisp/org-lint.el +++ b/lisp/org-lint.el @@ -964,6 +964,18 @@ Use \"export %s\" instead" reports)))) reports)) +(defun org-lint-invalid-image-alignment (ast) + (org-element-map ast 'paragraph + (lambda (p) + (let ((bad-align-re ":align[[:space:]]+nil") + (keyword-string (mapconcat + (lambda (attr) + (or (car-safe (org-element-property attr p)) "")) + '(:attr_org :attr_latex :attr_html) " "))) + (when (string-match-p bad-align-re keyword-string) + (list (org-element-begin p) + "nil is not a supported value for keyword attribute \":align\"")))))) + (defun org-lint-extraneous-element-in-footnote-section (ast) (org-element-map ast 'headline (lambda (h) @@ -1390,6 +1402,11 @@ Use \"export %s\" instead" #'org-lint-invalid-keyword-syntax :trust 'low) +(org-lint-add-checker 'invalid-image-alignment + "Report unsupported align attribute for keyword" + #'org-lint-invalid-image-alignment + :trust 'low) + (org-lint-add-checker 'invalid-block "Report invalid blocks" #'org-lint-invalid-block diff --git a/lisp/org.el b/lisp/org.el index 59fe3d2d3..8a8bd977d 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -16175,6 +16175,26 @@ cache Display remote images, and open them in separate buffers (const :tag "Display and silently update remote images" cache)) :safe #'symbolp) +(defcustom org-image-align nil + "How to align images previewed using `org-display-inline-images'. + +Only stand-alone image links are affected by this setting. These +are links without surrounding text. + +Possible values of this option are: + +nil Insert image at specified position. +left Insert image at specified position (same as nil). +center Center image previews. +right Right-align image previews." + :group 'org-appearance + :package-version '(Org . "9.7") + :type '(choice + (const :tag "Left align (or don\\='t align) image previews" nil) + (const :tag "Center image previews" center) + (const :tag "Right align image previews" right)) + :safe #'symbolp) + (defun org--create-inline-image (file width) "Create image located at FILE, or return nil. WIDTH is the width of the image. The image may not be created @@ -16293,19 +16313,20 @@ buffer boundaries with possible narrowing." (expand-file-name path)))) (when (and file (file-exists-p file)) (let ((width (org-display-inline-image--width link)) - (old (get-char-property-and-overlay - (org-element-property :begin link) + (align (org-image--align link)) + (old (get-char-property-and-overlay + (org-element-begin link) 'org-image-overlay))) (if (and (car-safe old) refresh) (image-flush (overlay-get (cdr old) 'display)) (let ((image (org--create-inline-image file width))) (when image (let ((ov (make-overlay - (org-element-property :begin link) + (org-element-begin link) (progn (goto-char - (org-element-property :end link)) - (skip-chars-backward " \t") + (org-element-end link)) + (unless (eolp) (skip-chars-backward " \t")) (point))))) ;; FIXME: See bug#59902. We cannot rely ;; on Emacs to update image if the file @@ -16319,6 +16340,15 @@ buffer boundaries with possible narrowing." (list 'org-display-inline-remove-overlay)) (when (boundp 'image-map) (overlay-put ov 'keymap image-map)) + (when align + (overlay-put + ov 'before-string + (propertize + " " 'face 'default + 'display + (pcase align + ("center" `(space :align-to (- center (0.5 . ,image)))) + ("right" `(space :align-to (- right ,image))))))) (push ov org-inline-image-overlays)))))))))))))))) (defvar visual-fill-column-width) ; Silence compiler warning @@ -16380,6 +16410,57 @@ buffer boundaries with possible narrowing." org-image-actual-width) (t nil)))) +(defun org-image--align (link) + "Determine the alignment of the image link. + +In decreasing order of priority, this is controlled: +- Per image by the value of `:center' or ``:align' in the +affiliated keyword `#+attr_org'. +- By the `#+attr_html' or `#+attr_latex` keywords with valid + `:center' or `:align' values. +- Globally by the user option `org-image-align'. + +The result is either nil or one of the strings \"left\", +\"center\" or \"right\". + +\"center\" will cause the image preview to be centered, \"right\" +will cause it to be right-aligned. A value of \"left\" or nil +implies no special alignment." + (let ((par (org-element-lineage link 'paragraph))) + ;; Only align when image is not surrounded by paragraph text: + (when (and (= (org-element-begin link) + (save-excursion + (goto-char (org-element-contents-begin par)) + (skip-chars-forward "\t ") + (point))) ;account for leading space + ;before link + (<= (- (org-element-contents-end par) + (org-element-end link)) + 1)) ;account for trailing newline + ;at end of paragraph + (save-match-data + ;; Look for a valid ":center t" or ":align left|center|right" + ;; attribute. + ;; + ;; An attr_org keyword has the highest priority, with + ;; attr_html/attr_latex next. Choosing between these two is + ;; unspecified. + (let ((center-re ":\\(center\\)[[:space:]]+t\\b") + (align-re ":align[[:space:]]+\\(left\\|center\\|right\\)\\b")) + (if-let ((attr-align + (cl-some + (lambda (attr-type) + (and-let* ((attr (car-safe (org-element-property attr-type par))) + ((or (string-match center-re attr) + (string-match align-re attr)))) + (match-string 1 attr))) + '(:attr_org :attr_html :attr_latex)))) + (when (member attr-align '("center" "right")) attr-align) + ;; No image-specific keyword, check global alignment property + (when (memq org-image-align '(center right)) + (symbol-name org-image-align)))))))) + + (defun org-display-inline-remove-overlay (ov after _beg _end &optional _len) "Remove inline-display overlay if a corresponding region is modified." (when (and ov after) -- 2.40.1