Spring naar hoofdtekst

Inhoudsopgave

Afbeeldingen als PDF met watermerk

Geplaatst op door ,
Laatste aanpassing op .

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... :-)