Compare commits

..
Author SHA1 Message Date
Anthony Stirling 2b173a030e text fixing 2025-12-03 10:48:13 +00:00
Anthony Stirling c5525d8676 Merge remote-tracking branch 'origin/V2' into latex2 2025-12-03 09:07:42 +00:00
74 changed files with 236 additions and 699 deletions
-1
View File
@@ -5,7 +5,6 @@ frontend/dist
frontend/build
frontend/.vite
frontend/.tauri
frontend/src-tauri/target
# Gradle build artifacts
.gradle
@@ -180,7 +180,7 @@ jobs:
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./docker/embedded/Dockerfile
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ needs.check-comment.outputs.pr_number }}
build-args: VERSION_TAG=alpha
+2 -2
View File
@@ -262,7 +262,7 @@ jobs:
strategy:
fail-fast: false
matrix:
docker-rev: ["docker/embedded/Dockerfile", "docker/embedded/Dockerfile.ultra-lite", "docker/embedded/Dockerfile.fat"]
docker-rev: ["Dockerfile", "Dockerfile.ultra-lite", "Dockerfile.fat"]
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
@@ -301,7 +301,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./${{ matrix.docker-rev }}
file: ./docker/backend/${{ matrix.docker-rev }}
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
+9 -10
View File
@@ -5,7 +5,6 @@ on:
push:
branches:
- V2-master
- alljavadocker
# cancel in-progress jobs if a new job is triggered
# This is useful to avoid running multiple builds for the same branch if a new commit is pushed
@@ -94,10 +93,10 @@ jobs:
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}
type=raw,value=latest
- name: Generate tags for latest (alljavadocker branch - test)
- name: Generate tags for latest (V2-demo branch - test)
id: meta-test
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
if: github.ref == 'refs/heads/alljavadocker'
if: github.ref == 'refs/heads/V2-demo'
with:
images: |
ghcr.io/stirling-tools/stirling-pdf-test
@@ -111,7 +110,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./docker/embedded/Dockerfile
file: ./docker/Dockerfile.unified
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
@@ -150,10 +149,10 @@ jobs:
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat
type=raw,value=latest-fat
- name: Generate tags for latest-fat (alljavadocker branch - test)
- name: Generate tags for latest-fat (V2-demo branch - test)
id: meta-fat-test
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
if: github.ref == 'refs/heads/alljavadocker'
if: github.ref == 'refs/heads/V2-demo'
with:
images: |
ghcr.io/stirling-tools/stirling-pdf-test
@@ -167,7 +166,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./docker/embedded/Dockerfile.fat
file: ./docker/Dockerfile.unified
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
@@ -204,10 +203,10 @@ jobs:
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite
type=raw,value=latest-ultra-lite
- name: Generate tags for ultra-lite (alljavadocker branch - test)
- name: Generate tags for ultra-lite (V2-demo branch - test)
id: meta-lite-test
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
if: github.ref == 'refs/heads/alljavadocker'
if: github.ref == 'refs/heads/V2-demo'
with:
images: |
ghcr.io/stirling-tools/stirling-pdf-test
@@ -221,7 +220,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./docker/embedded/Dockerfile.ultra-lite
file: ./docker/Dockerfile.unified-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
+3 -3
View File
@@ -107,7 +107,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./docker/embedded/Dockerfile
file: ./Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
@@ -152,7 +152,7 @@ jobs:
if: github.ref != 'refs/heads/main'
with:
context: .
file: ./docker/embedded/Dockerfile.ultra-lite
file: ./Dockerfile.ultra-lite
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
@@ -183,7 +183,7 @@ jobs:
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./docker/embedded/Dockerfile.fat
file: ./Dockerfile.fat
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
+1 -1
View File
@@ -66,7 +66,7 @@ jobs:
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./docker/embedded/Dockerfile
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
@@ -39,7 +39,6 @@ public class RequestUriUtils {
// Specific static files bundled with the frontend
if (normalizedUri.equals("/robots.txt")
|| normalizedUri.equals("/favicon.ico")
|| normalizedUri.equals("/manifest.json")
|| normalizedUri.equals("/site.webmanifest")
|| normalizedUri.equals("/manifest-classic.json")
|| normalizedUri.equals("/index.html")) {
@@ -31,12 +31,10 @@ import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.JobOwnershipService;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.WebResponseUtils;
import stirling.software.proprietary.security.config.PremiumEndpoint;
@Slf4j
@ConvertApi
@RequiredArgsConstructor
@PremiumEndpoint
public class ConvertPdfJsonController {
private final PdfJsonConversionService pdfJsonConversionService;
@@ -1,61 +1,20 @@
package stirling.software.SPDF.controller.web;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import jakarta.servlet.http.HttpServletRequest;
@Controller
public class ReactRoutingController {
@Value("${server.servlet.context-path:/}")
private String contextPath;
@GetMapping(value = {"/", "/index.html"}, produces = MediaType.TEXT_HTML_VALUE)
public ResponseEntity<String> serveIndexHtml(HttpServletRequest request)
throws IOException {
ClassPathResource resource = new ClassPathResource("static/index.html");
try (InputStream inputStream = resource.getInputStream()) {
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
// Replace %BASE_URL% with the actual context path for base href
String baseUrl = contextPath.endsWith("/") ? contextPath : contextPath + "/";
html = html.replace("%BASE_URL%", baseUrl);
// Also rewrite any existing <base> tag (Vite may have baked one in)
html =
html.replaceFirst(
"<base href=\\\"[^\\\"]*\\\"\\s*/?>",
"<base href=\\\"" + baseUrl + "\\\" />");
// Inject context path as a global variable for API calls
String contextPathScript =
"<script>window.STIRLING_PDF_API_BASE_URL = '" + baseUrl + "';</script>";
html = html.replace("</head>", contextPathScript + "</head>");
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(html);
}
@GetMapping(
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js)[^\\.]*$}")
public String forwardRootPaths() {
return "forward:/index.html";
}
@GetMapping(
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|manifest.*\\.json|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*$}")
public ResponseEntity<String> forwardRootPaths(HttpServletRequest request)
throws IOException {
return serveIndexHtml(request);
}
@GetMapping(
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
public ResponseEntity<String> forwardNestedPaths(HttpServletRequest request)
throws IOException {
return serveIndexHtml(request);
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
public String forwardNestedPaths() {
return "forward:/index.html";
}
}
@@ -324,14 +324,10 @@ public class SecurityConfiguration {
.authenticated());
// Handle User/Password Logins
if (securityProperties.isUserPass()) {
// v2: Authentication is handled via API (/api/v1/auth/login), not form login
// We configure form login to handle Spring Security redirects,
// but use /perform_login as the processing URL so /login remains a React route
http.formLogin(
formLogin ->
formLogin
.loginPage("/login") // Redirect here when unauthenticated
.loginProcessingUrl("/perform_login") // Process form posts here (not /login)
.loginPage("/login")
.successHandler(
new CustomAuthenticationSuccessHandler(
loginAttemptService,
-138
View File
@@ -1,138 +0,0 @@
# Stirling-PDF Dockerfile - Full version with embedded frontend
# Single JAR contains both frontend and backend
# Stage 1: Build application with embedded frontend
FROM gradle:8.14-jdk21 AS build
# Install Node.js and npm for frontend build
RUN apt-get update && apt-get install -y \
curl \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& npm --version \
&& node --version \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Copy gradle files for dependency resolution
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .
COPY gradle gradle/
COPY app/core/build.gradle core/.
COPY app/common/build.gradle common/.
COPY app/proprietary/build.gradle proprietary/.
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
# Set working directory
WORKDIR /app
# Copy entire project
COPY . .
# Build JAR with embedded frontend (includes security features controlled at runtime)
RUN DISABLE_ADDITIONAL_FEATURES=false \
STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
# Stage 2: Runtime image
FROM alpine:3.22.1
ARG VERSION_TAG
# Labels
LABEL org.opencontainers.image.title="Stirling-PDF"
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Full version"
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, API, Spring Boot, React"
# Copy scripts and fonts
COPY scripts /scripts
COPY app/core/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
# Copy built JAR from build stage
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
# Environment Variables
ENV VERSION_TAG=$VERSION_TAG \
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
PATH=$PATH:/opt/venv/bin \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# Install all dependencies
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
shadow \
su-exec \
openssl \
openssl-dev \
openjdk21-jre \
# Doc conversion
gcompat \
libc6-compat \
libreoffice \
ghostscript \
fontforge \
# pdftohtml
poppler-utils \
# OCR MY PDF
unpaper \
tesseract-ocr-data-eng \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
ocrmypdf \
# CV
py3-opencv \
python3 \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --upgrade pip setuptools && \
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
fc-cache -f -v && \
chmod +x /scripts/* && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /pipeline /usr/share/fonts/opentype/noto /configs /customFiles /tmp/stirling-pdf && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
-142
View File
@@ -1,142 +0,0 @@
# Stirling-PDF Dockerfile - Fat version with embedded frontend
# Single JAR contains both frontend and backend with extra fonts for air-gapped environments
# Stage 1: Build application with embedded frontend
FROM gradle:8.14-jdk21 AS build
# Install Node.js and npm for frontend build
RUN apt-get update && apt-get install -y \
curl \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& npm --version \
&& node --version \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Copy gradle files for dependency resolution
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .
COPY gradle gradle/
COPY app/core/build.gradle core/.
COPY app/common/build.gradle common/.
COPY app/proprietary/build.gradle proprietary/.
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
# Set working directory
WORKDIR /app
# Copy entire project
COPY . .
# Build JAR with embedded frontend (includes security features controlled at runtime)
RUN DISABLE_ADDITIONAL_FEATURES=false \
STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
# Stage 2: Runtime image
FROM alpine:3.22.1
ARG VERSION_TAG
# Labels
LABEL org.opencontainers.image.title="Stirling-PDF Fat"
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Fat version with extra fonts for air-gapped environments"
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, fat, air-gapped, API, Spring Boot, React"
# Copy scripts and fonts
COPY scripts /scripts
COPY app/core/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
# Copy built JAR from build stage
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
# Environment Variables
ENV VERSION_TAG=$VERSION_TAG \
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
FAT_DOCKER=true \
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
UNO_PATH=/usr/lib/libreoffice/program \
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
PATH=$PATH:/opt/venv/bin \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf
# Install all dependencies plus extra fonts for air-gapped environments
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
shadow \
su-exec \
openssl \
openssl-dev \
openjdk21-jre \
# Doc conversion
gcompat \
libc6-compat \
libreoffice \
ghostscript \
fontforge \
# pdftohtml
poppler-utils \
# OCR MY PDF
unpaper \
tesseract-ocr-data-eng \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-deu \
tesseract-ocr-data-fra \
tesseract-ocr-data-por \
ocrmypdf \
# Extra fonts for fat version
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \
# CV
py3-opencv \
python3 \
py3-pip \
py3-pillow@testing \
py3-pdf2image@testing && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --upgrade pip setuptools && \
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
mv /usr/share/tessdata /usr/share/tessdata-original && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
fc-cache -f -v && \
chmod +x /scripts/* && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /pipeline /usr/share/fonts/opentype/noto /configs /customFiles /tmp/stirling-pdf && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
-104
View File
@@ -1,104 +0,0 @@
# Stirling-PDF Dockerfile - Ultra-lite version with embedded frontend
# Single JAR contains both frontend and backend with minimal dependencies
# Stage 1: Build application with embedded frontend
FROM gradle:8.14-jdk21 AS build
# Install Node.js and npm for frontend build
RUN apt-get update && apt-get install -y \
curl \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& npm --version \
&& node --version \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Copy gradle files for dependency resolution
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .
COPY gradle gradle/
COPY app/core/build.gradle core/.
COPY app/common/build.gradle common/.
COPY app/proprietary/build.gradle proprietary/.
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
# Set working directory
WORKDIR /app
# Copy entire project
COPY . .
# Build ultra-lite JAR with embedded frontend (minimal features)
RUN DISABLE_ADDITIONAL_FEATURES=true \
STIRLING_PDF_DESKTOP_UI=false \
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
# Stage 2: Runtime image
FROM alpine:3.22.1
ARG VERSION_TAG
# Labels
LABEL org.opencontainers.image.title="Stirling-PDF Ultra-Lite"
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Ultra-lite version with minimal dependencies"
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.vendor="Stirling-Tools"
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
LABEL maintainer="Stirling-Tools"
LABEL org.opencontainers.image.authors="Stirling-Tools"
LABEL org.opencontainers.image.version="${VERSION_TAG}"
LABEL org.opencontainers.image.keywords="PDF, manipulation, ultra-lite, API, Spring Boot, React"
# Copy scripts
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
COPY scripts/installFonts.sh /scripts/installFonts.sh
# Copy built JAR from build stage
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
# Environment Variables
ENV VERSION_TAG=$VERSION_TAG \
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
PGID=1000 \
UMASK=022 \
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
TMPDIR=/tmp/stirling-pdf \
TEMP=/tmp/stirling-pdf \
TMP=/tmp/stirling-pdf \
ENDPOINTS_GROUPS_TO_REMOVE=CLI
# Install minimal dependencies
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
apk upgrade --no-cache -a && \
apk add --no-cache \
ca-certificates \
tzdata \
tini \
bash \
curl \
shadow \
su-exec \
openjdk21-jre && \
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
mkdir -p /usr/share/fonts/opentype/noto && \
chmod +x /scripts/*.sh && \
# User permissions
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /configs /customFiles /pipeline /tmp/stirling-pdf && \
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
EXPOSE 8080/tcp
# Set user and run command
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
CMD ["java", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=/tmp/stirling-pdf", "-jar", "/app.jar"]
+4 -4
View File
@@ -3,21 +3,21 @@
<head>
<meta charset="UTF-8" />
<base href="%BASE_URL%" />
<link rel="icon" href="modern-logo/favicon.ico" />
<link rel="icon" href="/modern-logo/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="The Free Adobe Acrobat alternative (10M+ Downloads)"
/>
<link rel="apple-touch-icon" href="modern-logo/logo192.png" />
<link rel="manifest" href="manifest.json" />
<link rel="apple-touch-icon" href="/modern-logo/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>Stirling PDF</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="src/index.tsx"></script>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
+147 -147
View File
@@ -11,26 +11,26 @@
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
"@dnd-kit/core": "^6.3.1",
"@embedpdf/core": "^1.5.0",
"@embedpdf/engines": "^1.5.0",
"@embedpdf/plugin-annotation": "^1.5.0",
"@embedpdf/plugin-bookmark": "^1.5.0",
"@embedpdf/plugin-export": "^1.5.0",
"@embedpdf/plugin-history": "^1.5.0",
"@embedpdf/plugin-interaction-manager": "^1.5.0",
"@embedpdf/plugin-loader": "^1.5.0",
"@embedpdf/plugin-pan": "^1.5.0",
"@embedpdf/plugin-print": "^1.5.0",
"@embedpdf/plugin-render": "^1.5.0",
"@embedpdf/plugin-rotate": "^1.5.0",
"@embedpdf/plugin-scroll": "^1.5.0",
"@embedpdf/plugin-search": "^1.5.0",
"@embedpdf/plugin-selection": "^1.5.0",
"@embedpdf/plugin-spread": "^1.5.0",
"@embedpdf/plugin-thumbnail": "^1.5.0",
"@embedpdf/plugin-tiling": "^1.5.0",
"@embedpdf/plugin-viewport": "^1.5.0",
"@embedpdf/plugin-zoom": "^1.5.0",
"@embedpdf/core": "^1.4.1",
"@embedpdf/engines": "^1.4.1",
"@embedpdf/plugin-annotation": "^1.4.1",
"@embedpdf/plugin-bookmark": "^1.4.1",
"@embedpdf/plugin-export": "^1.4.1",
"@embedpdf/plugin-history": "^1.4.1",
"@embedpdf/plugin-interaction-manager": "^1.4.1",
"@embedpdf/plugin-loader": "^1.4.1",
"@embedpdf/plugin-pan": "^1.4.1",
"@embedpdf/plugin-print": "^1.4.1",
"@embedpdf/plugin-render": "^1.4.1",
"@embedpdf/plugin-rotate": "^1.4.1",
"@embedpdf/plugin-scroll": "^1.4.1",
"@embedpdf/plugin-search": "^1.4.1",
"@embedpdf/plugin-selection": "^1.4.1",
"@embedpdf/plugin-spread": "^1.4.1",
"@embedpdf/plugin-thumbnail": "^1.4.1",
"@embedpdf/plugin-tiling": "^1.4.1",
"@embedpdf/plugin-viewport": "^1.4.1",
"@embedpdf/plugin-zoom": "^1.4.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iconify/react": "^6.0.2",
@@ -575,13 +575,13 @@
"license": "0BSD"
},
"node_modules/@embedpdf/core": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.5.0.tgz",
"integrity": "sha512-Yrh9XoVaT8cUgzgqpJ7hx5wg6BqQrCFirqqlSwVb+Ly9oNn4fZbR9GycIWmzJOU5XBnaOJjXfQSaDyoNP0woNA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.4.1.tgz",
"integrity": "sha512-TGpxn2CvAKRnOJWJ3bsK+dKBiCp75ehxftRUmv7wAmPomhnG5XrDfoWJungvO+zbbqAwso6PocdeXINVt3hlAw==",
"license": "MIT",
"dependencies": {
"@embedpdf/engines": "1.5.0",
"@embedpdf/models": "1.5.0"
"@embedpdf/engines": "1.4.1",
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"preact": "^10.26.4",
@@ -592,13 +592,13 @@
}
},
"node_modules/@embedpdf/engines": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.5.0.tgz",
"integrity": "sha512-/GzhjHFHWfOaX7vjgFJX/pyq668wYjoda1bZ9MpwF/EF000Wwy2Q0AOhprjldPFz8ASKjwKwqsXmaqrK99yOAQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.4.1.tgz",
"integrity": "sha512-yugIb5OwTI/1VnAaEvSYxAd2DvYBPkV/D7wytagyaOq98o3sqzcY2Q9zHt+LhnawA5KKG1e/FDPjCd4qm8gsvg==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0",
"@embedpdf/pdfium": "1.5.0"
"@embedpdf/models": "1.4.1",
"@embedpdf/pdfium": "1.4.1"
},
"peerDependencies": {
"preact": "^10.26.4",
@@ -609,31 +609,31 @@
}
},
"node_modules/@embedpdf/models": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.5.0.tgz",
"integrity": "sha512-x/1li3jdag+IzfZkcfRLKLqASLep4v6dgVi3z0JArwaicFra8k1IY2xaVTrwcZyx7pRb/rxvoO9yLHW0Y34NFw==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.4.1.tgz",
"integrity": "sha512-2nTg8Q1qpplBvspZJXMCZOA+/OILpfdNRPddlplxZXY/Upx0rzKXx/e6pXWW7AuOgtfGneT4h9tMs3A595/PdQ==",
"license": "MIT"
},
"node_modules/@embedpdf/pdfium": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.5.0.tgz",
"integrity": "sha512-PI32t2U4ThZC907n2Iwr8E5WqmC574G83u3V9ysNFl29N9kasrY9RiLSzU4W/yQvXPjIbpQHBsbMKXLjCFBI9w==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.4.1.tgz",
"integrity": "sha512-BekKEK4UNCwzj7xOffKn6WpL0FQHxq+mTj2iGI3N7OwAX2J/BO2G+rDOB+lvojQG+Dkpg8uqm427ZKJDRyLgVQ==",
"license": "MIT"
},
"node_modules/@embedpdf/plugin-annotation": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.5.0.tgz",
"integrity": "sha512-mxEPI6xYwOGaf9fYfoywuj6nwA10eHFPBuN066MzwphDk6DOHJGZ3Vq8zNQBXh20c/Lb25PL718D7MZWxZLUHg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.4.1.tgz",
"integrity": "sha512-d4HibNy6ecyDqx2Y2R8VjaqppSdjNofAJmU6VenOd88wn080sAUqvnkeVJ6ehJH5BoND4ymQrcAkcbVeYK0myA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0",
"@embedpdf/utils": "1.5.0"
"@embedpdf/models": "1.4.1",
"@embedpdf/utils": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-history": "1.5.0",
"@embedpdf/plugin-interaction-manager": "1.5.0",
"@embedpdf/plugin-selection": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-history": "1.4.1",
"@embedpdf/plugin-interaction-manager": "1.4.1",
"@embedpdf/plugin-selection": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -641,15 +641,15 @@
}
},
"node_modules/@embedpdf/plugin-bookmark": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-bookmark/-/plugin-bookmark-1.5.0.tgz",
"integrity": "sha512-s3C9PtVesy5X8Ds/C9TEElFiqfKGRklG/uNPTROpNoolfpi0h7qX2xqqh/9+FzKH2nHjVcPB7Pp432v16h7eRA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-bookmark/-/plugin-bookmark-1.4.1.tgz",
"integrity": "sha512-WnfBJdv+Eq5zsMfwDZ5RlXZMGpvKm/ccL6jlTVwtELBhu3wvhjjbBmZdheEOzHMC3VXMNYDMjCeaXkUG4nWoDA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -657,15 +657,15 @@
}
},
"node_modules/@embedpdf/plugin-export": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.5.0.tgz",
"integrity": "sha512-luk68mNW9l2X31qk4b02phKaqDl9aDXUAgHVz1EWrgwXQ3Oz9WEdu60utYARYDiepDo3Caadll8RwctYSf/anA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.4.1.tgz",
"integrity": "sha512-g89fREFM/zkt2Ai2Q5dWwDkhXgC/JmVyUniaMgm1fTG/MZ0Z05E7f34DUzX/CKcJyVjxEgl6tojBTMeUbm15bA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -674,15 +674,15 @@
}
},
"node_modules/@embedpdf/plugin-history": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.5.0.tgz",
"integrity": "sha512-p7PTNNaIr4gH3jLwX+eLJe1DeUXgi21kVGN6SRx/pocH8esg4jqoOeD/YiRRZoZnPOiy0jBXVhkPkwSmY7a2hQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.4.1.tgz",
"integrity": "sha512-5WLDiNMH6tACkLGGv/lJtNsDeozOhSbrh0mjD1btHun8u7Yscu/Vf8tdJRUOsd+nULivo2nQ2NFNKu0OTbVo8w==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -690,15 +690,15 @@
}
},
"node_modules/@embedpdf/plugin-interaction-manager": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.5.0.tgz",
"integrity": "sha512-ckHgTfvkW6c5Ta7Mc+Dl9C2foVnvEpqEJ84wyBnqrU0OWbe/jsiPhyKBVeartMGqNI/kVfaQTXupyrKhekAVmg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.4.1.tgz",
"integrity": "sha512-Ng02S9SFIAi9JZS5rI+NXSnZZ1Yk9YYRw4MlN2pig49qOyivZdz0oScZaYxQPewo8ccJkLeghjdeWswOBW/6cA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -707,15 +707,15 @@
}
},
"node_modules/@embedpdf/plugin-loader": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.5.0.tgz",
"integrity": "sha512-P4YpIZfaW69etYIjphyaL4cGl2pB14h3OdTE0tRQ2pZYZHFLTvlt4q9B3PVSdhlSrHK5nob7jfLGon2U7xCslg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.4.1.tgz",
"integrity": "sha512-m3ZOk8JygsLxoa4cZ+0BVB5pfRWuBCg2/gPqjhoFZNKTqAFw4J6HGUrhYKg94GRYe+w1cTJl/NbTBYuU5DOrsA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -724,17 +724,17 @@
}
},
"node_modules/@embedpdf/plugin-pan": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-pan/-/plugin-pan-1.5.0.tgz",
"integrity": "sha512-EMQ08dHqLkZmFVuLOO6h3AAinFPQoA1r6OlL9z+p0sswq31JAgd4X7+xjYIpI01z/V3+cTzPHzp7qwob5E4tbA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-pan/-/plugin-pan-1.4.1.tgz",
"integrity": "sha512-zmOZJ9dUqXiaV0F5GPf/5WTWf3jAEkiv153Tl3x8HT9Rfff+WQhV48NruCIBAy/T4jVt4aH7D1zt/B/ftvcdkA==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-interaction-manager": "1.5.0",
"@embedpdf/plugin-viewport": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-interaction-manager": "1.4.1",
"@embedpdf/plugin-viewport": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -743,15 +743,15 @@
}
},
"node_modules/@embedpdf/plugin-print": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-print/-/plugin-print-1.5.0.tgz",
"integrity": "sha512-rjorvNxAZfO9X4cFZVU9fHnldMWqMceJGmr3mH+yj7KdHePvNDDP+omyZyZKtxlUZENaeDI2h6k5z0GbhBz6sQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-print/-/plugin-print-1.4.1.tgz",
"integrity": "sha512-YEjU6rQVW8wb125JXl1wma95+JISwADZpfqZZOtvPBRABp6ce4byblDTNjWVmTYWSgKUZrlXCL3Ff3Ig+bjbjw==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=18.0.0",
"react-dom": ">=18.0.0",
@@ -760,15 +760,15 @@
}
},
"node_modules/@embedpdf/plugin-render": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.5.0.tgz",
"integrity": "sha512-ywwSj0ByrlkvrJIHKRzqxARkOZriki8VJUC+T4MV8fGyF4CzvCRJyKlPktahFz+VxhoodqTh7lBCib68dH+GvA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.4.1.tgz",
"integrity": "sha512-gKCdNKw6WBHBEpTc2DLBWIWOxzsNnaNbpfeY6C4f2Bum0EO+XW3Hl2oIx1uaRHjIhhnXso1J3QweqelsPwDGwg==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -777,15 +777,15 @@
}
},
"node_modules/@embedpdf/plugin-rotate": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.5.0.tgz",
"integrity": "sha512-5EmBCsq0VfrE3xWY6ofuVm8S6aK95EbAycRIk1wczcmTdvpsuXZ6P2ZaECUgYMcpZ6uAg4/kGf8X8VVZuCihSQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.4.1.tgz",
"integrity": "sha512-hVzHkKwMNH3tUhxqJGsj5qTLpYZXbj6E74AEcG0w/fz5FrK7EnofPqt0gRfYmIzxnQGIh+39BRtcp8gmx8UNnw==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -794,16 +794,16 @@
}
},
"node_modules/@embedpdf/plugin-scroll": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.5.0.tgz",
"integrity": "sha512-RNmTZCZ8X1mA8cw9M7TMDuhO9GtkOalGha2bBL3En3D1IlDRS7PzNNMSMV7eqT7OQICSTltlpJ8p8Qi5esvL/Q==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.4.1.tgz",
"integrity": "sha512-Y9O+matB4j4fLim5s/jn7qIi+lMC9vmDJRpJhiWe8bvD9oYLP2xfD/DdhFgAjRKcNhPoxC+j8q8QN5BMeGAv2Q==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-viewport": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-viewport": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -812,16 +812,16 @@
}
},
"node_modules/@embedpdf/plugin-search": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.5.0.tgz",
"integrity": "sha512-TB5b0H8Iobx/azVUBIlG2ClaKtf0y3/Xi3E/iB8BwvkIE2+g6EGfp8IMXIn8WDXST6bbvJEP31Ab0Ilp6SVkiw==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.4.1.tgz",
"integrity": "sha512-8JG4CbOcUsLuT0vHJJ4cECmu+Yn53EokWFUVXi2Mo/XvHjhrQuWmD7+y6s/qQPEpctFYWmUCXTDAX9ynPud+2Q==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-loader": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-loader": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -830,17 +830,17 @@
}
},
"node_modules/@embedpdf/plugin-selection": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.5.0.tgz",
"integrity": "sha512-zrxLBAZQoPswDuf9q9DrYaQc6B0Ysc2U1hueTjNH/4+ydfl0BFXZkKR63C2e3YmWtXvKjkoIj0GyPzsiBORLUw==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.4.1.tgz",
"integrity": "sha512-lo5Ytk1PH0PrRKv6zKVupm4t02VGsqIrnSIeP6NO8Ujx0wfqEhj//sqIuO/EwfFVJD8lcQIP9UUo9y8baCrEog==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-interaction-manager": "1.5.0",
"@embedpdf/plugin-viewport": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-interaction-manager": "1.4.1",
"@embedpdf/plugin-viewport": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -849,16 +849,16 @@
}
},
"node_modules/@embedpdf/plugin-spread": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.5.0.tgz",
"integrity": "sha512-3EU5Cp+fPQSiMjvMR/P2kXxXry/RlnxHLs4JeskAaH95QcqWW3VD+DrHkWSiLFkdhI18rNNGNlMc5RvDGvbXGQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.4.1.tgz",
"integrity": "sha512-l+SrDVGTiiItkt2cEtzv7V/X5HhmLbYHcQ8CFobGeIKdJtzKS1Nu/JSKqg7Ki7eCNgyPL1yMNfNE92bNKYVN4w==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-loader": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-loader": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -867,16 +867,16 @@
}
},
"node_modules/@embedpdf/plugin-thumbnail": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.5.0.tgz",
"integrity": "sha512-Z2qpyyr5s2M6460KDGu1Vk6rdbQFIoCpnyFAT6e7UaTIKkqJSNpmjqMsBU5PosYCFu/cClpHPvS7tg9/IKAk6g==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.4.1.tgz",
"integrity": "sha512-bN3msjI0PovazgbPK3LyugYVTwIDo0RyBUhBaG42FgJxeY3hmFOWTPgfUH1QF7twHlySnksIvHRFYR3nViryVw==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-render": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-render": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -885,18 +885,18 @@
}
},
"node_modules/@embedpdf/plugin-tiling": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.5.0.tgz",
"integrity": "sha512-0Vx9elHNpMM+zv8hEoZXBEm8Q0+4kU52LxOlTYRr1A5FskF836sUct6g1ngwK1bmfbAfpz+62PnYI2EeilDZig==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.4.1.tgz",
"integrity": "sha512-wgTfj5T8HV6KP61iiR63DVNrbVp8sPxTqa1Sm+2/D0jY+EPSSCmpt1/qYWiAXd1X+t78foOjCnfbo7fEMn5/pg==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-render": "1.5.0",
"@embedpdf/plugin-scroll": "1.5.0",
"@embedpdf/plugin-viewport": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-render": "1.4.1",
"@embedpdf/plugin-scroll": "1.4.1",
"@embedpdf/plugin-viewport": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -905,15 +905,15 @@
}
},
"node_modules/@embedpdf/plugin-viewport": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.5.0.tgz",
"integrity": "sha512-G8GDyYRhfehw72+r4qKkydnA5+AU8qH67g01Y12b0DzI0VIzymh/05Z4dK8DsY3jyWPXJfw2hlg5+KDHaMBHgQ==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.4.1.tgz",
"integrity": "sha512-+TgFHKPCLTBiDYe2DdsmTS37hwQgcZ3dYIc7bE0l5cp+GVwouu1h0MTmjL+90loizeWwCiu10E/zXR6hz+CUaQ==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0"
"@embedpdf/models": "1.4.1"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/core": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -922,19 +922,19 @@
}
},
"node_modules/@embedpdf/plugin-zoom": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.5.0.tgz",
"integrity": "sha512-LiDkCd5/IXg2CRORl1Yikan2op+AYXSxhHzCFatyBdwzVj+n4y9I74OwCI62Mar8WDAIMyXZDCQxGPToSm+zDw==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.4.1.tgz",
"integrity": "sha512-9HocmXnPZxqN06q7kyNAmLjgDHOEW8/8QfgNE3nMpRyNHIgnAjxvsWc9lApgp5ErDPG0cSDt0Cduil6nB3wSBQ==",
"license": "MIT",
"dependencies": {
"@embedpdf/models": "1.5.0",
"@embedpdf/models": "1.4.1",
"hammerjs": "^2.0.8"
},
"peerDependencies": {
"@embedpdf/core": "1.5.0",
"@embedpdf/plugin-interaction-manager": "1.5.0",
"@embedpdf/plugin-scroll": "1.5.0",
"@embedpdf/plugin-viewport": "1.5.0",
"@embedpdf/core": "1.4.1",
"@embedpdf/plugin-interaction-manager": "1.4.1",
"@embedpdf/plugin-scroll": "1.4.1",
"@embedpdf/plugin-viewport": "1.4.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -943,9 +943,9 @@
}
},
"node_modules/@embedpdf/utils": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.5.0.tgz",
"integrity": "sha512-L6jsAPQPGM8ne+MMFAd5gqXb1RNEgNyh16VvVUVKcVnJlBhwil59nVeEQ0cwPhjF5qVeY6MQDIOjBzJqkgXOYg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.4.1.tgz",
"integrity": "sha512-vvJ51Qsz3PyJWR2YvDMMpJXg4+YqdV7Vn2cusmW9sx+4EnAiBiw0HevEE+FepgFV8k+A0WbwXzmsujDIQJ7R4A==",
"license": "MIT",
"peerDependencies": {
"preact": "^10.26.4",
+20 -20
View File
@@ -7,26 +7,26 @@
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
"@dnd-kit/core": "^6.3.1",
"@embedpdf/core": "^1.5.0",
"@embedpdf/engines": "^1.5.0",
"@embedpdf/plugin-annotation": "^1.5.0",
"@embedpdf/plugin-bookmark": "^1.5.0",
"@embedpdf/plugin-export": "^1.5.0",
"@embedpdf/plugin-history": "^1.5.0",
"@embedpdf/plugin-interaction-manager": "^1.5.0",
"@embedpdf/plugin-loader": "^1.5.0",
"@embedpdf/plugin-pan": "^1.5.0",
"@embedpdf/plugin-print": "^1.5.0",
"@embedpdf/plugin-render": "^1.5.0",
"@embedpdf/plugin-rotate": "^1.5.0",
"@embedpdf/plugin-scroll": "^1.5.0",
"@embedpdf/plugin-search": "^1.5.0",
"@embedpdf/plugin-selection": "^1.5.0",
"@embedpdf/plugin-spread": "^1.5.0",
"@embedpdf/plugin-thumbnail": "^1.5.0",
"@embedpdf/plugin-tiling": "^1.5.0",
"@embedpdf/plugin-viewport": "^1.5.0",
"@embedpdf/plugin-zoom": "^1.5.0",
"@embedpdf/core": "^1.4.1",
"@embedpdf/engines": "^1.4.1",
"@embedpdf/plugin-annotation": "^1.4.1",
"@embedpdf/plugin-bookmark": "^1.4.1",
"@embedpdf/plugin-export": "^1.4.1",
"@embedpdf/plugin-history": "^1.4.1",
"@embedpdf/plugin-interaction-manager": "^1.4.1",
"@embedpdf/plugin-loader": "^1.4.1",
"@embedpdf/plugin-pan": "^1.4.1",
"@embedpdf/plugin-print": "^1.4.1",
"@embedpdf/plugin-render": "^1.4.1",
"@embedpdf/plugin-rotate": "^1.4.1",
"@embedpdf/plugin-scroll": "^1.4.1",
"@embedpdf/plugin-search": "^1.4.1",
"@embedpdf/plugin-selection": "^1.4.1",
"@embedpdf/plugin-spread": "^1.4.1",
"@embedpdf/plugin-thumbnail": "^1.4.1",
"@embedpdf/plugin-tiling": "^1.4.1",
"@embedpdf/plugin-viewport": "^1.4.1",
"@embedpdf/plugin-zoom": "^1.4.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iconify/react": "^6.0.2",
@@ -33,7 +33,7 @@ import MergeTypeIcon from '@mui/icons-material/MergeType';
import CallSplitIcon from '@mui/icons-material/CallSplit';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { Rnd } from 'react-rnd';
import NavigationWarningModal from '@core/components/shared/NavigationWarningModal';
import NavigationWarningModal from '@app/components/shared/NavigationWarningModal';
import {
PdfTextEditorViewData,
@@ -2147,14 +2147,20 @@ const selectionToolbarPosition = useMemo(() => {
// Determine text wrapping behavior based on whether text has been changed
const hasChanges = changed;
const widthExtended = resolvedWidth - baseWidth > 0.5;
const enableWrap = isParagraphLayout || widthExtended || isEditing || hasChanges;
// Only enable wrapping if:
// 1. It's paragraph layout (multi-line groups should wrap)
// 2. Width was manually extended (user explicitly made space for wrapping)
// 3. Has changes AND was already wrapping (preserve existing wrap state)
// DO NOT enable wrapping just because isEditing - text should only wrap when it actually overflows
const wasWrapping = isParagraphLayout || widthExtended;
const enableWrap = wasWrapping || (hasChanges && wasWrapping);
const whiteSpace = enableWrap ? 'pre-wrap' : 'pre';
const wordBreak = enableWrap ? 'break-word' : 'normal';
const overflowWrap = enableWrap ? 'break-word' : 'normal';
// For paragraph mode, allow height to grow to accommodate lines without wrapping
// For single-line mode, maintain fixed height based on PDF bounds
const useFlexibleHeight = isEditing || enableWrap || (isParagraphLayout && lineCount > 1);
const useFlexibleHeight = enableWrap || (isParagraphLayout && lineCount > 1);
// The renderGroupContainer wrapper adds 4px horizontal padding (2px left + 2px right)
// We need to add this to the container width to compensate, so the inner content
@@ -97,10 +97,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
strategy: ScrollStrategy.Vertical,
initialPage: 0,
}),
createPluginRegistration(RenderPluginPackage, {
withForms: true,
withAnnotations: true,
}),
createPluginRegistration(RenderPluginPackage),
// Register interaction manager (required for zoom and selection features)
createPluginRegistration(InteractionManagerPluginPackage),
@@ -85,10 +85,7 @@ export function LocalEmbedPDFWithAnnotations({
strategy: ScrollStrategy.Vertical,
initialPage: 0,
}),
createPluginRegistration(RenderPluginPackage, {
withForms: true,
withAnnotations: true,
}),
createPluginRegistration(RenderPluginPackage),
// Register interaction manager (required for annotations)
createPluginRegistration(InteractionManagerPluginPackage),
+3 -20
View File
@@ -1,24 +1,7 @@
// Base path - read from <base> tag at runtime to support dynamic subpaths
// Falls back to Vite's BASE_URL for build-time subpaths
// Normalized: no trailing slash, empty string instead of '.' or '/'
function getBasePath(): string {
// Try to read from <base> tag first (runtime subpath support)
if (typeof document !== 'undefined') {
const baseElement = document.querySelector('base');
if (baseElement) {
const href = baseElement.getAttribute('href');
if (href && href !== '%BASE_URL%') {
return href.replace(/\/$/, '').replace(/^\.$/, '').replace(/^\/$/, '');
}
}
}
// Fall back to Vite's BASE_URL (build-time subpath)
return (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '').replace(/^\/$/, '');
}
export const BASE_PATH = getBasePath();
// Base path from Vite config - build-time constant, normalized (no trailing slash)
// When no subpath, use empty string instead of '.' to avoid relative path issues
export const BASE_PATH = (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '');
// EmbedPDF needs time to remove annotations internally before a recreation runs.
// Without the buffer we occasionally end up with duplicate annotations or stale image data.
@@ -110,7 +110,7 @@ export const TourOrchestrationProvider: React.FC<{ children: React.ReactNode }>
const loadSampleFile = useCallback(async () => {
try {
const response = await fetch('samples/Sample.pdf');
const response = await fetch('/samples/Sample.pdf');
const blob = await response.blob();
const file = new File([blob], 'Sample.pdf', { type: 'application/pdf' });
@@ -43,6 +43,7 @@ import CertSign from "@app/tools/CertSign";
import BookletImposition from "@app/tools/BookletImposition";
import Flatten from "@app/tools/Flatten";
import Rotate from "@app/tools/Rotate";
import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor";
import ChangeMetadata from "@app/tools/ChangeMetadata";
import Crop from "@app/tools/Crop";
import Sign from "@app/tools/Sign";
@@ -890,6 +891,23 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: RedactSingleStepSettings,
synonyms: getSynonyms(t, "redact")
},
pdfTextEditor: {
icon: <LocalIcon icon="edit-square-outline-rounded" width="1.5rem" height="1.5rem" />,
name: t("home.pdfTextEditor.title", "PDF Text Editor"),
component: PdfTextEditor,
description: t(
"home.pdfTextEditor.desc",
"Review and edit text and images in PDFs with grouped text editing and PDF regeneration"
),
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
subcategoryId: SubcategoryId.GENERAL,
maxFiles: 1,
endpoints: ["text-editor-pdf"],
synonyms: getSynonyms(t, "pdfTextEditor"),
supportsAutomate: false,
automationSettings: null,
versionStatus: "alpha",
},
};
const regularTools = {} as RegularToolRegistry;
@@ -10,7 +10,7 @@ import { CONVERSION_ENDPOINTS } from '@app/constants/convertConstants';
import apiClient from '@app/services/apiClient';
import { downloadBlob, downloadTextAsFile } from '@app/utils/downloadUtils';
import { getFilenameFromHeaders } from '@app/utils/fileResponseUtils';
import { pdfWorkerManager } from '@core/services/pdfWorkerManager';
import { pdfWorkerManager } from '@app/services/pdfWorkerManager';
import { Util } from 'pdfjs-dist/legacy/build/pdf.mjs';
import {
PdfJsonDocument,
+1
View File
@@ -54,6 +54,7 @@ export const CORE_REGULAR_TOOL_IDS = [
'replaceColor',
'showJS',
'bookletImposition',
'pdfTextEditor',
] as const;
export const CORE_SUPER_TOOL_IDS = [
+1
View File
@@ -97,6 +97,7 @@ export const URL_TO_TOOL_MAP: Record<string, ToolId> = {
'/automate': 'automate',
'/sign': 'sign',
'/add-text': 'addText',
'/pdf-text-editor': 'pdfTextEditor',
// Developer tools
'/dev-api': 'devApi',
@@ -335,7 +335,7 @@ export default function AdminGeneralSection() {
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
<img
src="classic-logo/favicon.ico"
src="/classic-logo/favicon.ico"
alt={t('admin.settings.general.logoStyle.classicAlt', 'Classic logo')}
style={{ width: '24px', height: '24px' }}
/>
@@ -348,7 +348,7 @@ export default function AdminGeneralSection() {
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
<img
src="modern-logo/StirlingPDFLogoNoTextLight.svg"
src="/modern-logo/StirlingPDFLogoNoTextLight.svg"
alt={t('admin.settings.general.logoStyle.modernAlt', 'Modern logo')}
style={{ width: '24px', height: '24px' }}
/>
@@ -1,13 +1,5 @@
import { useMemo } from "react";
import LocalIcon from "@app/components/shared/LocalIcon";
import { useTranslation } from "react-i18next";
import { getSynonyms } from "@app/utils/toolSynonyms";
import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor";
import {
SubcategoryId,
ToolCategoryId,
type ProprietaryToolRegistry,
} from "@app/data/toolsTaxonomy";
import { type ProprietaryToolRegistry } from "@app/data/toolsTaxonomy";
/**
* Hook that provides the proprietary tool registry.
@@ -16,26 +8,5 @@ import {
* and will be included in the main tool registry.
*/
export function useProprietaryToolRegistry(): ProprietaryToolRegistry {
const { t } = useTranslation();
return useMemo<ProprietaryToolRegistry>(() => ({
pdfTextEditor: {
icon: <LocalIcon icon="edit-square-outline-rounded" width="1.5rem" height="1.5rem" />,
name: t("home.pdfTextEditor.title", "PDF Text Editor"),
component: PdfTextEditor,
description: t(
"home.pdfTextEditor.desc",
"Review and edit text and images in PDFs with grouped text editing and PDF regeneration"
),
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
subcategoryId: SubcategoryId.GENERAL,
maxFiles: 1,
endpoints: ["text-editor-pdf"],
synonyms: getSynonyms(t, "pdfTextEditor"),
supportsAutomate: false,
automationSettings: null,
versionStatus: "alpha",
requiresPremium: true,
},
}), [t]);
return useMemo<ProprietaryToolRegistry>(() => ({}), []);
}
@@ -5,7 +5,6 @@
*/
export const PROPRIETARY_REGULAR_TOOL_IDS = [
'pdfTextEditor',
] as const;
export const PROPRIETARY_SUPER_TOOL_IDS = [
+1 -3
View File
@@ -8,9 +8,7 @@ import { ToolId } from '@app/types/toolId';
import { URL_TO_TOOL_MAP as CORE_URL_TO_TOOL_MAP } from '@core/utils/urlMapping';
// Proprietary URL mappings
const PROPRIETARY_URL_MAPPINGS: Record<string, ToolId> = {
'/pdf-text-editor': 'pdfTextEditor',
};
const PROPRIETARY_URL_MAPPINGS: Record<string, ToolId> = {};
// Merge core and proprietary mappings
export const URL_TO_TOOL_MAP: Record<string, ToolId> = {