PDF to JPG
convert / v1.0.0
Render PDF pages as high-quality JPG images using ImageMagick.
Renders every page of a PDF as a numbered JPG image. Supports single files and recursive directory conversion, configurable DPI and JPG quality, optional output directories, and preserved subdirectories for batch output.
Requirements
- ImageMagick
- Ghostscript
Platforms
LinuxMacOS
Usage
./scripts/pdf-to-jpg.sh -i document.pdf
./scripts/pdf-to-jpg.sh --input document.pdf --output ./images --dpi 300 --quality 92
./scripts/pdf-to-jpg.sh -i ./documents -o ./images
./scripts/pdf-to-jpg.sh --input=document.pdf --dpi=600 --quality=95
Download
Install quickly or copy a command for your shell.
toolbox pdf-to-jpg
curl -fsSL "https://raw.githubusercontent.com/PiSaucer/toolbox/main/scripts/pdf-to-jpg.sh" -o "pdf-to-jpg.sh"
wget -O "pdf-to-jpg.sh" "https://raw.githubusercontent.com/PiSaucer/toolbox/main/scripts/pdf-to-jpg.sh"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/PiSaucer/toolbox/main/scripts/pdf-to-jpg.sh" -OutFile "pdf-to-jpg.sh"
python3 -c "import urllib.request; urllib.request.urlretrieve('https://raw.githubusercontent.com/PiSaucer/toolbox/main/scripts/pdf-to-jpg.sh', 'pdf-to-jpg.sh')"
Integrity
SHA256
05a15cc699912c9c6e31edeb913bbbdc7edce9816eb6bc85f31d7857c42df1b3
Copy and Paste Script
Use this when you want to copy the full script directly.
#!/bin/sh
# pdf-to-jpg.sh
# Copyright (c) 2026 PiSaucer
# Licensed under the MIT License
# Version 1.0.0
# Render PDF pages as high-quality JPG images
# Usage: ./pdf-to-jpg.sh -i file.pdf|dir [-o outdir] [--dpi 300] [-q 92]
# Color codes
# https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
NC='\033[0m' # No Color
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
# Default rendering settings
DPI=300
QUALITY=92
file=""
output=""
helpFunction() {
printf "PDF to JPG\n\n"
printf "Usage: ${YELLOW}%s -i file.pdf|dir [-o outdir] [--dpi 300] [-q 92]${NC}\n" "$0"
printf "\t-f, -i, --input Input PDF file or directory containing PDF files\n"
printf "\t-o, --output Output directory (optional; default: alongside input)\n"
printf "\t--dpi Render DPI (default: 300)\n"
printf "\t-q, --quality JPG quality from 1 to 100 (default: 92)\n"
printf "\t-h, --help Show this help message\n"
exit 1 # Exit script after printing help
}
# Print an error message
option_error() {
printf "${RED}Error: %s${NC}\n\n" "$1" >&2
helpFunction 1
}
# Read the value following an option and reject missing values
require_option_value() {
option_name="$1"
option_value="${2-}"
if [ -z "$option_value" ]; then
option_error "$option_name requires a value."
fi
}
# Ensure a numeric setting is an integer within the supported range
validate_integer() {
setting_name="$1"
setting_value="$2"
minimum="$3"
maximum="$4"
case "$setting_value" in
"" | *[!0-9]*)
option_error "$setting_name must be an integer from $minimum to $maximum."
;;
esac
if [ "$setting_value" -lt "$minimum" ] || [ "$setting_value" -gt "$maximum" ]; then
option_error "$setting_name must be an integer from $minimum to $maximum."
fi
}
# Prefer ImageMagick 7's magick command, with ImageMagick 6 as a fallback
find_imagemagick() {
if command -v magick >/dev/null 2>&1; then
printf "magick"
elif command -v convert >/dev/null 2>&1; then
printf "convert"
fi
}
# Render every page and use a zero-padded page number in each output filename
pdf_to_jpg() {
pdf_file="$1"
output_base="$2"
"$IMAGEMAGICK" \
-density "$DPI" \
-units PixelsPerInch \
"$pdf_file" \
-background white \
-alpha remove \
-alpha off \
-strip \
-quality "$QUALITY" \
"${output_base}-%03d.jpg"
}
# Determine the destination for one PDF and render it
process_pdf() {
pdf_file="$1"
if [ -n "$output" ]; then
if [ -n "$input_root" ]; then
# Preserve subdirectories during batch conversion to avoid name collisions
relative_path="${pdf_file#"$input_root"/}"
output_base="${output%/}/${relative_path%.*}"
else
output_base="${output%/}/$(basename "${pdf_file%.*}")"
fi
else
output_base="${pdf_file%.*}"
fi
output_directory=$(dirname "$output_base")
if ! mkdir -p "$output_directory"; then
printf "${RED}Error: could not create output directory: %s${NC}\n" "$output_directory" >&2
return 1
fi
if pdf_to_jpg "$pdf_file" "$output_base"; then
printf "${GREEN}Rendered %s -> %s-###.jpg (%s DPI, q=%s)${NC}\n" \
"$pdf_file" "$output_base" "$DPI" "$QUALITY"
else
printf "${RED}Error: failed to render %s${NC}\n" "$pdf_file" >&2
return 1
fi
}
# Parse short and long options, including --option=value forms
while [ $# -gt 0 ]; do
case "$1" in
-f | -i | --input)
require_option_value "$1" "${2-}"
file="$2"
shift 2
;;
--input=*)
file="${1#*=}"
[ -n "$file" ] || option_error "--input requires a value."
shift
;;
-o | --output)
require_option_value "$1" "${2-}"
output="$2"
shift 2
;;
--output=*)
output="${1#*=}"
[ -n "$output" ] || option_error "--output requires a value."
shift
;;
--dpi)
require_option_value "$1" "${2-}"
DPI="$2"
shift 2
;;
--dpi=*)
DPI="${1#*=}"
[ -n "$DPI" ] || option_error "--dpi requires a value."
shift
;;
-q | --quality)
require_option_value "$1" "${2-}"
QUALITY="$2"
shift 2
;;
--quality=*)
QUALITY="${1#*=}"
[ -n "$QUALITY" ] || option_error "--quality requires a value."
shift
;;
-h | --help)
helpFunction 0
;;
--)
shift
break
;;
-*)
option_error "unknown option: $1"
;;
*)
option_error "unexpected argument: $1"
;;
esac
done
# Reject positional arguments after the explicit end-of-options marker
if [ $# -gt 0 ]; then
option_error "unexpected argument: $1"
fi
if [ -z "$file" ]; then
option_error "missing input PDF file or directory."
fi
validate_integer "DPI" "$DPI" 1 2400
validate_integer "quality" "$QUALITY" 1 100
# Check for ImageMagick after parsing so --help works without dependencies
IMAGEMAGICK=$(find_imagemagick)
if [ -z "$IMAGEMAGICK" ]; then
printf "${RED}Error: ImageMagick is not installed (expected magick or convert).${NC}\n" >&2
exit 1
fi
# Convert one PDF or all PDFs below an input directory
if [ -d "$file" ]; then
input_root="${file%/}"
found_pdf=0
failed=0
# A temporary list avoids a pipeline subshell, so summary state remains available
pdf_list=$(mktemp "${TMPDIR:-/tmp}/pdf-to-jpg.XXXXXX") || exit 1
trap 'rm -f "$pdf_list"' 0
trap 'exit 130' HUP INT TERM
find "$input_root" -type f \( -name "*.pdf" -o -name "*.PDF" \) -print > "$pdf_list"
while IFS= read -r pdf_file; do
found_pdf=1
process_pdf "$pdf_file" || failed=$((failed + 1))
done < "$pdf_list"
if [ "$found_pdf" -eq 0 ]; then
printf "${YELLOW}No PDF files found in: %s${NC}\n" "$file"
exit 0
fi
if [ "$failed" -gt 0 ]; then
printf "${RED}%s PDF file(s) failed to render.${NC}\n" "$failed" >&2
exit 1
fi
elif [ -f "$file" ]; then
case "$file" in
*.[pP][dD][fF])
process_pdf "$file" || exit 1
;;
*)
option_error "input file must have a .pdf extension: $file"
;;
esac
else
printf "${RED}Error: input does not exist: %s${NC}\n" "$file" >&2
exit 1
fi