mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-12 17:52:13 +02:00
Merge branch 'Stirling-Tools:main' into main
This commit is contained in:
commit
ecec1e6350
@ -8,11 +8,29 @@
|
|||||||
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
||||||
"dockerfile": "../Dockerfile.dev"
|
"dockerfile": "../Dockerfile.dev"
|
||||||
},
|
},
|
||||||
|
"runArgs": [
|
||||||
|
"-e",
|
||||||
|
"GIT_EDITOR=code --wait",
|
||||||
|
"--security-opt",
|
||||||
|
"label=disable"
|
||||||
|
],
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"appPort": [8080],
|
"forwardPorts": [8080, 2002, 2003],
|
||||||
|
"portsAttributes": {
|
||||||
|
"8080": {
|
||||||
|
"label": "Stirling-PDF Dev Port"
|
||||||
|
},
|
||||||
|
"2002": {
|
||||||
|
"label": "unoserver Port"
|
||||||
|
},
|
||||||
|
"2003": {
|
||||||
|
"label": "UnoConvert Port"
|
||||||
|
}
|
||||||
|
},
|
||||||
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
|
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
|
||||||
"mounts": [
|
"mounts": [
|
||||||
"source=logs-volume,target=/workspace/logs,type=volume"
|
"source=logs-volume,target=/workspace/logs,type=volume",
|
||||||
|
"source=build-volume,target=/workspace/build,type=volume"
|
||||||
],
|
],
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
// Configure tool-specific properties.
|
// Configure tool-specific properties.
|
||||||
@ -97,18 +115,17 @@
|
|||||||
"Oracle.oracle-java", // Oracle Java extension with additional features for Java development
|
"Oracle.oracle-java", // Oracle Java extension with additional features for Java development
|
||||||
"streetsidesoftware.code-spell-checker", // Spell checker for code to avoid typos
|
"streetsidesoftware.code-spell-checker", // Spell checker for code to avoid typos
|
||||||
"vmware.vscode-boot-dev-pack", // Developer tools for Spring Boot by VMware
|
"vmware.vscode-boot-dev-pack", // Developer tools for Spring Boot by VMware
|
||||||
"vmware.vscode-spring-boot", // Spring Boot tools by VMware for enhanced Spring development
|
|
||||||
"vscjava.vscode-java-pack", // Java Extension Pack with essential Java tools for VS Code
|
"vscjava.vscode-java-pack", // Java Extension Pack with essential Java tools for VS Code
|
||||||
"vscjava.vscode-spring-boot-dashboard", // Spring Boot dashboard for managing and visualizing Spring Boot applications
|
|
||||||
"vscjava.vscode-spring-initializr", // Support for Spring Initializr to create new Spring projects
|
|
||||||
"EditorConfig.EditorConfig", // EditorConfig support for maintaining consistent coding styles
|
"EditorConfig.EditorConfig", // EditorConfig support for maintaining consistent coding styles
|
||||||
"ms-azuretools.vscode-docker", // Docker extension for Visual Studio Code
|
"ms-azuretools.vscode-docker", // Docker extension for Visual Studio Code
|
||||||
"charliermarsh.ruff" // Ruff extension for Ruff language support
|
"charliermarsh.ruff", // Ruff extension for Ruff language support
|
||||||
|
"github.vscode-github-actions" // GitHub Actions extension for Visual Studio Code
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
|
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
|
||||||
"remoteUser": "devuser",
|
"remoteUser": "devuser",
|
||||||
"shutdownAction": "stopContainer",
|
"shutdownAction": "stopContainer",
|
||||||
"postStartCommand": "./scripts/init-setup.sh"
|
"initializeCommand": "bash ./.devcontainer/git-init.sh",
|
||||||
|
"postStartCommand": "./.devcontainer/init-setup.sh"
|
||||||
}
|
}
|
||||||
|
19
.devcontainer/git-init.sh
Normal file
19
.devcontainer/git-init.sh
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GIT_USER=$(git config --get user.name)
|
||||||
|
GIT_EMAIL=$(git config --get user.email)
|
||||||
|
|
||||||
|
# Exit if GIT_USER or GIT_EMAIL is empty
|
||||||
|
if [ -z "$GIT_USER" ] || [ -z "$GIT_EMAIL" ]; then
|
||||||
|
echo "GIT_USER or GIT_EMAIL is not set. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git config --local user.name "$GIT_USER"
|
||||||
|
git config --local user.email "$GIT_EMAIL"
|
||||||
|
|
||||||
|
# This directory should contain custom Git hooks for the repository
|
||||||
|
# Set the path for Git hooks to /workspace/hooks
|
||||||
|
git config --local core.hooksPath '%(prefix)/workspace/hooks'
|
||||||
|
# Set the safe directory to the workspace path
|
||||||
|
git config --local --add safe.directory /workspace
|
75
.devcontainer/init-setup.sh
Normal file
75
.devcontainer/init-setup.sh
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Dev Container Initialization Script (init-setup.sh)
|
||||||
|
#
|
||||||
|
# This script runs when the Dev Container starts and provides guidance on
|
||||||
|
# how to interact with the project. It prints an ASCII logo, displays the
|
||||||
|
# current user, changes to the project root, and then shows helpful command
|
||||||
|
# instructions.
|
||||||
|
#
|
||||||
|
# Instructions for future developers:
|
||||||
|
#
|
||||||
|
# - To start the application, use:
|
||||||
|
# ./gradlew bootRun --no-daemon -Dspring-boot.run.fork=true -Dserver.address=0.0.0.0
|
||||||
|
#
|
||||||
|
# - To run tests, use:
|
||||||
|
# ./gradlew test
|
||||||
|
#
|
||||||
|
# - To build the project, use:
|
||||||
|
# ./gradlew build
|
||||||
|
#
|
||||||
|
# - For running pre-commit hooks (if configured), use:
|
||||||
|
# pre-commit run --all-files
|
||||||
|
#
|
||||||
|
# Make sure you are in the project root directory after this script executes.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "Devcontainer started successfully!"
|
||||||
|
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
GRADLE_VERSION=$(gradle -version | grep "^Gradle " | awk '{print $2}')
|
||||||
|
GRADLE_PATH=$(which gradle)
|
||||||
|
JAVA_VERSION=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}')
|
||||||
|
JAVA_PATH=$(which java)
|
||||||
|
|
||||||
|
echo """
|
||||||
|
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
||||||
|
/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___|
|
||||||
|
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
||||||
|
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
||||||
|
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
||||||
|
"""
|
||||||
|
echo -e "Stirling-PDF Version: \e[32m$VERSION\e[0m"
|
||||||
|
echo -e "Gradle Version: \e[32m$GRADLE_VERSION\e[0m"
|
||||||
|
echo -e "Gradle Path: \e[32m$GRADLE_PATH\e[0m"
|
||||||
|
echo -e "Java Version: \e[32m$JAVA_VERSION\e[0m"
|
||||||
|
echo -e "Java Path: \e[32m$JAVA_PATH\e[0m"
|
||||||
|
|
||||||
|
# Display current active user (for permission/debugging purposes)
|
||||||
|
echo -e "Current user: \e[32m$(whoami)\e[0m"
|
||||||
|
|
||||||
|
# Change directory to the project root (parent directory of the script)
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
echo -e "Changed to project root: \e[32m$(pwd)\e[0m"
|
||||||
|
|
||||||
|
# Display available commands for developers
|
||||||
|
echo "=================================================================="
|
||||||
|
echo "Available commands:"
|
||||||
|
echo ""
|
||||||
|
echo " To start unoserver: "
|
||||||
|
echo -e "\e[34m nohup /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0 > /tmp/unoserver.log 2>&1 &\e[0m"
|
||||||
|
echo
|
||||||
|
echo " To start the application: "
|
||||||
|
echo -e "\e[34m gradle bootRun\e[0m"
|
||||||
|
echo ""
|
||||||
|
echo " To run tests: "
|
||||||
|
echo -e "\e[34m gradle test\e[0m"
|
||||||
|
echo ""
|
||||||
|
echo " To build the project: "
|
||||||
|
echo -e "\e[34m gradle build\e[0m"
|
||||||
|
echo ""
|
||||||
|
echo " To run pre-commit hooks (if configured):"
|
||||||
|
echo -e "\e[34m pre-commit run --all-files -c .pre-commit-config.yaml\e[0m"
|
||||||
|
echo "=================================================================="
|
6
.github/labeler-config.yml
vendored
6
.github/labeler-config.yml
vendored
@ -54,8 +54,8 @@ Docker:
|
|||||||
- any-glob-to-any-file: '.github/workflows/build.yml'
|
- any-glob-to-any-file: '.github/workflows/build.yml'
|
||||||
- any-glob-to-any-file: '.github/workflows/push-docker.yml'
|
- any-glob-to-any-file: '.github/workflows/push-docker.yml'
|
||||||
- any-glob-to-any-file: 'Dockerfile'
|
- any-glob-to-any-file: 'Dockerfile'
|
||||||
- any-glob-to-any-file: 'Dockerfile.*'
|
- any-glob-to-any-file: 'Dockerfile.fat'
|
||||||
- any-glob-to-any-file: '!Dockerfile.dev'
|
- any-glob-to-any-file: 'Dockerfile.ultra-lite'
|
||||||
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
|
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
|
||||||
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
||||||
- any-glob-to-any-file: 'scripts/init.sh'
|
- any-glob-to-any-file: 'scripts/init.sh'
|
||||||
@ -67,7 +67,7 @@ Docker:
|
|||||||
Devtools:
|
Devtools:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: '.devcontainer/**/*'
|
- any-glob-to-any-file: '.devcontainer/**/*'
|
||||||
- any-glob-to-any-file: '!Dockerfile.dev'
|
- any-glob-to-any-file: 'Dockerfile.dev'
|
||||||
|
|
||||||
Test:
|
Test:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -26,7 +26,7 @@ clientWebUI/
|
|||||||
!cucumber/exampleFiles/
|
!cucumber/exampleFiles/
|
||||||
!cucumber/exampleFiles/example_html.zip
|
!cucumber/exampleFiles/example_html.zip
|
||||||
exampleYmlFiles/stirling/
|
exampleYmlFiles/stirling/
|
||||||
|
/testing/file_snapshots
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
.lock
|
.lock
|
||||||
|
@ -1,53 +1,54 @@
|
|||||||
# dockerfile.dev
|
# dockerfile.dev
|
||||||
|
|
||||||
# Basisimage: Gradle mit JDK 17 (Debian-basiert)
|
# Basisimage: Gradle mit JDK 17 (Debian-basiert)
|
||||||
FROM gradle:8.12-jdk17
|
FROM gradle:8.13-jdk17
|
||||||
|
|
||||||
# Als Root-Benutzer arbeiten, um benötigte Pakete zu installieren
|
# Als Root-Benutzer arbeiten, um benötigte Pakete zu installieren
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
|
# Set GRADLE_HOME und füge Gradle zum PATH hinzu
|
||||||
|
ENV GRADLE_HOME=/opt/gradle
|
||||||
|
ENV PATH="$GRADLE_HOME/bin:$PATH"
|
||||||
|
|
||||||
# Update und Installation zusätzlicher Pakete (Debian/Ubuntu-basiert)
|
# Update und Installation zusätzlicher Pakete (Debian/Ubuntu-basiert)
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
wget \
|
sudo \
|
||||||
ca-certificates \
|
|
||||||
tzdata \
|
|
||||||
tini \
|
|
||||||
bash \
|
|
||||||
curl \
|
|
||||||
libreoffice \
|
libreoffice \
|
||||||
poppler-utils \
|
poppler-utils \
|
||||||
qpdf \
|
qpdf \
|
||||||
|
# settings.yml | tessdataDir: /usr/share/tesseract-ocr/5/tessdata
|
||||||
tesseract-ocr \
|
tesseract-ocr \
|
||||||
tesseract-ocr-eng \
|
tesseract-ocr-eng \
|
||||||
fonts-dejavu \
|
fonts-terminus fonts-dejavu fonts-font-awesome fonts-noto fonts-noto-core fonts-noto-cjk fonts-noto-extra fonts-liberation fonts-linuxlibertine \
|
||||||
fonts-noto \
|
python3-uno \
|
||||||
python3 \
|
|
||||||
python3-pip \
|
|
||||||
python3-venv \
|
python3-venv \
|
||||||
|
# ss -tln
|
||||||
|
iproute2 \
|
||||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Setze die Environment Variable für setuptools
|
# Setze die Environment Variable für setuptools
|
||||||
ENV SETUPTOOLS_USE_DISTUTILS=local
|
ENV SETUPTOOLS_USE_DISTUTILS=local
|
||||||
|
|
||||||
# Installation der benötigten Python-Pakete
|
# Installation der benötigten Python-Pakete
|
||||||
RUN python3 -m venv /opt/venv \
|
RUN python3 -m venv --system-site-packages /opt/venv \
|
||||||
&& . /opt/venv/bin/activate \
|
&& . /opt/venv/bin/activate \
|
||||||
&& pip install --upgrade setuptools \
|
|
||||||
&& pip install --no-cache-dir WeasyPrint pdf2image pillow unoserver opencv-python-headless pre-commit
|
&& pip install --no-cache-dir WeasyPrint pdf2image pillow unoserver opencv-python-headless pre-commit
|
||||||
|
|
||||||
# Füge den venv-Pfad zur globalen PATH-Variable hinzu, damit die Tools verfügbar sind
|
# Füge den venv-Pfad zur globalen PATH-Variable hinzu, damit die Tools verfügbar sind
|
||||||
ENV PATH="/opt/venv/bin:$PATH"
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
# Erstelle notwendige Verzeichnisse und lege einen Nicht‑Root Benutzer an
|
COPY . /workspace
|
||||||
RUN mkdir -p /home/devuser/{configs,logs,customFiles,pipeline/watchedFolders,pipeline/finishedFolders} \
|
|
||||||
&& adduser --disabled-password --gecos '' devuser \
|
|
||||||
&& chown -R devuser:devuser /home/devuser
|
|
||||||
|
|
||||||
RUN mkdir -p /home/devuser/logs /workspace/logs /workspace/scripts /workspace/src/main/resources \
|
RUN adduser --disabled-password --gecos '' devuser \
|
||||||
&& chown -R devuser:devuser /home/devuser /workspace
|
&& chown -R devuser:devuser /home/devuser /workspace
|
||||||
|
RUN echo "devuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devuser \
|
||||||
|
&& chmod 0440 /etc/sudoers.d/devuser
|
||||||
|
|
||||||
# Setze das Arbeitsverzeichnis (wird später per Bind-Mount überschrieben)
|
# Setze das Arbeitsverzeichnis (wird später per Bind-Mount überschrieben)
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
RUN chmod +x /workspace/.devcontainer/git-init.sh
|
||||||
|
RUN sudo chmod +x /workspace/.devcontainer/init-setup.sh
|
||||||
|
|
||||||
# Wechsel zum Nicht‑Root Benutzer
|
# Wechsel zum Nicht‑Root Benutzer
|
||||||
USER devuser
|
USER devuser
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
# Build the application
|
# Build the application
|
||||||
FROM gradle:8.12-jdk17 AS build
|
FROM gradle:8.12-jdk21 AS build
|
||||||
|
|
||||||
|
COPY build.gradle .
|
||||||
|
COPY settings.gradle .
|
||||||
|
COPY gradlew .
|
||||||
|
COPY gradle gradle/
|
||||||
|
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||||
|
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@ -10,7 +16,7 @@ COPY . .
|
|||||||
# Build the application with DOCKER_ENABLE_SECURITY=false
|
# Build the application with DOCKER_ENABLE_SECURITY=false
|
||||||
RUN DOCKER_ENABLE_SECURITY=true \
|
RUN DOCKER_ENABLE_SECURITY=true \
|
||||||
STIRLING_PDF_DESKTOP_UI=false \
|
STIRLING_PDF_DESKTOP_UI=false \
|
||||||
./gradlew clean build
|
./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube
|
||||||
|
|
||||||
# Main stage
|
# Main stage
|
||||||
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
||||||
|
10
README.md
10
README.md
@ -120,7 +120,7 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
@ -128,7 +128,7 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
@ -148,13 +148,13 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.44.0"
|
version = "0.44.1"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
whoami
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
echo "Devcontainer started..."
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.apache.pdfbox.examples.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/** A custom RandomAccessRead implementation that deletes the file when closed */
|
||||||
|
@Slf4j
|
||||||
|
public class DeletingRandomAccessFile extends RandomAccessReadBufferedFile {
|
||||||
|
private final Path tempFilePath;
|
||||||
|
|
||||||
|
public DeletingRandomAccessFile(File file) throws IOException {
|
||||||
|
super(file);
|
||||||
|
this.tempFilePath = file.toPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
try {
|
||||||
|
super.close();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
boolean deleted = Files.deleteIfExists(tempFilePath);
|
||||||
|
if (deleted) {
|
||||||
|
log.info("Successfully deleted temp file: {}", tempFilePath);
|
||||||
|
} else {
|
||||||
|
log.warn("Failed to delete temp file (may not exist): {}", tempFilePath);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error deleting temp file: {}", tempFilePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ public class AnalysisController {
|
|||||||
summary = "Get PDF page count",
|
summary = "Get PDF page count",
|
||||||
description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO")
|
description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Integer> getPageCount(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Integer> getPageCount(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
return Map.of("pageCount", document.getNumberOfPages());
|
return Map.of("pageCount", document.getNumberOfPages());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ public class AnalysisController {
|
|||||||
summary = "Get basic PDF information",
|
summary = "Get basic PDF information",
|
||||||
description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO")
|
description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Object> getBasicInfo(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Object> getBasicInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
Map<String, Object> info = new HashMap<>();
|
Map<String, Object> info = new HashMap<>();
|
||||||
info.put("pageCount", document.getNumberOfPages());
|
info.put("pageCount", document.getNumberOfPages());
|
||||||
info.put("pdfVersion", document.getVersion());
|
info.put("pdfVersion", document.getVersion());
|
||||||
@ -62,7 +62,7 @@ public class AnalysisController {
|
|||||||
description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO")
|
description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, String> getDocumentProperties(@ModelAttribute PDFFile file)
|
public Map<String, String> getDocumentProperties(@ModelAttribute PDFFile file)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
PDDocumentInformation info = document.getDocumentInformation();
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
Map<String, String> properties = new HashMap<>();
|
Map<String, String> properties = new HashMap<>();
|
||||||
properties.put("title", info.getTitle());
|
properties.put("title", info.getTitle());
|
||||||
@ -83,7 +83,7 @@ public class AnalysisController {
|
|||||||
description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO")
|
description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO")
|
||||||
public List<Map<String, Float>> getPageDimensions(@ModelAttribute PDFFile file)
|
public List<Map<String, Float>> getPageDimensions(@ModelAttribute PDFFile file)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
List<Map<String, Float>> dimensions = new ArrayList<>();
|
List<Map<String, Float>> dimensions = new ArrayList<>();
|
||||||
PDPageTree pages = document.getPages();
|
PDPageTree pages = document.getPages();
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ public class AnalysisController {
|
|||||||
description =
|
description =
|
||||||
"Returns count and details of form fields. Input:PDF Output:JSON Type:SISO")
|
"Returns count and details of form fields. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Object> getFormFields(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Object> getFormFields(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
Map<String, Object> formInfo = new HashMap<>();
|
Map<String, Object> formInfo = new HashMap<>();
|
||||||
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
|
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ public class AnalysisController {
|
|||||||
summary = "Get annotation information",
|
summary = "Get annotation information",
|
||||||
description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO")
|
description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Object> getAnnotationInfo(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Object> getAnnotationInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
Map<String, Object> annotInfo = new HashMap<>();
|
Map<String, Object> annotInfo = new HashMap<>();
|
||||||
int totalAnnotations = 0;
|
int totalAnnotations = 0;
|
||||||
Map<String, Integer> annotationTypes = new HashMap<>();
|
Map<String, Integer> annotationTypes = new HashMap<>();
|
||||||
@ -150,7 +150,7 @@ public class AnalysisController {
|
|||||||
description =
|
description =
|
||||||
"Returns list of fonts used in the document. Input:PDF Output:JSON Type:SISO")
|
"Returns list of fonts used in the document. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Object> getFontInfo(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Object> getFontInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
Map<String, Object> fontInfo = new HashMap<>();
|
Map<String, Object> fontInfo = new HashMap<>();
|
||||||
Set<String> fontNames = new HashSet<>();
|
Set<String> fontNames = new HashSet<>();
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ public class AnalysisController {
|
|||||||
description =
|
description =
|
||||||
"Returns encryption and permission details. Input:PDF Output:JSON Type:SISO")
|
"Returns encryption and permission details. Input:PDF Output:JSON Type:SISO")
|
||||||
public Map<String, Object> getSecurityInfo(@ModelAttribute PDFFile file) throws IOException {
|
public Map<String, Object> getSecurityInfo(@ModelAttribute PDFFile file) throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) {
|
||||||
Map<String, Object> securityInfo = new HashMap<>();
|
Map<String, Object> securityInfo = new HashMap<>();
|
||||||
PDEncryption encryption = document.getEncryption();
|
PDEncryption encryption = document.getEncryption();
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ public class CropController {
|
|||||||
description =
|
description =
|
||||||
"This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
"This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")
|
||||||
public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
|
public ResponseEntity<byte[]> cropPdf(@ModelAttribute CropPdfForm form) throws IOException {
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(form.getFileInput().getBytes());
|
PDDocument sourceDocument = pdfDocumentFactory.load(form);
|
||||||
|
|
||||||
PDDocument newDocument =
|
PDDocument newDocument =
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
|
@ -100,8 +100,8 @@ public class MergeController {
|
|||||||
};
|
};
|
||||||
case "byPDFTitle":
|
case "byPDFTitle":
|
||||||
return (file1, file2) -> {
|
return (file1, file2) -> {
|
||||||
try (PDDocument doc1 = pdfDocumentFactory.load(file1.getBytes());
|
try (PDDocument doc1 = pdfDocumentFactory.load(file1);
|
||||||
PDDocument doc2 = pdfDocumentFactory.load(file2.getBytes())) {
|
PDDocument doc2 = pdfDocumentFactory.load(file2)) {
|
||||||
String title1 = doc1.getDocumentInformation().getTitle();
|
String title1 = doc1.getDocumentInformation().getTitle();
|
||||||
String title2 = doc2.getDocumentInformation().getTitle();
|
String title2 = doc2.getDocumentInformation().getTitle();
|
||||||
return title1.compareTo(title2);
|
return title1.compareTo(title2);
|
||||||
|
@ -63,7 +63,7 @@ public class MultiPageLayoutController {
|
|||||||
: (int) Math.sqrt(pagesPerSheet);
|
: (int) Math.sqrt(pagesPerSheet);
|
||||||
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
|
int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);
|
||||||
|
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes());
|
PDDocument sourceDocument = pdfDocumentFactory.load(file);
|
||||||
PDDocument newDocument =
|
PDDocument newDocument =
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
PDPage newPage = new PDPage(PDRectangle.A4);
|
PDPage newPage = new PDPage(PDRectangle.A4);
|
||||||
|
@ -250,7 +250,7 @@ public class RearrangePagesPDFController {
|
|||||||
String sortType = request.getCustomMode();
|
String sortType = request.getCustomMode();
|
||||||
try {
|
try {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfFile.getBytes());
|
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
||||||
|
|
||||||
// Split the page order string into an array of page numbers or range of numbers
|
// Split the page order string into an array of page numbers or range of numbers
|
||||||
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
|
String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
|
||||||
|
@ -51,7 +51,7 @@ public class ScalePagesController {
|
|||||||
String targetPDRectangle = request.getPageSize();
|
String targetPDRectangle = request.getPageSize();
|
||||||
float scaleFactor = request.getScaleFactor();
|
float scaleFactor = request.getScaleFactor();
|
||||||
|
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes());
|
PDDocument sourceDocument = pdfDocumentFactory.load(file);
|
||||||
PDDocument outputDocument =
|
PDDocument outputDocument =
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class SplitPDFController {
|
|||||||
String pages = request.getPageNumbers();
|
String pages = request.getPageNumbers();
|
||||||
// open the pdf document
|
// open the pdf document
|
||||||
|
|
||||||
document = pdfDocumentFactory.load(file.getBytes());
|
document = pdfDocumentFactory.load(file);
|
||||||
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
||||||
int totalPages = document.getNumberOfPages();
|
int totalPages = document.getNumberOfPages();
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||||
|
@ -139,7 +139,7 @@ public class SplitPdfByChaptersController {
|
|||||||
if (bookmarkLevel < 0) {
|
if (bookmarkLevel < 0) {
|
||||||
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
|
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
|
||||||
}
|
}
|
||||||
sourceDocument = pdfDocumentFactory.load(file.getBytes());
|
sourceDocument = pdfDocumentFactory.load(file);
|
||||||
|
|
||||||
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
|
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ public class SplitPdfBySectionsController {
|
|||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
|
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes());
|
PDDocument sourceDocument = pdfDocumentFactory.load(file);
|
||||||
|
|
||||||
// Process the PDF based on split parameters
|
// Process the PDF based on split parameters
|
||||||
int horiz = request.getHorizontalDivisions() + 1;
|
int horiz = request.getHorizontalDivisions() + 1;
|
||||||
|
@ -45,7 +45,7 @@ public class ToSinglePageController {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Load the source document
|
// Load the source document
|
||||||
PDDocument sourceDocument = pdfDocumentFactory.load(request.getFileInput().getBytes());
|
PDDocument sourceDocument = pdfDocumentFactory.load(request);
|
||||||
|
|
||||||
// Calculate total height and max width
|
// Calculate total height and max width
|
||||||
float totalHeight = 0;
|
float totalHeight = 0;
|
||||||
|
@ -74,7 +74,7 @@ public class ConvertImgPDFController {
|
|||||||
;
|
;
|
||||||
try {
|
try {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
byte[] newPdfBytes = rearrangePdfPages(file.getBytes(), pageOrderArr);
|
byte[] newPdfBytes = rearrangePdfPages(file, pageOrderArr);
|
||||||
|
|
||||||
ImageType colorTypeResult = ImageType.RGB;
|
ImageType colorTypeResult = ImageType.RGB;
|
||||||
if ("greyscale".equals(colorType)) {
|
if ("greyscale".equals(colorType)) {
|
||||||
@ -243,9 +243,10 @@ public class ConvertImgPDFController {
|
|||||||
* @return A byte array of the rearranged PDF.
|
* @return A byte array of the rearranged PDF.
|
||||||
* @throws IOException If an error occurs while processing the PDF.
|
* @throws IOException If an error occurs while processing the PDF.
|
||||||
*/
|
*/
|
||||||
private byte[] rearrangePdfPages(byte[] pdfBytes, String[] pageOrderArr) throws IOException {
|
private byte[] rearrangePdfPages(MultipartFile pdfFile, String[] pageOrderArr)
|
||||||
|
throws IOException {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfBytes);
|
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
||||||
int totalPages = document.getNumberOfPages();
|
int totalPages = document.getNumberOfPages();
|
||||||
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class ConvertPDFToOffice {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String outputFormat = request.getOutputFormat();
|
String outputFormat = request.getOutputFormat();
|
||||||
if ("txt".equals(request.getOutputFormat())) {
|
if ("txt".equals(request.getOutputFormat())) {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(inputFile)) {
|
||||||
PDFTextStripper stripper = new PDFTextStripper();
|
PDFTextStripper stripper = new PDFTextStripper();
|
||||||
String text = stripper.getText(document);
|
String text = stripper.getText(document);
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
@ -59,7 +59,7 @@ public class ExtractCSVController {
|
|||||||
String baseName = getBaseName(form.getFileInput().getOriginalFilename());
|
String baseName = getBaseName(form.getFileInput().getOriginalFilename());
|
||||||
List<CsvEntry> csvEntries = new ArrayList<>();
|
List<CsvEntry> csvEntries = new ArrayList<>();
|
||||||
|
|
||||||
try (PDDocument document = pdfDocumentFactory.load(form.getFileInput().getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(form)) {
|
||||||
List<Integer> pages = form.getPageNumbersList(document, true);
|
List<Integer> pages = form.getPageNumbersList(document, true);
|
||||||
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
|
SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm();
|
||||||
CSVFormat format =
|
CSVFormat format =
|
||||||
|
@ -49,7 +49,7 @@ public class FilterController {
|
|||||||
String text = request.getText();
|
String text = request.getText();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = pdfDocumentFactory.load(inputFile.getBytes());
|
PDDocument pdfDocument = pdfDocumentFactory.load(inputFile);
|
||||||
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
if (PdfUtils.hasText(pdfDocument, pageNumber, text))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||||
@ -66,7 +66,7 @@ public class FilterController {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String pageNumber = request.getPageNumbers();
|
String pageNumber = request.getPageNumbers();
|
||||||
|
|
||||||
PDDocument pdfDocument = pdfDocumentFactory.load(inputFile.getBytes());
|
PDDocument pdfDocument = pdfDocumentFactory.load(inputFile);
|
||||||
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
if (PdfUtils.hasImages(pdfDocument, pageNumber))
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||||
@ -83,7 +83,7 @@ public class FilterController {
|
|||||||
String pageCount = request.getPageCount();
|
String pageCount = request.getPageCount();
|
||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(inputFile.getBytes());
|
PDDocument document = pdfDocumentFactory.load(inputFile);
|
||||||
int actualPageCount = document.getNumberOfPages();
|
int actualPageCount = document.getNumberOfPages();
|
||||||
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
@ -117,7 +117,7 @@ public class FilterController {
|
|||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(inputFile.getBytes());
|
PDDocument document = pdfDocumentFactory.load(inputFile);
|
||||||
|
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
PDRectangle actualPageSize = firstPage.getMediaBox();
|
PDRectangle actualPageSize = firstPage.getMediaBox();
|
||||||
@ -193,7 +193,7 @@ public class FilterController {
|
|||||||
String comparator = request.getComparator();
|
String comparator = request.getComparator();
|
||||||
|
|
||||||
// Load the PDF
|
// Load the PDF
|
||||||
PDDocument document = pdfDocumentFactory.load(inputFile.getBytes());
|
PDDocument document = pdfDocumentFactory.load(inputFile);
|
||||||
|
|
||||||
// Get the rotation of the first page
|
// Get the rotation of the first page
|
||||||
PDPage firstPage = document.getPage(0);
|
PDPage firstPage = document.getPage(0);
|
||||||
|
@ -52,7 +52,7 @@ public class AutoRenameController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
|
Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback();
|
||||||
|
|
||||||
PDDocument document = pdfDocumentFactory.load(file.getBytes());
|
PDDocument document = pdfDocumentFactory.load(file);
|
||||||
PDFTextStripper reader =
|
PDFTextStripper reader =
|
||||||
new PDFTextStripper() {
|
new PDFTextStripper() {
|
||||||
List<LineInfo> lineInfos = new ArrayList<>();
|
List<LineInfo> lineInfos = new ArrayList<>();
|
||||||
|
@ -84,7 +84,7 @@ public class BlankPageController {
|
|||||||
int threshold = request.getThreshold();
|
int threshold = request.getThreshold();
|
||||||
float whitePercent = request.getWhitePercent();
|
float whitePercent = request.getWhitePercent();
|
||||||
|
|
||||||
try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(inputFile)) {
|
||||||
PDPageTree pages = document.getDocumentCatalog().getPages();
|
PDPageTree pages = document.getDocumentCatalog().getPages();
|
||||||
PDFTextStripper textStripper = new PDFTextStripper();
|
PDFTextStripper textStripper = new PDFTextStripper();
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public class DecompressPdfController {
|
|||||||
|
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
|
|
||||||
try (PDDocument document = pdfDocumentFactory.load(file.getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(file)) {
|
||||||
// Process all objects in document
|
// Process all objects in document
|
||||||
processAllObjects(document);
|
processAllObjects(document);
|
||||||
|
|
||||||
|
@ -95,8 +95,7 @@ public class ExtractImageScansController {
|
|||||||
// Check if input file is a PDF
|
// Check if input file is a PDF
|
||||||
if ("pdf".equalsIgnoreCase(extension)) {
|
if ("pdf".equalsIgnoreCase(extension)) {
|
||||||
// Load PDF document
|
// Load PDF document
|
||||||
try (PDDocument document =
|
try (PDDocument document = pdfDocumentFactory.load(form.getFileInput())) {
|
||||||
pdfDocumentFactory.load(form.getFileInput().getBytes())) {
|
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
pdfRenderer.setSubsamplingAllowed(true);
|
||||||
int pageCount = document.getNumberOfPages();
|
int pageCount = document.getNumberOfPages();
|
||||||
|
@ -67,7 +67,7 @@ public class ExtractImagesController {
|
|||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
String format = request.getFormat();
|
String format = request.getFormat();
|
||||||
boolean allowDuplicates = request.isAllowDuplicates();
|
boolean allowDuplicates = request.isAllowDuplicates();
|
||||||
PDDocument document = pdfDocumentFactory.load(file.getBytes());
|
PDDocument document = pdfDocumentFactory.load(file);
|
||||||
|
|
||||||
// Determine if multithreading should be used based on PDF size or number of pages
|
// Determine if multithreading should be used based on PDF size or number of pages
|
||||||
boolean useMultithreading = shouldUseMultithreading(file, document);
|
boolean useMultithreading = shouldUseMultithreading(file, document);
|
||||||
|
@ -50,7 +50,7 @@ public class FlattenController {
|
|||||||
public ResponseEntity<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception {
|
public ResponseEntity<byte[]> flatten(@ModelAttribute FlattenRequest request) throws Exception {
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
|
|
||||||
PDDocument document = pdfDocumentFactory.load(file.getBytes());
|
PDDocument document = pdfDocumentFactory.load(file);
|
||||||
Boolean flattenOnlyForms = request.getFlattenOnlyForms();
|
Boolean flattenOnlyForms = request.getFlattenOnlyForms();
|
||||||
|
|
||||||
if (Boolean.TRUE.equals(flattenOnlyForms)) {
|
if (Boolean.TRUE.equals(flattenOnlyForms)) {
|
||||||
|
@ -84,7 +84,7 @@ public class MetadataController {
|
|||||||
allRequestParams = new java.util.HashMap<String, String>();
|
allRequestParams = new java.util.HashMap<String, String>();
|
||||||
}
|
}
|
||||||
// Load the PDF file into a PDDocument
|
// Load the PDF file into a PDDocument
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfFile.getBytes());
|
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
||||||
|
|
||||||
// Get the document information from the PDF
|
// Get the document information from the PDF
|
||||||
PDDocumentInformation info = document.getDocumentInformation();
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
|
@ -55,8 +55,7 @@ public class PageNumbersController {
|
|||||||
String pagesToNumber = request.getPagesToNumber();
|
String pagesToNumber = request.getPagesToNumber();
|
||||||
String customText = request.getCustomText();
|
String customText = request.getCustomText();
|
||||||
int pageNumber = startingNumber;
|
int pageNumber = startingNumber;
|
||||||
byte[] fileBytes = file.getBytes();
|
PDDocument document = pdfDocumentFactory.load(file);
|
||||||
PDDocument document = pdfDocumentFactory.load(fileBytes);
|
|
||||||
float font_size = request.getFontSize();
|
float font_size = request.getFontSize();
|
||||||
String font_type = request.getFontType();
|
String font_type = request.getFontType();
|
||||||
float marginFactor;
|
float marginFactor;
|
||||||
|
@ -43,7 +43,7 @@ public class ShowJavascript {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
String script = "";
|
String script = "";
|
||||||
|
|
||||||
try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) {
|
try (PDDocument document = pdfDocumentFactory.load(inputFile)) {
|
||||||
|
|
||||||
if (document.getDocumentCatalog() != null
|
if (document.getDocumentCatalog() != null
|
||||||
&& document.getDocumentCatalog().getNames() != null) {
|
&& document.getDocumentCatalog().getNames() != null) {
|
||||||
|
@ -90,7 +90,7 @@ public class CertSignController {
|
|||||||
|
|
||||||
private static void sign(
|
private static void sign(
|
||||||
CustomPDDocumentFactory pdfDocumentFactory,
|
CustomPDDocumentFactory pdfDocumentFactory,
|
||||||
byte[] input,
|
MultipartFile input,
|
||||||
OutputStream output,
|
OutputStream output,
|
||||||
CreateSignature instance,
|
CreateSignature instance,
|
||||||
Boolean showSignature,
|
Boolean showSignature,
|
||||||
@ -179,7 +179,7 @@ public class CertSignController {
|
|||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
sign(
|
sign(
|
||||||
pdfDocumentFactory,
|
pdfDocumentFactory,
|
||||||
pdf.getBytes(),
|
pdf,
|
||||||
baos,
|
baos,
|
||||||
createSignature,
|
createSignature,
|
||||||
showSignature,
|
showSignature,
|
||||||
|
@ -126,7 +126,7 @@ public class GetInfoOnPDF {
|
|||||||
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
|
@Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO")
|
||||||
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
|
public ResponseEntity<byte[]> getPdfInfo(@ModelAttribute PDFFile request) throws IOException {
|
||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
try (PDDocument pdfBoxDoc = pdfDocumentFactory.load(inputFile.getBytes()); ) {
|
try (PDDocument pdfBoxDoc = pdfDocumentFactory.load(inputFile); ) {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
ObjectNode jsonOutput = objectMapper.createObjectNode();
|
||||||
|
|
||||||
|
@ -4,10 +4,12 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum UsernameAttribute {
|
public enum UsernameAttribute {
|
||||||
|
MAIL("mail"),
|
||||||
EMAIL("email"),
|
EMAIL("email"),
|
||||||
LOGIN("login"),
|
LOGIN("login"),
|
||||||
PROFILE("profile"),
|
PROFILE("profile"),
|
||||||
NAME("name"),
|
NAME("name"),
|
||||||
|
UID("uid"),
|
||||||
USERNAME("username"),
|
USERNAME("username"),
|
||||||
NICKNAME("nickname"),
|
NICKNAME("nickname"),
|
||||||
GIVEN_NAME("given_name"),
|
GIVEN_NAME("given_name"),
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package stirling.software.SPDF.model.api;
|
package stirling.software.SPDF.model.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.Loader;
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
@ -32,18 +30,6 @@ public class PDFWithPageNums extends PDFFile {
|
|||||||
requiredMode = RequiredMode.NOT_REQUIRED)
|
requiredMode = RequiredMode.NOT_REQUIRED)
|
||||||
private String pageNumbers;
|
private String pageNumbers;
|
||||||
|
|
||||||
@Hidden
|
|
||||||
public List<Integer> getPageNumbersList(boolean zeroCount) {
|
|
||||||
int pageCount = 0;
|
|
||||||
try {
|
|
||||||
pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
log.error("exception", e);
|
|
||||||
}
|
|
||||||
return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Hidden
|
@Hidden
|
||||||
public List<Integer> getPageNumbersList(PDDocument doc, boolean oneBased) {
|
public List<Integer> getPageNumbersList(PDDocument doc, boolean oneBased) {
|
||||||
int pageCount = 0;
|
int pageCount = 0;
|
||||||
|
@ -10,9 +10,9 @@ import java.nio.file.StandardCopyOption;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
|
import org.apache.pdfbox.examples.util.DeletingRandomAccessFile;
|
||||||
import org.apache.pdfbox.io.IOUtils;
|
import org.apache.pdfbox.io.IOUtils;
|
||||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||||
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
|
|
||||||
import org.apache.pdfbox.io.RandomAccessStreamCache.StreamCacheCreateFunction;
|
import org.apache.pdfbox.io.RandomAccessStreamCache.StreamCacheCreateFunction;
|
||||||
import org.apache.pdfbox.io.ScratchFile;
|
import org.apache.pdfbox.io.ScratchFile;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -102,16 +102,29 @@ public class CustomPDDocumentFactory {
|
|||||||
|
|
||||||
// Since we don't know the size upfront, buffer to a temp file
|
// Since we don't know the size upfront, buffer to a temp file
|
||||||
Path tempFile = createTempFile("pdf-stream-");
|
Path tempFile = createTempFile("pdf-stream-");
|
||||||
try {
|
|
||||||
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
return loadAdaptively(tempFile.toFile(), Files.size(tempFile));
|
return loadAdaptively(tempFile.toFile(), Files.size(tempFile));
|
||||||
} catch (IOException e) {
|
|
||||||
cleanupFile(tempFile);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PDDocument loadAdaptively(Object source, long contentSize) throws IOException {
|
/** Load with password from InputStream */
|
||||||
|
public PDDocument load(InputStream input, String password) throws IOException {
|
||||||
|
if (input == null) {
|
||||||
|
throw new IllegalArgumentException("InputStream cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we don't know the size upfront, buffer to a temp file
|
||||||
|
Path tempFile = createTempFile("pdf-stream-");
|
||||||
|
|
||||||
|
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
return loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the appropriate caching strategy based on file size and available memory. This
|
||||||
|
* common method is used by both password and non-password loading paths.
|
||||||
|
*/
|
||||||
|
private StreamCacheCreateFunction getStreamCacheFunction(long contentSize) {
|
||||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||||
@ -129,32 +142,38 @@ public class CustomPDDocumentFactory {
|
|||||||
usedMemory / (1024 * 1024),
|
usedMemory / (1024 * 1024),
|
||||||
maxMemory / (1024 * 1024));
|
maxMemory / (1024 * 1024));
|
||||||
|
|
||||||
// Determine caching strategy based on both file size and available memory
|
|
||||||
StreamCacheCreateFunction cacheFunction;
|
|
||||||
|
|
||||||
// If free memory is critically low, always use file-based caching
|
// If free memory is critically low, always use file-based caching
|
||||||
// In loadAdaptively method, replace current caching strategy decision with:
|
|
||||||
if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE
|
if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE
|
||||||
|| actualFreeMemory < MIN_FREE_MEMORY_BYTES) {
|
|| actualFreeMemory < MIN_FREE_MEMORY_BYTES) {
|
||||||
log.info(
|
log.info(
|
||||||
"Low memory detected ({}%), forcing file-based cache",
|
"Low memory detected ({}%), forcing file-based cache",
|
||||||
String.format("%.2f", freeMemoryPercent));
|
String.format("%.2f", freeMemoryPercent));
|
||||||
cacheFunction = createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
|
return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
|
||||||
} else if (contentSize < SMALL_FILE_THRESHOLD) {
|
} else if (contentSize < SMALL_FILE_THRESHOLD) {
|
||||||
log.info("Using memory-only cache for small document ({}KB)", contentSize / 1024);
|
log.info("Using memory-only cache for small document ({}KB)", contentSize / 1024);
|
||||||
cacheFunction = IOUtils.createMemoryOnlyStreamCache();
|
return IOUtils.createMemoryOnlyStreamCache();
|
||||||
} else if (contentSize < LARGE_FILE_THRESHOLD) {
|
} else if (contentSize < LARGE_FILE_THRESHOLD) {
|
||||||
// For medium files (10-50MB), use a mixed approach
|
// For medium files (10-50MB), use a mixed approach
|
||||||
log.info(
|
log.info(
|
||||||
"Using mixed memory/file cache for medium document ({}MB)",
|
"Using mixed memory/file cache for medium document ({}MB)",
|
||||||
contentSize / (1024 * 1024));
|
contentSize / (1024 * 1024));
|
||||||
cacheFunction =
|
return createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE));
|
||||||
createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE));
|
|
||||||
} else {
|
} else {
|
||||||
log.info("Using file-based cache for large document");
|
log.info("Using file-based cache for large document");
|
||||||
cacheFunction = createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
|
return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the existing loadAdaptively method to use the common function */
|
||||||
|
private PDDocument loadAdaptively(Object source, long contentSize) throws IOException {
|
||||||
|
// Get the appropriate caching strategy
|
||||||
|
StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize);
|
||||||
|
|
||||||
|
//If small handle as bytes and remove original file
|
||||||
|
if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) {
|
||||||
|
source = Files.readAllBytes(file.toPath());
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
PDDocument document;
|
PDDocument document;
|
||||||
if (source instanceof File file) {
|
if (source instanceof File file) {
|
||||||
document = loadFromFile(file, contentSize, cacheFunction);
|
document = loadFromFile(file, contentSize, cacheFunction);
|
||||||
@ -168,6 +187,50 @@ public class CustomPDDocumentFactory {
|
|||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Load a PDF with password protection using adaptive loading strategies */
|
||||||
|
private PDDocument loadAdaptivelyWithPassword(Object source, long contentSize, String password)
|
||||||
|
throws IOException {
|
||||||
|
// Get the appropriate caching strategy
|
||||||
|
StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize);
|
||||||
|
//If small handle as bytes and remove original file
|
||||||
|
if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) {
|
||||||
|
source = Files.readAllBytes(file.toPath());
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
PDDocument document;
|
||||||
|
if (source instanceof File file) {
|
||||||
|
document = loadFromFileWithPassword(file, contentSize, cacheFunction, password);
|
||||||
|
} else if (source instanceof byte[] bytes) {
|
||||||
|
document = loadFromBytesWithPassword(bytes, contentSize, cacheFunction, password);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
postProcessDocument(document);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load a file with password */
|
||||||
|
private PDDocument loadFromFileWithPassword(
|
||||||
|
File file, long size, StreamCacheCreateFunction cache, String password)
|
||||||
|
throws IOException {
|
||||||
|
return Loader.loadPDF(new DeletingRandomAccessFile(file), password, null, null, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load bytes with password */
|
||||||
|
private PDDocument loadFromBytesWithPassword(
|
||||||
|
byte[] bytes, long size, StreamCacheCreateFunction cache, String password)
|
||||||
|
throws IOException {
|
||||||
|
if (size >= SMALL_FILE_THRESHOLD) {
|
||||||
|
log.info("Writing large byte array to temp file for password-protected PDF");
|
||||||
|
Path tempFile = createTempFile("pdf-bytes-");
|
||||||
|
|
||||||
|
Files.write(tempFile, bytes);
|
||||||
|
return Loader.loadPDF(tempFile.toFile(), password, null, null, cache);
|
||||||
|
}
|
||||||
|
return Loader.loadPDF(bytes, password, null, null, cache);
|
||||||
|
}
|
||||||
|
|
||||||
private StreamCacheCreateFunction createScratchFileCacheFunction(MemoryUsageSetting settings) {
|
private StreamCacheCreateFunction createScratchFileCacheFunction(MemoryUsageSetting settings) {
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
@ -185,11 +248,7 @@ public class CustomPDDocumentFactory {
|
|||||||
|
|
||||||
private PDDocument loadFromFile(File file, long size, StreamCacheCreateFunction cache)
|
private PDDocument loadFromFile(File file, long size, StreamCacheCreateFunction cache)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (size >= EXTREMELY_LARGE_THRESHOLD) {
|
return Loader.loadPDF(new DeletingRandomAccessFile(file), "", null, null, cache);
|
||||||
log.info("Loading extremely large file via buffered access");
|
|
||||||
return Loader.loadPDF(new RandomAccessReadBufferedFile(file), "", null, null, cache);
|
|
||||||
}
|
|
||||||
return Loader.loadPDF(file, "", null, null, cache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache)
|
private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache)
|
||||||
@ -197,12 +256,9 @@ public class CustomPDDocumentFactory {
|
|||||||
if (size >= SMALL_FILE_THRESHOLD) {
|
if (size >= SMALL_FILE_THRESHOLD) {
|
||||||
log.info("Writing large byte array to temp file");
|
log.info("Writing large byte array to temp file");
|
||||||
Path tempFile = createTempFile("pdf-bytes-");
|
Path tempFile = createTempFile("pdf-bytes-");
|
||||||
try {
|
|
||||||
Files.write(tempFile, bytes);
|
Files.write(tempFile, bytes);
|
||||||
return Loader.loadPDF(tempFile.toFile(), "", null, null, cache);
|
return loadFromFile(tempFile.toFile(), size, cache);
|
||||||
} finally {
|
|
||||||
cleanupFile(tempFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Loader.loadPDF(bytes, "", null, null, cache);
|
return Loader.loadPDF(bytes, "", null, null, cache);
|
||||||
}
|
}
|
||||||
@ -225,12 +281,9 @@ public class CustomPDDocumentFactory {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Path tempFile = createTempFile("pdf-save-");
|
Path tempFile = createTempFile("pdf-save-");
|
||||||
try {
|
|
||||||
document.save(tempFile.toFile());
|
document.save(tempFile.toFile());
|
||||||
return Files.readAllBytes(tempFile);
|
return Files.readAllBytes(tempFile);
|
||||||
} finally {
|
|
||||||
cleanupFile(tempFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,17 +311,6 @@ public class CustomPDDocumentFactory {
|
|||||||
return Files.createTempDirectory(prefix + tempCounter.incrementAndGet() + "-");
|
return Files.createTempDirectory(prefix + tempCounter.incrementAndGet() + "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clean up a temporary file */
|
|
||||||
private void cleanupFile(Path file) {
|
|
||||||
try {
|
|
||||||
if (Files.deleteIfExists(file)) {
|
|
||||||
log.info("Deleted temp file: {}", file);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.info("Error deleting temp file {}", file, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create new document bytes based on an existing document */
|
/** Create new document bytes based on an existing document */
|
||||||
public byte[] createNewBytesBasedOnOldDocument(byte[] oldDocument) throws IOException {
|
public byte[] createNewBytesBasedOnOldDocument(byte[] oldDocument) throws IOException {
|
||||||
try (PDDocument document = load(oldDocument)) {
|
try (PDDocument document = load(oldDocument)) {
|
||||||
@ -339,20 +381,11 @@ public class CustomPDDocumentFactory {
|
|||||||
|
|
||||||
/** Load from a MultipartFile */
|
/** Load from a MultipartFile */
|
||||||
public PDDocument load(MultipartFile pdfFile) throws IOException {
|
public PDDocument load(MultipartFile pdfFile) throws IOException {
|
||||||
return load(pdfFile.getBytes());
|
return load(pdfFile.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load with password from MultipartFile */
|
/** Load with password from MultipartFile */
|
||||||
public PDDocument load(MultipartFile fileInput, String password) throws IOException {
|
public PDDocument load(MultipartFile fileInput, String password) throws IOException {
|
||||||
return load(fileInput.getBytes(), password);
|
return load(fileInput.getInputStream(), password);
|
||||||
}
|
|
||||||
|
|
||||||
/** Load with password from byte array */
|
|
||||||
private PDDocument load(byte[] bytes, String password) throws IOException {
|
|
||||||
// Since we don't have direct password support in the adaptive loader,
|
|
||||||
// we'll need to use PDFBox's Loader directly
|
|
||||||
PDDocument document = Loader.loadPDF(bytes, password);
|
|
||||||
pdfMetadataService.setDefaultMetadata(document);
|
|
||||||
return document;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -27,11 +28,11 @@ public class FileInfo {
|
|||||||
// Formats the file size into a human-readable string.
|
// Formats the file size into a human-readable string.
|
||||||
public String getFormattedFileSize() {
|
public String getFormattedFileSize() {
|
||||||
if (fileSize >= 1024 * 1024 * 1024) {
|
if (fileSize >= 1024 * 1024 * 1024) {
|
||||||
return String.format("%.2f GB", fileSize / (1024.0 * 1024 * 1024));
|
return String.format(Locale.US, "%.2f GB", fileSize / (1024.0 * 1024 * 1024));
|
||||||
} else if (fileSize >= 1024 * 1024) {
|
} else if (fileSize >= 1024 * 1024) {
|
||||||
return String.format("%.2f MB", fileSize / (1024.0 * 1024));
|
return String.format(Locale.US, "%.2f MB", fileSize / (1024.0 * 1024));
|
||||||
} else if (fileSize >= 1024) {
|
} else if (fileSize >= 1024) {
|
||||||
return String.format("%.2f KB", fileSize / 1024.0);
|
return String.format(Locale.US, "%.2f KB", fileSize / 1024.0);
|
||||||
} else {
|
} else {
|
||||||
return String.format("%d Bytes", fileSize);
|
return String.format("%d Bytes", fileSize);
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,15 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
|
public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
|
||||||
File tempFile = Files.createTempFile("temp", null).toFile();
|
File tempFile = Files.createTempFile("temp", null).toFile();
|
||||||
try (FileOutputStream os = new FileOutputStream(tempFile)) {
|
try (InputStream inputStream = multipartFile.getInputStream();
|
||||||
os.write(multipartFile.getBytes());
|
FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tempFile;
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
###########
|
###########
|
||||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
addPageNumbers.fontSize=Font Size
|
addPageNumbers.fontSize=Розмір шрифту
|
||||||
addPageNumbers.fontName=Font Name
|
addPageNumbers.fontName=Назва шрифту
|
||||||
pdfPrompt=Оберіть PDF(и)
|
pdfPrompt=Оберіть PDF(и)
|
||||||
multiPdfPrompt=Оберіть PDFи (2+)
|
multiPdfPrompt=Оберіть PDFи (2+)
|
||||||
multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи
|
multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи
|
||||||
@ -56,15 +56,15 @@ userNotFoundMessage=Користувача не знайдено.
|
|||||||
incorrectPasswordMessage=Поточний пароль невірний.
|
incorrectPasswordMessage=Поточний пароль невірний.
|
||||||
usernameExistsMessage=Нове ім'я користувача вже існує.
|
usernameExistsMessage=Нове ім'я користувача вже існує.
|
||||||
invalidUsernameMessage=Недійсне ім’я користувача, ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
|
invalidUsernameMessage=Недійсне ім’я користувача, ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
|
||||||
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
|
invalidPasswordMessage=Пароль не повинен бути порожнім і не повинен мати пробілів на початку або в кінці.
|
||||||
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
confirmPasswordErrorMessage=Новий пароль і підтвердження нового пароля мають збігатися.
|
||||||
deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему.
|
deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему.
|
||||||
deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено.
|
deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено.
|
||||||
downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача
|
downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача
|
||||||
disabledCurrentUserMessage=The current user cannot be disabled
|
disabledCurrentUserMessage=Поточного користувача неможливо вимкнути
|
||||||
downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься.
|
downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=Користувач уже існує як користувач OAuth2.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=Користувач уже існує як веб-користувач.
|
||||||
error=Помилка
|
error=Помилка
|
||||||
oops=Упс!
|
oops=Упс!
|
||||||
help=Допомога
|
help=Допомога
|
||||||
@ -77,18 +77,18 @@ color=Колір
|
|||||||
sponsor=Спонсор
|
sponsor=Спонсор
|
||||||
info=Інформація
|
info=Інформація
|
||||||
pro=Pro
|
pro=Pro
|
||||||
page=Page
|
page=Сторінка
|
||||||
pages=Pages
|
pages=Сторінки
|
||||||
loading=Loading...
|
loading=Завантаження...
|
||||||
addToDoc=Add to Document
|
addToDoc=Додати до документу
|
||||||
reset=Reset
|
reset=Скинути
|
||||||
apply=Apply
|
apply=Застосувати
|
||||||
|
|
||||||
legal.privacy=Privacy Policy
|
legal.privacy=Політика конфіденційності
|
||||||
legal.terms=Terms and Conditions
|
legal.terms=Правила та умови
|
||||||
legal.accessibility=Accessibility
|
legal.accessibility=Доступність
|
||||||
legal.cookie=Cookie Policy
|
legal.cookie=Політика використання файлів cookie
|
||||||
legal.impressum=Impressum
|
legal.impressum=Вихідні дані
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
@ -100,7 +100,7 @@ pipeline.defaultOption=Користувацький
|
|||||||
pipeline.submitButton=Надіслати
|
pipeline.submitButton=Надіслати
|
||||||
pipeline.help=Довідка з конвеєрної обробки
|
pipeline.help=Довідка з конвеєрної обробки
|
||||||
pipeline.scanHelp=Довідка зі сканування папок
|
pipeline.scanHelp=Довідка зі сканування папок
|
||||||
pipeline.deletePrompt=Are you sure you want to delete pipeline
|
pipeline.deletePrompt=Ви впевнені, що хочете видалити конвеєр?
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@ -118,27 +118,27 @@ pipelineOptions.validateButton=Перевірити
|
|||||||
########################
|
########################
|
||||||
# ENTERPRISE EDITION #
|
# ENTERPRISE EDITION #
|
||||||
########################
|
########################
|
||||||
enterpriseEdition.button=Upgrade to Pro
|
enterpriseEdition.button=Оновлення до Pro
|
||||||
enterpriseEdition.warning=This feature is only available to Pro users.
|
enterpriseEdition.warning=Ця функція доступна лише для користувачів Pro.
|
||||||
enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
|
enterpriseEdition.yamlAdvert=Stirling PDF Pro підтримує конфігураційні файли YAML та інші функції SSO.
|
||||||
enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
|
enterpriseEdition.ssoAdvert=Шукаєте більше функцій керування користувачами? Перегляньте Stirling PDF Pro
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# Analytics #
|
# Analytics #
|
||||||
#################
|
#################
|
||||||
analytics.title=Do you want make Stirling PDF better?
|
analytics.title=Бажаєте покращити Stirling PDF?
|
||||||
analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
|
analytics.paragraph1=Stirling PDF увімкнув аналітику, щоб допомогти нам покращити продукт. Ми не відстежуємо жодну особисту інформацію чи вміст файлів.
|
||||||
analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
|
analytics.paragraph2=Увімкніть аналітику, щоб допомогти Stirling-PDF розвиватися та дозволити нам краще розуміти наших користувачів.
|
||||||
analytics.enable=Enable analytics
|
analytics.enable=Увімкнути аналітику
|
||||||
analytics.disable=Disable analytics
|
analytics.disable=Вимкнути аналітику
|
||||||
analytics.settings=You can change the settings for analytics in the config/settings.yml file
|
analytics.settings=Ви можете змінити параметри аналітики у файлі config/settings.yml
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
#############
|
#############
|
||||||
navbar.favorite=Обране
|
navbar.favorite=Обране
|
||||||
navbar.recent=New and recently updated
|
navbar.recent=Новий і нещодавно оновлений
|
||||||
navbar.darkmode=Темний режим
|
navbar.darkmode=Темний режим
|
||||||
navbar.language=Мови
|
navbar.language=Мови
|
||||||
navbar.settings=Налаштування
|
navbar.settings=Налаштування
|
||||||
@ -151,7 +151,7 @@ navbar.sections.convertFrom=Конвертувати з PDF
|
|||||||
navbar.sections.security=Підпис та Безпека
|
navbar.sections.security=Підпис та Безпека
|
||||||
navbar.sections.advance=Додаткове
|
navbar.sections.advance=Додаткове
|
||||||
navbar.sections.edit=Перегляд та Редагування
|
navbar.sections.edit=Перегляд та Редагування
|
||||||
navbar.sections.popular=Popular
|
navbar.sections.popular=Популярне
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
@ -167,9 +167,9 @@ settings.downloadOption.3=Завантажити файл
|
|||||||
settings.zipThreshold=Zip-файли, коли кількість завантажених файлів перевищує
|
settings.zipThreshold=Zip-файли, коли кількість завантажених файлів перевищує
|
||||||
settings.signOut=Вийти
|
settings.signOut=Вийти
|
||||||
settings.accountSettings=Налаштування акаунта
|
settings.accountSettings=Налаштування акаунта
|
||||||
settings.bored.help=Enables easter egg game
|
settings.bored.help=Вмикає гру «пасхальне яйце».
|
||||||
settings.cacheInputs.name=Save form inputs
|
settings.cacheInputs.name=Зберігати дані форм
|
||||||
settings.cacheInputs.help=Enable to store previously used inputs for future runs
|
settings.cacheInputs.help=Увімкнути для збереження раніше використаних вхідних даних для майбутніх прогонів
|
||||||
|
|
||||||
changeCreds.title=Змінити облікові дані
|
changeCreds.title=Змінити облікові дані
|
||||||
changeCreds.header=Оновіть дані вашого облікового запису
|
changeCreds.header=Оновіть дані вашого облікового запису
|
||||||
@ -210,7 +210,7 @@ adminUserSettings.user=Користувач
|
|||||||
adminUserSettings.addUser=Додати нового користувача
|
adminUserSettings.addUser=Додати нового користувача
|
||||||
adminUserSettings.deleteUser=Видалити користувача
|
adminUserSettings.deleteUser=Видалити користувача
|
||||||
adminUserSettings.confirmDeleteUser=Видалити цього користувача?
|
adminUserSettings.confirmDeleteUser=Видалити цього користувача?
|
||||||
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
adminUserSettings.confirmChangeUserStatus=Чи потрібно вимкнути/ввімкнути користувача?
|
||||||
adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
|
adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
|
||||||
adminUserSettings.roles=Ролі
|
adminUserSettings.roles=Ролі
|
||||||
adminUserSettings.role=Роль
|
adminUserSettings.role=Роль
|
||||||
@ -224,36 +224,36 @@ adminUserSettings.forceChange=Примусити користувача змін
|
|||||||
adminUserSettings.submit=Зберегти користувача
|
adminUserSettings.submit=Зберегти користувача
|
||||||
adminUserSettings.changeUserRole=Змінити роль користувача
|
adminUserSettings.changeUserRole=Змінити роль користувача
|
||||||
adminUserSettings.authenticated=Автентифіковано
|
adminUserSettings.authenticated=Автентифіковано
|
||||||
adminUserSettings.editOwnProfil=Edit own profile
|
adminUserSettings.editOwnProfil=Редагувати власний профіль
|
||||||
adminUserSettings.enabledUser=enabled user
|
adminUserSettings.enabledUser=активний користувач
|
||||||
adminUserSettings.disabledUser=disabled user
|
adminUserSettings.disabledUser=заблокований користувач
|
||||||
adminUserSettings.activeUsers=Active Users:
|
adminUserSettings.activeUsers=Активні користувачі:
|
||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Заблоковані користувачі:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Всього користувачів:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Останній запит
|
||||||
|
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Імпорт/експорт бази даних
|
||||||
database.header=Database Import/Export
|
database.header=Імпорт/експорт бази даних
|
||||||
database.fileName=File Name
|
database.fileName=Ім'я файлу
|
||||||
database.creationDate=Creation Date
|
database.creationDate=Дата створення
|
||||||
database.fileSize=File Size
|
database.fileSize=Розмір файлу
|
||||||
database.deleteBackupFile=Delete Backup File
|
database.deleteBackupFile=Видалити файл резервної копії
|
||||||
database.importBackupFile=Import Backup File
|
database.importBackupFile=Імпортувати файл резервної копії
|
||||||
database.createBackupFile=Create Backup File
|
database.createBackupFile=Створити файл резервної копії
|
||||||
database.downloadBackupFile=Download Backup File
|
database.downloadBackupFile=Завантажте файл резервної копії
|
||||||
database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
|
database.info_1=При імпорті даних важливо забезпечити правильну структуру. Якщо ви не впевнені у своїх діях, зверніться за професійною допомогою. Помилка в структурі може призвести до збоїв у роботі програми та призвести до повної непрацездатності.
|
||||||
database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
|
database.info_2=Ім'я файлу під час завантаження не має значення. Воно буде перейменовано на формат backup_user_yyyyMMddHHmm.sql для забезпечення одноманітності найменувань.
|
||||||
database.submit=Import Backup
|
database.submit=Імпорт резервної копії
|
||||||
database.importIntoDatabaseSuccessed=Import into database successed
|
database.importIntoDatabaseSuccessed=Імпорт до бази даних виконано вдало
|
||||||
database.backupCreated=Database backup successful
|
database.backupCreated=Резервне копіювання бази даних успішно
|
||||||
database.fileNotFound=File not Found
|
database.fileNotFound=Файл не знайдено
|
||||||
database.fileNullOrEmpty=File must not be null or empty
|
database.fileNullOrEmpty=Файл не має бути пустим
|
||||||
database.failedImportFile=Failed Import File
|
database.failedImportFile=Не вдалося імпортувати файл
|
||||||
database.notSupported=This function is not available for your database connection.
|
database.notSupported=Ця функція недоступна для вашого підключення до бази даних.
|
||||||
|
|
||||||
session.expired=Your session has expired. Please refresh the page and try again.
|
session.expired=Ваш сеанс закінчився. Будь ласка, оновіть сторінку та повторіть спробу.
|
||||||
session.refreshPage=Refresh Page
|
session.refreshPage=Оновити сторінку
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -262,128 +262,128 @@ home.desc=Ваш локальний універсальний магазин д
|
|||||||
home.searchBar=Пошук функцій...
|
home.searchBar=Пошук функцій...
|
||||||
|
|
||||||
|
|
||||||
home.viewPdf.title=View/Edit PDF
|
home.viewPdf.title=Перегляд/редагування PDF
|
||||||
home.viewPdf.desc=Перегляд, анотація, додавання тексту або зображень
|
home.viewPdf.desc=Перегляд, анотація, додавання тексту або зображень
|
||||||
viewPdf.tags=view,read,annotate,text,image
|
viewPdf.tags=перегляд,читання,анотації,текст,зображення
|
||||||
|
|
||||||
home.setFavorites=Set Favourites
|
home.setFavorites=Налаштувати обрані
|
||||||
home.hideFavorites=Hide Favourites
|
home.hideFavorites=Приховати обрані
|
||||||
home.showFavorites=Show Favourites
|
home.showFavorites=Показати обрані
|
||||||
home.legacyHomepage=Old homepage
|
home.legacyHomepage=Стара сторінка
|
||||||
home.newHomePage=Try our new homepage!
|
home.newHomePage=Спробуйте нову сторінку!
|
||||||
home.alphabetical=Alphabetical
|
home.alphabetical=Абеткою
|
||||||
home.globalPopularity=Global Popularity
|
home.globalPopularity=Глобальною поулярністю
|
||||||
home.sortBy=Sort by:
|
home.sortBy=Сортувати за:
|
||||||
|
|
||||||
home.multiTool.title=Мультіінструмент PDF
|
home.multiTool.title=Мультіінструмент PDF
|
||||||
home.multiTool.desc=Об'єднання, поворот, зміна порядку та видалення сторінок
|
home.multiTool.desc=Об'єднання, поворот, зміна порядку та видалення сторінок
|
||||||
multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
|
multiTool.tags=мультиінструмент,багатоопераційний,інтерфейс,перетягування,клієнтська частина,інтерактивний
|
||||||
|
|
||||||
home.merge.title=Об'єднати
|
home.merge.title=Об'єднати
|
||||||
home.merge.desc=Легко об'єднуйте кілька PDF-файлів у один.
|
home.merge.desc=Легко об'єднуйте кілька PDF-файлів у один.
|
||||||
merge.tags=merge,Page operations,Back end,server side
|
merge.tags=об'єднання,операції зі сторінками,серверна частина
|
||||||
|
|
||||||
home.split.title=Розділити
|
home.split.title=Розділити
|
||||||
home.split.desc=Розділіть PDF-файли на кілька документів
|
home.split.desc=Розділіть PDF-файли на кілька документів
|
||||||
split.tags=Page operations,divide,Multi Page,cut,server side
|
split.tags=операції зі сторінками,розділення,багатосторінковий,вирізання,серверна частина
|
||||||
|
|
||||||
home.rotate.title=Повернути
|
home.rotate.title=Повернути
|
||||||
home.rotate.desc=Легко повертайте ваші PDF-файли.
|
home.rotate.desc=Легко повертайте ваші PDF-файли.
|
||||||
rotate.tags=server side
|
rotate.tags=серверна частина
|
||||||
|
|
||||||
|
|
||||||
home.imageToPdf.title=Зображення в PDF
|
home.imageToPdf.title=Зображення в PDF
|
||||||
home.imageToPdf.desc=Перетворення зображення (PNG, JPEG, GIF) в PDF.
|
home.imageToPdf.desc=Перетворення зображення (PNG, JPEG, GIF) в PDF.
|
||||||
imageToPdf.tags=conversion,img,jpg,picture,photo
|
imageToPdf.tags=конвертація,зображення,jpg,картинка,фото
|
||||||
|
|
||||||
home.pdfToImage.title=PDF в зображення
|
home.pdfToImage.title=PDF в зображення
|
||||||
home.pdfToImage.desc=Перетворення PDF в зображення. (PNG, JPEG, GIF)
|
home.pdfToImage.desc=Перетворення PDF в зображення. (PNG, JPEG, GIF)
|
||||||
pdfToImage.tags=conversion,img,jpg,picture,photo
|
pdfToImage.tags=конвертація,зображення,jpg,картинка,фото
|
||||||
|
|
||||||
home.pdfOrganiser.title=Реорганізація
|
home.pdfOrganiser.title=Реорганізація
|
||||||
home.pdfOrganiser.desc=Видалення/перестановка сторінок у будь-якому порядку
|
home.pdfOrganiser.desc=Видалення/перестановка сторінок у будь-якому порядку
|
||||||
pdfOrganiser.tags=duplex,even,odd,sort,move
|
pdfOrganiser.tags=двосторонній друк,парні,непарні,сортування,переміщення
|
||||||
|
|
||||||
|
|
||||||
home.addImage.title=Додати зображення
|
home.addImage.title=Додати зображення
|
||||||
home.addImage.desc=Додає зображення у вказане місце в PDF (в розробці)
|
home.addImage.desc=Додає зображення у вказане місце в PDF (в розробці)
|
||||||
addImage.tags=img,jpg,picture,photo
|
addImage.tags=зображення,jpg,картинка,фото
|
||||||
|
|
||||||
home.watermark.title=Додати водяний знак
|
home.watermark.title=Додати водяний знак
|
||||||
home.watermark.desc=Додайте свій водяний знак до документа PDF.
|
home.watermark.desc=Додайте свій водяний знак до документа PDF.
|
||||||
watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo
|
watermark.tags=текст,повторний,мітка,власний,авторське право,торговельна марка,зображення,jpg,картинка,фото
|
||||||
|
|
||||||
home.permissions.title=Змінити дозволи
|
home.permissions.title=Змінити дозволи
|
||||||
home.permissions.desc=Змініть дозволи вашого документа PDF
|
home.permissions.desc=Змініть дозволи вашого документа PDF
|
||||||
permissions.tags=read,write,edit,print
|
permissions.tags=читання,запис,редагування,друк
|
||||||
|
|
||||||
|
|
||||||
home.removePages.title=Видалення
|
home.removePages.title=Видалення
|
||||||
home.removePages.desc=Видаліть непотрібні сторінки з документа PDF.
|
home.removePages.desc=Видаліть непотрібні сторінки з документа PDF.
|
||||||
removePages.tags=Remove pages,delete pages
|
removePages.tags=видалити сторінки,видалення сторінок
|
||||||
|
|
||||||
home.addPassword.title=Додати пароль
|
home.addPassword.title=Додати пароль
|
||||||
home.addPassword.desc=Зашифруйте документ PDF паролем.
|
home.addPassword.desc=Зашифруйте документ PDF паролем.
|
||||||
addPassword.tags=secure,security
|
addPassword.tags=безпека,захист
|
||||||
|
|
||||||
home.removePassword.title=Видалити пароль
|
home.removePassword.title=Видалити пароль
|
||||||
home.removePassword.desc=Зніміть захист паролем з вашого документа PDF.
|
home.removePassword.desc=Зніміть захист паролем з вашого документа PDF.
|
||||||
removePassword.tags=secure,Decrypt,security,unpassword,delete password
|
removePassword.tags=безпека,розшифровка,захист,видалення пароля
|
||||||
|
|
||||||
home.compressPdfs.title=Стиснути
|
home.compressPdfs.title=Стиснути
|
||||||
home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір.
|
home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір.
|
||||||
compressPdfs.tags=squish,small,tiny
|
compressPdfs.tags=стиск,маленький,крихітний
|
||||||
|
|
||||||
|
|
||||||
home.changeMetadata.title=Змінити метадані
|
home.changeMetadata.title=Змінити метадані
|
||||||
home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF
|
home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF
|
||||||
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=заголовок,автор,дата,створення,час,видавець,виробник,статистика
|
||||||
|
|
||||||
home.fileToPDF.title=Конвертувати файл в PDF
|
home.fileToPDF.title=Конвертувати файл в PDF
|
||||||
home.fileToPDF.desc=Конвертуйте майже будь-який файл в PDF (DOCX, PNG, XLS, PPT, TXT та інші)
|
home.fileToPDF.desc=Конвертуйте майже будь-який файл в PDF (DOCX, PNG, XLS, PPT, TXT та інші)
|
||||||
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
|
fileToPDF.tags=перетворення,формат,документ,картинка,презентація,текст,конвертація,офіс,документи,word,excel,powerpoint
|
||||||
|
|
||||||
home.ocr.title=OCR/Очищення сканування
|
home.ocr.title=OCR/Очищення сканування
|
||||||
home.ocr.desc=Очищення сканування та виявлення тексту на зображеннях у файлі PDF та повторне додавання його як текст.
|
home.ocr.desc=Очищення сканування та виявлення тексту на зображеннях у файлі PDF та повторне додавання його як текст.
|
||||||
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
|
ocr.tags=розпізнавання,текст,зображення,сканування,читання,ідентифікація,виявлення,редагований
|
||||||
|
|
||||||
|
|
||||||
home.extractImages.title=Витягнути зображення
|
home.extractImages.title=Витягнути зображення
|
||||||
home.extractImages.desc=Витягує всі зображення з PDF і зберігає їх у zip
|
home.extractImages.desc=Витягує всі зображення з PDF і зберігає їх у zip
|
||||||
extractImages.tags=picture,photo,save,archive,zip,capture,grab
|
extractImages.tags=зображення,фото,збереження,архів,zip,захоплення,захоплення
|
||||||
|
|
||||||
home.pdfToPDFA.title=PDF в PDF/A
|
home.pdfToPDFA.title=PDF в PDF/A
|
||||||
home.pdfToPDFA.desc=Перетворення PDF в PDF/A для довготривалого зберігання
|
home.pdfToPDFA.desc=Перетворення PDF в PDF/A для довготривалого зберігання
|
||||||
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
|
pdfToPDFA.tags=архів,довгостроковий,стандартний,конверсія,зберігання,консервація
|
||||||
|
|
||||||
home.PDFToWord.title=PDF в Word
|
home.PDFToWord.title=PDF в Word
|
||||||
home.PDFToWord.desc=Перетворення PDF в формати Word (DOC, DOCX та ODT)
|
home.PDFToWord.desc=Перетворення PDF в формати Word (DOC, DOCX та ODT)
|
||||||
PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile
|
PDFToWord.tags=doc,docx,odt,word,перетворення,формат,перетворення,офіс,microsoft,docfile
|
||||||
|
|
||||||
home.PDFToPresentation.title=PDF в презентацію
|
home.PDFToPresentation.title=PDF в презентацію
|
||||||
home.PDFToPresentation.desc=Перетворення PDF в формати презентацій (PPT, PPTX та ODP)
|
home.PDFToPresentation.desc=Перетворення PDF в формати презентацій (PPT, PPTX та ODP)
|
||||||
PDFToPresentation.tags=slides,show,office,microsoft
|
PDFToPresentation.tags=слайди,шоу,офіс,майкрософт
|
||||||
|
|
||||||
home.PDFToText.title=PDF в Text/RTF
|
home.PDFToText.title=PDF в Text/RTF
|
||||||
home.PDFToText.desc=Перетворення PDF в текстовий або RTF формат
|
home.PDFToText.desc=Перетворення PDF в текстовий або RTF формат
|
||||||
PDFToText.tags=richformat,richtextformat,rich text format
|
PDFToText.tags=richformat,richtextformat,формат rich text,rtf
|
||||||
|
|
||||||
home.PDFToHTML.title=PDF в HTML
|
home.PDFToHTML.title=PDF в HTML
|
||||||
home.PDFToHTML.desc=Перетворення PDF в формат HTML
|
home.PDFToHTML.desc=Перетворення PDF в формат HTML
|
||||||
PDFToHTML.tags=web content,browser friendly
|
PDFToHTML.tags=веб-контент,зручний для перегляду
|
||||||
|
|
||||||
|
|
||||||
home.PDFToXML.title=PDF в XML
|
home.PDFToXML.title=PDF в XML
|
||||||
home.PDFToXML.desc=Перетворення PDF в формат XML
|
home.PDFToXML.desc=Перетворення PDF в формат XML
|
||||||
PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert
|
PDFToXML.tags=вилучення даних,структурований вміст,взаємодія,перетворення,перетворення
|
||||||
|
|
||||||
home.ScannerImageSplit.title=Виявлення/розділення відсканованих фотографій
|
home.ScannerImageSplit.title=Виявлення/розділення відсканованих фотографій
|
||||||
home.ScannerImageSplit.desc=Розділяє кілька фотографій з фото/PDF
|
home.ScannerImageSplit.desc=Розділяє кілька фотографій з фото/PDF
|
||||||
ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize
|
ScannerImageSplit.tags=окремий,автоматичне визначення,сканування,кілька фотографій,упорядкування
|
||||||
|
|
||||||
home.sign.title=Підпис
|
home.sign.title=Підпис
|
||||||
home.sign.desc=Додає підпис до PDF за допомогою малюнка, тексту або зображення
|
home.sign.desc=Додає підпис до PDF за допомогою малюнка, тексту або зображення
|
||||||
sign.tags=authorize,initials,drawn-signature,text-sign,image-signature
|
sign.tags=авторизувати,ініціали,намальований-підпис,текстовий-підпис,зображення-підпис
|
||||||
|
|
||||||
home.flatten.title=Згладжування
|
home.flatten.title=Згладжування
|
||||||
home.flatten.desc=Видалення всіх інтерактивних елементів та форм з PDF
|
home.flatten.desc=Видалення всіх інтерактивних елементів та форм з PDF
|
||||||
@ -391,162 +391,162 @@ flatten.tags=static,deactivate,non-interactive,streamline
|
|||||||
|
|
||||||
home.repair.title=Ремонт
|
home.repair.title=Ремонт
|
||||||
home.repair.desc=Намагається відновити пошкоджений/зламаний PDF
|
home.repair.desc=Намагається відновити пошкоджений/зламаний PDF
|
||||||
repair.tags=fix,restore,correction,recover
|
repair.tags=виправити,відновити,виправити,відновити
|
||||||
|
|
||||||
home.removeBlanks.title=Видалити порожні сторінки
|
home.removeBlanks.title=Видалити порожні сторінки
|
||||||
home.removeBlanks.desc=Виявляє та видаляє порожні сторінки з документа
|
home.removeBlanks.desc=Виявляє та видаляє порожні сторінки з документа
|
||||||
removeBlanks.tags=cleanup,streamline,non-content,organize
|
removeBlanks.tags=очищення,упорядкування,без вмісту,упорядкування
|
||||||
|
|
||||||
home.removeAnnotations.title=Видалити анотації
|
home.removeAnnotations.title=Видалити анотації
|
||||||
home.removeAnnotations.desc=Видаляє всі коментарі/анотації з PDF
|
home.removeAnnotations.desc=Видаляє всі коментарі/анотації з PDF
|
||||||
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
removeAnnotations.tags=коментарі,виділення,примітки,розмітка,видалення
|
||||||
|
|
||||||
home.compare.title=Порівняння
|
home.compare.title=Порівняння
|
||||||
home.compare.desc=Порівнює та показує різницю між двома PDF-документами
|
home.compare.desc=Порівнює та показує різницю між двома PDF-документами
|
||||||
compare.tags=differentiate,contrast,changes,analysis
|
compare.tags=диференціація,контраст,зміни,аналіз
|
||||||
|
|
||||||
home.certSign.title=Підписати сертифікатом
|
home.certSign.title=Підписати сертифікатом
|
||||||
home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12)
|
home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12)
|
||||||
certSign.tags=authenticate,PEM,P12,official,encrypt
|
certSign.tags=автентифікація,pem,p12,офіційний,шифрування
|
||||||
|
|
||||||
home.removeCertSign.title=Видалити підпис сертифікатом
|
home.removeCertSign.title=Видалити підпис сертифікатом
|
||||||
home.removeCertSign.desc=Видалити підпис сертифікатом з PDF-документу
|
home.removeCertSign.desc=Видалити підпис сертифікатом з PDF-документу
|
||||||
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
|
removeCertSign.tags=автентифікація,pem,p12,офіційний,розшифрувати
|
||||||
|
|
||||||
home.pageLayout.title=Об'єднати сторінки
|
home.pageLayout.title=Об'єднати сторінки
|
||||||
home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку
|
home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку
|
||||||
pageLayout.tags=merge,composite,single-view,organize
|
pageLayout.tags=об'єднати,скласти,єдиний перегляд,упорядкувати
|
||||||
|
|
||||||
home.scalePages.title=Змінити розмір/масштаб сторінки
|
home.scalePages.title=Змінити розмір/масштаб сторінки
|
||||||
home.scalePages.desc=Змінити розмір/масштаб сторінки та/або її вмісту.
|
home.scalePages.desc=Змінити розмір/масштаб сторінки та/або її вмісту.
|
||||||
scalePages.tags=resize,modify,dimension,adapt
|
scalePages.tags=змінити розмір,змінити,розмір,адаптувати
|
||||||
|
|
||||||
home.pipeline.title=Конвеєр (розширений)
|
home.pipeline.title=Конвеєр (розширений)
|
||||||
home.pipeline.desc=Виконуйте кілька дій з PDF-файлами, визначаючи сценарії конвеєрної обробки.
|
home.pipeline.desc=Виконуйте кілька дій з PDF-файлами, визначаючи сценарії конвеєрної обробки.
|
||||||
pipeline.tags=automate,sequence,scripted,batch-process
|
pipeline.tags=автоматизація,послідовність,сценарій,scripted,batch-process
|
||||||
|
|
||||||
home.add-page-numbers.title=Додати номера сторінок
|
home.add-page-numbers.title=Додати номера сторінок
|
||||||
home.add-page-numbers.desc=Додає номера сторінок по всьому документу в заданому місці
|
home.add-page-numbers.desc=Додає номера сторінок по всьому документу в заданому місці
|
||||||
add-page-numbers.tags=paginate,label,organize,index
|
add-page-numbers.tags=розбити на сторінки,позначити,упорядкувати,індексувати
|
||||||
|
|
||||||
home.auto-rename.title=Автоматичне перейменування PDF-файлу
|
home.auto-rename.title=Автоматичне перейменування PDF-файлу
|
||||||
home.auto-rename.desc=Автоматичне перейменування файлу PDF на основі його виявленого заголовку
|
home.auto-rename.desc=Автоматичне перейменування файлу PDF на основі його виявленого заголовку
|
||||||
auto-rename.tags=auto-detect,header-based,organize,relabel
|
auto-rename.tags=автоматичне визначення,на основі заголовка,організація,зміна міток
|
||||||
|
|
||||||
home.adjust-contrast.title=Налаштування кольорів/контрастності
|
home.adjust-contrast.title=Налаштування кольорів/контрастності
|
||||||
home.adjust-contrast.desc=Налаштування контрастності, насиченості та яскравості файлу PDF
|
home.adjust-contrast.desc=Налаштування контрастності, насиченості та яскравості файлу PDF
|
||||||
adjust-contrast.tags=color-correction,tune,modify,enhance
|
adjust-contrast.tags=корекція кольору,налаштування,зміна,покращення
|
||||||
|
|
||||||
home.crop.title=Обрізати PDF-файл
|
home.crop.title=Обрізати PDF-файл
|
||||||
home.crop.desc=Обрізати PDF-файл, щоб зменшити його розмір (текст залишається!)
|
home.crop.desc=Обрізати PDF-файл, щоб зменшити його розмір (текст залишається!)
|
||||||
crop.tags=trim,shrink,edit,shape
|
crop.tags=обрізати,зменшувати,редагувати,формувати
|
||||||
|
|
||||||
home.autoSplitPDF.title=Автоматичне розділення сторінок
|
home.autoSplitPDF.title=Автоматичне розділення сторінок
|
||||||
home.autoSplitPDF.desc=Автоматичне розділення відсканованого PDF-файлу за допомогою фізичного роздільника відсканованих сторінок QR-коду
|
home.autoSplitPDF.desc=Автоматичне розділення відсканованого PDF-файлу за допомогою фізичного роздільника відсканованих сторінок QR-коду
|
||||||
autoSplitPDF.tags=QR-based,separate,scan-segment,organize
|
autoSplitPDF.tags=на основі qr,відокремити,сканувати сегмент,упорядкувати
|
||||||
|
|
||||||
home.sanitizePdf.title=Санітарна обробка
|
home.sanitizePdf.title=Санітарна обробка
|
||||||
home.sanitizePdf.desc=Видалення скриптів та інших елементів з PDF-файлів
|
home.sanitizePdf.desc=Видалення скриптів та інших елементів з PDF-файлів
|
||||||
sanitizePdf.tags=clean,secure,safe,remove-threats
|
sanitizePdf.tags=чистка,безпека,безпечні,віддалення загроз
|
||||||
|
|
||||||
home.URLToPDF.title=URL/сайт у PDF
|
home.URLToPDF.title=URL/сайт у PDF
|
||||||
home.URLToPDF.desc=Конвертує будь-який http(s)URL у PDF
|
home.URLToPDF.desc=Конвертує будь-який http(s)URL у PDF
|
||||||
URLToPDF.tags=web-capture,save-page,web-to-doc,archive
|
URLToPDF.tags=веб-захоплення,збереження сторінки,веб-документ,архів
|
||||||
|
|
||||||
home.HTMLToPDF.title=HTML у PDF
|
home.HTMLToPDF.title=HTML у PDF
|
||||||
home.HTMLToPDF.desc=Конвертує будь-який HTML-файл або zip-файл у PDF.
|
home.HTMLToPDF.desc=Конвертує будь-який HTML-файл або zip-файл у PDF.
|
||||||
HTMLToPDF.tags=markup,web-content,transformation,convert
|
HTMLToPDF.tags=розмітка,веб-контент,перетворення,конвертація
|
||||||
|
|
||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown у PDF
|
home.MarkdownToPDF.title=Markdown у PDF
|
||||||
home.MarkdownToPDF.desc=Конвертує будь-який файл Markdown у PDF
|
home.MarkdownToPDF.desc=Конвертує будь-який файл Markdown у PDF
|
||||||
MarkdownToPDF.tags=markup,web-content,transformation,convert
|
MarkdownToPDF.tags=розмітка,веб-контент,перетворення,конвертація
|
||||||
|
|
||||||
home.PDFToMarkdown.title=PDF to Markdown
|
home.PDFToMarkdown.title=PDF у Markdown
|
||||||
home.PDFToMarkdown.desc=Converts any PDF to Markdown
|
home.PDFToMarkdown.desc=Конвертує будь-який файл PDF у Markdown
|
||||||
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
|
PDFToMarkdown.tags=розмітка,веб-вміст,трансформація,перетворення,md
|
||||||
|
|
||||||
home.getPdfInfo.title=Отримати ВСЮ інформацію у форматі PDF
|
home.getPdfInfo.title=Отримати ВСЮ інформацію у форматі PDF
|
||||||
home.getPdfInfo.desc=Збирає будь-яку можливу інформацію у PDF-файлах.
|
home.getPdfInfo.desc=Збирає будь-яку можливу інформацію у PDF-файлах.
|
||||||
getPdfInfo.tags=infomation,data,stats,statistics
|
getPdfInfo.tags=інформація,дані,статистика,статистика
|
||||||
|
|
||||||
|
|
||||||
home.extractPage.title=Видобути сторінку(и)
|
home.extractPage.title=Видобути сторінку(и)
|
||||||
home.extractPage.desc=Видобуває обрані сторінки з PDF
|
home.extractPage.desc=Видобуває обрані сторінки з PDF
|
||||||
extractPage.tags=extract
|
extractPage.tags=екстракт
|
||||||
|
|
||||||
|
|
||||||
home.PdfToSinglePage.title=PDF на одну велику сторінку
|
home.PdfToSinglePage.title=PDF на одну велику сторінку
|
||||||
home.PdfToSinglePage.desc=Об'єднує всі сторінки PDF в одну велику сторінку.
|
home.PdfToSinglePage.desc=Об'єднує всі сторінки PDF в одну велику сторінку.
|
||||||
PdfToSinglePage.tags=single page
|
PdfToSinglePage.tags=одну сторінку
|
||||||
|
|
||||||
|
|
||||||
home.showJS.title=Показати JavaScript
|
home.showJS.title=Показати JavaScript
|
||||||
home.showJS.desc=Шукає та відображає будь-який JS, вбудований у PDF-файл.
|
home.showJS.desc=Шукає та відображає будь-який JS, вбудований у PDF-файл.
|
||||||
showJS.tags=JS
|
showJS.tags=js
|
||||||
|
|
||||||
home.autoRedact.title=Автоматичне редагування
|
home.autoRedact.title=Автоматичне редагування
|
||||||
home.autoRedact.desc=Автоматичне затемнення (чорніння) тексту в PDF на основі вхідного тексту
|
home.autoRedact.desc=Автоматичне затемнення (чорніння) тексту в PDF на основі вхідного тексту
|
||||||
autoRedact.tags=Redact,Hide,black out,black,marker,hidden
|
autoRedact.tags=редагувати,приховати,затемнити,чорний,маркер,приховано
|
||||||
|
|
||||||
home.redact.title=Manual Redaction
|
home.redact.title=Ручне редагування
|
||||||
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
|
home.redact.desc=Редагує PDF-файл на основі виділеного тексту, намальованих форм і/або вибраних сторінок
|
||||||
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
redact.tags=редагувати,приховати,затемнити,чорний,маркер,приховано,вручну
|
||||||
|
|
||||||
home.tableExtraxt.title=PDF у CSV
|
home.tableExtraxt.title=PDF у CSV
|
||||||
home.tableExtraxt.desc=Видобуває таблиці з PDF та перетворює їх у CSV
|
home.tableExtraxt.desc=Видобуває таблиці з PDF та перетворює їх у CSV
|
||||||
tableExtraxt.tags=CSV,Table Extraction,extract,convert
|
tableExtraxt.tags=csv,видобуток таблиці,вилучення,конвертація
|
||||||
|
|
||||||
|
|
||||||
home.autoSizeSplitPDF.title=Автоматичне розділення за розміром/кількістю
|
home.autoSizeSplitPDF.title=Автоматичне розділення за розміром/кількістю
|
||||||
home.autoSizeSplitPDF.desc=Розділяє один PDF на кілька документів на основі розміру, кількості сторінок або кількості документів
|
home.autoSizeSplitPDF.desc=Розділяє один PDF на кілька документів на основі розміру, кількості сторінок або кількості документів
|
||||||
autoSizeSplitPDF.tags=pdf,split,document,organization
|
autoSizeSplitPDF.tags=pdf,розділити,документ,організація
|
||||||
|
|
||||||
|
|
||||||
home.overlay-pdfs.title=Накладення PDF
|
home.overlay-pdfs.title=Накладення PDF
|
||||||
home.overlay-pdfs.desc=Накладення одного PDF поверх іншого PDF
|
home.overlay-pdfs.desc=Накладення одного PDF поверх іншого PDF
|
||||||
overlay-pdfs.tags=Overlay
|
overlay-pdfs.tags=накладання
|
||||||
|
|
||||||
home.split-by-sections.title=Розділення PDF за секціями
|
home.split-by-sections.title=Розділення PDF за секціями
|
||||||
home.split-by-sections.desc=Розділення кожної сторінки PDF на менші горизонтальні та вертикальні секції
|
home.split-by-sections.desc=Розділення кожної сторінки PDF на менші горизонтальні та вертикальні секції
|
||||||
split-by-sections.tags=Section Split, Divide, Customize
|
split-by-sections.tags=розділ розділу,розділення,налаштування
|
||||||
|
|
||||||
home.AddStampRequest.title=Додати печатку на PDF
|
home.AddStampRequest.title=Додати печатку на PDF
|
||||||
home.AddStampRequest.desc=Додавання текстової або зображення печатки у вказані місця
|
home.AddStampRequest.desc=Додавання текстової або зображення печатки у вказані місця
|
||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=штамп,додати зображення,центральне зображення,водяний знак,pdf,вставити,налаштувати
|
||||||
|
|
||||||
|
|
||||||
home.removeImagePdf.title=Remove image
|
home.removeImagePdf.title=Видалити зображення
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=Видаляє зображення з PDF для зменшення розміру файлу
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=видалення зображення,операції зі сторінками,серверна частина
|
||||||
|
|
||||||
|
|
||||||
home.splitPdfByChapters.title=Split PDF by Chapters
|
home.splitPdfByChapters.title=Розділити PDF за розділами
|
||||||
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
|
home.splitPdfByChapters.desc=Розділяє PDF на кілька файлів на основі структури його розділів
|
||||||
splitPdfByChapters.tags=split,chapters,bookmarks,organize
|
splitPdfByChapters.tags=поділ,глави,закладки,організація
|
||||||
|
|
||||||
home.validateSignature.title=Validate PDF Signature
|
home.validateSignature.title=Перевірка підпису PDF
|
||||||
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
home.validateSignature.desc=Перевірка цифрових підписів та сертифікатів у PDF-документах
|
||||||
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
validateSignature.tags=підпис,перевірка,валідація,pdf,сертифікат,цифровий підпис,перевірка підпису,перевірка сертифіката
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Replace-Invert-Color
|
replace-color.title=Заміна-інверсія кольору
|
||||||
replace-color.header=Replace-Invert Color PDF
|
replace-color.header=Заміна-інверсія кольору PDF
|
||||||
home.replaceColorPdf.title=Replace and Invert Color
|
home.replaceColorPdf.title=Заміна та інверсія кольору
|
||||||
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
home.replaceColorPdf.desc=Замінює колір тексту та фону у PDF та інвертує всі кольори PDF для зменшення розміру файлу
|
||||||
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
replaceColorPdf.tags=Заміна кольору, операції зі сторінками, Серверна частина
|
||||||
replace-color.selectText.1=Replace or Invert color Options
|
replace-color.selectText.1=Параметри заміни або інверсії кольору
|
||||||
replace-color.selectText.2=Default(Default high contrast colors)
|
replace-color.selectText.2=За замовчуванням (кольори високого розмаїття)
|
||||||
replace-color.selectText.3=Custom(Customized colors)
|
replace-color.selectText.3=Користувальницькі (настроювані кольори)
|
||||||
replace-color.selectText.4=Full-Invert(Invert all colors)
|
replace-color.selectText.4=Повна інверсія (інвертувати всі кольори)
|
||||||
replace-color.selectText.5=High contrast color options
|
replace-color.selectText.5=Параметри високого розмаїття
|
||||||
replace-color.selectText.6=white text on black background
|
replace-color.selectText.6=білий текст на чорному тлі
|
||||||
replace-color.selectText.7=Black text on white background
|
replace-color.selectText.7=чорний текст на білому тлі
|
||||||
replace-color.selectText.8=Yellow text on black background
|
replace-color.selectText.8=жовтий текст на чорному тлі
|
||||||
replace-color.selectText.9=Green text on black background
|
replace-color.selectText.9=зелений текст на чорному тлі
|
||||||
replace-color.selectText.10=Choose text Color
|
replace-color.selectText.10=Вибрати колір тексту
|
||||||
replace-color.selectText.11=Choose background Color
|
replace-color.selectText.11=Вибрати колір тла
|
||||||
replace-color.submit=Replace
|
replace-color.submit=Замінити
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -571,12 +571,12 @@ login.oauth2InvalidUserInfoResponse=Недійсна відповідь з ін
|
|||||||
login.oauth2invalidRequest=Недійсний запит
|
login.oauth2invalidRequest=Недійсний запит
|
||||||
login.oauth2AccessDenied=Доступ заблоковано
|
login.oauth2AccessDenied=Доступ заблоковано
|
||||||
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
|
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
|
||||||
login.oauth2InvalidIdToken=Недійсний Id токен
|
login.oauth2InvalidIdToken=Недійсний ідентифікаційний токен
|
||||||
login.relyingPartyRegistrationNotFound=No relying party registration found
|
login.relyingPartyRegistrationNotFound=Реєстрацію довіряючої сторони не знайдено
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=Користувач деактивовано, вхід з цим ім'ям користувача заблоковано. Зверніться до адміністратора.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=Ви вже увійшли до
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=пристроїв (а). Будь ласка, вийдіть із цих пристроїв і спробуйте знову.
|
||||||
login.toManySessions=You have too many active sessions
|
login.toManySessions=У вас дуже багато активних сесій
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
autoRedact.title=Автоматичне редагування
|
autoRedact.title=Автоматичне редагування
|
||||||
@ -591,31 +591,31 @@ autoRedact.convertPDFToImageLabel=Перетворити PDF в зображен
|
|||||||
autoRedact.submitButton=Надіслати
|
autoRedact.submitButton=Надіслати
|
||||||
|
|
||||||
#redact
|
#redact
|
||||||
redact.title=Manual Redaction
|
redact.title=Ручне редагування
|
||||||
redact.header=Manual Redaction
|
redact.header=Ручне редагування
|
||||||
redact.submit=Redact
|
redact.submit=Редагувати
|
||||||
redact.textBasedRedaction=Text based Redaction
|
redact.textBasedRedaction=Редагування на основі тексту
|
||||||
redact.pageBasedRedaction=Page-based Redaction
|
redact.pageBasedRedaction=Редагування на основі сторінок
|
||||||
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
|
redact.convertPDFToImageLabel=Перетворити PDF на PDF-зображення (використовується для видалення тексту за рамкою)
|
||||||
redact.pageRedactionNumbers.title=Pages
|
redact.pageRedactionNumbers.title=Сторінки
|
||||||
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
redact.pageRedactionNumbers.placeholder=(наприклад, 1,2,8 або 4,7,12-16 або 2n-1)
|
||||||
redact.redactionColor.title=Redaction Color
|
redact.redactionColor.title=Колір редагування
|
||||||
redact.export=Export
|
redact.export=Експорт
|
||||||
redact.upload=Upload
|
redact.upload=Завантажити
|
||||||
redact.boxRedaction=Box draw redaction
|
redact.boxRedaction=Редагування малюванням рамки
|
||||||
redact.zoom=Zoom
|
redact.zoom=Масштаб
|
||||||
redact.zoomIn=Zoom in
|
redact.zoomIn=Збільшити
|
||||||
redact.zoomOut=Zoom out
|
redact.zoomOut=Зменшити
|
||||||
redact.nextPage=Next Page
|
redact.nextPage=Наступна сторінка
|
||||||
redact.previousPage=Previous Page
|
redact.previousPage=Попередня сторінка
|
||||||
redact.toggleSidebar=Toggle Sidebar
|
redact.toggleSidebar=Перемикати бічну панель
|
||||||
redact.showThumbnails=Show Thumbnails
|
redact.showThumbnails=Показати мініатюри
|
||||||
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
|
redact.showDocumentOutline=Показати структуру документа (подвійне клацання для розгортання/згортання всіх елементів)
|
||||||
redact.showAttatchments=Show Attachments
|
redact.showAttatchments=Показати вкладення
|
||||||
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
|
redact.showLayers=Показати шари (подвійне клацання для скидання всіх шарів до стану за умовчанням)
|
||||||
redact.colourPicker=Colour Picker
|
redact.colourPicker=Вибір кольору
|
||||||
redact.findCurrentOutlineItem=Find current outline item
|
redact.findCurrentOutlineItem=Знайти поточний елемент структури
|
||||||
redact.applyChanges=Apply Changes
|
redact.applyChanges=Застосувати зміни
|
||||||
|
|
||||||
#showJS
|
#showJS
|
||||||
showJS.title=Показати JavaScript
|
showJS.title=Показати JavaScript
|
||||||
@ -831,14 +831,14 @@ removeAnnotations.submit=Видалити
|
|||||||
#compare
|
#compare
|
||||||
compare.title=Порівняння
|
compare.title=Порівняння
|
||||||
compare.header=Порівняння PDF
|
compare.header=Порівняння PDF
|
||||||
compare.highlightColor.1=Highlight Color 1:
|
compare.highlightColor.1=Колір виділення 1:
|
||||||
compare.highlightColor.2=Highlight Color 2:
|
compare.highlightColor.2=Колір виділення 2:
|
||||||
compare.document.1=Документ 1
|
compare.document.1=Документ 1
|
||||||
compare.document.2=Документ 2
|
compare.document.2=Документ 2
|
||||||
compare.submit=Порівняти
|
compare.submit=Порівняти
|
||||||
compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
|
compare.complex.message=Один або обидва надані документи є великими файлами, точність порівняння може бути знижена
|
||||||
compare.large.file.message=One or Both of the provided documents are too large to process
|
compare.large.file.message=Один або обидва надані документи занадто великі для обробки
|
||||||
compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
|
compare.no.text.message=Вибрані PDF-файли не містять текстового вмісту. Будь ласка, виберіть PDF-файли з текстом для порівняння.
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Підпис
|
sign.title=Підпис
|
||||||
@ -848,18 +848,18 @@ sign.draw=Намалювати підпис
|
|||||||
sign.text=Ввід тексту
|
sign.text=Ввід тексту
|
||||||
sign.clear=Очистити
|
sign.clear=Очистити
|
||||||
sign.add=Додати
|
sign.add=Додати
|
||||||
sign.saved=Saved Signatures
|
sign.saved=Збережені підписи
|
||||||
sign.save=Save Signature
|
sign.save=Зберегти підпис
|
||||||
sign.personalSigs=Personal Signatures
|
sign.personalSigs=Особисті підписи
|
||||||
sign.sharedSigs=Shared Signatures
|
sign.sharedSigs=Загальні підписи
|
||||||
sign.noSavedSigs=No saved signatures found
|
sign.noSavedSigs=Збережені підписи не знайдено
|
||||||
sign.addToAll=Add to all pages
|
sign.addToAll=Додати на всі сторінки
|
||||||
sign.delete=Delete
|
sign.delete=Видалити
|
||||||
sign.first=First page
|
sign.first=Перша сторінка
|
||||||
sign.last=Last page
|
sign.last=Остання сторінка
|
||||||
sign.next=Next page
|
sign.next=Наступна сторінка
|
||||||
sign.previous=Previous page
|
sign.previous=Попередня сторінка
|
||||||
sign.maintainRatio=Toggle maintain aspect ratio
|
sign.maintainRatio=Переключити збереження пропорцій
|
||||||
|
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
@ -886,7 +886,7 @@ ScannerImageSplit.selectText.7=Мінімальна площа контуру:
|
|||||||
ScannerImageSplit.selectText.8=Встановлює мінімальний поріг площі контуру для фотографії
|
ScannerImageSplit.selectText.8=Встановлює мінімальний поріг площі контуру для фотографії
|
||||||
ScannerImageSplit.selectText.9=Розмір рамки:
|
ScannerImageSplit.selectText.9=Розмір рамки:
|
||||||
ScannerImageSplit.selectText.10=Встановлює розмір додаваної та видаляної рамки, щоб запобігти появі білих рамок на виході (за замовчуванням: 1).
|
ScannerImageSplit.selectText.10=Встановлює розмір додаваної та видаляної рамки, щоб запобігти появі білих рамок на виході (за замовчуванням: 1).
|
||||||
ScannerImageSplit.info=Python is not installed. It is required to run.
|
ScannerImageSplit.info=Python не встановлено. Він необхідний роботи.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@ -976,45 +976,45 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n
|
|||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=Мультіінструмент PDF
|
multiTool.title=Мультиінструмент PDF
|
||||||
multiTool.header=Мультіінструмент PDF
|
multiTool.header=Мультиінструмент PDF
|
||||||
multiTool.uploadPrompts=Ім'я файлу
|
multiTool.uploadPrompts=Ім'я файлу
|
||||||
multiTool.selectAll=Select All
|
multiTool.selectAll=Вибрати все
|
||||||
multiTool.deselectAll=Deselect All
|
multiTool.deselectAll=Скасувати вибір усіх
|
||||||
multiTool.selectPages=Page Select
|
multiTool.selectPages=Вибір сторінки
|
||||||
multiTool.selectedPages=Selected Pages
|
multiTool.selectedPages=Вибрані сторінки
|
||||||
multiTool.page=Page
|
multiTool.page=Сторінка
|
||||||
multiTool.deleteSelected=Delete Selected
|
multiTool.deleteSelected=Видалити вибрані
|
||||||
multiTool.downloadAll=Export
|
multiTool.downloadAll=Експорт
|
||||||
multiTool.downloadSelected=Export Selected
|
multiTool.downloadSelected=Експорт вибраних
|
||||||
|
|
||||||
multiTool.insertPageBreak=Insert Page Break
|
multiTool.insertPageBreak=Вставити розрив сторінки
|
||||||
multiTool.addFile=Add File
|
multiTool.addFile=Додати файл
|
||||||
multiTool.rotateLeft=Rotate Left
|
multiTool.rotateLeft=Повернути вліво
|
||||||
multiTool.rotateRight=Rotate Right
|
multiTool.rotateRight=Повернути праворуч
|
||||||
multiTool.split=Split
|
multiTool.split=Розділити
|
||||||
multiTool.moveLeft=Move Left
|
multiTool.moveLeft=Перемістити вліво
|
||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Перемістити праворуч
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Видалити
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Вибрано сторінок
|
||||||
multiTool.undo=Undo
|
multiTool.undo=Скасувати
|
||||||
multiTool.redo=Redo
|
multiTool.redo=Повторити
|
||||||
|
|
||||||
#decrypt
|
#decrypt
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
decrypt.passwordPrompt=Цей файл захищений паролем. Будь ласка, введіть пароль:
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
decrypt.cancelled=Операцію скасовано для PDF: {0}
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
decrypt.noPassword=Не надано пароль для зашифрованого PDF: {0}
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
decrypt.invalidPassword=Будь ласка, спробуйте ще раз з правильним паролем.
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
decrypt.invalidPasswordHeader=Неправильний пароль або непідтримуване шифрування для PDF: {0}
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
decrypt.unexpectedError=Виникла помилка при обробці файлу. Будь ласка, спробуйте ще раз.
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
decrypt.serverError=Помилка сервера під час розшифровки: {0}
|
||||||
decrypt.success=File decrypted successfully.
|
decrypt.success=Файл успішно розшифровано.
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=Ця функція також доступна на нашій <a href="{0}">сторінці мультиінструменту</a>. Спробуйте її для покращеного посторінкового інтерфейсу та додаткових можливостей!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View/Edit PDF
|
viewPdf.title=Перегляд/редагування PDF
|
||||||
viewPdf.header=Переглянути PDF
|
viewPdf.header=Переглянути PDF
|
||||||
|
|
||||||
#pageRemover
|
#pageRemover
|
||||||
@ -1280,15 +1280,15 @@ survey.please=Будь-ласка, пройдіть опитування!
|
|||||||
survey.disabled=(Вікно з опитування буде відключено у наступних оновленнях, але буде доступне внизу сторінки)
|
survey.disabled=(Вікно з опитування буде відключено у наступних оновленнях, але буде доступне внизу сторінки)
|
||||||
survey.button=Пройти опитування
|
survey.button=Пройти опитування
|
||||||
survey.dontShowAgain=Не показувати це вікно
|
survey.dontShowAgain=Не показувати це вікно
|
||||||
survey.meeting.1=If you're using Stirling PDF at work, we'd love to speak to you. We're offering technical support sessions in exchange for a 15 minute user discovery session.
|
survey.meeting.1=Якщо ви використовуєте Stirling PDF на роботі, ми будемо раді поговорити з вами. Ми пропонуємо сеанси технічної підтримки в обмін на 15-хвилинний сеанс пошуку користувачів.
|
||||||
survey.meeting.2=This is a chance to:
|
survey.meeting.2=Це можливість:
|
||||||
survey.meeting.3=Get help with deployment, integrations, or troubleshooting
|
survey.meeting.3=Отримайте допомогу щодо розгортання, інтеграції або усунення несправностей
|
||||||
survey.meeting.4=Provide direct feedback on performance, edge cases, and feature gaps
|
survey.meeting.4=Надайте прямий відгук про продуктивність, крайні випадки та недоліки функцій
|
||||||
survey.meeting.5=Help us refine Stirling PDF for real-world enterprise use
|
survey.meeting.5=Допоможіть нам удосконалити Stirling PDF для реального корпоративного використання
|
||||||
survey.meeting.6=If you're interested, you can book time with our team directly. (English speaking only)
|
survey.meeting.6=Якщо ви зацікавлені, ви можете забронювати час безпосередньо з нашою командою. (тільки англомовний)
|
||||||
survey.meeting.7=Looking forward to digging into your use cases and making Stirling PDF even better!
|
survey.meeting.7=З нетерпінням чекаємо на можливість розібратися у ваших сценаріях використання та зробити Stirling PDF ще кращим!
|
||||||
survey.meeting.notInterested=Not a business and/or interested in a meeting?
|
survey.meeting.notInterested=Не бізнес і/або зацікавлені у зустрічі?
|
||||||
survey.meeting.button=Book meeting
|
survey.meeting.button=Зустріч
|
||||||
|
|
||||||
#error
|
#error
|
||||||
error.sorry=Вибачте за незручності!
|
error.sorry=Вибачте за незручності!
|
||||||
@ -1305,70 +1305,70 @@ error.discordSubmit=Discord - Надіслати повідомлення під
|
|||||||
|
|
||||||
|
|
||||||
#remove-image
|
#remove-image
|
||||||
removeImage.title=Remove image
|
removeImage.title=Видалити зображення
|
||||||
removeImage.header=Remove image
|
removeImage.header=Видалити зображення
|
||||||
removeImage.removeImage=Remove image
|
removeImage.removeImage=Видалити зображення
|
||||||
removeImage.submit=Remove image
|
removeImage.submit=Видалити зображення
|
||||||
|
|
||||||
|
|
||||||
splitByChapters.title=Split PDF by Chapters
|
splitByChapters.title=Розділити PDF по главам
|
||||||
splitByChapters.header=Split PDF by Chapters
|
splitByChapters.header=Розділити PDF по главам
|
||||||
splitByChapters.bookmarkLevel=Bookmark Level
|
splitByChapters.bookmarkLevel=Уровень закладок
|
||||||
splitByChapters.includeMetadata=Include Metadata
|
splitByChapters.includeMetadata=Включити метаданні
|
||||||
splitByChapters.allowDuplicates=Allow Duplicates
|
splitByChapters.allowDuplicates=Разрешить публикации
|
||||||
splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure.
|
splitByChapters.desc.1=Цей інструмент розділяє PDF-файл на кілька PDF-файлів на основі своєї структури глави.
|
||||||
splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.).
|
splitByChapters.desc.2=Уровень закладок: виберіть рівень закладок для розподілу (0 для верхнього рівня, 1 для другого рівня і т.д.).
|
||||||
splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF.
|
splitByChapters.desc.3=Включити метаданні: якщо позначено, метаданні вихідного PDF будуть включені в кожен розділений PDF.
|
||||||
splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs.
|
splitByChapters.desc.4=Розрішити публікації: якщо позначено, можна створити окремий PDF із кількох закладок на одній сторінці.
|
||||||
splitByChapters.submit=Split PDF
|
splitByChapters.submit=Розділити PDF
|
||||||
|
|
||||||
#File Chooser
|
#File Chooser
|
||||||
fileChooser.click=Click
|
fileChooser.click=Натисніть
|
||||||
fileChooser.or=or
|
fileChooser.or=або
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Перетащите
|
||||||
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
fileChooser.dragAndDropPDF=Перетащите PDF-файл
|
||||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
fileChooser.dragAndDropImage=Перетащите файл зображення
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Перетащите файл(и) сюда
|
||||||
fileChooser.extractPDF=Extracting...
|
fileChooser.extractPDF=Видобування...
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=Releases
|
releases.footer=Релізи
|
||||||
releases.title=Release Notes
|
releases.title=Примечания к релизу
|
||||||
releases.header=Release Notes
|
releases.header=Примечания к релизу
|
||||||
releases.current.version=Current Release
|
releases.current.version=Текущий релиз
|
||||||
releases.note=Release notes are only available in English
|
releases.note=Примітка до релізу доступна тільки на англійській мові
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Перевірка підписів PDF
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Перевірка цифрових підписів
|
||||||
validateSignature.selectPDF=Select signed PDF file
|
validateSignature.selectPDF=Виберіть підписаний PDF-файл
|
||||||
validateSignature.submit=Validate Signatures
|
validateSignature.submit=Перевірити підписи
|
||||||
validateSignature.results=Validation Results
|
validateSignature.results=Результаты проверки
|
||||||
validateSignature.status=Status
|
validateSignature.status=Статус
|
||||||
validateSignature.signer=Signer
|
validateSignature.signer=Підписант
|
||||||
validateSignature.date=Date
|
validateSignature.date=Дата
|
||||||
validateSignature.reason=Reason
|
validateSignature.reason=Причина
|
||||||
validateSignature.location=Location
|
validateSignature.location=Местоположение
|
||||||
validateSignature.noSignatures=No digital signatures found in this document
|
validateSignature.noSignatures=В цьому документі не знайдено цифрових підписів
|
||||||
validateSignature.status.valid=Valid
|
validateSignature.status.valid=Дійна
|
||||||
validateSignature.status.invalid=Invalid
|
validateSignature.status.invalid=Недійсна
|
||||||
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
|
validateSignature.chain.invalid=Перевірка цепочки сертифікатів не удалась - неможливо перевірити особистість підписанта
|
||||||
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
|
validateSignature.trust.invalid=Сертифікат відсутній у довіреному сховищі - джерело не може бути перевірено
|
||||||
validateSignature.cert.expired=Certificate has expired
|
validateSignature.cert.expired=Срок дії сертифіката істеку
|
||||||
validateSignature.cert.revoked=Certificate has been revoked
|
validateSignature.cert.revoked=Сертифікат був отозван
|
||||||
validateSignature.signature.info=Signature Information
|
validateSignature.signature.info=Інформація про підписи
|
||||||
validateSignature.signature=Signature
|
validateSignature.signature=Подпись
|
||||||
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
|
validateSignature.signature.mathValid=Подпись математически корректна, НО:
|
||||||
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
|
validateSignature.selectCustomCert=Користувачський файл сертифіката X.509 (Необов'язково)
|
||||||
validateSignature.cert.info=Certificate Details
|
validateSignature.cert.info=Сведения про сертифікати
|
||||||
validateSignature.cert.issuer=Issuer
|
validateSignature.cert.issuer=Издатель
|
||||||
validateSignature.cert.subject=Subject
|
validateSignature.cert.subject=суб'єкт
|
||||||
validateSignature.cert.serialNumber=Serial Number
|
validateSignature.cert.serialNumber=Серийний номер
|
||||||
validateSignature.cert.validFrom=Valid From
|
validateSignature.cert.validFrom=Дійсний з
|
||||||
validateSignature.cert.validUntil=Valid Until
|
validateSignature.cert.validUntil=Дійсний до
|
||||||
validateSignature.cert.algorithm=Algorithm
|
validateSignature.cert.algorithm=Алгоритм
|
||||||
validateSignature.cert.keySize=Key Size
|
validateSignature.cert.keySize=Розмір ключа
|
||||||
validateSignature.cert.version=Version
|
validateSignature.cert.version=Версія
|
||||||
validateSignature.cert.keyUsage=Key Usage
|
validateSignature.cert.keyUsage=Використання ключа
|
||||||
validateSignature.cert.selfSigned=Self-Signed
|
validateSignature.cert.selfSigned=Самоподписанный
|
||||||
validateSignature.cert.bits=bits
|
validateSignature.cert.bits=біт
|
||||||
|
32
src/test/java/stirling/software/SPDF/utils/FileInfoTest.java
Normal file
32
src/test/java/stirling/software/SPDF/utils/FileInfoTest.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class FileInfoTest {
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "{index}: fileSize={0}")
|
||||||
|
@CsvSource({
|
||||||
|
"0, '0 Bytes'",
|
||||||
|
"1023, '1023 Bytes'",
|
||||||
|
"1024, '1.00 KB'",
|
||||||
|
"1048575, '1024.00 KB'", // Do we really want this as result?
|
||||||
|
"1048576, '1.00 MB'",
|
||||||
|
"1073741823, '1024.00 MB'", // Do we really want this as result?
|
||||||
|
"1073741824, '1.00 GB'"
|
||||||
|
})
|
||||||
|
void testGetFormattedFileSize(long fileSize, String expectedFormattedSize) {
|
||||||
|
FileInfo fileInfo = new FileInfo(
|
||||||
|
"example.txt",
|
||||||
|
"/path/to/example.txt",
|
||||||
|
LocalDateTime.now(),
|
||||||
|
fileSize,
|
||||||
|
LocalDateTime.now().minusDays(1));
|
||||||
|
|
||||||
|
assertEquals(expectedFormattedSize, fileInfo.getFormattedFileSize());
|
||||||
|
}
|
||||||
|
}
|
175
testing/test.sh
175
testing/test.sh
@ -39,6 +39,136 @@ check_health() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to capture file list from a Docker container
|
||||||
|
capture_file_list() {
|
||||||
|
local container_name=$1
|
||||||
|
local output_file=$2
|
||||||
|
|
||||||
|
echo "Capturing file list from $container_name..."
|
||||||
|
# Get all files in one command, output directly from Docker to avoid path issues
|
||||||
|
# Skip proc, sys, dev, and the specified LibreOffice config directory
|
||||||
|
# Also skip PDFBox and LibreOffice temporary files
|
||||||
|
docker exec $container_name sh -c "find / -type f \
|
||||||
|
-not -path '*/proc/*' \
|
||||||
|
-not -path '*/sys/*' \
|
||||||
|
-not -path '*/dev/*' \
|
||||||
|
-not -path '/config/*' \
|
||||||
|
-not -path '/logs/*' \
|
||||||
|
-not -path '*/home/stirlingpdfuser/.config/libreoffice/*' \
|
||||||
|
-not -path '*/tmp/PDFBox*' \
|
||||||
|
-not -path '*/tmp/hsperfdata_stirlingpdfuser/*' \
|
||||||
|
-not -path '*/tmp/lu*' \
|
||||||
|
-not -path '*/tmp/tmp*' \
|
||||||
|
2>/dev/null | xargs -I{} sh -c 'stat -c \"%n %s %Y\" \"{}\" 2>/dev/null || true' | sort" > "$output_file"
|
||||||
|
|
||||||
|
# Check if the output file has content
|
||||||
|
if [ ! -s "$output_file" ]; then
|
||||||
|
echo "WARNING: Failed to capture file list or container returned empty list"
|
||||||
|
echo "Trying alternative approach..."
|
||||||
|
|
||||||
|
# Alternative simpler approach - just get paths as a fallback
|
||||||
|
docker exec $container_name sh -c "find / -type f \
|
||||||
|
-not -path '*/proc/*' \
|
||||||
|
-not -path '*/sys/*' \
|
||||||
|
-not -path '*/dev/*' \
|
||||||
|
-not -path '/config/*' \
|
||||||
|
-not -path '/logs/*' \
|
||||||
|
-not -path '*/home/stirlingpdfuser/.config/libreoffice/*' \
|
||||||
|
-not -path '*/tmp/PDFBox*' \
|
||||||
|
-not -path '*/tmp/hsperfdata_stirlingpdfuser/*' \
|
||||||
|
-not -path '*/tmp/lu*' \
|
||||||
|
-not -path '*/tmp/tmp*' \
|
||||||
|
2>/dev/null | sort" > "$output_file"
|
||||||
|
|
||||||
|
if [ ! -s "$output_file" ]; then
|
||||||
|
echo "ERROR: All attempts to capture file list failed"
|
||||||
|
# Create a dummy entry to prevent diff errors
|
||||||
|
echo "NO_FILES_FOUND 0 0" > "$output_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "File list captured to $output_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to compare before and after file lists
|
||||||
|
compare_file_lists() {
|
||||||
|
local before_file=$1
|
||||||
|
local after_file=$2
|
||||||
|
local diff_file=$3
|
||||||
|
local container_name=$4 # Added container_name parameter
|
||||||
|
|
||||||
|
echo "Comparing file lists..."
|
||||||
|
|
||||||
|
# Check if files exist and have content
|
||||||
|
if [ ! -s "$before_file" ] || [ ! -s "$after_file" ]; then
|
||||||
|
echo "WARNING: One or both file lists are empty."
|
||||||
|
|
||||||
|
if [ ! -s "$before_file" ]; then
|
||||||
|
echo "Before file is empty: $before_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$after_file" ]; then
|
||||||
|
echo "After file is empty: $after_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create empty diff file
|
||||||
|
> "$diff_file"
|
||||||
|
|
||||||
|
# Check if we at least have the after file to look for temp files
|
||||||
|
if [ -s "$after_file" ]; then
|
||||||
|
echo "Checking for temp files in the after snapshot..."
|
||||||
|
grep -i "tmp\|temp" "$after_file" > "${diff_file}.tmp"
|
||||||
|
if [ -s "${diff_file}.tmp" ]; then
|
||||||
|
echo "WARNING: Temporary files found:"
|
||||||
|
cat "${diff_file}.tmp"
|
||||||
|
echo "Printing docker logs due to temporary file detection:"
|
||||||
|
docker logs "$container_name" # Print logs when temp files are found
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "No temporary files found in the after snapshot."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Both files exist and have content, proceed with diff
|
||||||
|
diff "$before_file" "$after_file" > "$diff_file"
|
||||||
|
|
||||||
|
if [ -s "$diff_file" ]; then
|
||||||
|
echo "Detected changes in files:"
|
||||||
|
cat "$diff_file"
|
||||||
|
|
||||||
|
# Extract only added files (lines starting with ">")
|
||||||
|
grep "^>" "$diff_file" > "${diff_file}.added" || true
|
||||||
|
if [ -s "${diff_file}.added" ]; then
|
||||||
|
echo "New files created during test:"
|
||||||
|
cat "${diff_file}.added" | sed 's/^> //'
|
||||||
|
|
||||||
|
# Check for tmp files
|
||||||
|
grep -i "tmp\|temp" "${diff_file}.added" > "${diff_file}.tmp" || true
|
||||||
|
if [ -s "${diff_file}.tmp" ]; then
|
||||||
|
echo "WARNING: Temporary files detected:"
|
||||||
|
cat "${diff_file}.tmp"
|
||||||
|
echo "Printing docker logs due to temporary file detection:"
|
||||||
|
docker logs "$container_name" # Print logs when temp files are found
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract only removed files (lines starting with "<")
|
||||||
|
grep "^<" "$diff_file" > "${diff_file}.removed" || true
|
||||||
|
if [ -s "${diff_file}.removed" ]; then
|
||||||
|
echo "Files removed during test:"
|
||||||
|
cat "${diff_file}.removed" | sed 's/^< //'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No file changes detected during test."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# Function to test a Docker Compose configuration
|
# Function to test a Docker Compose configuration
|
||||||
test_compose() {
|
test_compose() {
|
||||||
local compose_file=$1
|
local compose_file=$1
|
||||||
@ -91,7 +221,7 @@ main() {
|
|||||||
|
|
||||||
# Building Docker images
|
# Building Docker images
|
||||||
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
|
# docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile .
|
||||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
docker build --build-arg VERSION_TAG=alpha -t docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||||
|
|
||||||
# Test each configuration
|
# Test each configuration
|
||||||
run_tests "Stirling-PDF-Ultra-Lite" "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml"
|
run_tests "Stirling-PDF-Ultra-Lite" "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml"
|
||||||
@ -147,16 +277,55 @@ main() {
|
|||||||
run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml"
|
run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
|
# Create directory for file snapshots if it doesn't exist
|
||||||
|
SNAPSHOT_DIR="$PROJECT_ROOT/testing/file_snapshots"
|
||||||
|
mkdir -p "$SNAPSHOT_DIR"
|
||||||
|
|
||||||
|
# Capture file list before running behave tests
|
||||||
|
BEFORE_FILE="$SNAPSHOT_DIR/files_before_behave.txt"
|
||||||
|
AFTER_FILE="$SNAPSHOT_DIR/files_after_behave.txt"
|
||||||
|
DIFF_FILE="$SNAPSHOT_DIR/files_diff.txt"
|
||||||
|
|
||||||
|
# Define container name variable for consistency
|
||||||
|
CONTAINER_NAME="Stirling-PDF-Security-Fat-with-login"
|
||||||
|
|
||||||
|
capture_file_list "$CONTAINER_NAME" "$BEFORE_FILE"
|
||||||
|
|
||||||
cd "testing/cucumber"
|
cd "testing/cucumber"
|
||||||
if python -m behave; then
|
if python -m behave; then
|
||||||
|
# Wait 10 seconds before capturing the file list after tests
|
||||||
|
echo "Waiting 5 seconds for any file operations to complete..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Capture file list after running behave tests
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
capture_file_list "$CONTAINER_NAME" "$AFTER_FILE"
|
||||||
|
|
||||||
|
# Compare file lists
|
||||||
|
if compare_file_lists "$BEFORE_FILE" "$AFTER_FILE" "$DIFF_FILE" "$CONTAINER_NAME"; then
|
||||||
|
echo "No unexpected temporary files found."
|
||||||
|
passed_tests+=("Stirling-PDF-Regression")
|
||||||
|
else
|
||||||
|
echo "WARNING: Unexpected temporary files detected after behave tests!"
|
||||||
|
failed_tests+=("Stirling-PDF-Regression-Temp-Files")
|
||||||
|
fi
|
||||||
|
|
||||||
passed_tests+=("Stirling-PDF-Regression")
|
passed_tests+=("Stirling-PDF-Regression")
|
||||||
else
|
else
|
||||||
failed_tests+=("Stirling-PDF-Regression")
|
failed_tests+=("Stirling-PDF-Regression")
|
||||||
echo "Printing docker logs of failed regression"
|
echo "Printing docker logs of failed regression"
|
||||||
docker logs "Stirling-PDF-Security-Fat-with-login"
|
docker logs "$CONTAINER_NAME"
|
||||||
echo "Printed docker logs of failed regression"
|
echo "Printed docker logs of failed regression"
|
||||||
|
|
||||||
|
# Still capture file list after failure for analysis
|
||||||
|
# Wait 10 seconds before capturing the file list
|
||||||
|
echo "Waiting 5 seconds before capturing file list..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
capture_file_list "$CONTAINER_NAME" "$AFTER_FILE"
|
||||||
|
compare_file_lists "$BEFORE_FILE" "$AFTER_FILE" "$DIFF_FILE" "$CONTAINER_NAME"
|
||||||
fi
|
fi
|
||||||
cd "$PROJECT_ROOT"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker-compose -f "./exampleYmlFiles/test_cicd.yml" down
|
docker-compose -f "./exampleYmlFiles/test_cicd.yml" down
|
||||||
|
@ -2,122 +2,173 @@
|
|||||||
|
|
||||||
# Function to check a single webpage
|
# Function to check a single webpage
|
||||||
check_webpage() {
|
check_webpage() {
|
||||||
local url=$(echo "$1" | tr -d '\r') # Remove carriage returns
|
local url=$(echo "$1" | tr -d '\r') # Remove carriage returns
|
||||||
local base_url=$(echo "$2" | tr -d '\r')
|
local base_url=$(echo "$2" | tr -d '\r')
|
||||||
local full_url="${base_url}${url}"
|
local full_url="${base_url}${url}"
|
||||||
local timeout=10
|
local timeout=10
|
||||||
echo -n "Testing $full_url ... "
|
local result_file="$3"
|
||||||
|
|
||||||
# Use curl to fetch the page with timeout
|
|
||||||
response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url")
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "FAILED - Connection error or timeout $full_url "
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Split response into body and status code
|
# Use curl to fetch the page with timeout
|
||||||
HTTP_STATUS=$(echo "$response" | tail -n1)
|
response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url")
|
||||||
BODY=$(echo "$response" | sed '$d')
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "FAILED - Connection error or timeout $full_url" >> "$result_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Check HTTP status
|
# Split response into body and status code
|
||||||
if [ "$HTTP_STATUS" != "200" ]; then
|
HTTP_STATUS=$(echo "$response" | tail -n1)
|
||||||
echo "FAILED - HTTP Status: $HTTP_STATUS"
|
BODY=$(echo "$response" | sed '$d')
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if response contains HTML
|
# Check HTTP status
|
||||||
if ! printf '%s' "$BODY" | grep -q "<!DOCTYPE html>\|<html"; then
|
if [ "$HTTP_STATUS" != "200" ]; then
|
||||||
echo "FAILED - Response is not HTML"
|
echo "FAILED - HTTP Status: $HTTP_STATUS - $full_url" >> "$result_file"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "OK"
|
# Check if response contains HTML
|
||||||
return 0
|
if ! printf '%s' "$BODY" | grep -q "<!DOCTYPE html>\|<html"; then
|
||||||
|
echo "FAILED - Response is not HTML - $full_url" >> "$result_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OK - $full_url" >> "$result_file"
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main function to test all URLs from the list
|
# Function to test a URL and update counters
|
||||||
|
test_url() {
|
||||||
|
local url="$1"
|
||||||
|
local base_url="$2"
|
||||||
|
local tmp_dir="$3"
|
||||||
|
local url_index="$4"
|
||||||
|
local result_file="${tmp_dir}/result_${url_index}.txt"
|
||||||
|
|
||||||
|
if ! check_webpage "$url" "$base_url" "$result_file"; then
|
||||||
|
echo "1" > "${tmp_dir}/failed_${url_index}"
|
||||||
|
else
|
||||||
|
echo "0" > "${tmp_dir}/failed_${url_index}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main function to test all URLs from the list in parallel
|
||||||
test_all_urls() {
|
test_all_urls() {
|
||||||
local url_file=$1
|
local url_file="$1"
|
||||||
local base_url=${2:-"http://localhost:8080"}
|
local base_url="${2:-"http://localhost:8080"}"
|
||||||
local failed_count=0
|
local max_parallel="${3:-10}" # Default to 10 parallel processes
|
||||||
local total_count=0
|
local failed_count=0
|
||||||
local start_time=$(date +%s)
|
local total_count=0
|
||||||
|
local start_time=$(date +%s)
|
||||||
|
local tmp_dir=$(mktemp -d)
|
||||||
|
local active_jobs=0
|
||||||
|
local url_index=0
|
||||||
|
|
||||||
echo "Starting webpage tests..."
|
echo "Starting webpage tests..."
|
||||||
echo "Base URL: $base_url"
|
echo "Base URL: $base_url"
|
||||||
echo "Number of lines: $(wc -l < "$url_file")"
|
echo "Number of lines: $(wc -l < "$url_file")"
|
||||||
echo "----------------------------------------"
|
echo "Max parallel jobs: $max_parallel"
|
||||||
|
echo "----------------------------------------"
|
||||||
while IFS= read -r url || [ -n "$url" ]; do
|
|
||||||
# Skip empty lines and comments
|
|
||||||
[[ -z "$url" || "$url" =~ ^#.*$ ]] && continue
|
|
||||||
|
|
||||||
((total_count++))
|
|
||||||
if ! check_webpage "$url" "$base_url"; then
|
|
||||||
((failed_count++))
|
|
||||||
fi
|
|
||||||
done < "$url_file"
|
|
||||||
|
|
||||||
local end_time=$(date +%s)
|
# Process each URL
|
||||||
local duration=$((end_time - start_time))
|
while IFS= read -r url || [ -n "$url" ]; do
|
||||||
|
# Skip empty lines and comments
|
||||||
|
[[ -z "$url" || "$url" =~ ^#.*$ ]] && continue
|
||||||
|
|
||||||
|
((total_count++))
|
||||||
|
((url_index++))
|
||||||
|
|
||||||
echo "----------------------------------------"
|
# Run the check in background
|
||||||
echo "Test Summary:"
|
test_url "$url" "$base_url" "$tmp_dir" "$url_index" &
|
||||||
echo "Total tests: $total_count"
|
|
||||||
echo "Failed tests: $failed_count"
|
# Track the job
|
||||||
echo "Passed tests: $((total_count - failed_count))"
|
((active_jobs++))
|
||||||
echo "Duration: ${duration} seconds"
|
|
||||||
|
# If we've reached max_parallel, wait for a job to finish
|
||||||
|
if [ $active_jobs -ge $max_parallel ]; then
|
||||||
|
wait -n # Wait for any child process to exit
|
||||||
|
((active_jobs--))
|
||||||
|
fi
|
||||||
|
done < "$url_file"
|
||||||
|
|
||||||
return $failed_count
|
# Wait for remaining jobs to finish
|
||||||
|
wait
|
||||||
|
|
||||||
|
# Print results in order and count failures
|
||||||
|
for i in $(seq 1 $url_index); do
|
||||||
|
if [ -f "${tmp_dir}/result_${i}.txt" ]; then
|
||||||
|
cat "${tmp_dir}/result_${i}.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${tmp_dir}/failed_${i}" ]; then
|
||||||
|
failed_count=$((failed_count + $(cat "${tmp_dir}/failed_${i}")))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
|
||||||
|
local end_time=$(date +%s)
|
||||||
|
local duration=$((end_time - start_time))
|
||||||
|
|
||||||
|
echo "----------------------------------------"
|
||||||
|
echo "Test Summary:"
|
||||||
|
echo "Total tests: $total_count"
|
||||||
|
echo "Failed tests: $failed_count"
|
||||||
|
echo "Passed tests: $((total_count - failed_count))"
|
||||||
|
echo "Duration: ${duration} seconds"
|
||||||
|
|
||||||
|
return $failed_count
|
||||||
}
|
}
|
||||||
|
|
||||||
# Print usage information
|
# Print usage information
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 [-f url_file] [-b base_url]"
|
echo "Usage: $0 [-f url_file] [-b base_url] [-p max_parallel]"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -f url_file Path to file containing URLs to test (required)"
|
echo " -f url_file Path to file containing URLs to test (required)"
|
||||||
echo " -b base_url Base URL to prepend to test URLs (default: http://localhost:8080)"
|
echo " -b base_url Base URL to prepend to test URLs (default: http://localhost:8080)"
|
||||||
exit 1
|
echo " -p max_parallel Maximum number of parallel requests (default: 10)"
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution
|
# Main execution
|
||||||
main() {
|
main() {
|
||||||
local url_file=""
|
local url_file=""
|
||||||
local base_url="http://localhost:8080"
|
local base_url="http://localhost:8080"
|
||||||
|
local max_parallel=10
|
||||||
|
|
||||||
# Parse command line options
|
# Parse command line options
|
||||||
while getopts ":f:b:h" opt; do
|
while getopts ":f:b:p:h" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
f) url_file="$OPTARG" ;;
|
f) url_file="$OPTARG" ;;
|
||||||
b) base_url="$OPTARG" ;;
|
b) base_url="$OPTARG" ;;
|
||||||
h) usage ;;
|
p) max_parallel="$OPTARG" ;;
|
||||||
\?) echo "Invalid option -$OPTARG" >&2; usage ;;
|
h) usage ;;
|
||||||
esac
|
\?) echo "Invalid option -$OPTARG" >&2; usage ;;
|
||||||
done
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# Check if URL file is provided
|
# Check if URL file is provided
|
||||||
if [ -z "$url_file" ]; then
|
if [ -z "$url_file" ]; then
|
||||||
echo "Error: URL file is required"
|
echo "Error: URL file is required"
|
||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if URL file exists
|
# Check if URL file exists
|
||||||
if [ ! -f "$url_file" ]; then
|
if [ ! -f "$url_file" ]; then
|
||||||
echo "Error: URL list file not found: $url_file"
|
echo "Error: URL list file not found: $url_file"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run tests using the URL list
|
# Run tests using the URL list
|
||||||
if test_all_urls "$url_file" "$base_url"; then
|
if test_all_urls "$url_file" "$base_url" "$max_parallel"; then
|
||||||
echo "All webpage tests passed!"
|
echo "All webpage tests passed!"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo "Some webpage tests failed!"
|
echo "Some webpage tests failed!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run main if script is executed directly
|
# Run main if script is executed directly
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
main "$@"
|
main "$@"
|
||||||
fi
|
fi
|
Loading…
Reference in New Issue
Block a user