Skip to content

Instantly share code, notes, and snippets.

@Arahnoid
Last active March 12, 2022 20:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arahnoid/8ba6e7289c4a12f7f3a04b9cfb290454 to your computer and use it in GitHub Desktop.
Save Arahnoid/8ba6e7289c4a12f7f3a04b9cfb290454 to your computer and use it in GitHub Desktop.
Collection of useful emacs-lisp functions for working with images

Editing images in Markdown files

ig/md-imagemagick-add-shadow

When an image has large bits of white color it may merge with the document background. To avoid this lets add a shadow to the image. We can parse the file path from a image object in Markdown file and send it to ImageMagic.

(defun ig/md-imagemagick-add-shadow ()
  "Add shadow to a Markdown image element"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "file=%s;
        convert $file \\
        \\( +clone -background '#A4B1BC' -shadow 100x5+0+0 \\) \\
        +swap -background none -layers merge +repage $file;
        echo 'Added shadow to:' $file "
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-imagemagick-max-w

Some images are just unnecessary wide for a Markdown document. We will specify a max width value and if an image is larger than the specified size we will scale it down.

(defun ig/md-imagemagick-max-w ()
  "Scale down image to max width defined by user"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format " file=%s; width=%s;
        convert \"$file\" -resize \"$width\"x10000\\> \"$file\";
        echo Scaled down to W = \"$width\"px \"$file\""
                             (replace-regexp-in-string "%20" "\\\\ " img-path)
                             (read-string "Enter max image width:"))))))

ig/md-imagemagick-liquid-scale

Scaling an image size down is not always a best solution. Images of tables will benefit from a liquid scaling.

(defun ig/md-imagemagick-liquid-scale ()
  "Scale down image to width defined by user.
Is better to use a percent value"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "file=%s; width=%s;
        convert \"$file\" -liquid-rescale \"$width\"x100%%\\! \"$file\"
        echo Changed image width to \"$width\"px \"$file\" "
                             (replace-regexp-in-string "%20" "\\\\ " img-path)
                             (read-string "Enter max image width:"))))))

ig/md-image-size-info

Is good to know what size an image has.

(defun ig/md-image-size-info ()
  "Show image information, size, with, height"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "magick identify -format 'Image Size:%%b, W:%%[fx:w]px, H:%%[fx:h]px' %s"
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-open-image-in-editor

Not everything can be automated. In some cases I will need to open a image from markdown in an external editor.

(defun ig/md-open-image-in-editor ()
  "Open a markdown image in an external editor"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "open %s"
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-open-image-in-affinity-designer

(defun ig/md-open-image-in-affinity-designer ()
  "Open markdown image under cursor in Affinity Designer"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "open -a 'Affinity Designer' %s"
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-open-image-in-affinity-photo

(defun ig/md-open-image-in-affinity-photo ()
  "Open markdown image under cursor in Affinity Photo"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "open -a 'Affinity Photo' %s"
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-open-image-in-finder

(defun ig/md-open-image-in-finder ()
  "Open a markdown image in Finder"
  (interactive "@*")
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\s-*!\\[\\([^][]*\\)\\](\\([^()]*\\))?"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    ;; If the search didn't error out, then we exited the loop with
    ;; `looking-at' matching `regexp'. The match data therefore contains
    ;; the parts of the image link.
    (skip-syntax-forward "-")
    (let ((img-path (match-string 2)))
      (shell-command (format "open -R %s"
                             (replace-regexp-in-string "%20" "\\\\ "  img-path))))))

ig/md-rename-image

(defun ig/md-rename-image ()
  "Rename the file pointed by the link under point and update the link."
  (interactive)
  ;; Look for a Markdown image element before the cursor or immediately
  ;; after the cursor. Don't look any further than the start of the current
  ;; paragraph.
  (let ((limit (save-excursion
                 (backward-paragraph)
                 (point)))
        (regexp "\\(!\\[[a-z s]*\\](\\)\\([./-a-z0-9]*[.png\|.jpg|.svg]\\)\\()\\)"))
    (if (and (eq ?! (char-after (1- (point))))
             (eq ?\[ (char-after (point))))
        (backward-char))
    (while (not (looking-at regexp))
      (search-backward "![" limit))
    (skip-syntax-forward "-")
    (let* ((remove (list (match-beginning 0) (match-end 0)))
          (desc (match-string 1))
          (filepath (match-string 2))
           (path (file-name-directory filepath))
           (filename (file-name-nondirectory filepath))
           (ext (file-name-extension filepath))
           (prompt (read-string (format "New name (was '%s'): " filename)))
           (namewithext (concat prompt "." ext))
           (newfilename (string-replace " " "-" (downcase namewithext))))
      ;; Rename file
      (rename-file (concat path filename) (concat path newfilename))
      ;; Update link
      (unless (string-empty-p newfilename)
        (apply #'delete-region remove)
        (insert (concat desc path newfilename ")" ))))))

ig/md-screenshot-from-clipboard

(defun ig/md-screenshot-from-clipboard ()
  "Take image from clipboard and paste it
in the .media folder in the same folder as the current opened file"
  (interactive)
  (setq filename
        (concat
         (make-temp-name
          (concat ".media/"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (unless (file-exists-p (file-name-directory filename))
    (make-directory (file-name-directory filename)))
  (if (eq system-type 'darwin)
      (shell-command(concat "pngpaste " filename)))
  (if (eq system-type 'gnu/linux)
      (shell-command(concat "xclip -se c -t image/png -o > " filename)))
  (if (file-exists-p filename)
      (insert (concat "![](" filename ")"))))

ig/md-surrond-images-with-blank-lines

(defun ig/md-surrond-images-with-blank-lines ()
  "Surround markdown images with blank lines."
  (interactive)
  (beginning-of-buffer)
  (while (re-search-forward "^!\\[")
    (save-excursion
      (end-of-line 0)
      (open-line 1))
    (end-of-line)
    (open-line 1)))

Editing images in an org file

The code bellow will look for a _images folder in the current opened file folder. Next it will generate file name Current Date and Time plus Unique name and File format e.g. 20201127_193108_UO9Jg4.png

(defun ig/screenshot-from-clipboard ()
  "Take image from clipboard and paste it
in the _images folder in the same folder as the current opened file"
  (interactive)
  (setq filename
        (concat
         (make-temp-name
          (concat "_images/"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (unless (file-exists-p (file-name-directory filename))
    (make-directory (file-name-directory filename)))
  (if (eq system-type 'darwin)
      (shell-command(concat "pngpaste " filename)))
  (if (eq system-type 'gnu/linux)
      (shell-command(concat "xclip -se c -t image/png -o > " filename)))
  (if (file-exists-p filename)
      (insert (concat "[[file:" filename "]]"))))

This code is similar to the code above only it asks user to select a square area to capture

(defun ig/screenshot-manual ()
  "Take image from clipboard and paste it
in the _images folder in the same folder as the current opened file"
  (interactive)
  (setq filename
        (concat
         (make-temp-name
          (concat "_images/"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (unless (file-exists-p (file-name-directory filename))
    (make-directory (file-name-directory filename)))
  (lower-frame)
  (if (eq system-type 'darwin)
      (call-process "screencapture" nil nil nil "-i" filename))
  (if (eq system-type 'gnu/linux)
      (call-process "import" nil nil nil filename))
  (raise-frame)
  (if (file-exists-p filename)
      (insert (concat "[[file:" filename "]]"))))

Select file from a disk and Insert it at the cursor position from drive.

(defun ig/insert-file-from-disk (&optional file)
  "Select file from a disk and Insert it at the cursor position from drive.
Asks for file location and optionaly for a label"
  (interactive "fSelect a file:")
  (setq description (read-string "Enter file name:"))
  (if (string-empty-p description)
      (insert (concat "[[file:" file "]]"))
    (insert (concat "[[file:" file "][" description "]]"))))
(defun ig/org-rename-link ()
  "Rename the file pointed by the link under point and update the link."
  (interactive)
  (when (org-in-regexp org-link-bracket-re 1)
    (let* ((remove (list (match-beginning 0) (match-end 0)))
           (link (org-link-unescape (match-string-no-properties 1)))
           (desc (when (match-end 2) (match-string-no-properties 2)))
           (type (progn (string-match "\\`\\([^:]*:\\)?\\([^:]*\\)\\(::.*\\)?"
                                      link)
                        (match-string 1 link)))
           (filepath (match-string 2 link))
           (search (match-string 3 link))
           (filename (file-name-nondirectory filepath))
           (ext (file-name-extension filepath))
           (path (file-name-directory filepath))
           (prompt (read-string (format "New name (was '%s'): " filename)))
           (namewithext (concat prompt "." ext))
           (newfilename (string-replace " " "-" (downcase namewithext))))

      ;; Rename file
      (rename-file (concat path filename) (concat path newfilename))
      ;; Update link
      (unless (string-empty-p newfilename)
        (apply #'delete-region remove)
        (insert (org-link-make-string (concat type path newfilename search) desc))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment