9

currently i save my pictures with org-download as an attach for an org file or via directory method. I wonder, if it possible to include a picture as base64 coded string (or anything else) to always have text and picture in one file. Attach or directory method saves the picture into another directory below org dir. A very user friendly solution will be an automatic encode into base64 after drop an image and automatic decode, if i want to view it.

I want to put both in one org-file. Does anybody know a solution, or is there any existing solution?

Drew
  • 75,699
  • 9
  • 109
  • 225
space_held
  • 191
  • 8
  • It is no problem to save a picture as base64 inside an org file. The actual question is whether it is possible to automatically show the corresponding picture and to export the picture with selected exporters. – Tobias May 19 '18 at 19:42
  • thanks for the answer. How could i save the picture inside the org file ? For clarification: I dont want to save the picture in another org file, i want it included in the original org file. – space_held May 20 '18 at 17:25
  • Please don't cross-post. Original question: https://www.reddit.com/r/orgmode/comments/8ku3dp/is_it_possible_to_include_a_base64_image_in_org/ Answer: http://mbork.pl/2017-12-04_Embedding_files_in_Org-mode – mankoff May 21 '18 at 10:47

1 Answers1

7

This is already the second request I know of for displaying images without files on hard-disk. So maybe it is really time for a feature request at the org-mode mailing list (org-submit-bug-report).

In the question about youtube links user Adam already asked about displaying the preview images of youtube® videos.

I wrote up the package org-yt for the previous request. Please see my answer to the youtube question for comments about the org-mode internals.

For answering this question I generalized that solution to allow customized data images.

One registers the function that generates the image data from the link as :image-data-fun in org-link-properties. The registered function takes three arguments:

  1. the protocol type, e.g., img
  2. the link, e.g., the byte64 encoded data
  3. the link description

If you are willing to use the new version of package org-yt the following code for your special case is quite short and clear.

(require 'org-yt)

(defun org-inline-data-image (_protocol link _description)
  "Interpret LINK as base64-encoded image data."
  (base64-decode-string link))

(org-link-set-parameters
 "img"
 :image-data-fun #'org-inline-data-image)

You can easily add exporters to the link parameters of img with the :export property in the variable org-link-parameters (see the doc of that variable). An example would be an html exporter which just prepends data:image/png;base64, to the image data and embeds that stuff into an img tag.

Avoiding citations as solutions I also post a self-contained solution here. You can avoid pulling in the youtube stuff by using the following code.

UPDATE: The full code contains now a function org-download-screenshot-img for yanking image files from the clipboard. It is bases on org-download.el. So you need that package to make it work.

The function org-download-screenshot-img is bound to S-C-y in org-mode at the end of the code.

(require 'org)
(require 'org-element)
(require 'subr-x) ;; for when-let

(defun org-image-update-overlay (file link &optional data-p refresh)
  "Create image overlay for FILE associtated with org-element LINK.
If DATA-P is non-nil FILE is not a file name but a string with the image data.
If REFRESH is non-nil don't download the file but refresh the image.
See also `create-image'.
This function is almost a duplicate of a part of `org-display-inline-images'."
  (when (or data-p (file-exists-p file))
    (let ((width
       ;; Apply `org-image-actual-width' specifications.
       (cond
        ((not (image-type-available-p 'imagemagick)) nil)
        ((eq org-image-actual-width t) nil)
        ((listp org-image-actual-width)
         (or
          ;; First try to find a width among
          ;; attributes associated to the paragraph
          ;; containing link.
          (let ((paragraph
             (let ((e link))
               (while (and (setq e (org-element-property
                        :parent e))
                   (not (eq (org-element-type e)
                        'paragraph))))
               e)))
        (when paragraph
          (save-excursion
            (goto-char (org-element-property :begin paragraph))
            (when
            (re-search-forward
             "^[ \t]*#\\+attr_.*?: +.*?:width +\\(\\S-+\\)"
             (org-element-property
              :post-affiliated paragraph)
             t)
              (string-to-number (match-string 1))))))
          ;; Otherwise, fall-back to provided number.
          (car org-image-actual-width)))
        ((numberp org-image-actual-width)
         org-image-actual-width)))
      (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 (create-image file
                   (and width 'imagemagick)
                   data-p
                   :width width)))
      (when image
        (let* ((link
            ;; If inline image is the description
            ;; of another link, be sure to
            ;; consider the latter as the one to
            ;; apply the overlay on.
            (let ((parent
               (org-element-property :parent link)))
              (if (eq (org-element-type parent) 'link)
              parent
            link)))
           (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))
          (push ov org-inline-image-overlays)
          ov)))))))

(defun org-display-user-inline-images (&optional _include-linked _refresh beg end)
  "Like `org-display-inline-images' but for image data links.
_INCLUDE-LINKED and _REFRESH are ignored.
Restrict to region between BEG and END if both are non-nil.
Image data links have a :image-data-fun parameter.
\(See `org-link-set-parameters'.)
The value of the :image-data-fun parameter is a function
taking the PROTOCOL, the LINK, and the DESCRIPTION as arguments.
If that function returns nil the link is not interpreted as image.
Otherwise the return value is the image data string to be displayed.

Note that only bracket links are allowed as image data links
with one of the formats [[PROTOCOL:LINK]] or [[PROTOCOL:LINK][DESCRIPTION]] are recognized."
  (interactive)
  (when (and (called-interactively-p 'any)
         (use-region-p))
    (setq beg (region-beginning)
      end (region-end)))
  (when (display-graphic-p)
    (org-with-wide-buffer
     (goto-char (or beg (point-min)))
     (when-let ((image-data-link-parameters
     (cl-loop for link-par-entry in org-link-parameters
          with fun
          when (setq fun (plist-get (cdr link-par-entry) :image-data-fun))
          collect (cons (car link-par-entry) fun)))
    (image-data-link-re (regexp-opt (mapcar 'car image-data-link-parameters)))
    (re (format "\\[\\[\\(%s\\):\\([^]]+\\)\\]\\(?:\\[\\([^]]+\\)\\]\\)?\\]"
        image-data-link-re)))
       (while (re-search-forward re end t)
     (let* ((protocol (match-string-no-properties 1))
    (link (match-string-no-properties 2))
    (description (match-string-no-properties 3))
    (image-data-link (assoc-string protocol image-data-link-parameters))
    (el (save-excursion (goto-char (match-beginning 1)) (org-element-context)))
    image-data)
       (when (and el
              (eq (org-element-type el) 'link))
         (setq image-data
           (or (let ((old (get-char-property-and-overlay
                   (org-element-property :begin el)
                   'org-image-overlay)))
             (and old
                  (car-safe old)
                  (overlay-get (cdr old) 'display)))
           (funcall (cdr image-data-link) protocol link description)))
         (when image-data
           (let ((ol (org-image-update-overlay image-data el t t)))
         (when (and ol description)
           (overlay-put ol 'after-string description)))))))))))

(advice-add #'org-display-inline-images :after #'org-display-user-inline-images)

(defun org-inline-data-image (_protocol link _description)
  "Interpret LINK as base64-encoded image data."
  (base64-decode-string link))

(org-link-set-parameters
 "img"
 :image-data-fun #'org-inline-data-image)

(require 'org-download)

(defun org-download-screenshot-img ()
  "Capture screenshot and insert img link with base64 encoded data."
  (interactive)
  (let ((file (expand-file-name org-download-screenshot-file)))
    (shell-command (format org-download-screenshot-method file))
    (insert "[[img:"
            (with-temp-buffer
              (let ((coding-system-for-read 'no-conversion))
                (insert-file-contents file)
                (base64-encode-region (point-min) (point-max) t)
                (buffer-string)))
            "]]"))
  (org-display-user-inline-images))

(defun org-activate-yank-img-links ()
  "Activate keybinding S-C-y for yanking [[img:...]] links in function `org-mode'.
Hook this function into `org-mode-hook'."
  (org-defkey org-mode-map (kbd "S-C-y") #'org-download-screenshot-img))

(add-hook 'org-mode-hook #'org-activate-yank-img-links)

I've tested the code with

  • emacs -q avoiding other customization
  • package-initialize to use the newer org-version 9.1.6 installed on my computer
  • emacs-version 25.1.50.2

Next there follows an example org-mode file that works for me with the above Lisp code.

#+ATTR_ORG: :width 100
[[img:iVBORw0KGgoAAAANSUhEUgAAACUAAAAuCAAAAACKMo8cAAACmElEQVR4nIXUW0iUQRjG8f+uq2ZK
ipVJRpklZUh2UhPUGy0LjVIIMq+KAoOIzSKIkkCkG6EiqG6lg1ReFEUqJSRqUdrBq05IBpXSwdTW
s7FPF7Pq96mrc/XOzG/eGV5mBs3SfqS8M4ET/+1nVkaCL/SfqScx558v9K96kuO7NZf6syX8veZS
/ekBtZpLeTK5IEkankV5MjgiSb0Frvin/tRQFttGJQ2lAsEtM6uhnST2StIxSC5m3chMajCH6C+S
9NpJUr+KuTGDGsxh/itJUjY8kzxRW6er0V0EmRo0QZ4kVTjap6rRAhyVJiyAJ5LUHXxxihrJx1co
dbhY4zU8z65GdkOZLz4D501UGWlTA7lQYtbr3zKcHSb8XmBVv9PgxHinBtLG4w7aDpX44pZYODex
ZD9UTHRYRWi7JPWfDSTw2sS4JxQ+T6pLsPRyU93pJbC4fnL327B+ssdw0vg1z+20nDEfSi1KXekA
bH7gtSBPCLyY7DoEDc0D0ZkbAR5+PGny3tlHVJflfVkSjJY44nwJC6HIMmNVZcAbSdJIOFy3zFhf
bRtwC4DGPhzZ1gdsWbEWiBmTJLetDrYd/7oA7krSanD7UY0AZEr6ADyyKsu5Ws2RGqAOXOm2j0Xj
P4b2mIFsaQekWFMJ715ficYiYXsk8HIwBE7ZVQ3PTdQElJcDRXVArV1V47tfJUBj30KYdxACeuzq
l3OFV5KGF0HEsErN4TbIrpRKqyRVAQekby4AjtqVkzzuA7oCHIaYHAAypnyxekuCpDpgkyTdBODr
1B21kk/yJgP3JKk3CIjVNHWcclUByaZwWUDhdNVM5NUICGgxI4/dbnf9dOVdDmC+SD8NydQoYWAO
1RkGC9pmQeZ+VYfFNc6G9B8xtL2tqj3MQgAAAABJRU5ErkJggg==]]
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • 1
    Hello,thanks for this code and explanation. But unfortunately i'm not familiar with emacs programming. Therefore i dont know how to use your code. Could you pls give me a hint how to test and use it? Right now i inseert your code into my emacs config. Emacs starts right away, but what do i have to do to insert a picture? Regards Poul – space_held May 28 '18 at 17:23
  • @space_held The example org file at the end of the answer shows you how to use the new functionality. In your org-file you input `[[img:XXX]]` where `XXX` stands for the base64-encoded image. As a first step save the example to an org file on your disk, open it, and try `M-x org-display-inline-images`. How to insert the base64-encoded string for a png image in the clipboard is a (very) different question which you should ask separately here on emacs.sx. I already found an answer for the X11 implementation of the emacs gui. But, I don't know your system. So I cannot add that stuff here. – Tobias May 28 '18 at 23:01
  • Hello and thank you!? I tested it and it works so far.i will give you my System : Windows 8.1 64bit,emacs 26 rc1. Any Chance to input the encoded string thea easy way? Regards poul – space_held May 31 '18 at 19:11
  • @space_held At the end of https://www.gnu.org/software/emacs/manual/html_node/elisp/Window-System-Selections.html there is a section about `gui-get-selection` on MS-Windows. It makes clear that on Windows one has only text selections. Selections of other types are ignored. That means one cannot avoid external programs and files to get images from the Windows clipboard. Currently I only have access to a Linux notebook where I implemented pasting images from the clipboard and I hope that stuff works without changes on Cygwin. But I don't have any hope for the Windows-Emacs. – Tobias May 31 '18 at 19:47
  • Sorry but i think that i dont understand it correct. Currently i use the package org-download to insert images. This is done through drag and drop or clipboard. Does it mean, that the developer clears the "ignoring of other types", as you wrote above? Regards Poul – space_held Jun 01 '18 at 11:16
  • @space_held You wrote: "Does it mean, that the developer clears the "ignoring of other types", as you wrote above?" No that is not possible. As the name `org-download` suggests the package downloads the image to a file on hard-disk and includes it. [It uses the external program `imagemagick/convert` on Windows](https://github.com/abo-abo/org-download). I interpreted your question such that you wanted to avoid external files. Directly pasting images from the clipboard within emacs does only work on Linux-like systems (hopefully including cygwin). – Tobias Jun 01 '18 at 19:43
  • hello, thank you for the answer. Now i understand. To insert a file avoiding external files on disk i have to base64-encoding an image and than insert it with [[img:xxx]]. Correct? If so, i have to search for a package or script thats encode an image. Do you know such package? Regards Poul – space_held Jun 02 '18 at 07:13
  • do you mean your code above? – space_held Jun 03 '18 at 11:11
  • @space_held The code below `UPDATE` in this answer contains now a function `org-download-screenshot-img` for pasting an image in the required form `[[img:xxx]]` with base64-encoded data from the clipboard and for displaying it. That function is bound to the key sequence `S-C-y` in `org-mode-hook`. – Tobias Jun 03 '18 at 23:45
  • Hallo, if i call the function with S-C-y, the following message appears: Unzulгsiger Parameter - /tmp let: Opening input file: No such file or directory, c:/tmp/screenshot.png Do i have to configure anything? Regards Poul – space_held Jun 05 '18 at 17:36
  • @space_held You need to configure `org-download-screenshot-file`. I didn't mention it since I thought you would be some kind of `org-download`-power user. You also need `imagemagick/convert` for Windows since that is the external application capturing the image from the clipboard. – Tobias Jun 05 '18 at 21:46
  • @space_held You also need [`imagemagick/convert`](http://www.imagemagick.org/Usage/windows/) for Windows since that is the external application capturing the image from the clipboard. Configure `org-download-screenshot-method` appropriately. – Tobias Jun 05 '18 at 21:51
  • hello, i checked again with imagemagick installed. Unfortunately it is not working as aspected. If i copy an image into the windows clipboard and tried to insert the image with C-S-y. The following message occur: "convert: NoBitmapOnClipboard `' @ error/clipboard.c/ReadCLIPBOARDImage/158. convert: NoImagesDefined `~/screenshot.png' @ error/convert.c/ConvertImageCommand/3275. let: Opening input file: No such file or directory, c:/Users/Held/screenshot.png" Is it possible to insert the image via clipboard and copy/paste? Regards – space_held Jun 11 '18 at 17:19
  • @space_held I intentionally based this solution on [org-download.el](https://github.com/abo-abo/org-download/blob/a57beffd0f09b218a9487d1750960878c1d5b12c/org-download.el#L36). 1st: You wrote in your question that you already used it. I assumed that you also tried pasting from clipboard and only wanted to avoid saving it as file. 2nd: Try to use pasting from clipboard with `org-download-screenshot`. If that does not work for you open an issue at [the org-download issue tracker](https://github.com/abo-abo/org-download/issues/new). – Tobias Jun 11 '18 at 19:53
  • hello, i rechecked with org-download. the following occurs: Drag and drop works ok. The picture is inserted as File "IMG_20180527_084811_2018-06-12_19-24-13.jpg" is now a task attachment. :PROPERTIES: :ID: 49578a89-39ed-425c-810c-c4b085699000 :Attachments: IMG_20180527_084811_2018-06-12_19-24-13.jpg :CREATED: <2018-06-12 Di 19:24> :END: #+DOWNLOADED: file:C%3A/Users/Held/Desktop/IMG_20180527_084811.jpg @ 2018-06-12 19:24:13 #+attr_html: :width 100px - Poul – space_held Jun 12 '18 at 17:29
  • i will ask the developer of org-download about pasting from clipboard on windows. - Regards Poul – space_held Jun 12 '18 at 17:48
  • @space_held I've added `expand-file-name` such that `~` should work in `org-download-screenshot-file`. Could you retry? – Tobias Jun 13 '18 at 11:53
  • hello, thanks for your edits! i have tested your code: 1. pasting an image with org-download-screenshot results "url-retrieve-internal: Bad url: ~/screenshot.png" 2. using C-S-y results "convert: NoBitmapOnClipboard `' @ error/clipboard.c/ReadCLIPBOARDImage/158. convert: NoImagesDefined `c:/Users/Held/screenshot.png' @ error/convert.c/ConvertImageCommand/3275. let: Opening input file: No such file or directory, c:/Users/Held/screenshot.png" 3. Drag and drop results ok "File "IMG_20180527_084819_2018-06-13_19-42-35.jpg" is now a task attachment." Does this help? Regards - Poul – space_held Jun 13 '18 at 17:46
  • @space_held Do you have imagemagick-support in your emacs version? `M-: (image-type-available-p 'imagemagick)` – Tobias Jun 14 '18 at 08:52
  • the result show nil. This is not good, isn't? How could i change that, or is there a version with support? Regards Poul – space_held Jun 14 '18 at 18:16
  • @space_held The [message 00777 on emacs-devel](https://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00777.html) says: "@@ -154,7 +154,7 @@ Note also that we need to disable Imagemagick because Emacs does not yet support it on Windows." Without imagemagick-support at least the actual-width specification will not work. Additionally it may be that some image formats are not recognized. Note that the cygwin port of emacs supports imagemagick. – Tobias Jun 14 '18 at 20:41
  • hello, sorry for the delay. I tested different emacs versions (cygwin, emax ). Both versions supports imagemagick. Your code works also. Great! BUT: After import only one picture, emacs is unusable, because it is sooo slow. I read about the emacs problem with longlines... So i think, that the code is ok, but emacs wont like it because of longline. Because i read, that there is no solution for this, i will think about other solutions. Thanks for the support. – space_held Jul 15 '18 at 16:01