mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
> [!WARNING] > Cooldown could not be applied because no publication date was available from the registry. > Bumps the eclipse-temurin group with 1 update in the /docker/backend directory: eclipse-temurin. Bumps the eclipse-temurin group with 1 update in the /docker/base directory: eclipse-temurin. Bumps the eclipse-temurin group with 1 update in the /docker/embedded directory: eclipse-temurin. Updates `eclipse-temurin` from `fbcf915` to `b4c93a5` Updates `eclipse-temurin` from `fbcf915` to `b4c93a5` Updates `eclipse-temurin` from `fbcf915` to `b4c93a5` Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
665 lines
31 KiB
Docker
665 lines
31 KiB
Docker
# Stirling-PDF Base Image
|
|
# Pre-built tools: Calibre, Ghostscript, QPDF, ImageMagick, Python venv with AI/OCR packages
|
|
# This image is reused by Dockerfile and Dockerfile.fat to avoid rebuilding tools on every app release
|
|
|
|
ARG TARGETPLATFORM
|
|
|
|
# Stage 1: Build and strip Calibre
|
|
FROM ubuntu:noble@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517 AS calibre-build
|
|
ARG TARGETPLATFORM
|
|
ARG CALIBRE_VERSION=9.13.0
|
|
ARG CALIBRE_STRIP_WEBENGINE=false
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
set -eux; \
|
|
apt-get update; \
|
|
apt-get install -y --no-install-recommends \
|
|
ca-certificates curl xz-utils libnss3 libfontconfig1 \
|
|
libgl1 libegl1 libdbus-1-3 libasound2t64 libxcomposite1 \
|
|
libxrandr2 libxkbcommon0 libxi6 libxtst6 libopengl0 \
|
|
poppler-utils tzdata; \
|
|
rm -rf /var/lib/apt/lists/*; \
|
|
\
|
|
case "$(uname -m)" in \
|
|
x86_64) CALIBRE_ARCH="x86_64" ;; \
|
|
aarch64) CALIBRE_ARCH="arm64" ;; \
|
|
*) echo "Unsupported arch: $(uname -m)"; exit 1 ;; \
|
|
esac; \
|
|
\
|
|
for attempt in 1 2 3; do \
|
|
echo "Downloading Calibre (attempt $attempt/3)..."; \
|
|
if curl -fsSL --max-time 300 --retry 3 --retry-delay 5 --retry-max-time 900 \
|
|
"https://download.calibre-ebook.com/${CALIBRE_VERSION}/calibre-${CALIBRE_VERSION}-${CALIBRE_ARCH}.txz" \
|
|
-o /tmp/calibre.txz; then \
|
|
break; \
|
|
fi; \
|
|
if [ $attempt -lt 3 ]; then sleep 10; fi; \
|
|
done; \
|
|
mkdir -p /opt/calibre; \
|
|
tar xJf /tmp/calibre.txz -C /opt/calibre; \
|
|
rm /tmp/calibre.txz; \
|
|
\
|
|
# We only need Qt6 WebEngine (Chromium) for ebook->PDF output.
|
|
# PDF INPUT now uses the pdftohtml engine (poppler), not Qt.
|
|
rm -f /opt/calibre/lib/libQt6Designer* \
|
|
/opt/calibre/lib/libQt6Multimedia* \
|
|
/opt/calibre/lib/libQt6SpatialAudio.so.* \
|
|
/opt/calibre/lib/libQt6NetworkAuth.so.* \
|
|
/opt/calibre/lib/libQt6Concurrent.so.* \
|
|
/opt/calibre/lib/libQt6OpenGLWidgets.so.* \
|
|
/opt/calibre/lib/libQt6QuickWidgets.so.* \
|
|
/opt/calibre/lib/libQt6Svg.so.* \
|
|
/opt/calibre/lib/libQt6SvgWidgets.so.* \
|
|
/opt/calibre/lib/libQt6Pdf*.so.* \
|
|
/opt/calibre/lib/libQt6ShaderTools.so.* \
|
|
/opt/calibre/lib/libQt6SerialPort.so.* \
|
|
/opt/calibre/lib/libQt6Sensors.so.* \
|
|
/opt/calibre/lib/libQt6Test.so.* \
|
|
/opt/calibre/lib/libQt6Sql.so.* \
|
|
/opt/calibre/lib/libQt6RemoteObjects.so.* \
|
|
/opt/calibre/lib/libQt6Help.so.* \
|
|
/opt/calibre/lib/libQt6VirtualKeyboard.so.* \
|
|
/opt/calibre/lib/libQt6WaylandClient.so.* \
|
|
/opt/calibre/lib/libQt6WaylandCompositor.so.* \
|
|
/opt/calibre/lib/libQt6Bluetooth.so.* \
|
|
/opt/calibre/lib/libQt6Nfc.so.* \
|
|
/opt/calibre/lib/libQt6Charts.so.* \
|
|
/opt/calibre/lib/libQt6DataVisualization.so.* \
|
|
/opt/calibre/lib/libQt6Scxml.so.* \
|
|
/opt/calibre/lib/libQt6StateMachine.so.* \
|
|
/opt/calibre/lib/libQt6TextToSpeech.so.* \
|
|
/opt/calibre/lib/libQt63D*.so.* \
|
|
/opt/calibre/lib/libavcodec.so.* \
|
|
/opt/calibre/lib/libavfilter.so.* \
|
|
/opt/calibre/lib/libavformat.so.* \
|
|
/opt/calibre/lib/libavutil.so.* \
|
|
/opt/calibre/lib/libavdevice.so.* \
|
|
/opt/calibre/lib/libpostproc.so.* \
|
|
/opt/calibre/lib/libswresample.so.* \
|
|
/opt/calibre/lib/libswscale.so.* \
|
|
/opt/calibre/lib/libspeex.so.* \
|
|
/opt/calibre/lib/libFLAC.so.* \
|
|
/opt/calibre/lib/libopus.so.* \
|
|
/opt/calibre/lib/libvorbis*.so.* \
|
|
/opt/calibre/lib/libasyncns.so.* \
|
|
/opt/calibre/lib/libspeechd.so.* \
|
|
/opt/calibre/lib/libespeak-ng.so.* \
|
|
/opt/calibre/lib/libonnxruntime.so.* \
|
|
/opt/calibre/lib/libgio-2.0.so.* \
|
|
/opt/calibre/lib/libzstd.so.* \
|
|
/opt/calibre/lib/libhunspell-1.7.so.* \
|
|
/opt/calibre/lib/libbrotlienc.so.* \
|
|
/opt/calibre/lib/libbrotlicommon.so.* \
|
|
/opt/calibre/lib/libbrotlidec.so.* \
|
|
/opt/calibre/lib/libstemmer.so.* \
|
|
/opt/calibre/lib/libmtp.so.* \
|
|
/opt/calibre/lib/libncursesw.so.* \
|
|
/opt/calibre/lib/libchm.so.* \
|
|
/opt/calibre/lib/libgcrypt.so.* \
|
|
/opt/calibre/lib/libgpg-error.so.* \
|
|
/opt/calibre/lib/libicuio.so.* \
|
|
/opt/calibre/lib/libreadline.so.* \
|
|
/opt/calibre/lib/libusb-1.0.so.* \
|
|
/opt/calibre/lib/libpulse*.so.* \
|
|
/opt/calibre/lib/libsndfile.so.* \
|
|
/opt/calibre/lib/libmpv.so.* \
|
|
/opt/calibre/lib/libass.so.* \
|
|
/opt/calibre/lib/librubberband.so.* \
|
|
/opt/calibre/lib/libsamplerate.so.*; \
|
|
rm -rf /opt/calibre/lib/qt6/plugins/platformthemes \
|
|
/opt/calibre/lib/qt6/plugins/multimedia \
|
|
/opt/calibre/lib/qt6/plugins/designer \
|
|
/opt/calibre/lib/qt6/plugins/virtualkeyboard \
|
|
/opt/calibre/lib/qt6/plugins/wayland* \
|
|
/opt/calibre/lib/qt6/plugins/texttospeech \
|
|
/opt/calibre/lib/qt6/plugins/position \
|
|
/opt/calibre/lib/qt6/plugins/sensors \
|
|
/opt/calibre/lib/qt6/plugins/sqldrivers \
|
|
/opt/calibre/lib/qt6/plugins/canbus \
|
|
/opt/calibre/lib/qt6/plugins/sceneparsers \
|
|
/opt/calibre/lib/qt6/plugins/renderers \
|
|
/opt/calibre/lib/qt6/plugins/geometryloaders \
|
|
/opt/calibre/lib/qt6/plugins/generic \
|
|
/opt/calibre/lib/qt6/plugins/qmltooling \
|
|
/opt/calibre/lib/qt6/libexec/QtWebEngineProcess.bak; \
|
|
rm -rf /opt/calibre/plugins/sqldrivers \
|
|
/opt/calibre/plugins/multimedia \
|
|
/opt/calibre/plugins/wayland-shell-integration \
|
|
/opt/calibre/plugins/wayland-graphics-integration-client \
|
|
/opt/calibre/plugins/wayland-decoration-client \
|
|
/opt/calibre/plugins/texttospeech \
|
|
/opt/calibre/plugins/platformthemes \
|
|
/opt/calibre/plugins/platforminputcontexts \
|
|
/opt/calibre/plugins/egldeviceintegrations \
|
|
/opt/calibre/plugins/iconengines; \
|
|
\
|
|
# Remove GUI executables but keep ebook-convert, ebook-meta, and calibre-parallel.
|
|
rm -f /opt/calibre/calibre \
|
|
/opt/calibre/calibre-server \
|
|
/opt/calibre/calibre-smtp \
|
|
/opt/calibre/calibre-debug \
|
|
/opt/calibre/calibre-customize \
|
|
/opt/calibre/calibredb \
|
|
/opt/calibre/ebook-viewer \
|
|
/opt/calibre/ebook-edit \
|
|
/opt/calibre/ebook-polish \
|
|
/opt/calibre/ebook-device \
|
|
/opt/calibre/fetch-ebook-metadata \
|
|
/opt/calibre/lrf2lrs \
|
|
/opt/calibre/lrs2lrf \
|
|
/opt/calibre/markdown-calibre \
|
|
/opt/calibre/web2disk; \
|
|
\
|
|
# Remove Python modules not needed for conversion.
|
|
rm -rf /opt/calibre/lib/calibre/gui2 \
|
|
/opt/calibre/lib/calibre/devices \
|
|
/opt/calibre/lib/calibre/library \
|
|
/opt/calibre/lib/calibre/db \
|
|
/opt/calibre/lib/calibre/srv \
|
|
/opt/calibre/lib/calibre/spell \
|
|
/opt/calibre/lib/calibre/live \
|
|
/opt/calibre/lib/calibre/utils/piper \
|
|
/opt/calibre/lib/calibre/utils/certgen.so \
|
|
/opt/calibre/lib/calibre/utils/https \
|
|
/opt/calibre/lib/calibre/utils/mdns; \
|
|
\
|
|
# Remove resources not needed for CLI conversion.
|
|
rm -rf /opt/calibre/resources/images \
|
|
/opt/calibre/resources/icons \
|
|
/opt/calibre/resources/icons.rcc \
|
|
/opt/calibre/resources/content-server \
|
|
/opt/calibre/resources/editor* \
|
|
/opt/calibre/resources/viewer \
|
|
/opt/calibre/resources/viewer.js \
|
|
/opt/calibre/resources/viewer.html \
|
|
/opt/calibre/resources/recipes \
|
|
/opt/calibre/resources/dictionaries \
|
|
/opt/calibre/resources/hyphenation \
|
|
/opt/calibre/resources/catalog \
|
|
/opt/calibre/resources/calibre-mimetypes.xml \
|
|
/opt/calibre/resources/changelog.json \
|
|
/opt/calibre/resources/user-agent-data.json \
|
|
/opt/calibre/resources/builtin_recipes.zip \
|
|
/opt/calibre/resources/builtin_recipes.xml \
|
|
/opt/calibre/resources/stylelint-bundle.min.js \
|
|
/opt/calibre/resources/stylelint.js \
|
|
/opt/calibre/resources/rapydscript \
|
|
/opt/calibre/resources/quick_start \
|
|
/opt/calibre/resources/piper-voices.json \
|
|
/opt/calibre/resources/images.qrc \
|
|
/opt/calibre/resources/mozilla-ca-certs.pem \
|
|
/opt/calibre/resources/ebook-convert-complete.calibre_msgpack \
|
|
/opt/calibre/resources/mathjax \
|
|
/opt/calibre/resources/common-english-words.txt \
|
|
/opt/calibre/resources/calibre-portable.sh \
|
|
/opt/calibre/resources/calibre-portable.bat \
|
|
/opt/calibre/resources/metadata_sqlite.sql \
|
|
/opt/calibre/resources/notes_sqlite.sql \
|
|
/opt/calibre/resources/fts_sqlite.sql \
|
|
/opt/calibre/resources/fts_triggers.sql \
|
|
/opt/calibre/resources/jacket \
|
|
/opt/calibre/resources/editor-functions.json \
|
|
/opt/calibre/resources/calibre-ebook-root-CA.crt \
|
|
/opt/calibre/resources/csscolorparser.js \
|
|
/opt/calibre/resources/lookup.js \
|
|
/opt/calibre/resources/pdf-mathjax-loader.js \
|
|
/opt/calibre/resources/scraper.js \
|
|
/opt/calibre/resources/toc.js \
|
|
/opt/calibre/resources/user-manual-translation-stats.json \
|
|
/opt/calibre/resources/pin-template.svg \
|
|
/opt/calibre/resources/scripts.calibre_msgpack \
|
|
/opt/calibre/resources/fonts \
|
|
/opt/calibre/resources/qtwebengine_devtools_resources.pak \
|
|
/opt/calibre/lib/calibre/ebooks/docx/images \
|
|
/opt/calibre/share \
|
|
/opt/calibre/man; \
|
|
\
|
|
# Remove translations and localization while keeping required libraries.
|
|
# Keep iso639.calibre_msgpack (required)
|
|
# Keep only en-US.pak from qtwebengine_locales (required for WebEngine)
|
|
rm -rf /opt/calibre/lib/qt6/translations; \
|
|
if [ -d /opt/calibre/translations ]; then \
|
|
find /opt/calibre/translations -mindepth 1 -maxdepth 1 ! -name 'qtwebengine_locales' -exec rm -rf {} + 2>/dev/null || true; \
|
|
if [ -d /opt/calibre/translations/qtwebengine_locales ]; then \
|
|
find /opt/calibre/translations/qtwebengine_locales -type f ! -name 'en-US.pak' -delete 2>/dev/null || true; \
|
|
fi; \
|
|
fi; \
|
|
if [ -d /opt/calibre/resources/localization ]; then \
|
|
rm -rf /opt/calibre/resources/localization/locales.zip \
|
|
/opt/calibre/resources/localization/stats.calibre_msgpack \
|
|
/opt/calibre/resources/localization/website-languages.txt; \
|
|
find /opt/calibre/resources/localization -mindepth 1 -maxdepth 1 ! -name 'iso639.calibre_msgpack' -exec rm -rf {} +; \
|
|
fi; \
|
|
\
|
|
# Strip debug symbols from calibre extension modules.
|
|
# Exclude Qt6 libs: libQt6WebEngineCore and friends embed Chromium V8 JIT code
|
|
# and internal resource blobs that strip corrupts, causing segfaults at render time.
|
|
find /opt/calibre/lib -name '*.so*' \
|
|
! -name 'libQt6*' \
|
|
-exec strip --strip-unneeded {} + 2>/dev/null || true; \
|
|
\
|
|
# Remove Python bytecode caches.
|
|
find /opt/calibre -type d -name __pycache__ \
|
|
-exec rm -rf {} + 2>/dev/null || true; \
|
|
find /opt/calibre -name '*.pyc' -delete 2>/dev/null || true; \
|
|
\
|
|
# Verify conversion still works
|
|
# NOTE: txt->epub used intentionally NOT txt->pdf.
|
|
# Calibre 7+ uses WebEngine (Chromium) for PDF output, which requires kernel
|
|
# capabilities unavailable in Docker RUN steps and segfaults under QEMU.
|
|
# epub output exercises the same Python/plugin stack without touching WebEngine.
|
|
/opt/calibre/ebook-convert --version; \
|
|
echo "Hello" > /tmp/test.txt; \
|
|
/opt/calibre/ebook-convert /tmp/test.txt /tmp/test.epub; \
|
|
rm -f /tmp/test.txt /tmp/test.epub; \
|
|
\
|
|
# Verify pdftohtml (poppler) is available for the pdftohtml PDF engine.
|
|
pdftohtml -v >/dev/null 2>&1 && echo "pdftohtml OK" || { echo "ERROR: pdftohtml not found"; exit 1; }; \
|
|
echo "=== Calibre stripped successfully ==="
|
|
|
|
# Optional: strip Chromium/WebEngine (~80 MB savings) when PDF output via Calibre is not needed.
|
|
# Build with --build-arg CALIBRE_STRIP_WEBENGINE=true to enable.
|
|
RUN if [ "${CALIBRE_STRIP_WEBENGINE}" = "true" ]; then \
|
|
echo "Stripping Calibre WebEngine (Chromium), PDF output via Calibre will be disabled"; \
|
|
rm -rf /opt/calibre/lib/qt6/libexec/QtWebEngineProcess \
|
|
/opt/calibre/lib/qt6/resources \
|
|
/opt/calibre/lib/libQt6WebEngine*.so.* \
|
|
/opt/calibre/lib/libQt6Quick*.so.* \
|
|
/opt/calibre/lib/libQt6Qml*.so.* \
|
|
/opt/calibre/translations/qtwebengine_locales 2>/dev/null || true; \
|
|
echo "WebEngine stripped, Calibre PDF output disabled"; \
|
|
else \
|
|
echo "CALIBRE_STRIP_WEBENGINE=false, keeping WebEngine for PDF output"; \
|
|
fi
|
|
|
|
|
|
# Stage 2: Build Ghostscript from source
|
|
FROM ubuntu:noble@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517 AS gs-build
|
|
ARG TARGETPLATFORM
|
|
ARG GS_VERSION=10.07.1
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
--mount=type=cache,target=/tmp/gs-build,id=gs-build-${TARGETPLATFORM:-local} \
|
|
apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential curl ca-certificates libfontconfig1-dev && rm -rf /var/lib/apt/lists/* && \
|
|
GS_TAG="gs$(printf '%s' "${GS_VERSION}" | tr -d '.')" && \
|
|
cd /tmp/gs-build && \
|
|
rm -rf ghostscript-* && \
|
|
(test -d "ghostscript-${GS_VERSION}" || curl -fsSL --max-time 300 --retry 3 --retry-delay 5 "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/${GS_TAG}/ghostscript-${GS_VERSION}.tar.gz" | tar xz) && \
|
|
cd "ghostscript-${GS_VERSION}" && \
|
|
./configure \
|
|
--prefix=/usr/local \
|
|
--without-x \
|
|
--disable-cups \
|
|
--disable-gtk && \
|
|
make -j"$(nproc)" && \
|
|
make install && \
|
|
cd ..
|
|
|
|
|
|
# Stage 3: Build PDF Tools (QPDF and ImageMagick 7)
|
|
FROM ubuntu:noble@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517 AS pdf-tools-build
|
|
ARG TARGETPLATFORM
|
|
ARG QPDF_VERSION=12.4.0
|
|
ARG IM_VERSION=7.1.2-29
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
--mount=type=cache,target=/tmp/pdf-tools-build,id=pdf-tools-${TARGETPLATFORM:-local} \
|
|
apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential cmake libssl-dev libjpeg-dev zlib1g-dev curl ca-certificates pkg-config \
|
|
libpng-dev libtiff-dev libwebp-dev libxml2-dev libfreetype6-dev liblcms2-dev libzip-dev liblqr-1-0-dev \
|
|
libltdl-dev libtool && rm -rf /var/lib/apt/lists/* && \
|
|
cd /tmp/pdf-tools-build && \
|
|
rm -rf qpdf-* ImageMagick-* && \
|
|
# Build QPDF
|
|
(test -d "qpdf-${QPDF_VERSION}" || curl -fsSL --max-time 300 --retry 3 --retry-delay 5 "https://github.com/qpdf/qpdf/releases/download/v${QPDF_VERSION}/qpdf-${QPDF_VERSION}.tar.gz" | tar xz) && \
|
|
cd "qpdf-${QPDF_VERSION}" && \
|
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DALLOW_CRYPTO_OPENSSL=ON -DDEFAULT_CRYPTO=openssl && \
|
|
cmake --build build --parallel "$(nproc)" && \
|
|
cmake --install build --strip && \
|
|
cd .. && \
|
|
# Build ImageMagick 7
|
|
(test -d "ImageMagick-${IM_VERSION}" || curl -fsSL --max-time 300 --retry 3 --retry-delay 5 "https://github.com/ImageMagick/ImageMagick/archive/refs/tags/${IM_VERSION}.tar.gz" | tar xz) && \
|
|
cd "ImageMagick-${IM_VERSION}" && \
|
|
./configure --prefix=/usr/local --with-modules --with-perl=no --with-magick-plus-plus=no --with-quantum-depth=16 --disable-static --enable-shared && \
|
|
make -j"$(nproc)" && \
|
|
make install-strip && \
|
|
# Enable PDF/PS/EPS in policy
|
|
sed -i 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /usr/local/etc/ImageMagick-7/policy.xml && \
|
|
sed -i 's/rights="none" pattern="PS"/rights="read|write" pattern="PS"/' /usr/local/etc/ImageMagick-7/policy.xml && \
|
|
sed -i 's/rights="none" pattern="EPS"/rights="read|write" pattern="EPS"/' /usr/local/etc/ImageMagick-7/policy.xml && \
|
|
cd .. && \
|
|
ldconfig /usr/local/lib
|
|
|
|
# Stage ImageMagick outputs into a single directory so runtime can import them with one COPY
|
|
# (reduces 4 separate COPY layers to 1 independent --link layer).
|
|
RUN mkdir -p /magick-export/usr/bin \
|
|
/magick-export/usr/local/lib \
|
|
/magick-export/usr/local/etc && \
|
|
cp /usr/local/bin/magick /magick-export/usr/bin/ && \
|
|
cp -a /usr/local/lib/libMagick*.so* /magick-export/usr/local/lib/ && \
|
|
cp -a /usr/local/lib/ImageMagick-7* /magick-export/usr/local/lib/ && \
|
|
cp -a /usr/local/etc/ImageMagick-7 /magick-export/usr/local/etc/
|
|
|
|
|
|
# Stage 4: Build Python venv
|
|
FROM ubuntu:noble@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517 AS python-venv-build
|
|
ARG TARGETPLATFORM
|
|
ARG UNOSERVER_VERSION=3.7
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 python3-venv ca-certificates binutils && \
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \
|
|
python3 -m venv /opt/venv --system-site-packages && \
|
|
/opt/venv/bin/pip install --no-cache-dir --prefer-binary --only-binary=:all: \
|
|
weasyprint pdf2image opencv-python-headless ocrmypdf \
|
|
cryptography \
|
|
"unoserver==${UNOSERVER_VERSION}" && \
|
|
find /opt/venv -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true && \
|
|
find /opt/venv \( -name '*.pyc' -o -name '*.pyi' \) -delete 2>/dev/null || true && \
|
|
rm -rf /opt/venv/lib/python*/site-packages/pip \
|
|
/opt/venv/lib/python*/site-packages/pip-*.dist-info \
|
|
/opt/venv/lib/python*/site-packages/setuptools \
|
|
/opt/venv/lib/python*/site-packages/setuptools-*.dist-info && \
|
|
find /opt/venv -name '*.so' -exec strip --strip-unneeded {} + 2>/dev/null || true
|
|
|
|
|
|
# Final runtime image - the actual base image
|
|
FROM eclipse-temurin:25-jre-noble@sha256:b4c93a50fc67612798db73d68ca3b0ee4ebdd51736e59cca370e689b9797037e AS runtime
|
|
|
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive \
|
|
LANG=C.UTF-8 \
|
|
LC_ALL=C.UTF-8 \
|
|
TESS_BASE_PATH=/usr/share/tesseract-ocr/5/tessdata
|
|
|
|
ARG UNOSERVER_VERSION=3.7
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
set -eux; \
|
|
apt-get update; \
|
|
# Add LibreOffice Fresh PPA for latest version (26.2.x)
|
|
apt-get install -y --no-install-recommends software-properties-common; \
|
|
add-apt-repository -y ppa:libreoffice/ppa; \
|
|
apt-get update; \
|
|
apt-get install -y --no-install-recommends \
|
|
# Core tools
|
|
ca-certificates tzdata tini bash fontconfig curl \
|
|
# ffmpeg disabled due to raised CVEs
|
|
# ffmpeg \
|
|
poppler-utils fontforge \
|
|
unpaper pngquant \
|
|
# Fonts: full coverage for standard + fat variants
|
|
fonts-dejavu \
|
|
fonts-liberation2 \
|
|
fonts-crosextra-caladea fonts-crosextra-carlito \
|
|
fonts-noto-core fonts-noto-mono fonts-noto-extra \
|
|
fonts-noto-cjk poppler-data \
|
|
fonts-freefont-ttf fonts-terminus \
|
|
# Python runtime & UNO bridge
|
|
python3 python3-uno python3-packaging \
|
|
# OCR
|
|
tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu tesseract-ocr-fra \
|
|
tesseract-ocr-por tesseract-ocr-chi-sim \
|
|
# Tesseract OSD for orientation detection
|
|
tesseract-ocr-osd \
|
|
# Graphics / AWT headless
|
|
libcairo2 libpango-1.0-0 libpangoft2-1.0-0 libgdk-pixbuf-2.0-0 \
|
|
libfreetype6 libfontconfig1 libx11-6 libxt6 libxext6 libxrender1 \
|
|
libxtst6 libxi6 libxinerama1 libxkbcommon0 libsm6 libice6 \
|
|
# Qt/EGL for Calibre CLI
|
|
libegl1 libgl1 libopengl0 libdbus-1-3 libglib2.0-0 libnss3 \
|
|
libasound2t64 libxcomposite1 libxrandr2 \
|
|
# Virtual framebuffer (required for headless LibreOffice Impress/Draw)
|
|
xvfb x11-utils coreutils \
|
|
libreoffice-writer-nogui libreoffice-calc-nogui \
|
|
libreoffice-impress-nogui libreoffice-draw-nogui \
|
|
libreoffice-base-nogui libreoffice-java-common \
|
|
; \
|
|
\
|
|
# Verify and fix LibreOffice
|
|
libreoffice --version; \
|
|
soffice --version 2>/dev/null || true; \
|
|
# Rebuild UNO bridge type database
|
|
/usr/lib/libreoffice/program/soffice.bin --headless --convert-to pdf /dev/null 2>/dev/null || true; \
|
|
# Force font cache rebuild
|
|
fc-cache -f -v 2>&1 | awk 'NR <= 20'; \
|
|
\
|
|
# Cleanup stage.
|
|
\
|
|
# Remove PPA helper, no longer needed after apt-get update
|
|
apt-get remove --purge -y software-properties-common || true; \
|
|
apt-get autoremove --purge -y || true; \
|
|
rm -rf /var/lib/apt/lists/*; \
|
|
\
|
|
# Docs / man / info / icons / themes / GUI assets (headless server)
|
|
rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/info/* \
|
|
/usr/share/lintian/* /usr/share/linda/* \
|
|
/usr/share/icons/* /usr/share/themes/* \
|
|
/usr/share/javascript/* \
|
|
/usr/share/gtk-3.0/* \
|
|
/usr/share/fontforge/pixmaps \
|
|
/usr/share/fontforge/osx \
|
|
/usr/share/fontforge/cidmap \
|
|
/usr/share/fontforge/prefs \
|
|
/usr/share/liblangtag/* \
|
|
/usr/share/tcltk/* \
|
|
/usr/share/python-wheels/* \
|
|
/usr/share/glib-2.0/schemas/* \
|
|
/usr/share/mime/* \
|
|
/usr/share/xml/iso-codes \
|
|
/usr/share/GConf \
|
|
/usr/share/bash-completion \
|
|
/usr/share/zsh \
|
|
/usr/share/libmysofa \
|
|
/usr/share/alsa \
|
|
/usr/share/iso-codes \
|
|
/usr/share/perl5 \
|
|
/usr/share/libthai \
|
|
/usr/share/libexttextcat \
|
|
/usr/share/openal \
|
|
/usr/share/gcc; \
|
|
\
|
|
find /usr/share/locale -mindepth 1 -maxdepth 1 -type d \
|
|
! -name 'en*' -exec rm -rf {} + 2>/dev/null || true; \
|
|
rm -rf /usr/share/i18n/locales /usr/share/i18n/charmaps; \
|
|
\
|
|
rm -rf /usr/lib/libreoffice/share/gallery \
|
|
/usr/lib/libreoffice/share/template \
|
|
/usr/lib/libreoffice/share/wizards \
|
|
/usr/lib/libreoffice/share/autotext \
|
|
/usr/lib/libreoffice/help \
|
|
/usr/lib/libreoffice/share/config/images_*.zip \
|
|
/usr/lib/libreoffice/share/basic \
|
|
/usr/lib/libreoffice/share/Scripts \
|
|
/usr/lib/libreoffice/share/autocorr \
|
|
/usr/lib/libreoffice/share/classification \
|
|
/usr/lib/libreoffice/share/wordbook \
|
|
/usr/lib/libreoffice/share/fingerprint \
|
|
/usr/lib/libreoffice/share/xdg \
|
|
/usr/lib/libreoffice/share/numbertext \
|
|
/usr/lib/libreoffice/share/shell \
|
|
/usr/lib/libreoffice/share/palette \
|
|
/usr/lib/libreoffice/share/theme_definitions \
|
|
/usr/lib/libreoffice/share/xslt \
|
|
/usr/lib/libreoffice/share/labels \
|
|
/usr/lib/libreoffice/share/dtd \
|
|
/usr/lib/libreoffice/share/tipoftheday \
|
|
/usr/lib/libreoffice/share/toolbarmode \
|
|
/usr/lib/libreoffice/share/psprint \
|
|
/usr/lib/libreoffice/CREDITS.fodt \
|
|
/usr/lib/libreoffice/LICENSE.html; \
|
|
\
|
|
# Remove unused LO extensions (GUI-only; not needed for document conversion)
|
|
rm -rf /usr/lib/libreoffice/share/extensions/wiki-publisher \
|
|
/usr/lib/libreoffice/share/extensions/nlpsolver \
|
|
/usr/lib/libreoffice/share/extensions/dict-* 2>/dev/null || true; \
|
|
# Remove LO database components (LO Base; not needed for Writer/Calc/Impress conversion)
|
|
rm -rf /usr/lib/libreoffice/program/libdba* \
|
|
/usr/lib/libreoffice/program/libdbahsql* \
|
|
/usr/lib/libreoffice/program/libdbu* \
|
|
/usr/lib/libreoffice/program/libreport* 2>/dev/null || true; \
|
|
\
|
|
rm -rf /usr/lib/python3.12/test \
|
|
/usr/lib/python3.12/idlelib \
|
|
/usr/lib/python3.12/tkinter \
|
|
/usr/lib/python3.12/lib2to3 \
|
|
/usr/lib/python3.12/pydoc_data; \
|
|
\
|
|
rm -rf /usr/lib/python3/dist-packages/scipy \
|
|
/usr/lib/python3/dist-packages/sympy \
|
|
/usr/lib/python3/dist-packages/mpmath; \
|
|
\
|
|
rm -rf \
|
|
/usr/lib/python3/dist-packages/cffi \
|
|
/usr/lib/python3/dist-packages/cffi-*.dist-info \
|
|
/usr/lib/python3/dist-packages/_cffi_backend*.so \
|
|
/usr/lib/python3/dist-packages/_cffi_backend*.cpython*.so \
|
|
2>/dev/null || true; \
|
|
\
|
|
# Strip debug symbols from ALL shared libraries
|
|
find /usr/lib -name '*.so*' -type f \
|
|
-not -path '*/jvm/*' \
|
|
-not -path '*/libreoffice/*' \
|
|
-exec strip --strip-unneeded {} + 2>/dev/null || true; \
|
|
\
|
|
# Remove GPU backends not needed for headless operation.
|
|
MULTIARCH_LIBDIR=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null \
|
|
|| find /usr/lib -maxdepth 1 -type d -name '*-linux-gnu' | head -1); \
|
|
rm -f \
|
|
"${MULTIARCH_LIBDIR}"/libLLVM*.so* \
|
|
"${MULTIARCH_LIBDIR}"/libgallium*.so* \
|
|
2>/dev/null || true; \
|
|
\
|
|
find /usr/lib/python3* -type d -name __pycache__ \
|
|
-exec rm -rf {} + 2>/dev/null || true; \
|
|
find /usr/lib/python3* \( -name '*.pyc' -o -name '*.pyi' \) \
|
|
-delete 2>/dev/null || true; \
|
|
\
|
|
rm -rf /usr/share/bug /usr/share/lintian /usr/share/linda \
|
|
/var/lib/dpkg/info/*.list \
|
|
/var/lib/dpkg/info/*.md5sums \
|
|
/var/lib/dpkg/info/*.conffiles \
|
|
/var/lib/dpkg/info/*.postinst \
|
|
/var/lib/dpkg/info/*.preinst \
|
|
/var/lib/dpkg/info/*.prerm \
|
|
/var/lib/dpkg/info/*.postrm \
|
|
/var/lib/dpkg/info/*.triggers \
|
|
/var/lib/dpkg/info/*.shlibs \
|
|
/var/lib/dpkg/info/*.symbols \
|
|
/var/lib/dpkg/info/*.templates \
|
|
/var/log/dpkg.log /var/log/apt/* \
|
|
/usr/local/share/ghostscript/*/doc \
|
|
/usr/local/share/ghostscript/*/examples \
|
|
/usr/share/ImageMagick-*/doc \
|
|
/usr/share/ImageMagick-*/www; \
|
|
\
|
|
# Tesseract training configs (not needed for OCR, but keep configs/ for hocr/txt output)
|
|
rm -rf /usr/share/tesseract-ocr/*/tessdata/tessconfigs; \
|
|
\
|
|
# Noto fonts: keep Regular weight only (~370MB savings)
|
|
find /usr/share/fonts/truetype/noto -type f \
|
|
! -name '*Regular*' -delete 2>/dev/null || true; \
|
|
find /usr/share/fonts/opentype -type f \
|
|
! -name '*Regular*' -delete 2>/dev/null || true; \
|
|
# DejaVu: keep Regular and Bold only
|
|
find /usr/share/fonts/truetype -name '*DejaVu*' \
|
|
! -name '*-Regular*' ! -name '*-Bold*' ! -name '*Bold.ttf' \
|
|
! -name 'DejaVuSans.ttf' ! -name 'DejaVuSerif.ttf' ! -name 'DejaVuSansMono.ttf' \
|
|
-type f -delete 2>/dev/null || true; \
|
|
# Remove empty font directories after cleanup
|
|
find /usr/share/fonts -type d -empty -delete 2>/dev/null || true; \
|
|
\
|
|
# gconv: keep only essential charset conversion modules
|
|
GCONV_DIR=$(find /usr/lib -type d -name gconv 2>/dev/null | head -1); \
|
|
if [ -n "$GCONV_DIR" ] && [ -d "$GCONV_DIR" ]; then \
|
|
mkdir -p /tmp/gconv-keep; \
|
|
for mod in UTF-8.so UTF-16.so UTF-32.so UTF-7.so \
|
|
ISO8859-1.so ISO8859-15.so ISO8859-2.so ISO8859-9.so \
|
|
UNICODE.so CP1252.so CP1251.so CP1250.so \
|
|
EUC-JP.so EUC-KR.so EUC-CN.so \
|
|
SHIFT_JIS.so GB18030.so BIG5.so \
|
|
gconv-modules gconv-modules.d gconv-modules.cache; do \
|
|
[ -e "$GCONV_DIR/$mod" ] && \
|
|
cp -a "$GCONV_DIR/$mod" /tmp/gconv-keep/ 2>/dev/null || true; \
|
|
done; \
|
|
rm -rf "$GCONV_DIR"/*; \
|
|
cp -a /tmp/gconv-keep/* "$GCONV_DIR/" 2>/dev/null || true; \
|
|
rm -rf /tmp/gconv-keep; \
|
|
fi; \
|
|
\
|
|
# Misc caches
|
|
rm -rf /var/cache/fontconfig/* /tmp/*
|
|
|
|
# External tool layers, all use --link for independent layer caching and parallel pulls.
|
|
COPY --link --from=calibre-build /opt/calibre /opt/calibre
|
|
COPY --link --from=pdf-tools-build /usr/local/bin/qpdf /usr/bin/qpdf
|
|
# ImageMagick: 4 layers collapsed to 1 via the magick-export staging dir in pdf-tools-build
|
|
COPY --link --from=pdf-tools-build /magick-export/ /
|
|
COPY --link --from=gs-build /usr/local/bin/gs /usr/local/bin/gs
|
|
COPY --link --from=gs-build /usr/local/share/ghostscript /usr/local/share/ghostscript
|
|
# Python venv pre-built (no pip install at runtime, no build tools needed)
|
|
COPY --link --from=python-venv-build /opt/venv /opt/venv
|
|
|
|
RUN ldconfig /usr/local/lib && \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
/opt/venv/bin/python -c "import cffi; print('cffi OK:', cffi.__version__)" && \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
/opt/venv/bin/python -c "import cv2; print('OpenCV', cv2.__version__)" && \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
/opt/venv/bin/python -c "import ocrmypdf; print('ocrmypdf OK')" && \
|
|
find /opt/venv -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
# Non-root user
|
|
ARG PUID=1000
|
|
ARG PGID=1000
|
|
|
|
RUN set -eux; \
|
|
if ! getent group stirlingpdfgroup >/dev/null 2>&1; then \
|
|
groupadd -g "${PGID}" stirlingpdfgroup 2>/dev/null \
|
|
|| groupadd stirlingpdfgroup; \
|
|
fi; \
|
|
if ! id -u stirlingpdfuser >/dev/null 2>&1; then \
|
|
useradd -m -u "${PUID}" -g stirlingpdfgroup \
|
|
-d /home/stirlingpdfuser -s /bin/bash stirlingpdfuser 2>/dev/null \
|
|
|| useradd -m -g stirlingpdfgroup \
|
|
-d /home/stirlingpdfuser -s /bin/bash stirlingpdfuser; \
|
|
fi
|
|
|
|
# Application directories
|
|
RUN set -eux; \
|
|
mkdir -p /configs /configs/cache /configs/heap_dumps /logs /customFiles \
|
|
/pipeline/watchedFolders /pipeline/finishedFolders \
|
|
/storage \
|
|
/tmp/stirling-pdf/heap_dumps; \
|
|
chown -R stirlingpdfuser:stirlingpdfgroup \
|
|
/home/stirlingpdfuser /configs /logs /customFiles /pipeline /storage \
|
|
/tmp/stirling-pdf; \
|
|
chmod 750 /tmp/stirling-pdf; \
|
|
chmod 750 /tmp/stirling-pdf/heap_dumps
|
|
|
|
# Tool symlinks
|
|
RUN set -eux; \
|
|
ln -sf /opt/calibre/ebook-convert /usr/bin/ebook-convert; \
|
|
ln -sf /opt/venv/bin/unoconvert /usr/local/bin/unoconvert; \
|
|
ln -sf /opt/venv/bin/unoserver /usr/local/bin/unoserver; \
|
|
ln -sf /opt/venv/bin/ocrmypdf /usr/local/bin/ocrmypdf; \
|
|
ln -sf /opt/venv/bin/weasyprint /usr/local/bin/weasyprint; \
|
|
ln -sf /opt/venv/bin/unoping /usr/local/bin/unoping; \
|
|
fc-cache -f
|
|
|
|
# Metadata labels - base image
|
|
LABEL org.opencontainers.image.title="Stirling-PDF Base" \
|
|
org.opencontainers.image.description="Pre-built base image with Calibre, Ghostscript, QPDF, ImageMagick, LibreOffice, OCRmyPDF and dependencies" \
|
|
org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF" \
|
|
org.opencontainers.image.licenses="MIT" \
|
|
org.opencontainers.image.vendor="Stirling-Tools" \
|
|
org.opencontainers.image.url="https://www.stirlingpdf.com" \
|
|
org.opencontainers.image.documentation="https://docs.stirlingpdf.com" \
|
|
maintainer="Stirling-Tools" \
|
|
org.opencontainers.image.authors="Stirling-Tools"
|