Inleiding
Soms wil je een reeks afbeeldingen samenvoegen tot één PDF document. In mijn geval waren het scans van losse vellen bladmuziek die ik als één geheel wilde opslaan. Tot slot wilde ik ook nog de mogelijkheid om een watermerk toe te voegen.
Tools
Manipuleren van afbeeldingen, het samenvoegen van en goochelen met PDF-bestanden;
dat klinkt naar een klus voor de ImageMagick tools: convert
en
composite
. Deze programma's zijn zó extreem veelzijdig... Gelukkig is er een
goede documentatie en zijn er talloze voorbeelden op internet te vinden. Ik
besloot een script te schrijven dat mij het werk uit handen ging nemen.
Eén van de uitdagingen was het consequente papierformaat van het uiteindelijke PDF-bestand. Met dank aan een antwoord op StackExchange was dat snel opgelost.
Onderzoek
Met de commando's zelf ben je er nog niet. Om argumenten te kunnen meegeven en
interpreteren wil ook de shell (Bash) correct en volledig worden aangesproken.
Eerst maakte ik een eenvoudige variant die één bestand in een PDF plaatste.
Daarna volgde een groep bestanden met behulp van de ${1}
, ${2}
en
${@}
-variabelen, zie ook positionele parameters.
Tot dat moment was de bestandsnaam van het uiteindelijke PDF-bestand nog 'hard coded', dat wil zeggen voorgeschreven vanuit het script. Dat kan variabel, toch? Ja, maar dan wel fatsoenlijk, met behulp van optie-argumenten.
Daarna was het nog maar een kleine stap om ook het aanbrengen van een watermerk
aan het script toe te voegen. Ook nu begon ik eerst met één bestand, daarna de
integratie in het omliggende script. Hierbij wilde ik de oproep van convert
centraal neerleggen, om dubbele stukken code te vermijden. Ik moest een manier
vinden om de te verwerken bestanden (${FILES[@]}
) al dan niet vanuit een
tijdelijke map te halen...
Parameter expansion
Ik experimenteerde met een tweede array
, maar dat was geen goed idee.
Uiteindelijk bood Bash zelf een keurig nette oplossing in de vorm van
parameter expansion. Met één commando kan ik, eenmaal in de watermerk-if
,
de bestandsnamen voorzien van een tijdelijke map (${WORKDIR}
):
FILES=( "${FILES[@]/#/${WORKDIR}/}" );
Update
Maar wat, als je een mix van afbeeldingen (JPG) en PDF-documenten van één pagina
wil samenvoegen tot één gezamenlijke PDF? Daarop was het originele script niet
voorbereid. Aldus bouwde ik met de hulp van de uitgebreide documentatie een
extra for
-loop waarin een eventueel PDF bestand wordt omgezet naar een
JPG-bestand met hoge kwaliteit.
Script
#!/bin/bash
RESOLUTION=150;
OUTPUTFILE="outfile";
WATERMARKFILE=;
WORKDIR=;
CONVERT="$( which convert )";
COMPOSITE="$( which composite )";
USAGE="Usage: "$(basename ${0})" [-w watermark-file] [-o outfile]
infile [infile [...]]";
if [ ${#} -eq 0 ]; then
echo "${USAGE}";
exit 0;
fi
while getopts ":h?w:o:" opt; do
case "$opt" in
\?)
echo "${USAGE}";
exit 0;
;;
w) WATERMARKFILE="${OPTARG}";
;;
o) OUTPUTFILE="${OPTARG%%.pdf}";
;;
esac
done
shift $((OPTIND-1))
[ "$1" = "--" ] && shift
if [ ${#} -lt 1 ]; then
echo "Error: At least one infile is required.";
exit 1;
fi
if [ "${WATERMARKFILE}x" != "x" ] && [ ! -f "${WATERMARKFILE}" ]; then
echo "Error: watermark-file cannot be found.";
exit 2;
fi
FILES=("${@}");
# Convert PDF input files to high quality JPG
for i in "${!FILES[@]}"; do
if [[ ${FILES[${i}]} =~ \.pdf$ ]]; then
convert -density 150 -trim "${FILES[${i}]}" -quality 100 -flatten \
"${FILES[${i}]%%.pdf}.jpg";
FILES[${i}]="${FILES[${i}]%%.pdf}.jpg";
fi
done
# Add watermark
if [ -f "${WATERMARKFILE}" ]; then
WORKDIR="$( mktemp --directory )";
for i in "${FILES[@]}"; do
"${COMPOSITE}" -tile -gravity Center -watermark 20x50 \
"${WATERMARKFILE}" "${i}" "${WORKDIR}/${i}";
done
FILES=( "${FILES[@]/#/${WORKDIR}/}" );
fi
# Here's where the magick happens
"${CONVERT}" "${FILES[@]}" -compress jpeg -quality 70 \
-resize $((RESOLUTION*827/100))x$((RESOLUTION*1169/100)) \
-density ${RESOLUTION}x${RESOLUTION} \
-repage $((RESOLUTION*827/100))x$((RESOLUTION*1169/100)) \
"${OUTPUTFILE}.pdf";
# Clean-up
if [ ! -z "${WORKDIR}" ] && [ -d "${WORKDIR}" ]; then
rm -r "${WORKDIR}";
fi
# Exit normally
exit 0;
Conclusie
Opgeslagen als toa4pdf.sh
kan ik het script als volgt oproepen:
toa4pdf.sh -o UiteindelijkePDF -w /pad/naar/watermerk.png *.jpg *.pdf;
N.B.: Ik heb geen idee hoe het script reageert op PDF-documenten met meerdere pagina's als input... :-)