RetroCode UK

Published Date Apr 7, 2020 Reading Time ~4 minutes RSS Feed Vim Linux Productivity

Add Markdown Images Using Vim

I have recently been working on workflow for this blog, and one of the initial stumbling blocks was adding images.

The solution I now have in place works as follows:

  1. Press <Leader>I to be presented with a pop-up dialogue (using zenity) to select the folder containing images to be included.
  2. After selecting the folder, sxiv pops up with a gallery of the images where m marks an image, and q quits (and confirms).
  3. With the selection of images, they are now optimised into a sub-folder of the blog post.
  4. Finally, links to the newly optimised images are added in to the content of markdown.

1. Selecting a folder

This first script takes an optional argument which is the start folder for the selection. This could be ~/Pictures, which would likely be a good default for image path selections, although it also makes this script more generic to simply have it default to the home folder.

Source: select-path.sh

#!/usr/bin/env bash

START_PATH="${1:-$HOME}"
zenity --file-selection --directory --filename="$START_PATH"

Selecting a folder

2. Selecting images in a folder

This second script takes a required argument, which is a path. It doesn’t care where this path comes from, but in the case of the example in this post, it is the path as selected by the user from the script above.

Source: select-images-in-path.sh

#!/usr/bin/env bash

test -d "$1" || exitWithError "User selected folder does not exist"

# Select with `m` and confirm with `q`
sxiv -f -N "Select image(s)" -o -t "$1"

Selecting images from the folder

3. Optimise the images

This next script takes an image file, and a destination path, and will both optimise the image and also strip out any exif information for it, placing it in the specified destination.

Source: image-optimise-to.sh

#!/usr/bin/env bash
# $0 "target/path" "source/path/file_name"
mkdir -p "$1"
test -d "$1" || exit 1
test -f "$2" || exit 1

SOURCE_IMAGE="$2"
TARGET_PATH="$1"
TARGET_IMAGE="$TARGET_PATH/$(basename "$2")"

# Args: Source Image, Target Image
convert "$SOURCE_IMAGE" -quiet -resize 2000x800^ -gravity center -extent 2000x800 -strip -interlace Plane -gaussian-blur 0.05 -quality 85% "$TARGET_IMAGE"

4. Putting it all together and outputting to Vim

This final script calls each ofthe previous scripts, and only takes a single argument which is the destination folder for the selected, optimised images.

Source: blog-images-import-to.sh


#!/usr/bin/env bash
# Get user selected images, optimise to target and return markdown segment

exitWithError () {
    echo "$@" && exit 1
}

test -n "$1" || exitWithError "Target folder not specified"
mkdir -p "$1"
test -d "$1" || exitWithError "Target folder could not be created"
TARGET_FOLDER="$1"

SELECTED_IMAGES="$(select-images-in-path.sh "$(select-path.sh "$HOME/Pictures")")"

test -n "$SELECTED_IMAGES" || exitWithError "No images selected in path"

for SOURCE_IMAGE in $SELECTED_IMAGES; do
  # Source image path must be relative - readlink -f %
    ABSOLUTE_TARGET_DIR="$(readlink -f "$TARGET_FOLDER")"
    SOURCE_FILE="$(basename "$SOURCE_IMAGE")"

    # Echo images ready for Vim
    image-optimise-to.sh "$ABSOLUTE_TARGET_DIR" "$SOURCE_IMAGE" && \
      echo "![$SOURCE_FILE]($TARGET_FOLDER/$SOURCE_FILE)"
done

And finally, this is the snippet of code I use in Vim. It calls blog-images-import-to.sh with an argument being the current markdown file, without the .md extension. You will also notice that it also changes the current path to the path of the markdown file, so links work relative to the current file with images placed in the sub-folder.

function! ImportBlogImages(currentFile, currentPath)
    echom "Select a folder and image(s) to include in this post"
    exec 'cd ' . a:currentPath

    silent execute 'r!blog-images-import-to.sh ' . a:currentFile[:-4]
endfunction

" List contents of a path matching the filename (without .md) - pipe through
nmap <Leader>I :call ImportBlogImages(expand("%"), expand("%:p:h"))<CR>

Conclusion

You will notice that each of the parts of the process are separate scripts, each with its own purpose. This is deliberate, and allows any of the scripts to be rewritten, perhaps to use different applications or libraries, or used in combination with different scripts for different purposes.

The part that inserts the mark-up is specific to markdown and its use for the blog, so each of the scripts are pulled together into a single script blog-images-import-to.sh, and is simply called via the function in Vim.