103 lines
2.4 KiB
Bash
103 lines
2.4 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
|
||
|
|
#################
|
||
|
|
# PREREQUISITES #
|
||
|
|
#################
|
||
|
|
|
||
|
|
# executables in PATH
|
||
|
|
for prereq in "python3" "gs" "pdftk"; do
|
||
|
|
if ! command -v "${prereq}" &> /dev/null; then
|
||
|
|
echo "required executable '${prereq}' not found!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# python virtual env with PyMuPDF
|
||
|
|
VENV_DIR="pdfsign.venv"
|
||
|
|
if [ -d "${VENV_DIR}" ]; then
|
||
|
|
# venv exists, activate it
|
||
|
|
source "${VENV_DIR}/bin/activate"
|
||
|
|
else
|
||
|
|
for prereq in "venv"; do
|
||
|
|
if ! python3 -c "import ${prereq}" &> /dev/null; then
|
||
|
|
echo "python module '${prereq}' not found!"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# create venv and activate
|
||
|
|
python3 -m "venv" "${VENV_DIR}"
|
||
|
|
source "${VENV_DIR}/bin/activate"
|
||
|
|
# install PyMuPDF
|
||
|
|
python3 -m "pip" install PyMuPDF
|
||
|
|
fi
|
||
|
|
|
||
|
|
|
||
|
|
############
|
||
|
|
# CLI ARGS #
|
||
|
|
############
|
||
|
|
|
||
|
|
if [ $# -lt 2 ]; then
|
||
|
|
echo "Usage: ${0} <input.pdf> <signed.png> [PAGE]"
|
||
|
|
echo
|
||
|
|
echo "Replaces page PAGE with the signed version."
|
||
|
|
echo "If PAGE is not given, the last page is replaced."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
IN_TEXT_PDF="${1}"
|
||
|
|
IN_SCAN_IMG="${2}"
|
||
|
|
|
||
|
|
OUT_PDF="${IN_TEXT_PDF%%.*}_pdfsign.pdf"
|
||
|
|
|
||
|
|
IN_TEXT_PDF_LEN="$( pdftk "${IN_TEXT_PDF}" dump_data | grep NumberOfPages | awk '{print $2}' )"
|
||
|
|
IN_PAGE="${3:-${IN_TEXT_PDF_LEN}}"
|
||
|
|
|
||
|
|
RANGES="B1"
|
||
|
|
if [ ${IN_PAGE} -gt 1 ]; then
|
||
|
|
RANGES="A1-$(( IN_PAGE - 1 )) ${RANGES}"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ ${IN_PAGE} -lt ${IN_TEXT_PDF_LEN} ]; then
|
||
|
|
RANGES="${RANGES} A$(( IN_PAGE + 1 ))-${IN_TEXT_PDF_LEN}"
|
||
|
|
fi
|
||
|
|
|
||
|
|
|
||
|
|
################
|
||
|
|
# MAIN PROGRAM #
|
||
|
|
################
|
||
|
|
|
||
|
|
GSFLAGS="-sDEVICE=pdfwrite -dSAFER -dNOPAUSE -dQUIET -dBATCH"
|
||
|
|
|
||
|
|
set -e
|
||
|
|
|
||
|
|
# extract IN_PAGE (text-only) from IN_TEXT_PDF
|
||
|
|
gs ${GSFLAGS} \
|
||
|
|
-dFirstPage="${IN_PAGE}" -dLastPage="${IN_PAGE}" \
|
||
|
|
-dFILTERIMAGE -dFILTERVECTOR -dFILTERTEXT=false \
|
||
|
|
-sOutputFile=".pdfsign_textonly.pdf" \
|
||
|
|
"${IN_TEXT_PDF}"
|
||
|
|
|
||
|
|
# use IN_SCAN_IMG as background, layering text from extracted page on top
|
||
|
|
python3 ./pdfsign.py \
|
||
|
|
".pdfsign_textonly.pdf" "${IN_SCAN_IMG}" \
|
||
|
|
".pdfsign_signed.pdf"
|
||
|
|
|
||
|
|
rm ".pdfsign_textonly.pdf"
|
||
|
|
|
||
|
|
# replace IN_PAGE in IN_TEXT_PDF layered page
|
||
|
|
pdftk A="${IN_TEXT_PDF}" B=".pdfsign_signed.pdf" \
|
||
|
|
cat ${RANGES} \
|
||
|
|
output ".pdfsign_concat.pdf"
|
||
|
|
|
||
|
|
rm ".pdfsign_signed.pdf"
|
||
|
|
|
||
|
|
# postprocess/compress pdf
|
||
|
|
gs ${GSFLAGS} \
|
||
|
|
-sPAPERSIZE=a4 -dFIXEDMEDIA -dPDFFitPage \
|
||
|
|
-dCompatibilityLevel=1.7 -dPDFSETTINGS=/ebook \
|
||
|
|
-sOutputFile="${OUT_PDF}" \
|
||
|
|
".pdfsign_concat.pdf"
|
||
|
|
|
||
|
|
rm ".pdfsign_concat.pdf"
|