6

When I toggle inline images I can see the image. But I want to rotate it. There is an option to specify a custom width using:

#+ATTR_ORG: :width 80

Is there a similar option to rotate the image?

Update (as of 24 July 2022):

After applying the patch provided in the answer the following works:

#+ATTR_ORG: :width 80 :rotation 90

Rotation values are in degrees and only work if its a multiple of 90 i.e. 90, 180 and 270 are valid. Any other values wont affect the rotation.

You can copy and paste the following to your init.el to have the rotation feature.

(with-eval-after-load 'org
  ;; Function redefinition
  (defun org-display-inline-images (&optional include-linked refresh beg end)
    "Display inline images.

An inline image is a link which follows either of these
conventions:

  1. Its path is a file with an extension matching return value
     from `image-file-name-regexp' and it has no contents.

  2. Its description consists in a single link of the previous
     type.  In this case, that link must be a well-formed plain
     or angle link, i.e., it must have an explicit \"file\" type.

Equip each image with the key-map `image-map'.

When optional argument INCLUDE-LINKED is non-nil, also links with
a text description part will be inlined.  This can be nice for
a quick look at those images, but it does not reflect what
exported files will look like.

When optional argument REFRESH is non-nil, refresh existing
images between BEG and END.  This will create new image displays
only if necessary.

BEG and END define the considered part.  They default to the
buffer boundaries with possible narrowing."
    (interactive "P")
    (when (display-graphic-p)
      (unless refresh
        (org-remove-inline-images)
        (when (fboundp 'clear-image-cache) (clear-image-cache)))
      (let ((end (or end (point-max))))
        (org-with-point-at (or beg (point-min))
          (let* ((case-fold-search t)
                 (file-extension-re (image-file-name-regexp))
                 (link-abbrevs (mapcar #'car
                                       (append org-link-abbrev-alist-local
                                               org-link-abbrev-alist)))
                 ;; Check absolute, relative file names and explicit
                 ;; "file:" links.  Also check link abbreviations since
                 ;; some might expand to "file" links.
                 (file-types-re
                  (format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?file:\\)"
                          (if (not link-abbrevs) ""
                            (concat "\\|" (regexp-opt link-abbrevs))))))
            (while (re-search-forward file-types-re end t)
              (let* ((link (org-element-lineage
                            (save-match-data (org-element-context))
                            '(link) t))
                     (linktype (org-element-property :type link))
                     (inner-start (match-beginning 1))
                     (path
                      (cond
                       ;; No link at point; no inline image.
                       ((not link) nil)
                       ;; File link without a description.  Also handle
                       ;; INCLUDE-LINKED here since it should have
                       ;; precedence over the next case.  I.e., if link
                       ;; contains filenames in both the path and the
                       ;; description, prioritize the path only when
                       ;; INCLUDE-LINKED is non-nil.
                       ((or (not (org-element-property :contents-begin link))
                            include-linked)
                        (and (or (equal "file" linktype)
                                 (equal "attachment" linktype))
                             (org-element-property :path link)))
                       ;; Link with a description.  Check if description
                       ;; is a filename.  Even if Org doesn't have syntax
                       ;; for those -- clickable image -- constructs, fake
                       ;; them, as in `org-export-insert-image-links'.
                       ((not inner-start) nil)
                       (t
                        (org-with-point-at inner-start
                          (and (looking-at
                                (if (char-equal ?< (char-after inner-start))
                                    org-link-angle-re
                                  org-link-plain-re))
                               ;; File name must fill the whole
                               ;; description.
                               (= (org-element-property :contents-end link)
                                  (match-end 0))
                               (match-string 2)))))))
                (when (and path (string-match-p file-extension-re path))
                  (let ((file (if (equal "attachment" linktype)
                                  (progn
                                    (require 'org-attach)
                                    (ignore-errors (org-attach-expand path)))
                                (expand-file-name path))))
                    (when (and file (file-exists-p file))
                      (let ((width (org-display-inline-image--width link))
                            (rotation (org-display-inline-image--rotation link))
                            (old (get-char-property-and-overlay
                                  (org-element-property :begin link)
                                  'org-image-overlay)))
                        (if (and (car-safe old) refresh)
                            (image-refresh (overlay-get (cdr old) 'display))
                          (let ((image (org--create-inline-image file width rotation)))
                            (when image
                              (let ((ov (make-overlay
                                         (org-element-property :begin link)
                                         (progn
                                           (goto-char
                                            (org-element-property :end link))
                                           (skip-chars-backward " \t")
                                           (point)))))
                                (overlay-put ov 'display image)
                                (overlay-put ov 'face 'default)
                                (overlay-put ov 'org-image-overlay t)
                                (overlay-put
                                 ov 'modification-hooks
                                 (list 'org-display-inline-remove-overlay))
                                (when (boundp 'image-map)
                                  (overlay-put ov 'keymap image-map))
                                (push ov org-inline-image-overlays))))))))))))))))

  ;; Function redefinition
  (defun org--create-inline-image (file width rotation)
    "Create image located at FILE, or return nil.
 WIDTH is the width of the image.  The image may not be created
 according to the value of `org-display-remote-inline-images'."
    (let* ((remote? (file-remote-p file))
           (file-or-data
            (pcase org-display-remote-inline-images
              ((guard (not remote?)) file)
              (`download (with-temp-buffer
                           (set-buffer-multibyte nil)
                           (insert-file-contents-literally file)
                           (buffer-string)))
              (`cache (let ((revert-without-query '(".")))
                        (with-current-buffer (find-file-noselect file)
                          (buffer-string))))
              (`skip nil)
              (other
               (message "Invalid value of `org-display-remote-inline-images': %S"
                        other)
               nil))))
      (when file-or-data
        (create-image file-or-data
                      (and (image-type-available-p 'imagemagick)
                           width
                           'imagemagick)
                      remote?
                      :width width
                      :rotation rotation))))

  ;; New function
  (defun org-display-inline-image--rotation (link)
    "Determine the display rotation of the image LINK, in degrees."
    ;; Apply `org-image-actual-width' specifications.
    (let* ((case-fold-search t)
           (par (org-element-lineage link '(paragraph)))
           (attr-re "^[ \t]*#\\+attr_.*?: +.*?:rotation +\\(\\S-+\\)")
           (par-end (org-element-property :post-affiliated par))
           ;; Try to find an attribute providing a :rot.
           (attr-rot
            (when (and par (org-with-point-at
                               (org-element-property :begin par)
                             (re-search-forward attr-re par-end t)))
              (match-string 1))))
      (when attr-rot (string-to-number attr-rot)))))
1
  • 1
    As a workaround, you can always preprocess the image, using e.g, the convert tool, so that Org mode (or anybody else using it) does not need to rotate it
    – NickD
    Jan 2, 2022 at 9:47

1 Answer 1

3
+100

The short answer, from looking at the source code, is that this seems not to be supported currently.

For completeness, I show that and how it can quickly be implemented. For example, it can be achieved by applying the following patch (for org-version 9.5.2. At least the patch should work against org.el at commit fbff082f7):

diff --git a/lisp/org.el b/lisp/org.el
index d58f6af55..33c64d7ae 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -16494,7 +16494,7 @@ cache       Display remote images, and open them in separate buffers
      (const :tag "Display and silently update remote images" cache))
   :safe #'symbolp)
 
-(defun org--create-inline-image (file width)
+(defun org--create-inline-image (file width rotation)
   "Create image located at FILE, or return nil.
 WIDTH is the width of the image.  The image may not be created
 according to the value of `org-display-remote-inline-images'."
@@ -16517,10 +16517,11 @@ according to the value of `org-display-remote-inline-images'."
     (when file-or-data
       (create-image file-or-data
            (and (image-type-available-p 'imagemagick)
-            width
-            'imagemagick)
-           remote?
-           :width width))))
+                         width
+                         'imagemagick)
+                    remote?
+                    :width width
+                    :rotation rotation))))
 
 (defun org-display-inline-images (&optional include-linked refresh beg end)
   "Display inline images.
@@ -16610,17 +16611,18 @@ buffer boundaries with possible narrowing."
                                   (require 'org-attach)
                  (ignore-errors (org-attach-expand path)))
                               (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)
-               'org-image-overlay)))
-             (if (and (car-safe old) refresh)
-             (image-refresh (overlay-get (cdr old) 'display))
-           (let ((image (org--create-inline-image file width)))
-             (when image
-               (let ((ov (make-overlay
-                      (org-element-property :begin link)
+         (when (and file (file-exists-p file))
+           (let ((width (org-display-inline-image--width link))
+                 (rotation (org-display-inline-image--rotation link))
+             (old (get-char-property-and-overlay
+               (org-element-property :begin link)
+               'org-image-overlay)))
+             (if (and (car-safe old) refresh)
+             (image-refresh (overlay-get (cdr old) 'display))
+           (let ((image (org--create-inline-image file width rotation)))
+             (when image
+               (let ((ov (make-overlay
+                      (org-element-property :begin link)
                       (progn
                     (goto-char
                      (org-element-property :end link))
@@ -16688,6 +16690,21 @@ buffer boundaries with possible narrowing."
     org-image-actual-width)
    (t nil)))
 
+(defun org-display-inline-image--rotation (link)
+  "Determine the display rotation of the image LINK, in degrees."
+  ;; Apply `org-image-actual-width' specifications.
+  (let* ((case-fold-search t)
+         (par (org-element-lineage link '(paragraph)))
+         (attr-re "^[ \t]*#\\+attr_.*?: +.*?:rotation +\\(\\S-+\\)")
+         (par-end (org-element-property :post-affiliated par))
+         ;; Try to find an attribute providing a :rot.
+         (attr-rot
+          (when (and par (org-with-point-at
+                             (org-element-property :begin par)
+                           (re-search-forward attr-re par-end t)))
+            (match-string 1))))
+    (string-to-number attr-rot)))
+
 (defun org-display-inline-remove-overlay (ov after _beg _end &optional _len)
   "Remove inline-display overlay if a corresponding region is modified."
   (let ((inhibit-modification-hooks t))

where I have taken (and slightly adapted) the code for display-inline-image--width from the function org-display-inline-image--width.

Unfortunately, for showing the required modifications, I can not quickly come up with a more efficient way than printing the diff/patch. But in effect, besides copying and adapting that code for the value, I have added only 1 more line of code and added rotation in two more places.

After applying the patch you could specify the rotation using e.g. #+ATTR_ORG: :rotation 90 for rotating the image 90 degrees in clockwise direction.

If you would like this feature to get added to org, then you could test this patch and if it works alright for you then open a feature request, I guess the patch is of sufficiently good quality. However, it looks like that with only a little bit more work, support could be added for all image properties (that can all be added after a single #+ATTR_ORG). If you like you could try to create a patch for that (as a nice elisp exercise).

15
  • You should specify the version of Org mode against which the patch is to be applied. I tried applying it against Org mode version 9.5.2 (release_9.5.2-289-gce75d2) and it failed to apply. How did you produce the patch? It is better to use git format-patch after committing your changes in a branch. And I would recommend that you put the calculation of the rotation argument in a separate function, similar to what the code does for the width by using org-display-inline-image--width.
    – NickD
    Jan 2, 2022 at 9:44
  • Thanks for the feedback. I was under the impression that I used the latest org-mode from Elpa as I am using Spacemacs, but now I found out that for Spacemacs to update it I first had to manually delete the previous installed org-version from the Elpa directory. Anyway, I have updated the 'patch' to org-mode 9.5.2 now (and will also move the 'value calculation' code to a separate function). I produced the patch/diff simply by using M-x diff-buffer-with-file and then simply copy-pasted the output here (it is not a git repo as I simply changed the file that got installed by Emacs). Jan 2, 2022 at 11:14
  • @NickD I would be interested to know how to best produce a patch from a modified (but not saved) buffer, so that diff code highlighting would work here (currently the highlighting does not work, possibly because my code is not 'valid' patch code). ps. I read many "simply's" in my previous comment, but I was too late (5 mins) to change it. Jan 2, 2022 at 11:30
  • 2
    I changed it to lang-diff from diff and there is some coloring, although I'm not sure it's useful. Feel free to revert. BTW, why (print rotation) and not just rotation in the third hunk?
    – NickD
    Jan 2, 2022 at 12:10
  • And BTW (part deux), the patch application still fails for me: it's off by a couple of hundred lines and only the first and last hunk apply cleanly; the second and third fail. The second fails because the code I have has an additional :scale 1 property. I'm not sure why the third one fails.
    – NickD
    Jan 2, 2022 at 12:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.