Merge branch 'Stirling-Tools:main' into main
This commit is contained in:
commit
7e4d8f45f6
253 changed files with 19791 additions and 18387 deletions
|
@ -1,2 +1,5 @@
|
|||
# Formatting
|
||||
5f771b785130154ed47952635b7acef371ffe0ec
|
||||
5f771b785130154ed47952635b7acef371ffe0ec
|
||||
|
||||
# Normalize files
|
||||
55d4fda01b2f39f5b7d7b4fda5214bd7ff0fd5dd
|
||||
|
|
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
|
@ -1,4 +1,18 @@
|
|||
# License Agreement for Contributions
|
||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under MPL 2.0 (Mozilla Public License Version 2.0) license.
|
||||
# Description
|
||||
|
||||
Please provide a summary of the changes, including relevant motivation and context.
|
||||
|
||||
Closes #(issue_number)
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] My changes generate no new warnings
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
|
||||
|
||||
(This does not change the general open-source nature of Stirling-PDF, simply moving from one license to another license)
|
||||
|
|
3
.github/workflows/pull_request_template.md
vendored
3
.github/workflows/pull_request_template.md
vendored
|
@ -1,3 +0,0 @@
|
|||
# License Agreement for Contributions
|
||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under MPL 2.0 (Mozilla Public License Version 2.0) license.
|
||||
(This does not change the open-source nature of Stirling-PDF, simply moving from one license to another license)
|
22
.github/workflows/push-docker.yml
vendored
22
.github/workflows/push-docker.yml
vendored
|
@ -3,7 +3,7 @@ name: Push Docker Image with VersionNumber
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
permissions:
|
||||
|
@ -15,13 +15,13 @@ jobs:
|
|||
steps:
|
||||
|
||||
- uses: actions/checkout@v3.5.2
|
||||
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3.11.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
|
||||
|
||||
- uses: gradle/gradle-build-action@v2.4.2
|
||||
env:
|
||||
|
@ -32,11 +32,11 @@ jobs:
|
|||
|
||||
- name: Make Gradle wrapper executable
|
||||
run: chmod +x gradlew
|
||||
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
|
@ -53,7 +53,7 @@ jobs:
|
|||
- name: Convert repository owner to lowercase
|
||||
id: repoowner
|
||||
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
||||
|
||||
|
||||
- name: Generate tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4.4.0
|
||||
|
@ -82,7 +82,7 @@ jobs:
|
|||
cache-to: type=gha,mode=max
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args:
|
||||
build-args:
|
||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
tags: |
|
||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
|
||||
|
||||
- name: Build and push Dockerfile-ultra-lite
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
|
@ -112,7 +112,7 @@ jobs:
|
|||
cache-to: type=gha,mode=max
|
||||
tags: ${{ steps.meta2.outputs.tags }}
|
||||
labels: ${{ steps.meta2.outputs.labels }}
|
||||
build-args:
|
||||
build-args:
|
||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
|
||||
|
@ -129,7 +129,7 @@ jobs:
|
|||
tags: |
|
||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
|
||||
|
||||
- name: Build and push Dockerfile-lite
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
|
@ -142,7 +142,7 @@ jobs:
|
|||
cache-to: type=gha,mode=max
|
||||
tags: ${{ steps.meta3.outputs.tags }}
|
||||
labels: ${{ steps.meta3.outputs.labels }}
|
||||
build-args:
|
||||
build-args:
|
||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
- name: Build and Push Helm Chart
|
||||
|
|
12
.github/workflows/releaseArtifacts.yml
vendored
12
.github/workflows/releaseArtifacts.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: Release Artifacts
|
||||
|
||||
on:
|
||||
release:
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
permissions:
|
||||
contents: write
|
||||
|
@ -19,13 +19,13 @@ jobs:
|
|||
file_suffix: ''
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.2
|
||||
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3.11.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
|
@ -42,11 +42,11 @@ jobs:
|
|||
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||
|
||||
|
||||
- name: Upload jar binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
|
|
6
.github/workflows/swagger.yml
vendored
6
.github/workflows/swagger.yml
vendored
|
@ -3,7 +3,7 @@ name: Update Swagger
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
push:
|
||||
|
@ -12,13 +12,13 @@ jobs:
|
|||
steps:
|
||||
|
||||
- uses: actions/checkout@v3.5.2
|
||||
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3.11.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
- name: Run Docker Compose Tests
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||
|
|
252
.gitignore
vendored
252
.gitignore
vendored
|
@ -1,127 +1,127 @@
|
|||
|
||||
|
||||
### Eclipse ###
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
.classpath
|
||||
.project
|
||||
version.properties
|
||||
pipeline/watchedFolders/
|
||||
pipeline/finishedFolders/
|
||||
#### Stirling-PDF Files ###
|
||||
customFiles/
|
||||
configs/
|
||||
watchedFolders/
|
||||
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
.lock
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
### Eclipse Patch ###
|
||||
# Spring Boot Tooling
|
||||
.sts4-cache/
|
||||
|
||||
### Git ###
|
||||
# Created by git for backups. To disable backups in Git:
|
||||
# $ git config --global mergetool.keepBackup false
|
||||
*.orig
|
||||
|
||||
# Created by git when using merge tools for conflicts
|
||||
*.BACKUP.*
|
||||
*.BASE.*
|
||||
*.LOCAL.*
|
||||
*.REMOTE.*
|
||||
*_BACKUP_*.txt
|
||||
*_BASE_*.txt
|
||||
*_LOCAL_*.txt
|
||||
*_REMOTE_*.txt
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
|
||||
/.vscode
|
||||
/.idea
|
||||
|
||||
# Ignore Mac DS_Store files
|
||||
.DS_Store
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
.classpath
|
||||
.project
|
||||
version.properties
|
||||
pipeline/watchedFolders/
|
||||
pipeline/finishedFolders/
|
||||
#### Stirling-PDF Files ###
|
||||
customFiles/
|
||||
configs/
|
||||
watchedFolders/
|
||||
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
.lock
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
### Eclipse Patch ###
|
||||
# Spring Boot Tooling
|
||||
.sts4-cache/
|
||||
|
||||
### Git ###
|
||||
# Created by git for backups. To disable backups in Git:
|
||||
# $ git config --global mergetool.keepBackup false
|
||||
*.orig
|
||||
|
||||
# Created by git when using merge tools for conflicts
|
||||
*.BACKUP.*
|
||||
*.BASE.*
|
||||
*.LOCAL.*
|
||||
*.REMOTE.*
|
||||
*_BACKUP_*.txt
|
||||
*_BASE_*.txt
|
||||
*_LOCAL_*.txt
|
||||
*_REMOTE_*.txt
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
|
||||
/.vscode
|
||||
/.idea
|
||||
|
||||
# Ignore Mac DS_Store files
|
||||
.DS_Store
|
||||
**/.DS_Store
|
116
Dockerfile
116
Dockerfile
|
@ -1,47 +1,69 @@
|
|||
# Use the base image
|
||||
FROM frooodle/stirling-pdf-base:version8
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
|
||||
|
||||
# Create user and group
|
||||
##RUN groupadd -g $PGID stirlingpdfgroup && \
|
||||
## useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
||||
## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /logs /customFiles /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
##&& \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original
|
||||
|
||||
# Copy necessary files
|
||||
COPY ./scripts/* /scripts/
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set font cache and permissions
|
||||
RUN fc-cache -f -v && chmod +x /scripts/*
|
||||
|
||||
##&& \
|
||||
## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
||||
## chmod +x /scripts/init.sh
|
||||
|
||||
# Expose necessary ports
|
||||
EXPOSE 8080
|
||||
|
||||
# Set user and run command
|
||||
##USER stirlingpdfuser
|
||||
ENTRYPOINT ["/scripts/init.sh"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
# Main stage
|
||||
FROM alpine:3.19.1
|
||||
|
||||
# JDK for app
|
||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||
echo "@testing 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 add --no-cache \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
tini \
|
||||
bash \
|
||||
curl \
|
||||
openjdk17-jre \
|
||||
# Doc conversion
|
||||
libreoffice@testing \
|
||||
# OCR MY PDF (unpaper for descew and other advanced featues)
|
||||
ocrmypdf \
|
||||
tesseract-ocr-data-eng \
|
||||
# CV
|
||||
py3-opencv \
|
||||
# python3/pip
|
||||
python3 && \
|
||||
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||
# uno unoconv and HTML
|
||||
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||
mv /usr/share/tessdata /usr/share/tessdata-original
|
||||
|
||||
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
COPY pipeline /pipeline
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Create user and group
|
||||
##RUN groupadd -g $PGID stirlingpdfgroup && \
|
||||
## useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
||||
## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME && \
|
||||
# Set up necessary directories and permissions
|
||||
RUN mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||
##&& \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \
|
||||
## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original && \
|
||||
# Set font cache and permissions
|
||||
fc-cache -f -v && \
|
||||
chmod +x /scripts/*
|
||||
## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
||||
## chmod +x /scripts/init.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Set user and run command
|
||||
##USER stirlingpdfuser
|
||||
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
|
|
@ -1,65 +1,61 @@
|
|||
# Build jbig2enc in a separate stage
|
||||
FROM bellsoft/liberica-openjdk-debian:17
|
||||
# use alpine
|
||||
FROM alpine:3.19.1
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
libreoffice-common \
|
||||
libreoffice-writer \
|
||||
libreoffice-calc \
|
||||
libreoffice-impress \
|
||||
unoconv && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY pipeline /pipeline
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||
echo "@testing 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 add --no-cache \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
tini \
|
||||
bash \
|
||||
curl \
|
||||
openjdk17-jre \
|
||||
# Doc conversion
|
||||
libreoffice@testing \
|
||||
# python and pip
|
||||
python3 && \
|
||||
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||
# uno unoconv and HTML
|
||||
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||
# Create user and group
|
||||
#RUN groupadd -g $PGID stirlingpdfgroup && \
|
||||
# useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
||||
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
|
||||
mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/fonts/opentype/noto /configs /customFiles
|
||||
|
||||
# Copy necessary files
|
||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set font cache and permissions
|
||||
RUN fc-cache -f -v && \
|
||||
chmod +x /scripts/init-without-ocr.sh && \
|
||||
chmod +x /scripts/download-security-jar.sh
|
||||
|
||||
|
||||
fc-cache -f -v && \
|
||||
chmod +x /scripts/*.sh
|
||||
# chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||
|
||||
|
||||
|
||||
|
||||
# Expose the application port
|
||||
EXPOSE 8080
|
||||
|
||||
# Set environment variables
|
||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF
|
||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=OpenCV,OCRmyPDF
|
||||
ENV DOCKER_ENABLE_SECURITY=false
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Run the application
|
||||
#USER stirlingpdfuser
|
||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
||||
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Build jbig2enc in a separate stage
|
||||
FROM bellsoft/liberica-openjdk-alpine:17
|
||||
# use alpine
|
||||
FROM alpine:3.19.1
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
|
@ -8,39 +8,44 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||
# PUID=1000 \
|
||||
# PUID=1000 \
|
||||
# PGID=1000 \
|
||||
# UMASK=022 \
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY pipeline /pipeline
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Create user and group using Alpine's addgroup and adduser
|
||||
#RUN addgroup -g $PGID stirlingpdfgroup && \
|
||||
# adduser -u $PUID -G stirlingpdfgroup -s /bin/sh -D stirlingpdfuser && \
|
||||
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
#RUN mkdir -p /scripts /configs /customFiles && \
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
|
||||
RUN mkdir -p /scripts /usr/share/fonts/opentype/noto /configs /customFiles
|
||||
COPY ./scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY ./scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY ./pipeline/ /pipeline/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||
RUN mkdir /configs /logs /customFiles && \
|
||||
# Set font cache and permissions
|
||||
#RUN chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||
|
||||
RUN chmod +x /scripts/init-without-ocr.sh && \
|
||||
chmod +x /scripts/download-security-jar.sh && \
|
||||
apk add --no-cache curl
|
||||
|
||||
# Expose the application port
|
||||
EXPOSE 8080
|
||||
chmod +x /scripts/*.sh && \
|
||||
apk add --no-cache \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
tini \
|
||||
bash \
|
||||
curl \
|
||||
openjdk17-jre && \
|
||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||
echo "@testing 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
|
||||
|
||||
# Set environment variables
|
||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
||||
|
||||
ENTRYPOINT ["/scripts/init-without-ocr.sh"]
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||
|
||||
# Run the application
|
||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
# Main stage
|
||||
FROM ubuntu:latest AS base
|
||||
|
||||
|
||||
|
||||
# JDK for app
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
openjdk-17-jre && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Doc conversion
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
libreoffice-common \
|
||||
libreoffice-writer \
|
||||
libreoffice-calc \
|
||||
libreoffice-impress \
|
||||
python3-uno \
|
||||
curl \
|
||||
unoconv && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# OCR MY PDF (unpaper for descew and other advanced featues)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common gnupg2 && \
|
||||
add-apt-repository ppa:alex-p/tesseract-ocr5 && apt install -y --no-install-recommends tesseract-ocr && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ghostscript \
|
||||
python3-pip \
|
||||
ocrmypdf \
|
||||
unpaper && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
mv /usr/share/tesseract-ocr /usr/share/tesseract-ocr-original && \
|
||||
pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir --upgrade ocrmypdf && \
|
||||
pip install --no-cache-dir --upgrade pillow==10.0.1 reportlab==3.6.13 wheel==0.38.1 setuptools==65.5.1 pyjwt==2.4.0 cryptography==39.0.1
|
||||
|
||||
|
||||
#CV and HTML
|
||||
RUN pip install --no-cache-dir opencv-python-headless WeasyPrint
|
|
@ -1,46 +1,46 @@
|
|||
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
||||
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
||||
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
||||
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||
| crop | ✔️ | | | | | | | | | ✔️ | |
|
||||
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
||||
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
||||
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
||||
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
||||
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
||||
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
||||
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
||||
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
||||
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
||||
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
||||
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
||||
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
||||
| compare | | | | ✔️ | | | | | | | ✔️ |
|
||||
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
||||
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
||||
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
||||
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
||||
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
||||
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
||||
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
||||
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||
| crop | ✔️ | | | | | | | | | ✔️ | |
|
||||
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
||||
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
||||
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
||||
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
||||
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
||||
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
||||
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
||||
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
||||
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
||||
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
||||
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
||||
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
||||
| compare | | | | ✔️ | | | | | | | ✔️ |
|
||||
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
||||
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
||||
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
||||
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
||||
| sign | | | | ✔️ | | | | | | | ✔️ |
|
|
@ -9,13 +9,13 @@ Fork Stirling-PDF and make a new branch out of Main
|
|||
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
||||
|
||||
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html
|
||||
and add a flag svg file to
|
||||
and add a flag svg file to
|
||||
https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
||||
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
||||
If your language isnt represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
||||
|
||||
|
||||
For example to add Polish you would add
|
||||
For example to add Polish you would add
|
||||
```html
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||
|
@ -23,7 +23,7 @@ For example to add Polish you would add
|
|||
```
|
||||
The data-language-code is the code used to reference the file in the next step.
|
||||
|
||||
Start by copying the existing english property file
|
||||
Start by copying the existing english property file
|
||||
|
||||
[https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Please update your tesseract docker volume path version from 4.00 to 5
|
|||
|
||||
## How does the OCR Work
|
||||
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition.
|
||||
All credit goes to them for this awesome work!
|
||||
All credit goes to them for this awesome work!
|
||||
|
||||
## Language Packs
|
||||
|
||||
|
@ -21,13 +21,13 @@ Depending on your requirements, you can choose the appropriate language pack for
|
|||
### Installing Language Packs
|
||||
|
||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/5/tessdata` (Debian) or `/usr/share/tesseract/tessdata` (Fedora)
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
||||
|
||||
# DO NOT REMOVE EXISTING ENG.TRAINEDDATA, IT'S REQUIRED.
|
||||
|
||||
#### Docker
|
||||
|
||||
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
||||
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
||||
#### Docker Compose
|
||||
Modify your `docker-compose.yml` file to include the following volume configuration:
|
||||
|
||||
|
@ -37,14 +37,14 @@ services:
|
|||
your_service_name:
|
||||
image: your_docker_image_name
|
||||
volumes:
|
||||
- /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata
|
||||
- /location/of/trainingData:/usr/share/tessdata
|
||||
```
|
||||
|
||||
|
||||
#### Docker run
|
||||
Add the following to your existing docker run command
|
||||
```bash
|
||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata
|
||||
-v /location/of/trainingData:/usr/share/tessdata
|
||||
```
|
||||
|
||||
#### Non-Docker
|
||||
|
|
88
Jenkinsfile
vendored
88
Jenkinsfile
vendored
|
@ -1,45 +1,45 @@
|
|||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'chmod 755 gradlew'
|
||||
sh './gradlew build'
|
||||
}
|
||||
}
|
||||
stage('Docker Build') {
|
||||
steps {
|
||||
script {
|
||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||
def image = "frooodle/s-pdf:$appVersion"
|
||||
sh "docker build -t $image ."
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Docker Push') {
|
||||
steps {
|
||||
script {
|
||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||
def image = "frooodle/s-pdf:$appVersion"
|
||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||
sh "docker push $image"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Helm Push') {
|
||||
steps {
|
||||
script {
|
||||
//TODO: Read chartVersion from Chart.yaml
|
||||
def chartVersion = '1.0.0'
|
||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||
sh "helm package chart/stirling-pdf"
|
||||
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'chmod 755 gradlew'
|
||||
sh './gradlew build'
|
||||
}
|
||||
}
|
||||
stage('Docker Build') {
|
||||
steps {
|
||||
script {
|
||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||
def image = "frooodle/s-pdf:$appVersion"
|
||||
sh "docker build -t $image ."
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Docker Push') {
|
||||
steps {
|
||||
script {
|
||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||
def image = "frooodle/s-pdf:$appVersion"
|
||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||
sh "docker push $image"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Helm Push') {
|
||||
steps {
|
||||
script {
|
||||
//TODO: Read chartVersion from Chart.yaml
|
||||
def chartVersion = '1.0.0'
|
||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||
sh "helm package chart/stirling-pdf"
|
||||
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ sudo apt-get update
|
|||
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip
|
||||
```
|
||||
|
||||
For Fedora-based systems use this command:
|
||||
For Fedora-based systems use this command:
|
||||
|
||||
```bash
|
||||
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-17-openjdk python3 python3-pip
|
||||
|
@ -95,14 +95,14 @@ For Debian-based systems, you can use the following command:
|
|||
|
||||
```bash
|
||||
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
```
|
||||
|
||||
For Fedora:
|
||||
|
||||
```bash
|
||||
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||
```
|
||||
|
||||
### Step 4: Clone and Build Stirling-PDF
|
||||
|
@ -139,8 +139,8 @@ Easiest is to use the langpacks provided by your repositories. Skip the other st
|
|||
Manual:
|
||||
|
||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tesseract-ocr/5/tessdata`
|
||||
3.
|
||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
||||
3.
|
||||
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
||||
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
||||
|
||||
|
@ -269,5 +269,5 @@ You can do this in the terminal by using the `export` command or -D arguements t
|
|||
```bash
|
||||
export APP_HOME_NAME="Stirling PDF"
|
||||
or
|
||||
-DAPP_HOME_NAME="Stirling PDF"
|
||||
-DAPP_HOME_NAME="Stirling PDF"
|
||||
```
|
||||
|
|
|
@ -110,7 +110,7 @@ Docker Run
|
|||
```bash
|
||||
docker run -d \
|
||||
-p 8080:8080 \
|
||||
-v /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata \
|
||||
-v /location/of/trainingData:/usr/share/tessdata \
|
||||
-v /location/of/extraConfigs:/configs \
|
||||
-v /location/of/logs:/logs \
|
||||
-e DOCKER_ENABLE_SECURITY=false \
|
||||
|
@ -131,7 +131,7 @@ services:
|
|||
ports:
|
||||
- '8080:8080'
|
||||
volumes:
|
||||
- /location/of/trainingData:/usr/share/tesseract-ocr/5/tessdata #Required for extra OCR languages
|
||||
- /location/of/trainingData:/usr/share/tessdata #Required for extra OCR languages
|
||||
- /location/of/extraConfigs:/configs
|
||||
# - /location/of/customFiles:/customFiles/
|
||||
# - /location/of/logs:/logs/
|
||||
|
|
|
@ -1,64 +1,64 @@
|
|||
|Technology | Ultra-Lite | Lite | Full |
|
||||
|----------------|:----------:|:----:|:----:|
|
||||
| Java | ✔️ | ✔️ | ✔️ |
|
||||
| JavaScript | ✔️ | ✔️ | ✔️ |
|
||||
| Libre | | ✔️ | ✔️ |
|
||||
| Python | | | ✔️ |
|
||||
| OpenCV | | | ✔️ |
|
||||
| OCRmyPDF | | | ✔️ |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Operation | Ultra-Lite | Lite | Full
|
||||
--------------------|------------|------|-----
|
||||
add-page-numbers | ✔️ | ✔️ | ✔️
|
||||
add-password | ✔️ | ✔️ | ✔️
|
||||
add-image | ✔️ | ✔️ | ✔️
|
||||
add-watermark | ✔️ | ✔️ | ✔️
|
||||
adjust-contrast | ✔️ | ✔️ | ✔️
|
||||
auto-split-pdf | ✔️ | ✔️ | ✔️
|
||||
auto-redact | ✔️ | ✔️ | ✔️
|
||||
auto-rename | ✔️ | ✔️ | ✔️
|
||||
cert-sign | ✔️ | ✔️ | ✔️
|
||||
crop | ✔️ | ✔️ | ✔️
|
||||
change-metadata | ✔️ | ✔️ | ✔️
|
||||
change-permissions | ✔️ | ✔️ | ✔️
|
||||
compare | ✔️ | ✔️ | ✔️
|
||||
extract-page | ✔️ | ✔️ | ✔️
|
||||
extract-images | ✔️ | ✔️ | ✔️
|
||||
flatten | ✔️ | ✔️ | ✔️
|
||||
get-info-on-pdf | ✔️ | ✔️ | ✔️
|
||||
img-to-pdf | ✔️ | ✔️ | ✔️
|
||||
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
||||
merge-pdfs | ✔️ | ✔️ | ✔️
|
||||
multi-page-layout | ✔️ | ✔️ | ✔️
|
||||
overlay-pdf | ✔️ | ✔️ | ✔️
|
||||
pdf-organizer | ✔️ | ✔️ | ✔️
|
||||
pdf-to-csv | ✔️ | ✔️ | ✔️
|
||||
pdf-to-img | ✔️ | ✔️ | ✔️
|
||||
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
||||
remove-pages | ✔️ | ✔️ | ✔️
|
||||
remove-password | ✔️ | ✔️ | ✔️
|
||||
rotate-pdf | ✔️ | ✔️ | ✔️
|
||||
sanitize-pdf | ✔️ | ✔️ | ✔️
|
||||
scale-pages | ✔️ | ✔️ | ✔️
|
||||
sign | ✔️ | ✔️ | ✔️
|
||||
show-javascript | ✔️ | ✔️ | ✔️
|
||||
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
||||
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
||||
split-pdfs | ✔️ | ✔️ | ✔️
|
||||
file-to-pdf | | ✔️ | ✔️
|
||||
pdf-to-html | | ✔️ | ✔️
|
||||
pdf-to-presentation | | ✔️ | ✔️
|
||||
pdf-to-text | | ✔️ | ✔️
|
||||
pdf-to-word | | ✔️ | ✔️
|
||||
pdf-to-xml | | ✔️ | ✔️
|
||||
repair | | ✔️ | ✔️
|
||||
xlsx-to-pdf | | ✔️ | ✔️
|
||||
compress-pdf | | | ✔️
|
||||
extract-image-scans | | | ✔️
|
||||
ocr-pdf | | | ✔️
|
||||
pdf-to-pdfa | | | ✔️
|
||||
remove-blanks | | | ✔️
|
||||
|Technology | Ultra-Lite | Lite | Full |
|
||||
|----------------|:----------:|:----:|:----:|
|
||||
| Java | ✔️ | ✔️ | ✔️ |
|
||||
| JavaScript | ✔️ | ✔️ | ✔️ |
|
||||
| Libre | | ✔️ | ✔️ |
|
||||
| Python | | | ✔️ |
|
||||
| OpenCV | | | ✔️ |
|
||||
| OCRmyPDF | | | ✔️ |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Operation | Ultra-Lite | Lite | Full
|
||||
--------------------|------------|------|-----
|
||||
add-page-numbers | ✔️ | ✔️ | ✔️
|
||||
add-password | ✔️ | ✔️ | ✔️
|
||||
add-image | ✔️ | ✔️ | ✔️
|
||||
add-watermark | ✔️ | ✔️ | ✔️
|
||||
adjust-contrast | ✔️ | ✔️ | ✔️
|
||||
auto-split-pdf | ✔️ | ✔️ | ✔️
|
||||
auto-redact | ✔️ | ✔️ | ✔️
|
||||
auto-rename | ✔️ | ✔️ | ✔️
|
||||
cert-sign | ✔️ | ✔️ | ✔️
|
||||
crop | ✔️ | ✔️ | ✔️
|
||||
change-metadata | ✔️ | ✔️ | ✔️
|
||||
change-permissions | ✔️ | ✔️ | ✔️
|
||||
compare | ✔️ | ✔️ | ✔️
|
||||
extract-page | ✔️ | ✔️ | ✔️
|
||||
extract-images | ✔️ | ✔️ | ✔️
|
||||
flatten | ✔️ | ✔️ | ✔️
|
||||
get-info-on-pdf | ✔️ | ✔️ | ✔️
|
||||
img-to-pdf | ✔️ | ✔️ | ✔️
|
||||
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
||||
merge-pdfs | ✔️ | ✔️ | ✔️
|
||||
multi-page-layout | ✔️ | ✔️ | ✔️
|
||||
overlay-pdf | ✔️ | ✔️ | ✔️
|
||||
pdf-organizer | ✔️ | ✔️ | ✔️
|
||||
pdf-to-csv | ✔️ | ✔️ | ✔️
|
||||
pdf-to-img | ✔️ | ✔️ | ✔️
|
||||
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
||||
remove-pages | ✔️ | ✔️ | ✔️
|
||||
remove-password | ✔️ | ✔️ | ✔️
|
||||
rotate-pdf | ✔️ | ✔️ | ✔️
|
||||
sanitize-pdf | ✔️ | ✔️ | ✔️
|
||||
scale-pages | ✔️ | ✔️ | ✔️
|
||||
sign | ✔️ | ✔️ | ✔️
|
||||
show-javascript | ✔️ | ✔️ | ✔️
|
||||
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
||||
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
||||
split-pdfs | ✔️ | ✔️ | ✔️
|
||||
file-to-pdf | | ✔️ | ✔️
|
||||
pdf-to-html | | ✔️ | ✔️
|
||||
pdf-to-presentation | | ✔️ | ✔️
|
||||
pdf-to-text | | ✔️ | ✔️
|
||||
pdf-to-word | | ✔️ | ✔️
|
||||
pdf-to-xml | | ✔️ | ✔️
|
||||
repair | | ✔️ | ✔️
|
||||
xlsx-to-pdf | | ✔️ | ✔️
|
||||
compress-pdf | | | ✔️
|
||||
extract-image-scans | | | ✔️
|
||||
ocr-pdf | | | ✔️
|
||||
pdf-to-pdfa | | | ✔️
|
||||
remove-blanks | | | ✔️
|
||||
|
|
32
build.gradle
32
build.gradle
|
@ -1,18 +1,18 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.1'
|
||||
id 'org.springframework.boot' version '3.2.2'
|
||||
id 'io.spring.dependency-management' version '1.1.3'
|
||||
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
||||
id "io.swagger.swaggerhub" version "1.3.2"
|
||||
id 'edu.sc.seis.launch4j' version '3.0.5'
|
||||
id 'com.diffplug.spotless' version '6.23.3'
|
||||
id 'com.diffplug.spotless' version '6.25.0'
|
||||
id 'com.github.jk1.dependency-license-report' version '2.5'
|
||||
}
|
||||
|
||||
import com.github.jk1.license.render.*
|
||||
|
||||
group = 'stirling.software'
|
||||
version = '0.20.0'
|
||||
version = '0.21.0'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
repositories {
|
||||
|
@ -91,20 +91,22 @@ dependencies {
|
|||
implementation 'ch.qos.logback:logback-core:1.4.14'
|
||||
implementation 'org.springframework:spring-webmvc:6.1.2'
|
||||
|
||||
implementation("io.github.pixee:java-security-toolkit:1.1.2")
|
||||
|
||||
implementation 'org.yaml:snakeyaml:2.2'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.1'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.1'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.2'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.2'
|
||||
|
||||
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.1'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.2'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.1"
|
||||
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.2"
|
||||
|
||||
//2.2.x requires rebuild of DB file.. need migration path
|
||||
implementation "com.h2database:h2:2.1.214"
|
||||
}
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.1'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.2'
|
||||
|
||||
// Batik
|
||||
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
||||
|
@ -136,19 +138,19 @@ dependencies {
|
|||
implementation ('com.opencsv:opencsv:5.9') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
|
||||
implementation ('org.apache.pdfbox:pdfbox:3.0.1'){
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
|
||||
implementation ('org.apache.pdfbox:xmpbox:3.0.1'){
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'io.micrometer:micrometer-core'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.2'
|
||||
implementation 'io.micrometer:micrometer-core:1.12.2'
|
||||
implementation group: 'com.google.zxing', name: 'core', version: '3.5.2'
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation 'org.commonmark:commonmark:0.21.0'
|
||||
|
@ -156,7 +158,7 @@ dependencies {
|
|||
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
||||
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
||||
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.2")
|
||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.28'
|
||||
}
|
||||
|
|
|
@ -43,6 +43,6 @@ spec:
|
|||
name: http
|
||||
{{- end }}
|
||||
protocol: TCP
|
||||
|
||||
|
||||
selector:
|
||||
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 8.7 KiB |
|
@ -1,310 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
width="99.537987mm"
|
||||
height="95.209366mm"
|
||||
viewBox="0 0 99.537987 95.209366"
|
||||
version="1.1"
|
||||
id="svg745"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 512 512"
|
||||
style="enable-background:new 0 0 512 512;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
|
||||
sodipodi:docname="stirling.svg"
|
||||
inkscape:export-filename="stirling.png"
|
||||
inkscape:export-xdpi="80"
|
||||
inkscape:export-ydpi="80"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
inkscape:export-filename="favicon.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview747"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.914906"
|
||||
inkscape:cx="184.17193"
|
||||
inkscape:cy="509.88845"
|
||||
inkscape:window-width="2293"
|
||||
inkscape:window-height="1387"
|
||||
inkscape:window-x="122"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg745" /><defs
|
||||
id="defs742"><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient72198"><stop
|
||||
style="stop-color:#ccd6d7;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop72194" /><stop
|
||||
style="stop-color:#0f3a3f;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop72196" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient71928"><stop
|
||||
style="stop-color:#4b0005;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop71924" /><stop
|
||||
style="stop-color:#8f000c;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop71926" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient71920"><stop
|
||||
style="stop-color:#4b0005;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop71916" /><stop
|
||||
style="stop-color:#8f000c;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop71918" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient69598"><stop
|
||||
style="stop-color:#6a0007;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop69594" /><stop
|
||||
style="stop-color:#b8000f;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop69596" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient46361"><stop
|
||||
style="stop-color:#f7f6f8;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop46359" /><stop
|
||||
style="stop-color:#b7b7b5;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop46357" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient40554"><stop
|
||||
style="stop-color:#f4f2f4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop40550" /><stop
|
||||
style="stop-color:#57767b;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop40552" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient39095"><stop
|
||||
style="stop-color:#285459;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop39093" /><stop
|
||||
style="stop-color:#a6b6af;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop39091" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient36672"><stop
|
||||
style="stop-color:#da453f;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop36668" /><stop
|
||||
style="stop-color:#a60008;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop36670" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient19524"><stop
|
||||
style="stop-color:#3a4b4f;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop19522" /><stop
|
||||
style="stop-color:#617979;stop-opacity:0.97461927;"
|
||||
offset="1"
|
||||
id="stop19520" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient17996"><stop
|
||||
style="stop-color:#401016;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop17994" /><stop
|
||||
style="stop-color:#761f19;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop17992" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient15569"><stop
|
||||
style="stop-color:#8ea8ad;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15565" /><stop
|
||||
style="stop-color:#e9e7eb;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop15567" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient15557"><stop
|
||||
style="stop-color:#9b0e11;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15553" /><stop
|
||||
style="stop-color:#370707;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop15555" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient15557"
|
||||
id="linearGradient15559"
|
||||
x1="120.06672"
|
||||
y1="63.25761"
|
||||
x2="135.16347"
|
||||
y2="78.078682"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient15569"
|
||||
id="linearGradient15571"
|
||||
x1="127.97037"
|
||||
y1="101.66144"
|
||||
x2="133.88971"
|
||||
y2="104.77026"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient17996"
|
||||
id="linearGradient17998"
|
||||
x1="117.9284"
|
||||
y1="86.055084"
|
||||
x2="130.67392"
|
||||
y2="76.945541"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient19524"
|
||||
id="linearGradient19528"
|
||||
x1="130.98172"
|
||||
y1="82.402977"
|
||||
x2="135.72115"
|
||||
y2="86.45166"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient36672"
|
||||
id="linearGradient36674"
|
||||
x1="63.797714"
|
||||
y1="74.81752"
|
||||
x2="96.636673"
|
||||
y2="120.29293"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient39095"
|
||||
id="linearGradient39097"
|
||||
x1="120.54506"
|
||||
y1="124.76902"
|
||||
x2="128.04152"
|
||||
y2="126.0704"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient40554"
|
||||
id="linearGradient40556"
|
||||
x1="113.84585"
|
||||
y1="114.04285"
|
||||
x2="119.65858"
|
||||
y2="128.50244"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient46361"
|
||||
id="linearGradient46363"
|
||||
x1="73.993439"
|
||||
y1="114.13906"
|
||||
x2="94.845322"
|
||||
y2="71.247383"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient69598"
|
||||
id="linearGradient69600"
|
||||
x1="95.854446"
|
||||
y1="114.66749"
|
||||
x2="103.77842"
|
||||
y2="120.1887"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient71920"
|
||||
id="linearGradient71922"
|
||||
x1="98.580376"
|
||||
y1="87.186958"
|
||||
x2="118.09738"
|
||||
y2="101.19449"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient71928"
|
||||
id="linearGradient71930"
|
||||
x1="78.278267"
|
||||
y1="97.433273"
|
||||
x2="92.313202"
|
||||
y2="104.33479"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient72198"
|
||||
id="linearGradient72200"
|
||||
x1="125.76636"
|
||||
y1="138.46817"
|
||||
x2="123.3327"
|
||||
y2="126.03291"
|
||||
gradientUnits="userSpaceOnUse" /></defs><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="background"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true"
|
||||
transform="translate(-51.420144,-44.470286)"><rect
|
||||
style="display:inline;fill:#ccd6d7;fill-opacity:1;stroke:none;stroke-width:4.13755;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.2"
|
||||
id="rect72067"
|
||||
width="99.481979"
|
||||
height="94.999512"
|
||||
x="51.476147"
|
||||
y="44.680138" /></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="shadow"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true"
|
||||
transform="translate(-51.420144,-44.470286)"><path
|
||||
style="display:inline;fill:url(#linearGradient72200);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 84.146049,134.73858 c 0,0 11.721038,2.48294 17.938661,2.91673 6.21763,0.43378 14.75251,0.59994 22.41237,-0.43379 8.01008,-1.081 13.19907,-2.22733 14.50043,-2.66112 1.30136,-0.43379 4.00784,-1.24297 4.15244,-2.25514 0.1446,-1.01217 -3.4703,-2.74733 -6.21763,-3.32571 -2.74732,-0.57838 -12.72444,-1.44596 -14.89337,-1.44596 -2.16894,0 -37.892901,7.20499 -37.892901,7.20499 z"
|
||||
id="path72192"
|
||||
sodipodi:nodetypes="cssssssc" /></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Origami"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true"
|
||||
transform="translate(-51.420144,-44.470286)"><path
|
||||
style="display:inline;fill:url(#linearGradient40556);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 84.460552,134.86721 35.165798,-6.85679 16.15467,-42.7383 z"
|
||||
id="path960"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:url(#linearGradient15571);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 135.71493,85.428056 0.3984,45.049024 -9.66213,-20.46173 z"
|
||||
id="path964"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient39097);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 119.60518,128.00293 16.5337,2.48693 -9.68769,-20.5512 z"
|
||||
id="path966"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient15559);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 118.42209,57.022622 12.70423,-2.766809 -0.0785,25.087175 -12.43878,-13.376518 z"
|
||||
id="path968"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient19528);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 135.72114,85.386768 -4.84219,-6.459493 0.0305,11.126604 z"
|
||||
id="path970"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient17998);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 119.10273,65.682415 11.96883,13.44935 -0.0899,10.819868 -11.88577,11.430427 z"
|
||||
id="path972"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient36674);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 62.145635,130.15618 62.043392,65.435258 c 0,0 0.179321,-2.778132 1.89516,-4.036097 1.874923,-1.374597 4.341768,-1.894096 4.341768,-1.894096 l 50.91788,-11.349167 -0.0113,53.144272 -34.733274,33.56547 z"
|
||||
id="path958"
|
||||
sodipodi:nodetypes="ccsccccc" /></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Letter"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true"
|
||||
transform="translate(-51.420144,-44.470286)"><path
|
||||
style="display:inline;fill:url(#linearGradient69600);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 94.780881,91.406803 16.870379,17.074877 -23.723345,23.00249 -18.122131,-17.99816 5.497473,-2.80607 18.404054,-0.0511 2.35163,-8.23071 z"
|
||||
id="path54894"
|
||||
sodipodi:nodetypes="cccccccc" /><path
|
||||
style="display:inline;fill:url(#linearGradient71930);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 72.440405,92.224764 16.15467,15.745686 4.089788,-6.79927 z"
|
||||
id="path54892" /><path
|
||||
style="display:inline;fill:url(#linearGradient71922);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 95.138739,84.965385 1.124691,-14.109776 22.92453,22.286787 0.008,8.164604 -3.28863,3.16649 z"
|
||||
id="path54890"
|
||||
sodipodi:nodetypes="cccccc"
|
||||
inkscape:label="path54890" /><path
|
||||
style="display:inline;fill:url(#linearGradient46363);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 95.138739,84.965385 h 1.226936 l -0.05112,-14.109776 c 0,0 -5.776827,-3.220709 -12.167126,-2.40275 -6.390296,0.817957 -8.151582,2.1248 -10.58233,4.396523 -1.90229,1.777838 -2.913974,3.527446 -3.987546,7.157132 -0.512646,1.733226 -0.281963,5.988892 0.613471,8.537436 0.664591,1.891528 2.453873,4.294281 4.958868,6.134686 2.662335,1.956002 8.281825,3.527443 8.281825,3.527443 0,0 5.134614,1.887351 5.572338,4.294281 0.308137,1.69437 -0.102243,3.22071 -1.635914,4.95887 -1.258314,1.42609 -3.62969,1.99377 -6.288054,1.07357 -2.658364,-0.92021 -6.139514,-3.85065 -7.106009,-4.90775 -0.817958,-0.89464 -2.820665,-3.06173 -2.890231,-4.294021 -0.01209,-0.214205 -1.229505,-0.0963 -1.229505,-0.0963 l -0.0723,14.256941 5.879073,2.24938 c 0,0 5.214483,1.78929 8.946415,1.43143 3.731934,-0.35786 7.617235,-0.51122 11.604778,-5.16336 3.987542,-4.65213 3.680812,-12.831715 2.147141,-15.899056 -1.533673,-3.067344 -3.561212,-6.138812 -10.480087,-8.281826 -5.776829,-1.789283 -7.872846,-3.01622 -8.128458,-4.396524 -0.255611,-1.380305 0.0091,-4.253646 2.760607,-5.214481 3.220711,-1.124693 5.623462,-0.05112 7.05489,1.12469 1.431425,1.175817 5.572339,5.623462 5.572339,5.623462 z"
|
||||
id="path46355"
|
||||
sodipodi:nodetypes="cccssssscssssscccssssssscc" /></g></svg>
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs173">
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="XMLID_5_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="304.496"
|
||||
y1="422.9102"
|
||||
x2="316.036"
|
||||
y2="326.2626">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#DCF1F3"
|
||||
id="stop156" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#C2C2C9"
|
||||
id="stop158" />
|
||||
</linearGradient>
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview171"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="219.91021"
|
||||
inkscape:cy="232.63813"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2054"
|
||||
inkscape:window-x="2869"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="XMLID_4_" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style150">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#C02223;}
|
||||
.st2{fill:#882425;}
|
||||
.st3{fill:url(#XMLID_5_);}
|
||||
.st4{fill:url(#XMLID_7_);}
|
||||
</style>
|
||||
|
||||
<g
|
||||
id="XMLID_4_">
|
||||
<path
|
||||
id="XMLID_131_"
|
||||
class="st1"
|
||||
d="M 347.01402,14.355825 98.978019,69.02261 C 73.825483,74.547445 55.942464,96.792175 55.942464,122.52628 v 315.06096 c 0,22.39012 16.719895,41.14548 38.819234,43.76251 L 224.8861,498.36042 339.48636,384.26465 455.76603,265.15425 453.73057,84.870162 C 453.43979,62.916214 433.08513,46.632491 411.71274,51.284984 l -28.78729,6.251786 0.14539,-13.666697 C 383.36162,24.678542 365.62399,10.284894 347.01402,14.355825 Z"
|
||||
sodipodi:nodetypes="ccssccccccccc"
|
||||
style="stroke-width:1.45391" /><path
|
||||
id="XMLID_117_"
|
||||
class="st2"
|
||||
d="m 383.21622,57.53677 v 285.8375 L 456.05681,265.00885 454.02135,78.763767 C 453.87595,59.863016 436.28372,45.905539 417.81914,49.97647 Z"
|
||||
style="stroke-width:1.45391" /><polygon
|
||||
id="XMLID_18_"
|
||||
class="st3"
|
||||
points="234.7,422.6 368.5,387.7 393.5,262.2 "
|
||||
style="fill:url(#XMLID_5_)"
|
||||
transform="matrix(1.4556308,0,0,1.4548265,-116.73161,-116.45231)" />
|
||||
<linearGradient
|
||||
id="XMLID_7_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="223.0838"
|
||||
y1="372.7559"
|
||||
x2="241.4174"
|
||||
y2="114.557"
|
||||
gradientTransform="matrix(1.4539039,0,0,1.4539039,-116.19976,-116.20474)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#DCF1F3"
|
||||
id="stop163" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#C2C2C9"
|
||||
id="stop165" />
|
||||
</linearGradient>
|
||||
<path
|
||||
id="XMLID_6_"
|
||||
class="st4"
|
||||
d="m 282.89686,214.84917 c 0,0 -22.24473,-28.93269 -38.67384,-36.78377 -10.46811,-4.94327 -26.02489,-6.83335 -38.23768,-0.72695 -18.02841,9.0142 -19.91848,34.31213 -3.34397,44.34406 3.92553,2.47165 9.15959,4.50711 15.99294,6.10641 36.63838,8.43264 97.12077,25.87949 89.70587,96.10304 0,0 -4.21633,65.86185 -73.56753,73.42215 -12.2128,1.30851 -24.57098,0.43617 -36.493,-2.32625 -16.42911,-3.63476 -45.50719,-11.04967 -59.75545,-19.91849 l -2.61703,-75.16682 h 6.97875 c 0,0 13.81208,33.43978 53.06749,49.57812 7.26952,2.90781 15.26599,4.07093 22.97168,2.90781 9.74116,-1.45391 21.22699,-6.68796 25.87949,-22.53551 0,0 7.85108,-23.11707 -32.85823,-35.76604 -32.56744,-10.17733 -63.24481,-20.64543 -75.89378,-54.95757 -5.961,-16.28371 -6.97874,-34.31212 -2.90781,-51.61358 5.37944,-22.53551 20.79082,-54.23062 64.40794,-67.89732 0,0 57.28381,-15.55677 96.53922,5.52484 l -1.74468,89.70587 z"
|
||||
style="fill:url(#XMLID_7_);stroke-width:1.45391" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4 KiB |
|
@ -21,7 +21,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Lite
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||
|
|
|
@ -20,7 +20,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Lite
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest
|
||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||
|
|
|
@ -21,7 +21,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
|
||||
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
||||
|
|
|
@ -21,7 +21,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Lite
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
||||
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||
|
|
|
@ -20,7 +20,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Ultra-lite
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Ultra-lite Latest
|
||||
UI_APPNAMENAVBAR: Stirling-PDF-Ultra-lite Latest
|
||||
|
|
|
@ -21,7 +21,7 @@ services:
|
|||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en_US
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest
|
||||
UI_APPNAMENAVBAR: Stirling-PDF Latest
|
||||
|
|
182
gradlew.bat
vendored
182
gradlew.bat
vendored
|
@ -1,91 +1,91 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
|
@ -16,7 +16,7 @@ public class PropSync {
|
|||
Map<String, String> enProps = linesToProps(enLines);
|
||||
|
||||
for (File file : files) {
|
||||
if (!file.getName().equals("messages_en_GB.properties")) {
|
||||
if (!"messages_en_GB.properties".equals(file.getName())) {
|
||||
System.out.println("Processing file: " + file.getName());
|
||||
List<String> lines;
|
||||
try {
|
||||
|
|
|
@ -4,7 +4,7 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
|||
if [ ! -f app-security.jar ]; then
|
||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
curl -L -o app-security.jar https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
|
||||
|
||||
# If the first download attempt failed, try with the 'v' prefix
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
# Copy the original tesseract-ocr files to the volume directory without overwriting existing files
|
||||
echo "Copying original files without overwriting existing files"
|
||||
mkdir -p /usr/share/tesseract-ocr
|
||||
cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr
|
||||
mkdir -p /usr/share/tessdata
|
||||
cp -rn /usr/share/tessdata-original/* /usr/share/tessdata
|
||||
|
||||
if [ -d /usr/share/tesseract-ocr/4.00/tessdata ]; then
|
||||
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* /usr/share/tesseract-ocr/5/tessdata/ || true;
|
||||
if [ -d /usr/share/tesseract-ocr/4.00/tessdata ]; then
|
||||
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* /usr/share/tessdata || true;
|
||||
fi
|
||||
|
||||
if [ -d /usr/share/tesseract-ocr/5/tessdata ]; then
|
||||
cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true;
|
||||
fi
|
||||
|
||||
# Check if TESSERACT_LANGS environment variable is set and is not empty
|
||||
|
@ -23,4 +27,4 @@ fi
|
|||
/scripts/download-security-jar.sh
|
||||
|
||||
# Run the main command
|
||||
exec "$@"
|
||||
exec "$@"
|
||||
|
|
|
@ -2,7 +2,7 @@ import argparse
|
|||
import sys
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
import os
|
||||
|
||||
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
||||
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
||||
|
@ -49,9 +49,9 @@ def auto_rotate(image, angle_threshold=1):
|
|||
angles = []
|
||||
for rho, theta in lines[:, 0]:
|
||||
angles.append((theta * 180) / np.pi - 90)
|
||||
|
||||
|
||||
angle = np.median(angles)
|
||||
|
||||
|
||||
if abs(angle) < angle_threshold:
|
||||
return image
|
||||
|
||||
|
@ -65,16 +65,16 @@ def auto_rotate(image, angle_threshold=1):
|
|||
|
||||
def crop_borders(image, border_color, tolerance=30):
|
||||
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
||||
|
||||
|
||||
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
if len(contours) == 0:
|
||||
return image
|
||||
|
||||
largest_contour = max(contours, key=cv2.contourArea)
|
||||
x, y, w, h = cv2.boundingRect(largest_contour)
|
||||
|
||||
|
||||
return image[y:y+h, x:x+w]
|
||||
|
||||
|
||||
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
||||
image = cv2.imread(input_file)
|
||||
background_color = estimate_background_color(image)
|
||||
|
@ -110,7 +110,7 @@ if __name__ == "__main__":
|
|||
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
|
||||
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
||||
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)
|
||||
|
|
|
@ -6,6 +6,8 @@ import java.net.Socket;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import io.github.pixee.security.SystemCommand;
|
||||
|
||||
public class LibreOfficeListener {
|
||||
|
||||
private static final long ACTIVITY_TIMEOUT = 20 * 60 * 1000; // 20 minutes
|
||||
|
@ -44,7 +46,7 @@ public class LibreOfficeListener {
|
|||
}
|
||||
|
||||
// Start the listener process
|
||||
process = Runtime.getRuntime().exec("unoconv --listener");
|
||||
process = SystemCommand.runCommand(Runtime.getRuntime(), "unoconv --listener");
|
||||
lastActivityTime = System.currentTimeMillis();
|
||||
|
||||
// Start a background thread to monitor the activity timeout
|
||||
|
|
|
@ -10,6 +10,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import io.github.pixee.security.SystemCommand;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import stirling.software.SPDF.config.ConfigInitializer;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
@ -24,7 +26,7 @@ public class SPdfApplication {
|
|||
public void init() {
|
||||
// Check if the BROWSER_OPEN environment variable is set to true
|
||||
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
||||
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
||||
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
|
||||
|
||||
if (browserOpen) {
|
||||
try {
|
||||
|
@ -34,7 +36,7 @@ public class SPdfApplication {
|
|||
Runtime rt = Runtime.getRuntime();
|
||||
if (os.contains("win")) {
|
||||
// For Windows
|
||||
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
||||
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -77,16 +77,11 @@ public class AppConfig {
|
|||
return Files.exists(Paths.get("/.dockerenv"));
|
||||
}
|
||||
|
||||
@Bean(name = "bookFormatsInstalled")
|
||||
public boolean bookFormatsInstalled() {
|
||||
return applicationProperties.getSystem().getCustomApplications().isInstallBookFormats();
|
||||
}
|
||||
|
||||
@Bean(name = "htmlFormatsInstalled")
|
||||
public boolean htmlFormatsInstalled() {
|
||||
@Bean(name = "bookAndHtmlFormatsInstalled")
|
||||
public boolean bookAndHtmlFormatsInstalled() {
|
||||
return applicationProperties
|
||||
.getSystem()
|
||||
.getCustomApplications()
|
||||
.isInstallAdvancedHtmlToPDF();
|
||||
.isInstallBookAndHtmlFormats();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class ConfigInitializer
|
|||
for (String line : templateLines) {
|
||||
String key = extractKey.apply(line);
|
||||
|
||||
if (line.trim().equalsIgnoreCase("AutomaticallyGenerated:")) {
|
||||
if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) {
|
||||
insideAutoGenerated = true;
|
||||
mergedLines.add(line);
|
||||
continue;
|
||||
|
|
|
@ -16,7 +16,7 @@ import org.springframework.stereotype.Service;
|
|||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
@DependsOn({"bookFormatsInstalled"})
|
||||
@DependsOn({"bookAndHtmlFormatsInstalled"})
|
||||
public class EndpointConfiguration {
|
||||
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||
|
@ -24,14 +24,14 @@ public class EndpointConfiguration {
|
|||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private boolean bookFormatsInstalled;
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@Autowired
|
||||
public EndpointConfiguration(
|
||||
ApplicationProperties applicationProperties,
|
||||
@Qualifier("bookFormatsInstalled") boolean bookFormatsInstalled) {
|
||||
@Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.bookFormatsInstalled = bookFormatsInstalled;
|
||||
this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled;
|
||||
init();
|
||||
processEnvironmentConfigs();
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ public class EndpointConfiguration {
|
|||
private void processEnvironmentConfigs() {
|
||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||
if (!bookFormatsInstalled) {
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
groupsToRemove.add("Calibre");
|
||||
}
|
||||
if (endpointsToRemove != null) {
|
||||
|
|
|
@ -26,12 +26,8 @@ public class PostStartupProcesses {
|
|||
private boolean runningInDocker;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bookFormatsInstalled")
|
||||
private boolean bookFormatsInstalled;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("htmlFormatsInstalled")
|
||||
private boolean htmlFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PostStartupProcesses.class);
|
||||
|
||||
|
@ -39,34 +35,11 @@ public class PostStartupProcesses {
|
|||
public void runInstallCommandBasedOnEnvironment() throws IOException, InterruptedException {
|
||||
List<List<String>> commands = new ArrayList<>();
|
||||
// Checking for DOCKER_INSTALL_BOOK_FORMATS environment variable
|
||||
if (bookFormatsInstalled) {
|
||||
if (bookAndHtmlFormatsInstalled) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
// Set up the timezone configuration commands
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"sh",
|
||||
"-c",
|
||||
"echo 'tzdata tzdata/Areas select Europe' | debconf-set-selections; "
|
||||
+ "echo 'tzdata tzdata/Zones/Europe select Berlin' | debconf-set-selections"));
|
||||
commands.add(tmpList);
|
||||
|
||||
// Install calibre with DEBIAN_FRONTEND set to noninteractive
|
||||
tmpList = new ArrayList<>();
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"sh",
|
||||
"-c",
|
||||
"DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends calibre"));
|
||||
commands.add(tmpList);
|
||||
}
|
||||
|
||||
// Checking for DOCKER_INSTALL_HTML_FORMATS environment variable
|
||||
if (htmlFormatsInstalled) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
// Add -y flag for automatic yes to prompts and --no-install-recommends to reduce size
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"apt-get", "install", "wkhtmltopdf", "-y", "--no-install-recommends"));
|
||||
tmpList.addAll(Arrays.asList("apk add --no-cache calibre"));
|
||||
commands.add(tmpList);
|
||||
}
|
||||
|
||||
|
@ -74,8 +47,6 @@ public class PostStartupProcesses {
|
|||
// Run the command
|
||||
if (runningInDocker) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
tmpList.addAll(Arrays.asList("apt-get", "update"));
|
||||
commands.add(0, tmpList);
|
||||
|
||||
for (List<String> list : commands) {
|
||||
ProcessExecutorResult returnCode =
|
||||
|
|
|
@ -115,4 +115,4 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -136,6 +137,7 @@ public class MultiPageLayoutController {
|
|||
byte[] result = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result,
|
||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_layoutChanged.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -75,7 +77,8 @@ public class PdfOverlayController {
|
|||
overlay.overlay(overlayGuide).save(outputStream);
|
||||
byte[] data = outputStream.toByteArray();
|
||||
String outputFilename =
|
||||
baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(baseFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_overlayed.pdf"; // Remove file extension and append .pdf
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
|
@ -135,7 +138,7 @@ public class PdfOverlayController {
|
|||
try (PDDocument overlayPdf = Loader.loadPDF(overlayFiles[overlayFileIndex])) {
|
||||
PDDocument singlePageDocument = new PDDocument();
|
||||
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
|
||||
File tempFile = File.createTempFile("overlay-page-", ".pdf");
|
||||
File tempFile = Files.createTempFile("overlay-page-", ".pdf").toFile();
|
||||
singlePageDocument.save(tempFile);
|
||||
singlePageDocument.close();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -57,7 +58,9 @@ public class RearrangePagesPDFController {
|
|||
}
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_removed_pages.pdf");
|
||||
}
|
||||
|
||||
private List<Integer> removeFirst(int totalPages) {
|
||||
|
@ -211,7 +214,8 @@ public class RearrangePagesPDFController {
|
|||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_rearranged.pdf");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed rearranging documents", e);
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -49,6 +50,8 @@ public class RotationController {
|
|||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_rotated.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -112,6 +113,7 @@ public class ScalePagesController {
|
|||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(),
|
||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scaled.pdf");
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_scaled.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -49,7 +50,7 @@ public class SplitPDFController {
|
|||
|
||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document);
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document, true);
|
||||
if (!pageNumbers.contains(document.getNumberOfPages() - 1))
|
||||
pageNumbers.add(document.getNumberOfPages() - 1);
|
||||
logger.info(
|
||||
|
@ -83,7 +84,9 @@ public class SplitPDFController {
|
|||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
// loop through the split documents and write them to the zip file
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -64,7 +65,9 @@ public class SplitPdfBySectionsController {
|
|||
sourceDocument.close();
|
||||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
byte[] data;
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
|
@ -121,8 +124,8 @@ public class SplitPdfBySectionsController {
|
|||
subDoc, subPage, AppendMode.APPEND, true, true)) {
|
||||
// Set clipping area and position
|
||||
float translateX = -subPageWidth * i;
|
||||
|
||||
//float translateY = height - subPageHeight * (verticalDivisions - j);
|
||||
|
||||
// float translateY = height - subPageHeight * (verticalDivisions - j);
|
||||
float translateY = -subPageHeight * (verticalDivisions - 1 - j);
|
||||
|
||||
contentStream.saveGraphicsState();
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -120,7 +121,9 @@ public class SplitPdfBySizeController {
|
|||
sourceDocument.close();
|
||||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
byte[] data;
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -22,28 +23,28 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class ConvertBookToPDFController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bookFormatsInstalled")
|
||||
private boolean bookFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/book/pdf")
|
||||
@Operation(
|
||||
summary =
|
||||
"Convert a BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) to PDF",
|
||||
description =
|
||||
"(Requires bookFormatsInstalled flag and Calibre installed) This endpoint takes an BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) input and converts it to PDF format.")
|
||||
"(Requires bookAndHtmlFormatsInstalled flag and Calibre installed) This endpoint takes an BOOK/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx) input and converts it to PDF format.")
|
||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute GeneralFile request) throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
||||
if (!bookFormatsInstalled) {
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
throw new IllegalArgumentException(
|
||||
"bookFormatsInstalled flag is False, this functionality is not avaiable");
|
||||
"bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable");
|
||||
}
|
||||
|
||||
if (fileInput == null) {
|
||||
throw new IllegalArgumentException("Please provide a file for conversion.");
|
||||
}
|
||||
|
||||
String originalFilename = fileInput.getOriginalFilename();
|
||||
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
|
||||
|
||||
if (originalFilename != null) {
|
||||
String originalFilenameLower = originalFilename.toLowerCase();
|
||||
|
|
|
@ -9,10 +9,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.GeneralFile;
|
||||
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
|
||||
import stirling.software.SPDF.utils.FileToPdf;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
|
@ -22,15 +23,16 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class ConvertHtmlToPDF {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("htmlFormatsInstalled")
|
||||
private boolean htmlFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/html/pdf")
|
||||
@Operation(
|
||||
summary = "Convert an HTML or ZIP (containing HTML and CSS) to PDF",
|
||||
description =
|
||||
"This endpoint takes an HTML or ZIP file input and converts it to a PDF format.")
|
||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute GeneralFile request) throws Exception {
|
||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute HTMLToPdfRequest request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
||||
if (fileInput == null) {
|
||||
|
@ -38,14 +40,17 @@ public class ConvertHtmlToPDF {
|
|||
"Please provide an HTML or ZIP file for conversion.");
|
||||
}
|
||||
|
||||
String originalFilename = fileInput.getOriginalFilename();
|
||||
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
|
||||
if (originalFilename == null
|
||||
|| (!originalFilename.endsWith(".html") && !originalFilename.endsWith(".zip"))) {
|
||||
throw new IllegalArgumentException("File must be either .html or .zip format.");
|
||||
}
|
||||
byte[] pdfBytes =
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
fileInput.getBytes(), originalFilename, htmlFormatsInstalled);
|
||||
request,
|
||||
fileInput.getBytes(),
|
||||
originalFilename,
|
||||
bookAndHtmlFormatsInstalled);
|
||||
|
||||
String outputFilename =
|
||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -54,9 +55,11 @@ public class ConvertImgPDFController {
|
|||
colorTypeResult = ImageType.BINARY;
|
||||
}
|
||||
// returns bytes for image
|
||||
boolean singleImage = singleOrMultiple.equals("single");
|
||||
boolean singleImage = "single".equals(singleOrMultiple);
|
||||
byte[] result = null;
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
try {
|
||||
result =
|
||||
PdfUtils.convertFromPdf(
|
||||
|
@ -96,7 +99,7 @@ public class ConvertImgPDFController {
|
|||
@Operation(
|
||||
summary = "Convert images to a PDF file",
|
||||
description =
|
||||
"This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images. Input:Image Output:PDF Type:SISO?")
|
||||
"This endpoint converts one or more images to a PDF file. Users can specify whether to stretch the images to fit the PDF page, and whether to automatically rotate the images. Input:Image Output:PDF Type:MISO")
|
||||
public ResponseEntity<byte[]> convertToPdf(@ModelAttribute ConvertToPdfRequest request)
|
||||
throws IOException {
|
||||
MultipartFile[] file = request.getFileInput();
|
||||
|
@ -113,6 +116,6 @@ public class ConvertImgPDFController {
|
|||
|
||||
private String getMediaType(String imageFormat) {
|
||||
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
||||
return mimeType.equals("null") ? "application/octet-stream" : mimeType;
|
||||
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -32,14 +33,14 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class ConvertMarkdownToPdf {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("htmlFormatsInstalled")
|
||||
private boolean htmlFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf")
|
||||
@Operation(
|
||||
summary = "Convert a Markdown file to PDF",
|
||||
description =
|
||||
"This endpoint takes a Markdown file input, converts it to HTML, and then to PDF format.")
|
||||
"This endpoint takes a Markdown file input, converts it to HTML, and then to PDF format. Input:MARKDOWN Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> markdownToPdf(@ModelAttribute GeneralFile request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
@ -48,7 +49,7 @@ public class ConvertMarkdownToPdf {
|
|||
throw new IllegalArgumentException("Please provide a Markdown file for conversion.");
|
||||
}
|
||||
|
||||
String originalFilename = fileInput.getOriginalFilename();
|
||||
String originalFilename = Filenames.toSimpleFileName(fileInput.getOriginalFilename());
|
||||
if (originalFilename == null || !originalFilename.endsWith(".md")) {
|
||||
throw new IllegalArgumentException("File must be in .md format.");
|
||||
}
|
||||
|
@ -68,7 +69,10 @@ public class ConvertMarkdownToPdf {
|
|||
|
||||
byte[] pdfBytes =
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
htmlContent.getBytes(), "converted.html", htmlFormatsInstalled);
|
||||
null,
|
||||
htmlContent.getBytes(),
|
||||
"converted.html",
|
||||
bookAndHtmlFormatsInstalled);
|
||||
|
||||
String outputFilename =
|
||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -31,7 +32,7 @@ public class ConvertOfficeController {
|
|||
|
||||
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
|
||||
// Check for valid file extension
|
||||
String originalFilename = inputFile.getOriginalFilename();
|
||||
String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
|
||||
if (originalFilename == null
|
||||
|| !isValidFileExtension(FilenameUtils.getExtension(originalFilename))) {
|
||||
throw new IllegalArgumentException("Invalid file extension");
|
||||
|
@ -89,7 +90,8 @@ public class ConvertOfficeController {
|
|||
byte[] pdfByteArray = convertToPdf(inputFile);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
pdfByteArray,
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_convertedToPDF.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -29,22 +30,22 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class ConvertPDFToBookController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bookFormatsInstalled")
|
||||
private boolean bookFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf/book")
|
||||
@Operation(
|
||||
summary =
|
||||
"Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF",
|
||||
description =
|
||||
"(Requires bookFormatsInstalled flag and Calibre installed) This endpoint Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF")
|
||||
"(Requires bookAndHtmlFormatsInstalled flag and Calibre installed) This endpoint Convert a PDF to a Book/comic (*.epub | *.mobi | *.azw3 | *.fb2 | *.txt | *.docx .. (others to include by chatgpt) to PDF")
|
||||
public ResponseEntity<byte[]> HtmlToPdf(@ModelAttribute PdfToBookRequest request)
|
||||
throws Exception {
|
||||
MultipartFile fileInput = request.getFileInput();
|
||||
|
||||
if (!bookFormatsInstalled) {
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
throw new IllegalArgumentException(
|
||||
"bookFormatsInstalled flag is False, this functionality is not avaiable");
|
||||
"bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable");
|
||||
}
|
||||
|
||||
if (fileInput == null) {
|
||||
|
@ -92,7 +93,8 @@ public class ConvertPDFToBookController {
|
|||
}
|
||||
|
||||
String outputFilename =
|
||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "."
|
||||
+ outputFormat; // Remove file extension and append .pdf
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ package stirling.software.SPDF.controller.api.converters;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
@ -9,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -17,6 +22,7 @@ import stirling.software.SPDF.model.api.converters.PdfToPresentationRequest;
|
|||
import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest;
|
||||
import stirling.software.SPDF.model.api.converters.PdfToWordRequest;
|
||||
import stirling.software.SPDF.utils.PDFToFile;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
|
@ -59,9 +65,21 @@ public class ConvertPDFToOffice {
|
|||
throws IOException, InterruptedException {
|
||||
MultipartFile inputFile = request.getFileInput();
|
||||
String outputFormat = request.getOutputFormat();
|
||||
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
||||
if ("txt".equals(request.getOutputFormat())) {
|
||||
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
|
||||
PDFTextStripper stripper = new PDFTextStripper();
|
||||
String text = stripper.getText(document);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
text.getBytes(),
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ ".txt",
|
||||
MediaType.TEXT_PLAIN);
|
||||
}
|
||||
} else {
|
||||
PDFToFile pdfToFile = new PDFToFile();
|
||||
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf/word")
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -63,7 +64,9 @@ public class ConvertPDFToPDFA {
|
|||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_PDFA.pdf";
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class ConvertWebsiteToPDF {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("htmlFormatsInstalled")
|
||||
private boolean htmlFormatsInstalled;
|
||||
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||
private boolean bookAndHtmlFormatsInstalled;
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||
@Operation(
|
||||
|
@ -53,7 +53,7 @@ public class ConvertWebsiteToPDF {
|
|||
|
||||
// Prepare the OCRmyPDF command
|
||||
List<String> command = new ArrayList<>();
|
||||
if (!htmlFormatsInstalled) {
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
command.add("weasyprint");
|
||||
} else {
|
||||
command.add("wkhtmltopdf");
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -43,7 +44,7 @@ public class FilterController {
|
|||
PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
|
||||
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
pdfDocument, inputFile.getOriginalFilename());
|
||||
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -60,7 +61,7 @@ public class FilterController {
|
|||
PDDocument pdfDocument = Loader.loadPDF(inputFile.getBytes());
|
||||
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
pdfDocument, inputFile.getOriginalFilename());
|
||||
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -133,7 +134,8 @@ public class AutoRenameController {
|
|||
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
||||
} else {
|
||||
logger.info("File has no good title to be found");
|
||||
return WebResponseUtils.pdfDocToWebResponse(document, file.getOriginalFilename());
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document, Filenames.toSimpleFileName(file.getOriginalFilename()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.google.zxing.PlanarYUVLuminanceSource;
|
|||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -43,6 +44,7 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
public class AutoSplitPdfController {
|
||||
|
||||
private static final String QR_CONTENT = "https://github.com/Stirling-Tools/Stirling-PDF";
|
||||
private static final String QR_CONTENT_OLD = "https://github.com/Frooodle/Stirling-PDF";
|
||||
|
||||
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
|
||||
@Operation(
|
||||
|
@ -63,12 +65,13 @@ public class AutoSplitPdfController {
|
|||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
|
||||
String result = decodeQRCode(bim);
|
||||
|
||||
if (QR_CONTENT.equals(result) && page != 0) {
|
||||
if ((QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result)) && page != 0) {
|
||||
splitDocuments.add(new PDDocument());
|
||||
}
|
||||
|
||||
if (!splitDocuments.isEmpty() && !QR_CONTENT.equals(result)) {
|
||||
if (!splitDocuments.isEmpty()
|
||||
&& !QR_CONTENT.equals(result)
|
||||
&& !QR_CONTENT_OLD.equals(result)) {
|
||||
splitDocuments.get(splitDocuments.size() - 1).addPage(document.getPage(page));
|
||||
} else if (page == 0) {
|
||||
PDDocument firstDocument = new PDDocument();
|
||||
|
@ -77,7 +80,7 @@ public class AutoSplitPdfController {
|
|||
}
|
||||
|
||||
// If duplexMode is true and current page is a divider, then skip next page
|
||||
if (duplexMode && QR_CONTENT.equals(result)) {
|
||||
if (duplexMode && (QR_CONTENT.equals(result) || QR_CONTENT_OLD.equals(result))) {
|
||||
page++;
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +98,9 @@ public class AutoSplitPdfController {
|
|||
document.close();
|
||||
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
byte[] data;
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
|
|
|
@ -2,23 +2,20 @@ package stirling.software.SPDF.controller.api.misc;
|
|||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
@ -27,13 +24,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
|
@ -41,6 +37,8 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
public class BlankPageController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BlankPageController.class);
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-blanks")
|
||||
@Operation(
|
||||
summary = "Remove blank pages from a PDF file",
|
||||
|
@ -63,56 +61,35 @@ public class BlankPageController {
|
|||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
|
||||
for (PDPage page : pages) {
|
||||
System.out.println("checking page " + pageIndex);
|
||||
logger.info("checking page " + pageIndex);
|
||||
textStripper.setStartPage(pageIndex + 1);
|
||||
textStripper.setEndPage(pageIndex + 1);
|
||||
String pageText = textStripper.getText(document);
|
||||
boolean hasText = !pageText.trim().isEmpty();
|
||||
|
||||
Boolean blank = false;
|
||||
if (hasText) {
|
||||
pagesToKeepIndex.add(pageIndex);
|
||||
System.out.println("page " + pageIndex + " has text");
|
||||
logger.info("page " + pageIndex + " has text, not blank");
|
||||
blank = false;
|
||||
} else {
|
||||
boolean hasImages = PdfUtils.hasImagesOnPage(page);
|
||||
if (hasImages) {
|
||||
System.out.println("page " + pageIndex + " has image");
|
||||
|
||||
Path tempFile = Files.createTempFile("image_", ".png");
|
||||
|
||||
logger.info("page " + pageIndex + " has image, running blank detection");
|
||||
// Render image and save as temp file
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 300);
|
||||
ImageIO.write(image, "png", tempFile.toFile());
|
||||
|
||||
List<String> command =
|
||||
new ArrayList<>(
|
||||
Arrays.asList(
|
||||
"python3",
|
||||
System.getProperty("user.dir")
|
||||
+ "/scripts/detect-blank-pages.py",
|
||||
tempFile.toString(),
|
||||
"--threshold",
|
||||
String.valueOf(threshold),
|
||||
"--white_percent",
|
||||
String.valueOf(whitePercent)));
|
||||
|
||||
// Run CLI command
|
||||
ProcessExecutorResult returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||
.runCommandWithOutputHandling(command);
|
||||
|
||||
// does contain data
|
||||
if (returnCode.getRc() == 0) {
|
||||
System.out.println(
|
||||
"page " + pageIndex + " has image which is not blank");
|
||||
pagesToKeepIndex.add(pageIndex);
|
||||
} else {
|
||||
System.out.println("Skipping, Image was blank for page #" + pageIndex);
|
||||
}
|
||||
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30);
|
||||
blank = isBlankImage(image, threshold, whitePercent, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
if (blank) {
|
||||
logger.info("Skipping, Image was blank for page #" + pageIndex);
|
||||
} else {
|
||||
logger.info("page " + pageIndex + " has image which is not blank");
|
||||
pagesToKeepIndex.add(pageIndex);
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
}
|
||||
System.out.print("pagesToKeep=" + pagesToKeepIndex.size());
|
||||
|
||||
// Remove pages not present in pagesToKeepIndex
|
||||
List<Integer> pageIndices =
|
||||
IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
|
||||
|
@ -125,7 +102,8 @@ public class BlankPageController {
|
|||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_blanksRemoved.pdf");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -134,4 +112,30 @@ public class BlankPageController {
|
|||
if (document != null) document.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isBlankImage(
|
||||
BufferedImage image, int threshold, double whitePercent, int blurSize) {
|
||||
if (image == null) {
|
||||
logger.info("Error: Image is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert to binary image based on the threshold
|
||||
int whitePixels = 0;
|
||||
int totalPixels = image.getWidth() * image.getHeight();
|
||||
|
||||
for (int i = 0; i < image.getHeight(); i++) {
|
||||
for (int j = 0; j < image.getWidth(); j++) {
|
||||
int color = image.getRGB(j, i) & 0xFF;
|
||||
if (color >= 255 - threshold) {
|
||||
whitePixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double whitePixelPercentage = (whitePixels / (double) totalPixels) * 100;
|
||||
logger.info(String.format("Page has white pixel percent of %.2f%%", whitePixelPercentage));
|
||||
|
||||
return whitePixelPercentage >= whitePercent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -264,7 +265,9 @@ public class CompressController {
|
|||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_Optimized.pdf";
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public class ExtractImageScansController {
|
|||
List<String> images = new ArrayList<>();
|
||||
|
||||
// Check if input file is a PDF
|
||||
if (extension.equalsIgnoreCase("pdf")) {
|
||||
if ("pdf".equalsIgnoreCase(extension)) {
|
||||
// Load PDF document
|
||||
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -66,7 +67,9 @@ public class ExtractImagesController {
|
|||
zos.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
int imageIndex = 1;
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
int pageNum = 0;
|
||||
Set<Integer> processedImages = new HashSet<>();
|
||||
// Iterate over each page
|
||||
|
@ -85,19 +88,19 @@ public class ExtractImagesController {
|
|||
// Convert image to desired format
|
||||
RenderedImage renderedImage = image.getImage();
|
||||
BufferedImage bufferedImage = null;
|
||||
if (format.equalsIgnoreCase("png")) {
|
||||
if ("png".equalsIgnoreCase(format)) {
|
||||
bufferedImage =
|
||||
new BufferedImage(
|
||||
renderedImage.getWidth(),
|
||||
renderedImage.getHeight(),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
} else if (format.equalsIgnoreCase("jpeg") || format.equalsIgnoreCase("jpg")) {
|
||||
} else if ("jpeg".equalsIgnoreCase(format) || "jpg".equalsIgnoreCase(format)) {
|
||||
bufferedImage =
|
||||
new BufferedImage(
|
||||
renderedImage.getWidth(),
|
||||
renderedImage.getHeight(),
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
} else if (format.equalsIgnoreCase("gif")) {
|
||||
} else if ("gif".equalsIgnoreCase(format)) {
|
||||
bufferedImage =
|
||||
new BufferedImage(
|
||||
renderedImage.getWidth(),
|
||||
|
|
|
@ -29,11 +29,11 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
@ -50,7 +50,7 @@ public class FakeScanControllerWIP {
|
|||
|
||||
// TODO
|
||||
@Hidden
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
|
||||
// @PostMapping(consumes = "multipart/form-data", value = "/fakeScan")
|
||||
@Operation(
|
||||
summary = "Repair a PDF file",
|
||||
description =
|
||||
|
@ -142,7 +142,9 @@ public class FakeScanControllerWIP {
|
|||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_scanned.pdf";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_scanned.pdf";
|
||||
return WebResponseUtils.boasToWebResponse(baos, outputFilename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -109,15 +110,15 @@ public class MetadataController {
|
|||
for (Entry<String, String> entry : allRequestParams.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
// Check if the key is a standard metadata key
|
||||
if (!key.equalsIgnoreCase("Author")
|
||||
&& !key.equalsIgnoreCase("CreationDate")
|
||||
&& !key.equalsIgnoreCase("Creator")
|
||||
&& !key.equalsIgnoreCase("Keywords")
|
||||
&& !key.equalsIgnoreCase("modificationDate")
|
||||
&& !key.equalsIgnoreCase("Producer")
|
||||
&& !key.equalsIgnoreCase("Subject")
|
||||
&& !key.equalsIgnoreCase("Title")
|
||||
&& !key.equalsIgnoreCase("Trapped")
|
||||
if (!"Author".equalsIgnoreCase(key)
|
||||
&& !"CreationDate".equalsIgnoreCase(key)
|
||||
&& !"Creator".equalsIgnoreCase(key)
|
||||
&& !"Keywords".equalsIgnoreCase(key)
|
||||
&& !"modificationDate".equalsIgnoreCase(key)
|
||||
&& !"Producer".equalsIgnoreCase(key)
|
||||
&& !"Subject".equalsIgnoreCase(key)
|
||||
&& !"Title".equalsIgnoreCase(key)
|
||||
&& !"Trapped".equalsIgnoreCase(key)
|
||||
&& !key.contains("customKey")
|
||||
&& !key.contains("customValue")) {
|
||||
info.setCustomMetadataValue(key, entry.getValue());
|
||||
|
@ -164,6 +165,8 @@ public class MetadataController {
|
|||
document.setDocumentInformation(info);
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_metadata.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -74,7 +75,7 @@ public class OCRController {
|
|||
throw new IOException("Please select at least one language.");
|
||||
}
|
||||
|
||||
if (!ocrRenderType.equals("hocr") && !ocrRenderType.equals("sandwich")) {
|
||||
if (!"hocr".equals(ocrRenderType) && !"sandwich".equals(ocrRenderType)) {
|
||||
throw new IOException("ocrRenderType wrong");
|
||||
}
|
||||
|
||||
|
@ -127,7 +128,7 @@ public class OCRController {
|
|||
if (cleanFinal != null && cleanFinal) {
|
||||
command.add("--clean-final");
|
||||
}
|
||||
if (ocrType != null && !ocrType.equals("")) {
|
||||
if (ocrType != null && !"".equals(ocrType)) {
|
||||
if ("skip-text".equals(ocrType)) {
|
||||
command.add("--skip-text");
|
||||
} else if ("force-ocr".equals(ocrType)) {
|
||||
|
@ -182,12 +183,16 @@ public class OCRController {
|
|||
|
||||
// Return the OCR processed PDF as a response
|
||||
String outputFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.pdf";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_OCR.pdf";
|
||||
|
||||
if (sidecar != null && sidecar) {
|
||||
// Create a zip file containing both the PDF and the text file
|
||||
String outputZipFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_OCR.zip";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_OCR.zip";
|
||||
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut =
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -30,7 +31,7 @@ public class OverlayImageController {
|
|||
@Operation(
|
||||
summary = "Overlay image onto a PDF file",
|
||||
description =
|
||||
"This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified. Input:PDF/IMAGE Output:PDF Type:MF-SISO")
|
||||
"This endpoint overlays an image onto a PDF file at the specified coordinates. The image can be overlaid on every page of the PDF if specified. Input:PDF/IMAGE Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> overlayImage(@ModelAttribute OverlayImageRequest request) {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
MultipartFile imageFile = request.getImageFile();
|
||||
|
@ -44,7 +45,9 @@ public class OverlayImageController {
|
|||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_overlayed.pdf");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to add image to PDF", e);
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -93,7 +94,7 @@ public class PageNumbersController {
|
|||
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
||||
.replace(
|
||||
"{filename}",
|
||||
file.getOriginalFilename()
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", ""))
|
||||
: String.valueOf(pageNumber);
|
||||
|
||||
|
@ -145,7 +146,8 @@ public class PageNumbersController {
|
|||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(),
|
||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_numbersAdded.pdf",
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_numbersAdded.pdf",
|
||||
MediaType.APPLICATION_PDF);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -65,7 +66,9 @@ public class RepairController {
|
|||
|
||||
// Return the optimized PDF as a response
|
||||
String outputFilename =
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_repaired.pdf";
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_repaired.pdf";
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -54,7 +55,8 @@ public class ShowJavascript {
|
|||
|
||||
script +=
|
||||
"// File: "
|
||||
+ inputFile.getOriginalFilename()
|
||||
+ Filenames.toSimpleFileName(
|
||||
inputFile.getOriginalFilename())
|
||||
+ ", Script: "
|
||||
+ name
|
||||
+ "\n"
|
||||
|
@ -66,12 +68,14 @@ public class ShowJavascript {
|
|||
|
||||
if (script.isEmpty()) {
|
||||
script =
|
||||
"PDF '" + inputFile.getOriginalFilename() + "' does not contain Javascript";
|
||||
"PDF '"
|
||||
+ Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
+ "' does not contain Javascript";
|
||||
}
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
script.getBytes(StandardCharsets.UTF_8),
|
||||
inputFile.getOriginalFilename() + ".js");
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + ".js");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||
import org.apache.pdfbox.util.Matrix;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.AddStampRequest;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/misc")
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
public class StampController {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/add-stamp")
|
||||
@Operation(
|
||||
summary = "Add stamp to a PDF file",
|
||||
description =
|
||||
"This endpoint adds a stamp to a given PDF file. Users can specify the stamp type (text or image), rotation, opacity, width spacer, and height spacer. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> addStamp(@ModelAttribute AddStampRequest request)
|
||||
throws IOException, Exception {
|
||||
MultipartFile pdfFile = request.getFileInput();
|
||||
String stampType = request.getStampType();
|
||||
String stampText = request.getStampText();
|
||||
MultipartFile stampImage = request.getStampImage();
|
||||
String alphabet = request.getAlphabet();
|
||||
float fontSize = request.getFontSize();
|
||||
float rotation = request.getRotation();
|
||||
float opacity = request.getOpacity();
|
||||
int position = request.getPosition(); // Updated to use 1-9 positioning logic
|
||||
float overrideX = request.getOverrideX(); // New field for X override
|
||||
float overrideY = request.getOverrideY(); // New field for Y override
|
||||
|
||||
String customColor = request.getCustomColor();
|
||||
float marginFactor;
|
||||
|
||||
switch (request.getCustomMargin().toLowerCase()) {
|
||||
case "small":
|
||||
marginFactor = 0.02f;
|
||||
break;
|
||||
case "medium":
|
||||
marginFactor = 0.035f;
|
||||
break;
|
||||
case "large":
|
||||
marginFactor = 0.05f;
|
||||
break;
|
||||
case "x-large":
|
||||
marginFactor = 0.075f;
|
||||
break;
|
||||
default:
|
||||
marginFactor = 0.035f;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the input PDF
|
||||
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||
|
||||
for (int pageIndex : pageNumbers) {
|
||||
int zeroBasedIndex = pageIndex - 1;
|
||||
if (zeroBasedIndex >= 0 && zeroBasedIndex < document.getNumberOfPages()) {
|
||||
PDPage page = document.getPage(zeroBasedIndex);
|
||||
PDRectangle pageSize = page.getMediaBox();
|
||||
float margin = marginFactor * (pageSize.getWidth() + pageSize.getHeight()) / 2;
|
||||
|
||||
PDPageContentStream contentStream =
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||
|
||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||
contentStream.setGraphicsStateParameters(graphicsState);
|
||||
|
||||
if ("text".equalsIgnoreCase(stampType)) {
|
||||
addTextStamp(
|
||||
contentStream,
|
||||
stampText,
|
||||
document,
|
||||
page,
|
||||
rotation,
|
||||
position,
|
||||
fontSize,
|
||||
alphabet,
|
||||
overrideX,
|
||||
overrideY,
|
||||
margin,
|
||||
customColor);
|
||||
} else if ("image".equalsIgnoreCase(stampType)) {
|
||||
addImageStamp(
|
||||
contentStream,
|
||||
stampImage,
|
||||
document,
|
||||
page,
|
||||
rotation,
|
||||
position,
|
||||
fontSize,
|
||||
overrideX,
|
||||
overrideY,
|
||||
margin);
|
||||
}
|
||||
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_stamped.pdf");
|
||||
}
|
||||
|
||||
private void addTextStamp(
|
||||
PDPageContentStream contentStream,
|
||||
String stampText,
|
||||
PDDocument document,
|
||||
PDPage page,
|
||||
float rotation,
|
||||
int position, // 1-9 positioning logic
|
||||
float fontSize,
|
||||
String alphabet,
|
||||
float overrideX, // X override
|
||||
float overrideY,
|
||||
float margin,
|
||||
String colorString) // Y override
|
||||
throws IOException {
|
||||
String resourceDir = "";
|
||||
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||
switch (alphabet) {
|
||||
case "arabic":
|
||||
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
|
||||
break;
|
||||
case "japanese":
|
||||
resourceDir = "static/fonts/Meiryo.ttf";
|
||||
break;
|
||||
case "korean":
|
||||
resourceDir = "static/fonts/malgun.ttf";
|
||||
break;
|
||||
case "chinese":
|
||||
resourceDir = "static/fonts/SimSun.ttf";
|
||||
break;
|
||||
case "roman":
|
||||
default:
|
||||
resourceDir = "static/fonts/NotoSans-Regular.ttf";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!"".equals(resourceDir)) {
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
|
||||
try (InputStream is = classPathResource.getInputStream();
|
||||
FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||
IOUtils.copy(is, os);
|
||||
}
|
||||
|
||||
font = PDType0Font.load(document, tempFile);
|
||||
tempFile.deleteOnExit();
|
||||
}
|
||||
|
||||
contentStream.setFont(font, fontSize);
|
||||
|
||||
Color redactColor;
|
||||
try {
|
||||
if (!colorString.startsWith("#")) {
|
||||
colorString = "#" + colorString;
|
||||
}
|
||||
redactColor = Color.decode(colorString);
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
redactColor = Color.LIGHT_GRAY;
|
||||
}
|
||||
|
||||
contentStream.setNonStrokingColor(redactColor);
|
||||
|
||||
PDRectangle pageSize = page.getMediaBox();
|
||||
float x, y;
|
||||
|
||||
if (overrideX >= 0 && overrideY >= 0) {
|
||||
// Use override values if provided
|
||||
x = overrideX;
|
||||
y = overrideY;
|
||||
} else {
|
||||
x = calculatePositionX(pageSize, position, fontSize, font, fontSize, stampText, margin);
|
||||
y =
|
||||
calculatePositionY(
|
||||
pageSize, position, calculateTextCapHeight(font, fontSize), margin);
|
||||
}
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(rotation), x, y));
|
||||
contentStream.showText(stampText);
|
||||
contentStream.endText();
|
||||
}
|
||||
|
||||
private void addImageStamp(
|
||||
PDPageContentStream contentStream,
|
||||
MultipartFile stampImage,
|
||||
PDDocument document,
|
||||
PDPage page,
|
||||
float rotation,
|
||||
int position, // 1-9 positioning logic
|
||||
float fontSize,
|
||||
float overrideX,
|
||||
float overrideY,
|
||||
float margin)
|
||||
throws IOException {
|
||||
|
||||
// Load the stamp image
|
||||
BufferedImage image = ImageIO.read(stampImage.getInputStream());
|
||||
|
||||
// Compute width based on original aspect ratio
|
||||
float aspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
||||
|
||||
// Desired physical height (in PDF points)
|
||||
float desiredPhysicalHeight = fontSize;
|
||||
|
||||
// Desired physical width based on the aspect ratio
|
||||
float desiredPhysicalWidth = desiredPhysicalHeight * aspectRatio;
|
||||
|
||||
// Convert the BufferedImage to PDImageXObject
|
||||
PDImageXObject xobject = LosslessFactory.createFromImage(document, image);
|
||||
|
||||
PDRectangle pageSize = page.getMediaBox();
|
||||
float x, y;
|
||||
|
||||
if (overrideX >= 0 && overrideY >= 0) {
|
||||
// Use override values if provided
|
||||
x = overrideX;
|
||||
y = overrideY;
|
||||
} else {
|
||||
x = calculatePositionX(pageSize, position, desiredPhysicalWidth, null, 0, null, margin);
|
||||
y = calculatePositionY(pageSize, position, fontSize, margin);
|
||||
}
|
||||
|
||||
contentStream.saveGraphicsState();
|
||||
contentStream.transform(Matrix.getTranslateInstance(x, y));
|
||||
contentStream.transform(Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0));
|
||||
contentStream.drawImage(xobject, 0, 0, desiredPhysicalWidth, desiredPhysicalHeight);
|
||||
contentStream.restoreGraphicsState();
|
||||
}
|
||||
|
||||
private float calculatePositionX(
|
||||
PDRectangle pageSize,
|
||||
int position,
|
||||
float contentWidth,
|
||||
PDFont font,
|
||||
float fontSize,
|
||||
String text,
|
||||
float margin)
|
||||
throws IOException {
|
||||
float actualWidth =
|
||||
(text != null) ? calculateTextWidth(text, font, fontSize) : contentWidth;
|
||||
switch (position % 3) {
|
||||
case 1: // Left
|
||||
return pageSize.getLowerLeftX() + margin;
|
||||
case 2: // Center
|
||||
return (pageSize.getWidth() - actualWidth) / 2;
|
||||
case 0: // Right
|
||||
return pageSize.getUpperRightX() - actualWidth - margin;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private float calculatePositionY(
|
||||
PDRectangle pageSize, int position, float height, float margin) {
|
||||
switch ((position - 1) / 3) {
|
||||
case 0: // Top
|
||||
return pageSize.getUpperRightY() - height - margin;
|
||||
case 1: // Middle
|
||||
return (pageSize.getHeight() - height) / 2;
|
||||
case 2: // Bottom
|
||||
return pageSize.getLowerLeftY() + margin;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private float calculateTextWidth(String text, PDFont font, float fontSize) throws IOException {
|
||||
return font.getStringWidth(text) / 1000 * fontSize;
|
||||
}
|
||||
|
||||
private float calculateTextCapHeight(PDFont font, float fontSize) {
|
||||
return font.getFontDescriptor().getCapHeight() / 1000 * fontSize;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package stirling.software.SPDF.controller.api.pipeline;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -39,6 +41,57 @@ public class ApiDocService {
|
|||
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
||||
}
|
||||
|
||||
Map<String, List<String>> outputToFileTypes = new HashMap<>();
|
||||
|
||||
public List getExtensionTypes(boolean output, String operationName) {
|
||||
if (outputToFileTypes.size() == 0) {
|
||||
outputToFileTypes.put("PDF", Arrays.asList("pdf"));
|
||||
outputToFileTypes.put(
|
||||
"IMAGE",
|
||||
Arrays.asList(
|
||||
"png", "jpg", "jpeg", "gif", "webp", "bmp", "tif", "tiff", "svg", "psd",
|
||||
"ai", "eps"));
|
||||
outputToFileTypes.put(
|
||||
"ZIP",
|
||||
Arrays.asList("zip", "rar", "7z", "tar", "gz", "bz2", "xz", "lz", "lzma", "z"));
|
||||
outputToFileTypes.put("WORD", Arrays.asList("doc", "docx", "odt", "rtf"));
|
||||
outputToFileTypes.put("CSV", Arrays.asList("csv"));
|
||||
outputToFileTypes.put("JS", Arrays.asList("js", "jsx"));
|
||||
outputToFileTypes.put("HTML", Arrays.asList("html", "htm", "xhtml"));
|
||||
outputToFileTypes.put("JSON", Arrays.asList("json"));
|
||||
outputToFileTypes.put("TXT", Arrays.asList("txt", "text", "md", "markdown"));
|
||||
outputToFileTypes.put("PPT", Arrays.asList("ppt", "pptx", "odp"));
|
||||
outputToFileTypes.put("XML", Arrays.asList("xml", "xsd", "xsl"));
|
||||
outputToFileTypes.put(
|
||||
"BOOK", Arrays.asList("epub", "mobi", "azw3", "fb2", "txt", "docx"));
|
||||
// type.
|
||||
}
|
||||
|
||||
if (apiDocsJsonRootNode == null || apiDocumentation.size() == 0) {
|
||||
loadApiDocumentation();
|
||||
}
|
||||
if (!apiDocumentation.containsKey(operationName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||
String description = endpoint.getDescription();
|
||||
Pattern pattern = null;
|
||||
if (output) {
|
||||
pattern = Pattern.compile("Output:(\\w+)");
|
||||
} else {
|
||||
pattern = Pattern.compile("Input:(\\w+)");
|
||||
}
|
||||
Matcher matcher = pattern.matcher(description);
|
||||
while (matcher.find()) {
|
||||
String type = matcher.group(1).toUpperCase();
|
||||
if (outputToFileTypes.containsKey(type)) {
|
||||
return outputToFileTypes.get(type);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
private UserServiceInterface userService;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -35,6 +36,9 @@ import org.springframework.util.MultiValueMap;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.github.pixee.security.ZipSecurity;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
|
@ -82,15 +86,11 @@ public class PipelineProcessor {
|
|||
operation,
|
||||
isMultiInputOperation);
|
||||
Map<String, Object> parameters = pipelineOperation.getParameters();
|
||||
String inputFileExtension = "";
|
||||
|
||||
// TODO
|
||||
// if (operationNode.has("inputFileType")) {
|
||||
// inputFileExtension = operationNode.get("inputFileType").asText();
|
||||
// } else {
|
||||
inputFileExtension = ".pdf";
|
||||
// }
|
||||
final String finalInputFileExtension = inputFileExtension;
|
||||
List<String> inputFileTypes = apiDocService.getExtensionTypes(false, operation);
|
||||
if (inputFileTypes == null) {
|
||||
inputFileTypes = new ArrayList<String>(Arrays.asList("ALL"));
|
||||
}
|
||||
// List outputFileTypes = apiDocService.getExtensionTypes(true, operation);
|
||||
|
||||
String url = getBaseUrl() + operation;
|
||||
|
||||
|
@ -98,38 +98,42 @@ public class PipelineProcessor {
|
|||
if (!isMultiInputOperation) {
|
||||
for (Resource file : outputFiles) {
|
||||
boolean hasInputFileType = false;
|
||||
if (file.getFilename().endsWith(inputFileExtension)) {
|
||||
hasInputFileType = true;
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
body.add("fileInput", file);
|
||||
for (String extension : inputFileTypes) {
|
||||
if ("ALL".equals(extension) || file.getFilename().endsWith(extension)) {
|
||||
hasInputFileType = true;
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
body.add("fileInput", file);
|
||||
|
||||
for (Entry<String, Object> entry : parameters.entrySet()) {
|
||||
body.add(entry.getKey(), entry.getValue());
|
||||
for (Entry<String, Object> entry : parameters.entrySet()) {
|
||||
body.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
||||
|
||||
// If the operation is filter and the response body is null or empty,
|
||||
// skip
|
||||
// this
|
||||
// file
|
||||
if (operation.startsWith("filter-")
|
||||
&& (response.getBody() == null
|
||||
|| response.getBody().length == 0)) {
|
||||
logger.info("Skipping file due to failing {}", operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
logPrintStream.println("Error: " + response.getBody());
|
||||
hasErrors = true;
|
||||
continue;
|
||||
}
|
||||
processOutputFiles(operation, response, newOutputFiles);
|
||||
}
|
||||
|
||||
ResponseEntity<byte[]> response = sendWebRequest(url, body);
|
||||
|
||||
// If the operation is filter and the response body is null or empty, skip
|
||||
// this
|
||||
// file
|
||||
if (operation.startsWith("filter-")
|
||||
&& (response.getBody() == null || response.getBody().length == 0)) {
|
||||
logger.info("Skipping file due to failing {}", operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
logPrintStream.println("Error: " + response.getBody());
|
||||
hasErrors = true;
|
||||
continue;
|
||||
}
|
||||
processOutputFiles(operation, file.getFilename(), response, newOutputFiles);
|
||||
}
|
||||
|
||||
if (!hasInputFileType) {
|
||||
logPrintStream.println(
|
||||
"No files with extension "
|
||||
+ inputFileExtension
|
||||
+ String.join(", ", inputFileTypes)
|
||||
+ " found for operation "
|
||||
+ operation);
|
||||
hasErrors = true;
|
||||
|
@ -138,13 +142,19 @@ public class PipelineProcessor {
|
|||
|
||||
} else {
|
||||
// Filter and collect all files that match the inputFileExtension
|
||||
List<Resource> matchingFiles =
|
||||
outputFiles.stream()
|
||||
.filter(
|
||||
file ->
|
||||
file.getFilename()
|
||||
.endsWith(finalInputFileExtension))
|
||||
.collect(Collectors.toList());
|
||||
List<Resource> matchingFiles;
|
||||
if (inputFileTypes.contains("ALL")) {
|
||||
matchingFiles = new ArrayList<>(outputFiles);
|
||||
} else {
|
||||
final List<String> finalinputFileTypes = inputFileTypes;
|
||||
matchingFiles =
|
||||
outputFiles.stream()
|
||||
.filter(
|
||||
file ->
|
||||
finalinputFileTypes.stream()
|
||||
.anyMatch(file.getFilename()::endsWith))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Check if there are matching files
|
||||
if (!matchingFiles.isEmpty()) {
|
||||
|
@ -164,11 +174,7 @@ public class PipelineProcessor {
|
|||
|
||||
// Handle the response
|
||||
if (response.getStatusCode().equals(HttpStatus.OK)) {
|
||||
processOutputFiles(
|
||||
operation,
|
||||
matchingFiles.get(0).getFilename(),
|
||||
response,
|
||||
newOutputFiles);
|
||||
processOutputFiles(operation, response, newOutputFiles);
|
||||
} else {
|
||||
// Log error if the response status is not OK
|
||||
logPrintStream.println(
|
||||
|
@ -178,7 +184,7 @@ public class PipelineProcessor {
|
|||
} else {
|
||||
logPrintStream.println(
|
||||
"No files with extension "
|
||||
+ inputFileExtension
|
||||
+ String.join(", ", inputFileTypes)
|
||||
+ " found for multi-input operation "
|
||||
+ operation);
|
||||
hasErrors = true;
|
||||
|
@ -211,11 +217,29 @@ public class PipelineProcessor {
|
|||
return restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||
}
|
||||
|
||||
public static String removeTrailingNaming(String filename) {
|
||||
// Splitting filename into name and extension
|
||||
int dotIndex = filename.lastIndexOf(".");
|
||||
if (dotIndex == -1) {
|
||||
// No extension found
|
||||
return filename;
|
||||
}
|
||||
String name = filename.substring(0, dotIndex);
|
||||
String extension = filename.substring(dotIndex);
|
||||
|
||||
// Finding the last underscore
|
||||
int underscoreIndex = name.lastIndexOf("_");
|
||||
if (underscoreIndex == -1) {
|
||||
// No underscore found
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Removing the last part and reattaching the extension
|
||||
return name.substring(0, underscoreIndex) + extension;
|
||||
}
|
||||
|
||||
private List<Resource> processOutputFiles(
|
||||
String operation,
|
||||
String fileName,
|
||||
ResponseEntity<byte[]> response,
|
||||
List<Resource> newOutputFiles)
|
||||
String operation, ResponseEntity<byte[]> response, List<Resource> newOutputFiles)
|
||||
throws IOException {
|
||||
// Define filename
|
||||
String newFilename;
|
||||
|
@ -227,7 +251,7 @@ public class PipelineProcessor {
|
|||
newFilename = extractFilename(response);
|
||||
} else {
|
||||
// Otherwise, keep the original filename.
|
||||
newFilename = fileName;
|
||||
newFilename = removeTrailingNaming(extractFilename(response));
|
||||
}
|
||||
|
||||
// Check if the response body is a zip file
|
||||
|
@ -312,7 +336,7 @@ public class PipelineProcessor {
|
|||
new ByteArrayResource(file.getBytes()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getOriginalFilename();
|
||||
return Filenames.toSimpleFileName(file.getOriginalFilename());
|
||||
}
|
||||
};
|
||||
outputFiles.add(fileResource);
|
||||
|
@ -335,7 +359,7 @@ public class PipelineProcessor {
|
|||
List<Resource> unzippedFiles = new ArrayList<>();
|
||||
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
ZipInputStream zis = new ZipInputStream(bais)) {
|
||||
ZipInputStream zis = ZipSecurity.createHardenedInputStream(bais)) {
|
||||
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -74,7 +75,7 @@ public class CertSignController {
|
|||
@Operation(
|
||||
summary = "Sign PDF with a Digital Certificate",
|
||||
description =
|
||||
"This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:MF-SISO")
|
||||
"This endpoint accepts a PDF file, a digital certificate and related information to sign the PDF. It then returns the digitally signed PDF file. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
|
||||
throws Exception {
|
||||
MultipartFile pdf = request.getFileInput();
|
||||
|
@ -123,7 +124,9 @@ public class CertSignController {
|
|||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
sign(pdf.getBytes(), baos, createSignature, name, location, reason);
|
||||
return WebResponseUtils.boasToWebResponse(
|
||||
baos, pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_signed.pdf");
|
||||
baos,
|
||||
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_signed.pdf");
|
||||
}
|
||||
|
||||
private static void sign(
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -43,7 +44,8 @@ public class PasswordController {
|
|||
document.setAllSecurityToBeRemoved(true);
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_password_removed.pdf");
|
||||
}
|
||||
|
||||
|
@ -88,10 +90,13 @@ public class PasswordController {
|
|||
if ("".equals(ownerPassword) && "".equals(password))
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_permissions.pdf");
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_passworded.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -104,7 +105,8 @@ public class RedactController {
|
|||
byte[] pdfContent = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
pdfContent,
|
||||
file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_redacted.pdf");
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_redacted.pdf");
|
||||
}
|
||||
|
||||
private void redactFoundText(
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -76,7 +77,8 @@ public class SanitizeController {
|
|||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_sanitized.pdf");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.io.File;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
|
@ -30,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
|
@ -75,7 +77,7 @@ public class WatermarkController {
|
|||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||
contentStream.setGraphicsStateParameters(graphicsState);
|
||||
|
||||
if (watermarkType.equalsIgnoreCase("text")) {
|
||||
if ("text".equalsIgnoreCase(watermarkType)) {
|
||||
addTextWatermark(
|
||||
contentStream,
|
||||
watermarkText,
|
||||
|
@ -86,7 +88,7 @@ public class WatermarkController {
|
|||
heightSpacer,
|
||||
fontSize,
|
||||
alphabet);
|
||||
} else if (watermarkType.equalsIgnoreCase("image")) {
|
||||
} else if ("image".equalsIgnoreCase(watermarkType)) {
|
||||
addImageWatermark(
|
||||
contentStream,
|
||||
watermarkImage,
|
||||
|
@ -104,7 +106,9 @@ public class WatermarkController {
|
|||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_watermarked.pdf");
|
||||
}
|
||||
|
||||
private void addTextWatermark(
|
||||
|
@ -139,10 +143,10 @@ public class WatermarkController {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!resourceDir.equals("")) {
|
||||
if (!"".equals(resourceDir)) {
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||
File tempFile = File.createTempFile("NotoSansFont", fileExtension);
|
||||
File tempFile = Files.createTempFile("NotoSansFont", fileExtension).toFile();
|
||||
try (InputStream is = classPathResource.getInputStream();
|
||||
FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||
IOUtils.copy(is, os);
|
||||
|
@ -155,9 +159,16 @@ public class WatermarkController {
|
|||
contentStream.setFont(font, fontSize);
|
||||
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
|
||||
|
||||
String[] textLines = watermarkText.split("\\\\n");
|
||||
float maxLineWidth = 0;
|
||||
|
||||
for (int i = 0; i < textLines.length; ++i) {
|
||||
maxLineWidth = Math.max(maxLineWidth, font.getStringWidth(textLines[i]));
|
||||
}
|
||||
|
||||
// Set size and location of text watermark
|
||||
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
|
||||
float watermarkHeight = heightSpacer + fontSize;
|
||||
float watermarkWidth = widthSpacer + maxLineWidth * fontSize / 1000;
|
||||
float watermarkHeight = heightSpacer + fontSize * textLines.length;
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
||||
|
@ -172,7 +183,12 @@ public class WatermarkController {
|
|||
(float) Math.toRadians(rotation),
|
||||
j * watermarkWidth,
|
||||
i * watermarkHeight));
|
||||
contentStream.showText(watermarkText);
|
||||
|
||||
for (int k = 0; k < textLines.length; ++k) {
|
||||
contentStream.showText(textLines[k]);
|
||||
contentStream.newLineAtOffset(0, -fontSize);
|
||||
}
|
||||
|
||||
contentStream.endText();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
public class ConverterWebController {
|
||||
|
||||
@ConditionalOnExpression("#{bookFormatsInstalled}")
|
||||
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
||||
@GetMapping("/book-to-pdf")
|
||||
@Hidden
|
||||
public String convertBookToPdfForm(Model model) {
|
||||
|
@ -21,7 +21,7 @@ public class ConverterWebController {
|
|||
return "convert/book-to-pdf";
|
||||
}
|
||||
|
||||
@ConditionalOnExpression("#{bookFormatsInstalled}")
|
||||
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
||||
@GetMapping("/pdf-to-book")
|
||||
@Hidden
|
||||
public String convertPdfToBookForm(Model model) {
|
||||
|
|
|
@ -84,7 +84,7 @@ public class MetricsController {
|
|||
for (Meter meter : meterRegistry.getMeters()) {
|
||||
if (meter.getId().getName().equals("http.requests")) {
|
||||
String method = meter.getId().getTag("method");
|
||||
if (method != null && method.equals("GET")) {
|
||||
if (method != null && "GET".equals(method)) {
|
||||
|
||||
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
||||
if (!endpoint.get().startsWith("/")) {
|
||||
|
@ -129,7 +129,7 @@ public class MetricsController {
|
|||
for (Meter meter : meterRegistry.getMeters()) {
|
||||
if (meter.getId().getName().equals("http.requests")) {
|
||||
String method = meter.getId().getTag("method");
|
||||
if (method != null && method.equals("GET")) {
|
||||
if (method != null && "GET".equals(method)) {
|
||||
String uri = meter.getId().getTag("uri");
|
||||
if (uri != null) {
|
||||
double currentCount = counts.getOrDefault(uri, 0.0);
|
||||
|
@ -197,7 +197,7 @@ public class MetricsController {
|
|||
for (Meter meter : meterRegistry.getMeters()) {
|
||||
if (meter.getId().getName().equals("http.requests")) {
|
||||
String method = meter.getId().getTag("method");
|
||||
if (method != null && method.equals("POST")) {
|
||||
if (method != null && "POST".equals(method)) {
|
||||
if (endpoint.isPresent() && !endpoint.get().isBlank()) {
|
||||
if (!endpoint.get().startsWith("/")) {
|
||||
endpoint = Optional.of("/" + endpoint.get());
|
||||
|
@ -235,7 +235,7 @@ public class MetricsController {
|
|||
for (Meter meter : meterRegistry.getMeters()) {
|
||||
if (meter.getId().getName().equals("http.requests")) {
|
||||
String method = meter.getId().getTag("method");
|
||||
if (method != null && method.equals("POST")) {
|
||||
if (method != null && "POST".equals(method)) {
|
||||
String uri = meter.getId().getTag("uri");
|
||||
if (uri != null) {
|
||||
double currentCount = counts.getOrDefault(uri, 0.0);
|
||||
|
|
|
@ -39,6 +39,13 @@ public class OtherWebController {
|
|||
return "misc/show-javascript";
|
||||
}
|
||||
|
||||
@GetMapping("/stamp")
|
||||
@Hidden
|
||||
public String stampForm(Model model) {
|
||||
model.addAttribute("currentPage", "stamp");
|
||||
return "misc/stamp";
|
||||
}
|
||||
|
||||
@GetMapping("/add-page-numbers")
|
||||
@Hidden
|
||||
public String addPageNumbersForm(Model model) {
|
||||
|
|
|
@ -290,31 +290,20 @@ public class ApplicationProperties {
|
|||
}
|
||||
|
||||
public static class CustomApplications {
|
||||
private boolean installBookFormats;
|
||||
private boolean installAdvancedHtmlToPDF;
|
||||
private boolean installBookAndHtmlFormats;
|
||||
|
||||
public boolean isInstallBookFormats() {
|
||||
return installBookFormats;
|
||||
public boolean isInstallBookAndHtmlFormats() {
|
||||
return installBookAndHtmlFormats;
|
||||
}
|
||||
|
||||
public void setInstallBookFormats(boolean installBookFormats) {
|
||||
this.installBookFormats = installBookFormats;
|
||||
}
|
||||
|
||||
public boolean isInstallAdvancedHtmlToPDF() {
|
||||
return installAdvancedHtmlToPDF;
|
||||
}
|
||||
|
||||
public void setInstallAdvancedHtmlToPDF(boolean installAdvancedHtmlToPDF) {
|
||||
this.installAdvancedHtmlToPDF = installAdvancedHtmlToPDF;
|
||||
public void setInstallBookAndHtmlFormats(boolean installBookAndHtmlFormats) {
|
||||
this.installBookAndHtmlFormats = installBookAndHtmlFormats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomApplications [installBookFormats="
|
||||
+ installBookFormats
|
||||
+ ", installAdvancedHtmlToPDF="
|
||||
+ installAdvancedHtmlToPDF
|
||||
return "CustomApplications [installBookAndHtmlFormats="
|
||||
+ installBookAndHtmlFormats
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class PDFWithPageNums extends PDFFile {
|
|||
private String pageNumbers;
|
||||
|
||||
@Hidden
|
||||
public List<Integer> getPageNumbersList() {
|
||||
public List<Integer> getPageNumbersList(boolean zeroCount) {
|
||||
int pageCount = 0;
|
||||
try {
|
||||
pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages();
|
||||
|
@ -33,13 +33,13 @@ public class PDFWithPageNums extends PDFFile {
|
|||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return GeneralUtils.parsePageString(pageNumbers, pageCount);
|
||||
return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount);
|
||||
}
|
||||
|
||||
@Hidden
|
||||
public List<Integer> getPageNumbersList(PDDocument doc) {
|
||||
public List<Integer> getPageNumbersList(PDDocument doc, boolean zeroCount) {
|
||||
int pageCount = 0;
|
||||
pageCount = doc.getNumberOfPages();
|
||||
return GeneralUtils.parsePageString(pageNumbers, pageCount);
|
||||
return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package stirling.software.SPDF.model.api.converters;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HTMLToPdfRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description = "Zoom level for displaying the website. Default is '1'.",
|
||||
defaultValue = "1")
|
||||
private float zoom;
|
||||
}
|
|
@ -12,6 +12,6 @@ public class PdfToTextOrRTFRequest extends PDFFile {
|
|||
|
||||
@Schema(
|
||||
description = "The output Text or RTF format",
|
||||
allowableValues = {"rtf", "txt:Text"})
|
||||
allowableValues = {"rtf", "txt"})
|
||||
private String outputFormat;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package stirling.software.SPDF.model.api.misc;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AddStampRequest extends PDFWithPageNums {
|
||||
|
||||
@Schema(
|
||||
description = "The stamp type (text or image)",
|
||||
allowableValues = {"text", "image"},
|
||||
required = true)
|
||||
private String stampType;
|
||||
|
||||
@Schema(description = "The stamp text")
|
||||
private String stampText;
|
||||
|
||||
@Schema(description = "The stamp image")
|
||||
private MultipartFile stampImage;
|
||||
|
||||
@Schema(
|
||||
description = "The selected alphabet",
|
||||
allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"},
|
||||
defaultValue = "roman")
|
||||
private String alphabet = "roman";
|
||||
|
||||
@Schema(description = "The font size of the stamp text", example = "30")
|
||||
private float fontSize = 30;
|
||||
|
||||
@Schema(description = "The rotation of the stamp in degrees", example = "0")
|
||||
private float rotation = 0;
|
||||
|
||||
@Schema(description = "The opacity of the stamp (0.0 - 1.0)", example = "0.5")
|
||||
private float opacity;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center, ..., 9: top-right)",
|
||||
example = "1")
|
||||
private int position;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Override X coordinate for stamp placement. If set, it will override the position-based calculation. Negative value means no override.",
|
||||
example = "-1")
|
||||
private float overrideX = -1; // Default to -1 indicating no override
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Override Y coordinate for stamp placement. If set, it will override the position-based calculation. Negative value means no override.",
|
||||
example = "-1")
|
||||
private float overrideY = -1; // Default to -1 indicating no override
|
||||
|
||||
@Schema(
|
||||
description = "Specifies the margin size for the stamp.",
|
||||
allowableValues = {"small", "medium", "large", "x-large"},
|
||||
defaultValue = "medium")
|
||||
private String customMargin = "medium";
|
||||
|
||||
@Schema(description = "The color for stamp", defaultValue = "#d3d3d3")
|
||||
private String customColor = "#d3d3d3";
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package stirling.software.SPDF.pdf;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.contentstream.operator.Operator;
|
||||
import org.apache.pdfbox.contentstream.operator.OperatorName;
|
||||
import org.apache.pdfbox.cos.COSBase;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
|
||||
public class ImageFinder extends org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine {
|
||||
private boolean hasImages = false;
|
||||
|
||||
public ImageFinder(PDPage page) {
|
||||
super(page);
|
||||
}
|
||||
|
||||
public boolean hasImages() {
|
||||
return hasImages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
|
||||
String operation = operator.getName();
|
||||
if (operation.equals(OperatorName.DRAW_OBJECT)) {
|
||||
COSBase base = operands.get(0);
|
||||
if (base instanceof COSName) {
|
||||
COSName objectName = (COSName) base;
|
||||
PDXObject xobject = getResources().getXObject(objectName);
|
||||
if (xobject instanceof PDImageXObject) {
|
||||
hasImages = true;
|
||||
} else if (xobject instanceof PDFormXObject) {
|
||||
PDFormXObject form = (PDFormXObject) xobject;
|
||||
ImageFinder innerFinder = new ImageFinder(getPage());
|
||||
innerFinder.processPage(getPage());
|
||||
if (innerFinder.hasImages()) {
|
||||
hasImages = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.processOperator(operator, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawImage(PDImage pdImage) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clip(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveTo(float x, float y) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lineTo(float x, float y) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3)
|
||||
throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getCurrentPoint() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closePath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endPath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void strokePath() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillPath(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillAndStrokePath(int windingRule) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shadingFill(COSName shadingName) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
// ... rest of the overridden methods
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package stirling.software.SPDF.utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -11,12 +13,18 @@ import java.util.stream.Stream;
|
|||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import io.github.pixee.security.ZipSecurity;
|
||||
|
||||
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
|
||||
public class FileToPdf {
|
||||
|
||||
public static byte[] convertHtmlToPdf(
|
||||
byte[] fileBytes, String fileName, boolean htmlFormatsInstalled)
|
||||
HTMLToPdfRequest request,
|
||||
byte[] fileBytes,
|
||||
String fileName,
|
||||
boolean htmlFormatsInstalled)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||
|
@ -27,46 +35,52 @@ public class FileToPdf {
|
|||
tempInputFile = Files.createTempFile("input_", ".html");
|
||||
Files.write(tempInputFile, fileBytes);
|
||||
} else {
|
||||
tempInputFile = unzipAndGetMainHtml(fileBytes);
|
||||
tempInputFile = Files.createTempFile("input_", ".zip");
|
||||
Files.write(tempInputFile, fileBytes);
|
||||
}
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
if (!htmlFormatsInstalled) {
|
||||
command.add("weasyprint");
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
} else {
|
||||
command.add("wkhtmltopdf");
|
||||
command.add("--enable-local-file-access");
|
||||
}
|
||||
command.add("ebook-convert");
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
command.add("--paper-size");
|
||||
command.add("a4");
|
||||
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
ProcessExecutorResult returnCode;
|
||||
if (fileName.endsWith(".zip")) {
|
||||
|
||||
if (htmlFormatsInstalled) {
|
||||
// command.add(1, "--allow");
|
||||
// command.add(2, tempInputFile.getParent().toString());
|
||||
if (request.getZoom() != 1.0) {
|
||||
// Create a temporary CSS file
|
||||
File tempCssFile = Files.createTempFile("customStyle", ".css").toFile();
|
||||
try (FileWriter writer = new FileWriter(tempCssFile)) {
|
||||
// Write the CSS rule to the file
|
||||
writer.write("body { zoom: " + request.getZoom() + "; }");
|
||||
}
|
||||
command.add("--extra-css");
|
||||
command.add(tempCssFile.getAbsolutePath());
|
||||
}
|
||||
returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||
.runCommandWithOutputHandling(
|
||||
command, tempInputFile.getParent().toFile());
|
||||
} else {
|
||||
|
||||
returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||
.runCommandWithOutputHandling(command);
|
||||
}
|
||||
|
||||
ProcessExecutorResult returnCode;
|
||||
|
||||
returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||
.runCommandWithOutputHandling(command);
|
||||
|
||||
pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
} catch (IOException e) {
|
||||
pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||
if (pdfBytes.length < 1) {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
|
||||
// Clean up temporary files
|
||||
Files.delete(tempOutputFile);
|
||||
Files.delete(tempInputFile);
|
||||
|
||||
if (fileName.endsWith(".zip")) {
|
||||
GeneralUtils.deleteDirectory(tempInputFile.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
return pdfBytes;
|
||||
|
@ -74,7 +88,8 @@ public class FileToPdf {
|
|||
|
||||
private static Path unzipAndGetMainHtml(byte[] fileBytes) throws IOException {
|
||||
Path tempDirectory = Files.createTempDirectory("unzipped_");
|
||||
try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(fileBytes))) {
|
||||
try (ZipInputStream zipIn =
|
||||
ZipSecurity.createHardenedInputStream(new ByteArrayInputStream(fileBytes))) {
|
||||
ZipEntry entry = zipIn.getNextEntry();
|
||||
while (entry != null) {
|
||||
Path filePath = tempDirectory.resolve(entry.getName());
|
||||
|
@ -102,7 +117,7 @@ public class FileToPdf {
|
|||
|
||||
// Prioritize 'index.html' if it exists, otherwise use the first .html file
|
||||
for (Path htmlFile : htmlFiles) {
|
||||
if (htmlFile.getFileName().toString().equals("index.html")) {
|
||||
if ("index.html".equals(htmlFile.getFileName().toString())) {
|
||||
return htmlFile;
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +145,6 @@ public class FileToPdf {
|
|||
command.add("ebook-convert");
|
||||
command.add(tempInputFile.toString());
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
ProcessExecutorResult returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.CALIBRE)
|
||||
.runCommandWithOutputHandling(command);
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -13,14 +12,18 @@ import java.nio.file.Paths;
|
|||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.HostValidator;
|
||||
import io.github.pixee.security.Urls;
|
||||
|
||||
public class GeneralUtils {
|
||||
|
||||
public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
|
||||
File tempFile = File.createTempFile("temp", null);
|
||||
File tempFile = Files.createTempFile("temp", null).toFile();
|
||||
try (FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||
os.write(multipartFile.getBytes());
|
||||
}
|
||||
|
@ -57,7 +60,8 @@ public class GeneralUtils {
|
|||
|
||||
public static boolean isValidURL(String urlStr) {
|
||||
try {
|
||||
new URL(urlStr);
|
||||
Urls.create(
|
||||
urlStr, Urls.HTTP_PROTOCOLS, HostValidator.DENY_COMMON_INFRASTRUCTURE_TARGETS);
|
||||
return true;
|
||||
} catch (MalformedURLException e) {
|
||||
return false;
|
||||
|
@ -112,17 +116,36 @@ public class GeneralUtils {
|
|||
}
|
||||
|
||||
public static List<Integer> parsePageString(String pageOrder, int totalPages) {
|
||||
return parsePageList(pageOrder.split(","), totalPages);
|
||||
return parsePageString(pageOrder, totalPages, false);
|
||||
}
|
||||
|
||||
public static List<Integer> parsePageString(
|
||||
String pageOrder, int totalPages, boolean isOneBased) {
|
||||
if (pageOrder == null || pageOrder.isEmpty()) {
|
||||
return Collections.singletonList(1);
|
||||
}
|
||||
if (pageOrder.matches("\\d+")) {
|
||||
// Convert the single number string to an integer and return it in a list
|
||||
return Collections.singletonList(Integer.parseInt(pageOrder));
|
||||
}
|
||||
return parsePageList(pageOrder.split(","), totalPages, isOneBased);
|
||||
}
|
||||
|
||||
public static List<Integer> parsePageList(String[] pageOrderArr, int totalPages) {
|
||||
return parsePageList(pageOrderArr, totalPages, false);
|
||||
}
|
||||
|
||||
public static List<Integer> parsePageList(
|
||||
String[] pageOrderArr, int totalPages, boolean isOneBased) {
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
|
||||
int adjustmentFactor = isOneBased ? 1 : 0;
|
||||
|
||||
// loop through the page order array
|
||||
for (String element : pageOrderArr) {
|
||||
if (element.equalsIgnoreCase("all")) {
|
||||
if ("all".equalsIgnoreCase(element)) {
|
||||
for (int i = 0; i < totalPages; i++) {
|
||||
newPageOrder.add(i);
|
||||
newPageOrder.add(i + adjustmentFactor);
|
||||
}
|
||||
// As all pages are already added, no need to check further
|
||||
break;
|
||||
|
@ -135,11 +158,11 @@ public class GeneralUtils {
|
|||
|
||||
if (element.contains("n")) {
|
||||
String[] parts = element.split("n");
|
||||
if (!parts[0].equals("") && parts[0] != null) {
|
||||
if (!"".equals(parts[0]) && parts[0] != null) {
|
||||
coefficient = Integer.parseInt(parts[0]);
|
||||
coefficientExists = true;
|
||||
}
|
||||
if (parts.length > 1 && !parts[1].equals("") && parts[1] != null) {
|
||||
if (parts.length > 1 && !"".equals(parts[1]) && parts[1] != null) {
|
||||
constant = Integer.parseInt(parts[1]);
|
||||
constantExists = true;
|
||||
}
|
||||
|
@ -153,7 +176,7 @@ public class GeneralUtils {
|
|||
pageNum += constantExists ? constant : 0;
|
||||
|
||||
if (pageNum <= totalPages && pageNum > 0) {
|
||||
newPageOrder.add(pageNum - 1);
|
||||
newPageOrder.add(pageNum - adjustmentFactor);
|
||||
}
|
||||
}
|
||||
} else if (element.contains("-")) {
|
||||
|
@ -168,11 +191,11 @@ public class GeneralUtils {
|
|||
// loop through the range of pages
|
||||
for (int j = start; j <= end; j++) {
|
||||
// print the current index
|
||||
newPageOrder.add(j - 1);
|
||||
newPageOrder.add(j - adjustmentFactor);
|
||||
}
|
||||
} else {
|
||||
// if the element is a single page
|
||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||
newPageOrder.add(Integer.parseInt(element) - adjustmentFactor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
|
||||
public class PDFToFile {
|
||||
|
@ -32,7 +34,7 @@ public class PDFToFile {
|
|||
}
|
||||
|
||||
// Get the original PDF file name without the extension
|
||||
String originalPdfFileName = inputFile.getOriginalFilename();
|
||||
String originalPdfFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
|
||||
String pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.'));
|
||||
|
||||
// Validate output format
|
||||
|
@ -87,7 +89,7 @@ public class PDFToFile {
|
|||
if (outputFiles.size() == 1) {
|
||||
// Return single output file
|
||||
File outputFile = outputFiles.get(0);
|
||||
if (outputFormat.equals("txt:Text")) {
|
||||
if ("txt:Text".equals(outputFormat)) {
|
||||
outputFormat = "txt";
|
||||
}
|
||||
fileName = pdfBaseName + "." + outputFormat;
|
||||
|
|
|
@ -2,8 +2,10 @@ package stirling.software.SPDF.utils;
|
|||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
@ -16,11 +18,15 @@ import javax.imageio.ImageWriter;
|
|||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
|
||||
import org.apache.pdfbox.pdmodel.PDResources;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
|
@ -31,7 +37,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.pdf.ImageFinder;
|
||||
import io.github.pixee.security.Filenames;
|
||||
|
||||
public class PdfUtils {
|
||||
|
||||
|
@ -62,6 +68,23 @@ public class PdfUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static List<RenderedImage> getAllImages(PDResources resources) throws IOException {
|
||||
List<RenderedImage> images = new ArrayList<>();
|
||||
|
||||
for (COSName name : resources.getXObjectNames()) {
|
||||
PDXObject object = resources.getXObject(name);
|
||||
|
||||
if (object instanceof PDImageXObject) {
|
||||
images.add(((PDImageXObject) object).getImage());
|
||||
|
||||
} else if (object instanceof PDFormXObject) {
|
||||
images.addAll(getAllImages(((PDFormXObject) object).getResources()));
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
public static boolean hasImages(PDDocument document, String pagesToCheck) throws IOException {
|
||||
String[] pageOrderArr = pagesToCheck.split(",");
|
||||
List<Integer> pageList =
|
||||
|
@ -94,9 +117,7 @@ public class PdfUtils {
|
|||
}
|
||||
|
||||
public static boolean hasImagesOnPage(PDPage page) throws IOException {
|
||||
ImageFinder imageFinder = new ImageFinder(page);
|
||||
imageFinder.processPage(page);
|
||||
return imageFinder.hasImages();
|
||||
return getAllImages(page.getResources()).size() > 0;
|
||||
}
|
||||
|
||||
public static boolean hasTextOnPage(PDPage page, String phrase) throws IOException {
|
||||
|
@ -113,7 +134,7 @@ public class PdfUtils {
|
|||
PDFTextStripper textStripper = new PDFTextStripper();
|
||||
String pdfText = "";
|
||||
|
||||
if (pagesToCheck == null || pagesToCheck.equals("all")) {
|
||||
if (pagesToCheck == null || "all".equals(pagesToCheck)) {
|
||||
pdfText = textStripper.getText(pdfDocument);
|
||||
} else {
|
||||
// remove whitespaces
|
||||
|
@ -199,8 +220,8 @@ public class PdfUtils {
|
|||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
if (singleImage) {
|
||||
if (imageType.toLowerCase().equals("tiff")
|
||||
|| imageType.toLowerCase().equals("tif")) {
|
||||
if ("tiff".equals(imageType.toLowerCase())
|
||||
|| "tif".equals(imageType.toLowerCase())) {
|
||||
// Write the images to the output stream as a TIFF with multiple frames
|
||||
ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next();
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
|
@ -280,7 +301,7 @@ public class PdfUtils {
|
|||
try (PDDocument doc = new PDDocument()) {
|
||||
for (MultipartFile file : files) {
|
||||
String contentType = file.getContentType();
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String originalFilename = Filenames.toSimpleFileName(file.getOriginalFilename());
|
||||
if (originalFilename != null
|
||||
&& (originalFilename.toLowerCase().endsWith(".tiff")
|
||||
|| originalFilename.toLowerCase().endsWith(".tif"))) {
|
||||
|
@ -301,7 +322,7 @@ public class PdfUtils {
|
|||
ImageProcessingUtils.convertColorType(image, colorType);
|
||||
// Use JPEGFactory if it's JPEG since JPEG is lossy
|
||||
PDImageXObject pdImage =
|
||||
(contentType != null && contentType.equals("image/jpeg"))
|
||||
(contentType != null && "image/jpeg".equals(contentType))
|
||||
? JPEGFactory.createFromImage(doc, convertedImage)
|
||||
: LosslessFactory.createFromImage(doc, convertedImage);
|
||||
addImageToDocument(doc, pdImage, fitOption, autoRotate);
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.github.pixee.security.BoundedLineReader;
|
||||
|
||||
public class ProcessExecutor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);
|
||||
|
@ -109,7 +111,10 @@ public class ProcessExecutor {
|
|||
process.getErrorStream(),
|
||||
StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = errorReader.readLine()) != null) {
|
||||
while ((line =
|
||||
BoundedLineReader.readLine(
|
||||
errorReader, 5_000_000))
|
||||
!= null) {
|
||||
errorLines.add(line);
|
||||
if (liveUpdates) logger.info(line);
|
||||
}
|
||||
|
@ -130,7 +135,10 @@ public class ProcessExecutor {
|
|||
process.getInputStream(),
|
||||
StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = outputReader.readLine()) != null) {
|
||||
while ((line =
|
||||
BoundedLineReader.readLine(
|
||||
outputReader, 5_000_000))
|
||||
!= null) {
|
||||
outputLines.add(line);
|
||||
if (liveUpdates) logger.info(line);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
|
||||
public class WebResponseUtils {
|
||||
|
||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||
|
@ -26,7 +28,7 @@ public class WebResponseUtils {
|
|||
|
||||
public static ResponseEntity<byte[]> multiPartFileToWebResponse(MultipartFile file)
|
||||
throws IOException {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileName = Filenames.toSimpleFileName(file.getOriginalFilename());
|
||||
MediaType mediaType = MediaType.parseMediaType(file.getContentType());
|
||||
|
||||
byte[] bytes = file.getBytes();
|
||||
|
|
|
@ -22,7 +22,7 @@ server.servlet.context-path=${SYSTEM_ROOTURIPATH:/}
|
|||
spring.devtools.restart.enabled=true
|
||||
spring.devtools.livereload.enabled=true
|
||||
|
||||
spring.thymeleaf.encoding=UTF-8
|
||||
spring.thymeleaf.encoding=UTF-8
|
||||
|
||||
server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:5m}
|
||||
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:300000}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
||||
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
||||
/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___|
|
||||
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
||||
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
||||
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
||||
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
||||
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
||||
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
||||
Powered by Spring Boot ${spring-boot.version}
|
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,7 @@ imgPrompt=Изберете изображение(я)
|
|||
genericSubmit=Подайте
|
||||
processTimeWarning=Предупреждение: Този процес може да отнеме до минута в зависимост от размера на файла
|
||||
pageOrderPrompt=Персонализиран ред на страниците (Въведете разделен със запетаи списък с номера на страници или функции като 2n+1):
|
||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
||||
goToPage=Давай
|
||||
true=Вярно
|
||||
false=Невярно
|
||||
|
@ -42,9 +43,10 @@ red=Червено
|
|||
green=Зелено
|
||||
blue=Синьо
|
||||
custom=Персонализиране...
|
||||
WorkInProgess=Work in progress, May not work or be buggy, Please report any ploblems!
|
||||
WorkInProgess=Work in progress, May not work or be buggy, Please report any problems!
|
||||
poweredBy=Powered by
|
||||
|
||||
yes=Yes
|
||||
no=No
|
||||
changedCredsMessage=Идентификационните данни са променени!
|
||||
notAuthenticatedMessage=Потребителят не е автентикиран.
|
||||
userNotFoundMessage=Потребителят не е намерен
|
||||
|
@ -68,6 +70,7 @@ pipelineOptions.header=Pipeline Configuration
|
|||
pipelineOptions.pipelineNameLabel=Pipeline Name
|
||||
pipelineOptions.saveSettings=Save Operation Settings
|
||||
pipelineOptions.pipelineNamePrompt=Enter pipeline name here
|
||||
pipelineOptions.selectOperation=Select Operation
|
||||
pipelineOptions.addOperationButton=Add operation
|
||||
pipelineOptions.pipelineHeader=Pipeline:
|
||||
pipelineOptions.saveButton=Download
|
||||
|
@ -118,7 +121,7 @@ account.accountSettings=Настройки на акаунта
|
|||
account.adminSettings=Настройки на администратора - Преглед и добавяне на потребители
|
||||
account.userControlSettings=Настройки за потребителски контрол
|
||||
account.changeUsername=Промени потребител
|
||||
account.changeUsername=Промени потребител
|
||||
account.newUsername=Ново потребителско име
|
||||
account.password=Парола за потвърждение
|
||||
account.oldPassword=Стара парола
|
||||
account.newPassword=Нова парола
|
||||
|
@ -360,11 +363,11 @@ PdfToSinglePage.tags=единична страница
|
|||
|
||||
home.showJS.title=Показване на Javascript
|
||||
home.showJS.desc=Търси и показва всеки JS, инжектиран в PDF
|
||||
showJS.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит
|
||||
showJS.tags=JS
|
||||
|
||||
home.autoRedact.title=Автоматично редактиране
|
||||
home.autoRedact.desc=Автоматично редактира (зачернява) текст в PDF въз основа на въведен текст
|
||||
showJS.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит
|
||||
autoRedact.tags=Редактиране,Скриване,затъмняване,черен,маркер,скрит
|
||||
|
||||
home.tableExtraxt.title=PDF to CSV
|
||||
home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV
|
||||
|
@ -384,6 +387,11 @@ home.split-by-sections.title=Split PDF by Sections
|
|||
home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections
|
||||
split-by-sections.tags=Section Split, Divide, Customize
|
||||
|
||||
home.AddStampRequest.title=Add Stamp to PDF
|
||||
home.AddStampRequest.desc=Add text or add image stamps at set locations
|
||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||
|
||||
|
||||
###########################
|
||||
# #
|
||||
# WEB PAGES #
|
||||
|
@ -403,7 +411,7 @@ autoRedact.title=Автоматично редактиране
|
|||
autoRedact.header=Автоматично редактиране
|
||||
autoRedact.colorLabel=Цвят
|
||||
autoRedact.textsToRedactLabel=Текст за редактиране (разделен с редове)
|
||||
autoRedact.textsToRedactPlaceholder=например: \nПоверително \nСтрого секретно
|
||||
autoRedact.textsToRedactPlaceholder=например: \nПоверително \nСтрого секретно
|
||||
autoRedact.useRegexLabel=Използване на Regex
|
||||
autoRedact.wholeWordSearchLabel=Търсене на цялата дума
|
||||
autoRedact.customPaddingLabel=Персонализирана допълнителна подложка
|
||||
|
@ -459,6 +467,37 @@ HTMLToPDF.header=HTML към PDF
|
|||
HTMLToPDF.help=Приема HTML файлове и ZIP файлове, съдържащи html/css/изображения и т.н
|
||||
HTMLToPDF.submit=Преобразуване
|
||||
HTMLToPDF.credit=Използва WeasyPrint
|
||||
HTMLToPDF.zoom=Zoom level for displaying the website.
|
||||
HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default)
|
||||
HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default)
|
||||
HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default)
|
||||
HTMLToPDF.printBackground=Render the background of websites.
|
||||
HTMLToPDF.defaultHeader=Enable Default Header (Name and page number)
|
||||
HTMLToPDF.cssMediaType=Change the CSS media type of the page.
|
||||
HTMLToPDF.none=None
|
||||
HTMLToPDF.print=Print
|
||||
HTMLToPDF.screen=Screen
|
||||
|
||||
|
||||
#AddStampRequest
|
||||
AddStampRequest.header=Stamp PDF
|
||||
AddStampRequest.title=Stamp PDF
|
||||
AddStampRequest.stampType=Stamp Type
|
||||
AddStampRequest.stampText=Stamp Text
|
||||
AddStampRequest.stampImage=Stamp Image
|
||||
AddStampRequest.alphabet=Alphabet
|
||||
AddStampRequest.fontSize=Font/Image Size
|
||||
AddStampRequest.rotation=Rotation
|
||||
AddStampRequest.opacity=Opacity
|
||||
AddStampRequest.position=Position
|
||||
AddStampRequest.overrideX=Override X Coordinate
|
||||
AddStampRequest.overrideY=Override Y Coordinate
|
||||
AddStampRequest.customMargin=Custom Margin
|
||||
AddStampRequest.customColor=Custom Text Color
|
||||
AddStampRequest.submit=Submit
|
||||
|
||||
|
||||
#sanitizePDF
|
||||
|
@ -619,7 +658,7 @@ ScannerImageSplit.selectText.8=Задава минималния праг на
|
|||
ScannerImageSplit.selectText.9=Размер на рамката:
|
||||
ScannerImageSplit.selectText.10=Задава размера на добавената и премахната граница, за да предотврати бели граници към изхода (по подразбиране: 1).
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
ocr.title=OCR / Почистване на сканиране
|
||||
ocr.header=Почистващи сканирания / OCR (оптично разпознаване на знаци)
|
||||
|
@ -738,8 +777,8 @@ imageToPDF.selectText.2=Автоматично завъртане на PDF
|
|||
imageToPDF.selectText.3=Файлова логика с много (Активирано само ако работите с множество изображения)
|
||||
imageToPDF.selectText.4=Сливане към един PDF
|
||||
imageToPDF.selectText.5=Преобразуване към отделни PDF файлове
|
||||
|
||||
|
||||
|
||||
|
||||
#pdfToImage
|
||||
pdfToImage.title=PDF към Изображение
|
||||
pdfToImage.header=PDF към Изображение
|
||||
|
@ -829,7 +868,6 @@ changeMetadata.keywords=Ключови думи:
|
|||
changeMetadata.modDate=Дата на промяна (гггг/ММ/дд ЧЧ:мм:сс):
|
||||
changeMetadata.producer=Продуцент:
|
||||
changeMetadata.subject=Тема:
|
||||
changeMetadata.title=Заглавие:
|
||||
changeMetadata.trapped=В капан:
|
||||
changeMetadata.selectText.4=Други метаданни:
|
||||
changeMetadata.selectText.5=Добавяне на персонализиране метаданни
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue