6

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?

Thanks Poul

  • 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: reddit.com/r/orgmode/comments/8ku3dp/… Answer: mbork.pl/2017-12-04_Embedding_files_in_Org-mode – mankoff May 21 '18 at 10:47
3

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==]]
  • 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 gnu.org/software/emacs/manual/html_node/elisp/… 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 1 '18 at 11:16

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.