Files

101 lines
4.0 KiB
Bash
Raw Permalink Normal View History

#!/bin/sh
# Lays out every module BentoPDF would otherwise fetch from a CDN under one
# directory, in the layout BentoPDF's own air-gap setup produces
# (scripts/prepare-airgap.sh upstream, its generated setup.sh). Runs in the
# Dockerfile's vendor stage.
#
# vendor.sh <bentopdf-source> <vendor.lock> <out-dir>
#
# Every package comes through `npm pack --json`, which verifies the tarball
# against the registry; this script then compares that integrity with the one
# committed in vendor.lock and fails the build on any difference, so a package
# cannot change under a pinned version unnoticed. A missing input fails too:
# nothing here skips.
set -eu
src=$1
lock=$2
out=$3
OCR_FONT_URL=https://rawcdn.githack.com/googlefonts/noto-fonts/ffebf8c1ee449e544955a7e813c54f9b73848eac/hinted/ttf/NotoSans/NotoSans-Regular.ttf
OCR_FONT_SHA256=b85c38ecea8a7cfb39c24e395a4007474fa5a4fc864f6ee33309eb4948d232d5
work=$(mktemp -d)
mkdir -p "$out/pymupdf" "$out/gs" "$out/cpdf" "$out/ocr/core" "$out/ocr/lang-data" \
"$out/ocr/fonts" "$out/embedpdf"
# Packs one spec into $work and checks it against the lock. Prints the tarball.
# Always called as an assignment (t=$(pack ...)): an exit inside a command
# substitution only ends the subshell, and only an assignment hands its status
# to `set -e`.
pack() {
spec=$1
want=$(awk -v s="$spec" '$1 == s { print $2 }' "$lock")
[ -n "$want" ] || { echo "vendor: $spec is not in $lock" >&2; exit 1; }
json=$(cd "$work" && npm pack --json --silent "$spec")
got=$(printf '%s' "$json" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const p=JSON.parse(s)[0];process.stdout.write(p.integrity+"\t"+p.filename)})')
[ "${got%% *}" = "$want" ] || {
echo "vendor: $spec integrity ${got%% *} differs from the lock's $want" >&2
exit 1
}
echo "$work/${got#* }"
}
# Unpacks a tarball's package/<sub> into a directory.
unpack() {
tarball=$1 sub=$2 dest=$3
tmp=$(mktemp -d)
tar -xzf "$tarball" -C "$tmp"
[ -e "$tmp/package/$sub" ] || { echo "vendor: $tarball has no package/$sub" >&2; exit 1; }
cp -R "$tmp/package/$sub"/. "$dest"/
rm -rf "$tmp"
}
# PyMuPDF and Ghostscript ship inside BentoPDF's repository, at the pinned commit.
set -- "$src"/bentopdf-airgap-bundle/bentopdf-pymupdf-wasm-*.tgz
[ -f "$1" ] || { echo "vendor: no PyMuPDF tarball in $src/bentopdf-airgap-bundle" >&2; exit 1; }
unpack "$1" . "$out/pymupdf"
set -- "$src"/bentopdf-airgap-bundle/bentopdf-gs-wasm-*.tgz
[ -f "$1" ] || { echo "vendor: no Ghostscript tarball in $src/bentopdf-airgap-bundle" >&2; exit 1; }
# Two loaders read this one directory: BentoPDF's own wants gs.js and gs.wasm
# at its root (upstream's air-gap layout copies only assets/), PyMuPDF's wrapper
# wants the package's dist/index.js under it - pdf-to-docx asked for
# /wasm/gs/dist/index.js and got a 404. The whole package, plus links at the
# root, serves both from one copy.
unpack "$1" . "$out/gs"
ln -s assets/gs.js "$out/gs/gs.js"
ln -s assets/gs.wasm "$out/gs/gs.wasm"
t=$(pack coherentpdf@2.5.5)
unpack "$t" dist "$out/cpdf"
tmp=$(mktemp -d)
t=$(pack tesseract.js@7.0.0)
tar -xzf "$t" -C "$tmp"
cp "$tmp/package/dist/worker.min.js" "$out/ocr/worker.min.js"
rm -rf "$tmp"
t=$(pack tesseract.js-core@7.0.0)
unpack "$t" . "$out/ocr/core"
for lang in deu fra ita eng; do
tmp=$(mktemp -d)
t=$(pack "@tesseract.js-data/$lang@1.0.0")
tar -xzf "$t" -C "$tmp"
cp "$tmp/package/4.0.0_best_int/$lang.traineddata.gz" "$out/ocr/lang-data/"
rm -rf "$tmp"
done
wget -q -O "$out/ocr/fonts/NotoSans-Regular.ttf" "$OCR_FONT_URL"
echo "$OCR_FONT_SHA256 $out/ocr/fonts/NotoSans-Regular.ttf" | sha256sum -c -s || {
echo "vendor: the OCR font's checksum differs from $OCR_FONT_SHA256" >&2
exit 1
}
# The PDF editor asks for <base>/fonts-latin@1.0.0/fonts/NotoSans-Regular.ttf.
for pkg in latin arabic hebrew; do
mkdir -p "$out/embedpdf/fonts-$pkg@1.0.0"
t=$(pack "@embedpdf/fonts-$pkg@1.0.0")
unpack "$t" . "$out/embedpdf/fonts-$pkg@1.0.0"
done
rm -rf "$work"
echo "vendor: $(find "$out" -type f | wc -l) files, $(du -sh "$out" | cut -f1)"