Whenever I need to convert, merge, or combine images or PDF files, I pull out my Terminal and attempt doing it first with CLI (command line interface) commands. Over time I’ve built an arsenal of CLI commands that 9/10 times does the trick faster than any other program.

Prerequisites

Most of the image manipulation commands require imagemagick and the PDF ones require poppler. Both of which are just a homebrew install away for macOS:

# image utilities
brew install "imagemagick"

# pdf utilities
brew install "poppler"

Merging

Merge images horizontally/vertically

# convert ships with imagemagick

## horizontal
convert +append image_1.png image_2.png merged
## vertical
convert -append image_1.png image_2.png merged // vertical

# using montage
# slightly better as it maintains transparency within images

## horizontal
montage 0_index.png 1_post.png -background none -tile 2x1 -geometry +0+0 PNG32:out.png
## vertical
montage 0_index.png 1_post.png -background none -tile 1x2 -geometry +0+0 PNG32:out.png

Merge images and PDFs

# convert ships with imagemagick
convert 1.png 2.pdf merged.pdf

Merge PDFs

# pdfunite ships with poppler
pdfunite output-1.pdf output-2.pdf output-3.pdf merged-1-3.pdf
pdfunite output-4.pdf output-5.pdf merged-4-5.pdf

# using native tools that comes with the macOS
"/System/Library/Automator/Combine PDF Pages.action/Contents/Resources/join.py" -o merged.pdf source1.pdf source2.pdf

# using ghostscript [also works with encrypted PDFs]
gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf source1.pdf source2.pdf source3.pdf

Splitting

Split multi-page PDF into single page PDFs

# pdfseparate & pdfunite ship with poppler

# split input.pdf into output-1.pdf output-2.pdf ... etc.
pdfseparate input.pdf output-%d.pdf
# split input.pdf from page 2 till the end
pdfseparate -f 2 input.pdf output-%d.pdf

Replacing single pages in PDF

A common usecase is to swap out a single page in a pdf. poppler doesn’t have a readymade command for this, instead i just use pdfseparate + pdfunite for this:

# input has 10 pages; special has 3 pages
# replace pages 5,6,7 in input with special pages

pdfseparate input.pdf input-%d.pdf
pdfseparate special.pdf special-%d.pdf

# using fish shell
for i in (seq 1 4)
    pdfunite input-$i
end

Resizing/Compression

By % or boundary

# convert ships with imagemagick
convert -resize 60%     src_img.png src_img_reduced.png
convert -resize 128x128 src_img.gif src_img_reduced.gif
  • Doesn’t overwrite
  • 60% is good for documents
  • 75% is acceptable image quality (though you probably want higher)

By keeping aspect ratio & max dimension

sips -Z 800 input.png --out output.png

# Z     maintain aspect ratio
# 800   max height and width to be used

In directory

# resize all images in directory
for f in *.jpeg
do
   convert -resize 60% "$f" dest/src_img_reduced.png
done

Converting

DNG → JPG

# sips ships natively with macOS
sips -Z 3072 -s format jpeg input.dng --out output.jpg

# Z     maintain aspect ratio
# -s    format jpeg
  • Overwrites
  • Aggressively reduces

PNG → JPG

# sips ships natively with macOS
sips -s format png input.jpg --out output.jpeg

HEIC → JPG

# sips ships natively with macOS
sips -s format jpeg input.heic --out output.jpeg

PDF → JPG

# convert ships with imagemagick
convert -density 150 -quality 100 -flatten -sharpen 0x1.0 -trim input.pdf output.jpg
# 3rd page of pdf
convert input.pdf[2] output.jpg
# convert all pages (2 means 2 digits will show on page count)
convert input.pdf output-%02d.jpg

# pdftoppm ships with poppler
# output is a prefix
# 150 is DPI
pdftoppm -jpeg -r 150 input.pdf output

JPG → PDF

# convert ships with imagemagick
convert 1.jpg 2.jpg merged.pdf

JPG → (A4 paper size) PDF

convert -density 80 input.jpeg -background white \
        -page a4  # -page Letter
        output.pdf

JPG → PNG

# sips ships natively with macOS
sips -s format png input.jpg --out output.png

PNG → WEBP

# install webp utility
brew install webp

cwebp input.png -o output.webp
cwebp -q 75 input.png -o output.webp
# -q 75 refers to the compression rate.

SVG → WEBP

# convert ships with imagemagick
convert -background none -density 200 input.svg output.webp

SVG → PNG

# convert ships with imagemagick
convert -background none -density 1000 -resize 48x is_logo_location.svg is_logo_location.png

PNG → GIF

# convert ships with imagemagick
convert -delay 5 -loop 0 *.png output.gif

MOV/MP4 → GIF

# convert ships with imagemagick
mkdir frames
# mov or m4v
# ffmpeg -i input.mov -vf scale=320:-1,format=rgb8,format=rgb24 -r 10 frames/ffout%3d.png
ffmpeg -i input.mov -vf scale=320:-1 -r 10 frames/ffout%3d.png
convert -delay 8 -loop 0 frames/ffout*.png frames/output.gif

# scale   [width in px]:-1
# r       framerate

Manipulating

Remove transparency

# flattens png to a solid color
# mogrify ships with imagemagick
mogrify -background white -flatten input*.png

Get information from image

Is image transparent?

# identify ships with imagemagick
identify -format '%[channels]' foo.png

List image dimensions

identify -format '"%w %h' foo.png

I’ll keep adding more as I discover new use cases.