Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
597e57c009 | ||
|
|
0011947c60 | ||
|
|
369b3a7ba6 | ||
|
|
dfa6dccee1 | ||
|
|
c94e5f3e55 | ||
|
|
171b603abe | ||
|
|
671f2cd361 | ||
|
|
c90bad8f35 | ||
|
|
b229222f45 |
@@ -1,5 +1,2 @@
|
|||||||
# Formatting
|
# Formatting
|
||||||
5f771b785130154ed47952635b7acef371ffe0ec
|
5f771b785130154ed47952635b7acef371ffe0ec
|
||||||
|
|
||||||
# Normalize files
|
|
||||||
55d4fda01b2f39f5b7d7b4fda5214bd7ff0fd5dd
|
|
||||||
51
.github/scripts/check_duplicates.py
vendored
51
.github/scripts/check_duplicates.py
vendored
@@ -1,51 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def find_duplicate_keys(file_path):
|
|
||||||
"""
|
|
||||||
Finds duplicate keys in a properties file and returns their occurrences.
|
|
||||||
|
|
||||||
This function reads a properties file, identifies any keys that occur more than
|
|
||||||
once, and returns a dictionary with these keys and the line numbers of their occurrences.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
file_path (str): The path to the properties file to be checked.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: A dictionary where each key is a duplicated key in the file, and the value is a list
|
|
||||||
of line numbers where the key occurs.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
lines = file.readlines()
|
|
||||||
|
|
||||||
keys = {}
|
|
||||||
duplicates = {}
|
|
||||||
|
|
||||||
for line_number, line in enumerate(lines, start=1):
|
|
||||||
line = line.strip()
|
|
||||||
if line and not line.startswith("#") and "=" in line:
|
|
||||||
key = line.split("=", 1)[0].strip()
|
|
||||||
if key in keys:
|
|
||||||
# If the key already exists, add the current line number
|
|
||||||
duplicates.setdefault(key, []).append(line_number)
|
|
||||||
# Also add the first instance of the key if not already done
|
|
||||||
if keys[key] not in duplicates[key]:
|
|
||||||
duplicates[key].insert(0, keys[key])
|
|
||||||
else:
|
|
||||||
# Store the line number of the first instance of the key
|
|
||||||
keys[key] = line_number
|
|
||||||
|
|
||||||
return duplicates
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
failed = False
|
|
||||||
for ar in sys.argv[1:]:
|
|
||||||
duplicates = find_duplicate_keys(ar)
|
|
||||||
if duplicates:
|
|
||||||
for key, lines in duplicates.items():
|
|
||||||
lines_str = ", ".join(map(str, lines))
|
|
||||||
print(f"{key} duplicated in {ar} on lines {lines_str}")
|
|
||||||
failed = True
|
|
||||||
if failed:
|
|
||||||
sys.exit(1)
|
|
||||||
84
.github/scripts/check_tabulator.py
vendored
84
.github/scripts/check_tabulator.py
vendored
@@ -1,84 +0,0 @@
|
|||||||
"""check_tabulator.py"""
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def check_tabs(file_path):
|
|
||||||
"""
|
|
||||||
Checks for tabs in the specified file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path (str): The path to the file to be checked.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if tabs are found, False otherwise.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
content = file.read()
|
|
||||||
|
|
||||||
if "\t" in content:
|
|
||||||
print(f"Tab found in {file_path}")
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def replace_tabs_with_spaces(file_path, replace_with=" "):
|
|
||||||
"""
|
|
||||||
Replaces tabs with a specified number of spaces in the file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path (str): The path to the file where tabs will be replaced.
|
|
||||||
replace_with (str): The character(s) to replace tabs with. Defaults to two spaces.
|
|
||||||
"""
|
|
||||||
with open(file_path, "r", encoding="utf-8") as file:
|
|
||||||
content = file.read()
|
|
||||||
|
|
||||||
updated_content = content.replace("\t", replace_with)
|
|
||||||
|
|
||||||
with open(file_path, "w", encoding="utf-8") as file:
|
|
||||||
file.write(updated_content)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main function to replace tabs with spaces in the provided files.
|
|
||||||
The replacement character and files to check are taken from command line arguments.
|
|
||||||
"""
|
|
||||||
# Create ArgumentParser instance
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Replace tabs in files with specified characters."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define optional argument `--replace_with`
|
|
||||||
parser.add_argument(
|
|
||||||
"--replace_with",
|
|
||||||
default=" ",
|
|
||||||
help="Character(s) to replace tabs with. Default is two spaces.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define argument for file paths
|
|
||||||
parser.add_argument("files", metavar="FILE", nargs="+", help="Files to process.")
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Extract replacement characters and files from the parsed arguments
|
|
||||||
replace_with = args.replace_with
|
|
||||||
files_checked = args.files
|
|
||||||
|
|
||||||
error = False
|
|
||||||
|
|
||||||
for file_path in files_checked:
|
|
||||||
if check_tabs(file_path):
|
|
||||||
replace_tabs_with_spaces(file_path, replace_with)
|
|
||||||
error = True
|
|
||||||
|
|
||||||
if error:
|
|
||||||
print("Error: Originally found tabs in HTML files, now replaced.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
67
.github/scripts/gradle_to_chart.py
vendored
67
.github/scripts/gradle_to_chart.py
vendored
@@ -1,67 +0,0 @@
|
|||||||
import re
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
# Paths to the files
|
|
||||||
chart_yaml_path = "chart/stirling-pdf/Chart.yaml"
|
|
||||||
gradle_path = "build.gradle"
|
|
||||||
|
|
||||||
|
|
||||||
def get_chart_version(path):
|
|
||||||
"""
|
|
||||||
Reads the appVersion from Chart.yaml.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): The file path to the Chart.yaml.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: The appVersion if found, otherwise an empty string.
|
|
||||||
"""
|
|
||||||
with open(path, encoding="utf-8") as file:
|
|
||||||
chart_yaml = yaml.safe_load(file)
|
|
||||||
return chart_yaml.get("appVersion", "")
|
|
||||||
|
|
||||||
|
|
||||||
def get_gradle_version(path):
|
|
||||||
"""
|
|
||||||
Extracts the version from build.gradle.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): The file path to the build.gradle.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: The version if found, otherwise an empty string.
|
|
||||||
"""
|
|
||||||
with open(path, encoding="utf-8") as file:
|
|
||||||
for line in file:
|
|
||||||
if "version =" in line:
|
|
||||||
# Extracts the value after 'version ='
|
|
||||||
return re.search(r'version\s*=\s*[\'"](.+?)[\'"]', line).group(1)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def update_chart_version(path, new_version):
|
|
||||||
"""
|
|
||||||
Updates the appVersion in Chart.yaml with a new version.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): The file path to the Chart.yaml.
|
|
||||||
new_version (str): The new version to update to.
|
|
||||||
"""
|
|
||||||
with open(path, encoding="utf-8") as file:
|
|
||||||
chart_yaml = yaml.safe_load(file)
|
|
||||||
chart_yaml["appVersion"] = new_version
|
|
||||||
with open(path, "w", encoding="utf-8") as file:
|
|
||||||
yaml.safe_dump(chart_yaml, file)
|
|
||||||
|
|
||||||
|
|
||||||
# Main logic
|
|
||||||
chart_version = get_chart_version(chart_yaml_path)
|
|
||||||
gradle_version = get_gradle_version(gradle_path)
|
|
||||||
|
|
||||||
if chart_version != gradle_version:
|
|
||||||
print(
|
|
||||||
f"Versions do not match. Updating Chart.yaml from {chart_version} to {gradle_version}."
|
|
||||||
)
|
|
||||||
update_chart_version(chart_yaml_path, gradle_version)
|
|
||||||
else:
|
|
||||||
print("Versions match. No update required.")
|
|
||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -3,14 +3,8 @@ name: "Build repo"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
paths-ignore:
|
|
||||||
- ".github/**"
|
|
||||||
- "**/*.md"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
paths-ignore:
|
|
||||||
- ".github/**"
|
|
||||||
- "**/*.md"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
33
.github/workflows/licenses-update.yml
vendored
33
.github/workflows/licenses-update.yml
vendored
@@ -32,30 +32,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json
|
mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json
|
||||||
|
|
||||||
- name: Set up git config
|
- name: Check for Changes
|
||||||
run: |
|
id: git-check
|
||||||
git config --global user.email "GitHub Action <action@github.com>"
|
|
||||||
git config --global user.name "GitHub Action <action@github.com>"
|
|
||||||
|
|
||||||
- name: Run git add
|
|
||||||
run: |
|
run: |
|
||||||
git add src/main/resources/static/3rdPartyLicenses.json
|
git add src/main/resources/static/3rdPartyLicenses.json
|
||||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
git diff --staged --exit-code || echo "changes=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Commit and Push Changes
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.changes == 'true'
|
||||||
uses: peter-evans/create-pull-request@v3
|
run: |
|
||||||
with:
|
git config --global user.name 'Stirling-PDF-Bot'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
git config --global user.email 'Stirling-PDF-Bot@stirlingtools.com'
|
||||||
commit-message: "Update 3rd Party Licenses"
|
git commit -m "Update 3rd Party Licenses"
|
||||||
committer: GitHub Action <action@github.com>
|
git push
|
||||||
author: GitHub Action <action@github.com>
|
|
||||||
signoff: true
|
|
||||||
branch: update-3rd-party-licenses
|
|
||||||
title: "Update 3rd Party Licenses"
|
|
||||||
body: |
|
|
||||||
Auto-generated by [create-pull-request][1]
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
draft: false
|
|
||||||
delete-branch: true
|
|
||||||
|
|
||||||
|
|||||||
55
.github/workflows/push-docker.yml
vendored
55
.github/workflows/push-docker.yml
vendored
@@ -3,10 +3,9 @@ name: Push Docker Image with VersionNumber
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
@@ -16,13 +15,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
|
||||||
- uses: gradle/gradle-build-action@v2.4.2
|
- uses: gradle/gradle-build-action@v2.4.2
|
||||||
env:
|
env:
|
||||||
@@ -33,11 +32,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Make Gradle wrapper executable
|
- name: Make Gradle wrapper executable
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2.1.0
|
uses: docker/login-action@v2.1.0
|
||||||
with:
|
with:
|
||||||
@@ -54,7 +53,7 @@ jobs:
|
|||||||
- name: Convert repository owner to lowercase
|
- name: Convert repository owner to lowercase
|
||||||
id: repoowner
|
id: repoowner
|
||||||
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
||||||
|
|
||||||
- name: Generate tags
|
- name: Generate tags
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4.4.0
|
uses: docker/metadata-action@v4.4.0
|
||||||
@@ -83,7 +82,7 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-args:
|
build-args:
|
||||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
@@ -100,7 +99,7 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
|
||||||
- name: Build and push Dockerfile-ultra-lite
|
- name: Build and push Dockerfile-ultra-lite
|
||||||
uses: docker/build-push-action@v4.0.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
@@ -113,6 +112,40 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta2.outputs.tags }}
|
tags: ${{ steps.meta2.outputs.tags }}
|
||||||
labels: ${{ steps.meta2.outputs.labels }}
|
labels: ${{ steps.meta2.outputs.labels }}
|
||||||
build-args:
|
build-args:
|
||||||
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Generate tags lite
|
||||||
|
id: meta3
|
||||||
|
uses: docker/metadata-action@v4.4.0
|
||||||
|
if: github.ref != 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
|
tags: |
|
||||||
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: Build and push Dockerfile-lite
|
||||||
|
uses: docker/build-push-action@v4.0.0
|
||||||
|
if: github.ref != 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile-lite
|
||||||
|
push: true
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
tags: ${{ steps.meta3.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta3.outputs.labels }}
|
||||||
|
build-args:
|
||||||
|
VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
- name: Build and Push Helm Chart
|
||||||
|
run: |
|
||||||
|
helm package chart/stirling-pdf
|
||||||
|
helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle
|
||||||
|
|||||||
12
.github/workflows/releaseArtifacts.yml
vendored
12
.github/workflows/releaseArtifacts.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: Release Artifacts
|
name: Release Artifacts
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -19,13 +19,13 @@ jobs:
|
|||||||
file_suffix: ''
|
file_suffix: ''
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ jobs:
|
|||||||
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|
||||||
- name: Upload jar binaries to release
|
- name: Upload jar binaries to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
9
.github/workflows/swagger.yml
vendored
9
.github/workflows/swagger.yml
vendored
@@ -3,9 +3,8 @@ name: Update Swagger
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
@@ -13,13 +12,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3.11.0
|
uses: actions/setup-java@v3.11.0
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
|
|
||||||
@@ -35,4 +34,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}"
|
curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}"
|
||||||
env:
|
env:
|
||||||
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
|
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
|
||||||
51
.github/workflows/sync_versions.yml
vendored
51
.github/workflows/sync_versions.yml
vendored
@@ -1,51 +0,0 @@
|
|||||||
name: Sync Versions
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- "build.gradle"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync-versions:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4.1.1
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5.0.0
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pip install pyyaml
|
|
||||||
- name: Sync versions
|
|
||||||
run: python .github/scripts/gradle_to_chart.py
|
|
||||||
- name: Set up git config
|
|
||||||
run: |
|
|
||||||
git config --global user.email "GitHub Action <action@github.com>"
|
|
||||||
git config --global user.name "GitHub Action <action@github.com>"
|
|
||||||
- name: Run git add
|
|
||||||
run: |
|
|
||||||
git add .
|
|
||||||
git diff --staged --quiet || git commit -m ":floppy_disk: Sync Versions
|
|
||||||
> Made via sync_versions.yml" || echo "no changes"
|
|
||||||
- name: Create Pull Request
|
|
||||||
uses: peter-evans/create-pull-request@v6.0.0
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commit-message: Update files
|
|
||||||
committer: GitHub Action <action@github.com>
|
|
||||||
author: GitHub Action <action@github.com>
|
|
||||||
signoff: true
|
|
||||||
branch: sync_version
|
|
||||||
title: ":floppy_disk: Update Version"
|
|
||||||
body: |
|
|
||||||
Auto-generated by [create-pull-request][1]
|
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
draft: false
|
|
||||||
delete-branch: true
|
|
||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Run Docker Compose Tests
|
- name: Run Docker Compose Tests
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./gradlew
|
chmod +x ./gradlew
|
||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)"
|
||||||
|
|||||||
252
.gitignore
vendored
252
.gitignore
vendored
@@ -1,127 +1,127 @@
|
|||||||
|
|
||||||
|
|
||||||
### Eclipse ###
|
### Eclipse ###
|
||||||
.metadata
|
.metadata
|
||||||
bin/
|
bin/
|
||||||
tmp/
|
tmp/
|
||||||
*.tmp
|
*.tmp
|
||||||
*.bak
|
*.bak
|
||||||
*.swp
|
*.swp
|
||||||
*~.nib
|
*~.nib
|
||||||
local.properties
|
local.properties
|
||||||
.settings/
|
.settings/
|
||||||
.loadpath
|
.loadpath
|
||||||
.recommenders
|
.recommenders
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
version.properties
|
version.properties
|
||||||
pipeline/watchedFolders/
|
pipeline/watchedFolders/
|
||||||
pipeline/finishedFolders/
|
pipeline/finishedFolders/
|
||||||
#### Stirling-PDF Files ###
|
#### Stirling-PDF Files ###
|
||||||
customFiles/
|
customFiles/
|
||||||
configs/
|
configs/
|
||||||
watchedFolders/
|
watchedFolders/
|
||||||
|
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
.lock
|
.lock
|
||||||
|
|
||||||
# External tool builders
|
# External tool builders
|
||||||
.externalToolBuilders/
|
.externalToolBuilders/
|
||||||
|
|
||||||
# Locally stored "Eclipse launch configurations"
|
# Locally stored "Eclipse launch configurations"
|
||||||
*.launch
|
*.launch
|
||||||
|
|
||||||
# PyDev specific (Python IDE for Eclipse)
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
*.pydevproject
|
*.pydevproject
|
||||||
|
|
||||||
# CDT-specific (C/C++ Development Tooling)
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
.cproject
|
.cproject
|
||||||
|
|
||||||
# CDT- autotools
|
# CDT- autotools
|
||||||
.autotools
|
.autotools
|
||||||
|
|
||||||
# Java annotation processor (APT)
|
# Java annotation processor (APT)
|
||||||
.factorypath
|
.factorypath
|
||||||
|
|
||||||
# PDT-specific (PHP Development Tools)
|
# PDT-specific (PHP Development Tools)
|
||||||
.buildpath
|
.buildpath
|
||||||
|
|
||||||
# sbteclipse plugin
|
# sbteclipse plugin
|
||||||
.target
|
.target
|
||||||
|
|
||||||
# Tern plugin
|
# Tern plugin
|
||||||
.tern-project
|
.tern-project
|
||||||
|
|
||||||
# TeXlipse plugin
|
# TeXlipse plugin
|
||||||
.texlipse
|
.texlipse
|
||||||
|
|
||||||
# STS (Spring Tool Suite)
|
# STS (Spring Tool Suite)
|
||||||
.springBeans
|
.springBeans
|
||||||
|
|
||||||
# Code Recommenders
|
# Code Recommenders
|
||||||
.recommenders/
|
.recommenders/
|
||||||
|
|
||||||
# Annotation Processing
|
# Annotation Processing
|
||||||
.apt_generated/
|
.apt_generated/
|
||||||
.apt_generated_test/
|
.apt_generated_test/
|
||||||
|
|
||||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
.cache-main
|
.cache-main
|
||||||
.scala_dependencies
|
.scala_dependencies
|
||||||
.worksheet
|
.worksheet
|
||||||
|
|
||||||
# Uncomment this line if you wish to ignore the project description file.
|
# Uncomment this line if you wish to ignore the project description file.
|
||||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||||
#.project
|
#.project
|
||||||
|
|
||||||
### Eclipse Patch ###
|
### Eclipse Patch ###
|
||||||
# Spring Boot Tooling
|
# Spring Boot Tooling
|
||||||
.sts4-cache/
|
.sts4-cache/
|
||||||
|
|
||||||
### Git ###
|
### Git ###
|
||||||
# Created by git for backups. To disable backups in Git:
|
# Created by git for backups. To disable backups in Git:
|
||||||
# $ git config --global mergetool.keepBackup false
|
# $ git config --global mergetool.keepBackup false
|
||||||
*.orig
|
*.orig
|
||||||
|
|
||||||
# Created by git when using merge tools for conflicts
|
# Created by git when using merge tools for conflicts
|
||||||
*.BACKUP.*
|
*.BACKUP.*
|
||||||
*.BASE.*
|
*.BASE.*
|
||||||
*.LOCAL.*
|
*.LOCAL.*
|
||||||
*.REMOTE.*
|
*.REMOTE.*
|
||||||
*_BACKUP_*.txt
|
*_BACKUP_*.txt
|
||||||
*_BASE_*.txt
|
*_BASE_*.txt
|
||||||
*_LOCAL_*.txt
|
*_LOCAL_*.txt
|
||||||
*_REMOTE_*.txt
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
### Java ###
|
### Java ###
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
|
||||||
# Log file
|
# Log file
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# BlueJ files
|
# BlueJ files
|
||||||
*.ctxt
|
*.ctxt
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
# Mobile Tools for Java (J2ME)
|
||||||
.mtj.tmp/
|
.mtj.tmp/
|
||||||
|
|
||||||
# Package Files #
|
# Package Files #
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
*.db
|
*.db
|
||||||
/build
|
/build
|
||||||
|
|
||||||
/.vscode
|
/.vscode
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
# Ignore Mac DS_Store files
|
# Ignore Mac DS_Store files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
repos:
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
||||||
rev: v0.2.1
|
|
||||||
hooks:
|
|
||||||
- id: ruff
|
|
||||||
args:
|
|
||||||
- --fix
|
|
||||||
- --line-length=127
|
|
||||||
files: ^((.github/scripts)/.+)?[^/]+\.py$
|
|
||||||
- id: ruff-format
|
|
||||||
files: ^((.github/scripts)/.+)?[^/]+\.py$
|
|
||||||
- repo: https://github.com/codespell-project/codespell
|
|
||||||
rev: v2.2.6
|
|
||||||
hooks:
|
|
||||||
- id: codespell
|
|
||||||
args:
|
|
||||||
- --ignore-words-list=
|
|
||||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
|
||||||
- --quiet-level=2
|
|
||||||
files: \.(properties|html|css|js|py|md)$
|
|
||||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: check-duplicate-properties-keys
|
|
||||||
name: Check Duplicate Properties Keys
|
|
||||||
entry: python .github/scripts/check_duplicates.py
|
|
||||||
language: python
|
|
||||||
files: ^(src)/.+\.properties$
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: check-html-tabs
|
|
||||||
name: Check HTML for tabs
|
|
||||||
# args: ["--replace_with= "]
|
|
||||||
entry: python .github/scripts/check_tabulator.py
|
|
||||||
language: python
|
|
||||||
exclude: ^src/main/resources/static/pdfjs/
|
|
||||||
files: ^.*(\.html|\.css|\.js)$
|
|
||||||
136
Dockerfile
136
Dockerfile
@@ -1,67 +1,69 @@
|
|||||||
# Main stage
|
# Main stage
|
||||||
FROM alpine:20240329
|
FROM alpine:3.19.1
|
||||||
|
|
||||||
# Copy necessary files
|
# JDK for app
|
||||||
COPY scripts /scripts
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
COPY pipeline /pipeline
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
apk add --no-cache \
|
||||||
COPY build/libs/*.jar app.jar
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
ARG VERSION_TAG
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
# Set Environment Variables
|
openjdk17-jre \
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
# Doc conversion
|
||||||
VERSION_TAG=$VERSION_TAG \
|
libreoffice@testing \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
# OCR MY PDF (unpaper for descew and other advanced featues)
|
||||||
HOME=/home/stirlingpdfuser \
|
ocrmypdf \
|
||||||
PUID=1000 \
|
tesseract-ocr-data-eng \
|
||||||
PGID=1000 \
|
# CV
|
||||||
UMASK=022
|
py3-opencv \
|
||||||
|
# python3/pip
|
||||||
|
python3 && \
|
||||||
# JDK for app
|
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
# uno unoconv and HTML
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
mv /usr/share/tessdata /usr/share/tessdata-original
|
||||||
apk add --no-cache \
|
|
||||||
ca-certificates \
|
|
||||||
tzdata \
|
|
||||||
tini \
|
ARG VERSION_TAG
|
||||||
bash \
|
|
||||||
curl \
|
# Set Environment Variables
|
||||||
openjdk17-jre \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
su-exec \
|
HOME=/home/stirlingpdfuser \
|
||||||
font-noto-cjk \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
shadow \
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||||
# Doc conversion
|
# PUID=1000 \
|
||||||
libreoffice@testing \
|
# PGID=1000 \
|
||||||
# pdftohtml
|
# UMASK=022 \
|
||||||
poppler-utils \
|
|
||||||
# OCR MY PDF (unpaper for descew and other advanced featues)
|
# Copy necessary files
|
||||||
ocrmypdf \
|
COPY scripts /scripts
|
||||||
tesseract-ocr-data-eng \
|
COPY pipeline /pipeline
|
||||||
# CV
|
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||||
py3-opencv \
|
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||||
# python3/pip
|
COPY build/libs/*.jar app.jar
|
||||||
python3 && \
|
|
||||||
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
# Create user and group
|
||||||
# uno unoconv and HTML
|
##RUN groupadd -g $PGID stirlingpdfgroup && \
|
||||||
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
## useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
||||||
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME && \
|
||||||
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
# Set up necessary directories and permissions
|
||||||
fc-cache -f -v && \
|
RUN mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
chmod +x /scripts/* && \
|
##&& \
|
||||||
chmod +x /scripts/init.sh && \
|
## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \
|
||||||
# User permissions
|
## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original && \
|
||||||
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
# Set font cache and permissions
|
||||||
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \
|
fc-cache -f -v && \
|
||||||
chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
chmod +x /scripts/*
|
||||||
tesseract --list-langs
|
## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \
|
||||||
|
## chmod +x /scripts/init.sh
|
||||||
EXPOSE 8080
|
|
||||||
|
EXPOSE 8080
|
||||||
# Set user and run command
|
|
||||||
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
# Set user and run command
|
||||||
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
##USER stirlingpdfuser
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
||||||
|
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||||
|
|||||||
61
Dockerfile-lite
Normal file
61
Dockerfile-lite
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# use alpine
|
||||||
|
FROM alpine:3.19.1
|
||||||
|
|
||||||
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
# Set Environment Variables
|
||||||
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
|
HOME=/home/stirlingpdfuser \
|
||||||
|
VERSION_TAG=$VERSION_TAG \
|
||||||
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||||
|
# PUID=1000 \
|
||||||
|
# PGID=1000 \
|
||||||
|
# UMASK=022 \
|
||||||
|
|
||||||
|
# Copy necessary files
|
||||||
|
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||||
|
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||||
|
COPY pipeline /pipeline
|
||||||
|
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto
|
||||||
|
COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto
|
||||||
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
openjdk17-jre \
|
||||||
|
# Doc conversion
|
||||||
|
libreoffice@testing \
|
||||||
|
# python and pip
|
||||||
|
python3 && \
|
||||||
|
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
|
||||||
|
# uno unoconv and HTML
|
||||||
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
||||||
|
# Create user and group
|
||||||
|
#RUN groupadd -g $PGID stirlingpdfgroup && \
|
||||||
|
# useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \
|
||||||
|
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||||
|
# Set up necessary directories and permissions
|
||||||
|
mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
|
# chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/fonts/opentype/noto /configs /customFiles
|
||||||
|
# Set font cache and permissions
|
||||||
|
fc-cache -f -v && \
|
||||||
|
chmod +x /scripts/*.sh
|
||||||
|
# chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV ENDPOINTS_GROUPS_TO_REMOVE=OpenCV,OCRmyPDF
|
||||||
|
ENV DOCKER_ENABLE_SECURITY=false
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
#USER stirlingpdfuser
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||||
|
CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"]
|
||||||
@@ -7,10 +7,10 @@ ARG VERSION_TAG
|
|||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \
|
JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75"
|
||||||
PUID=1000 \
|
# PUID=1000 \
|
||||||
PGID=1000 \
|
# PGID=1000 \
|
||||||
UMASK=022
|
# UMASK=022 \
|
||||||
|
|
||||||
# Copy necessary files
|
# Copy necessary files
|
||||||
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||||
@@ -18,10 +18,16 @@ COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
|||||||
COPY pipeline /pipeline
|
COPY pipeline /pipeline
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
|
# Create user and group using Alpine's addgroup and adduser
|
||||||
|
#RUN addgroup -g $PGID stirlingpdfgroup && \
|
||||||
|
# adduser -u $PUID -G stirlingpdfgroup -s /bin/sh -D stirlingpdfuser && \
|
||||||
|
# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME
|
||||||
# Set up necessary directories and permissions
|
# Set up necessary directories and permissions
|
||||||
|
#RUN mkdir -p /scripts /configs /customFiles && \
|
||||||
|
# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders
|
||||||
RUN mkdir /configs /logs /customFiles && \
|
RUN mkdir /configs /logs /customFiles && \
|
||||||
|
# Set font cache and permissions
|
||||||
|
#RUN chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
||||||
chmod +x /scripts/*.sh && \
|
chmod +x /scripts/*.sh && \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
@@ -29,16 +35,10 @@ RUN mkdir /configs /logs /customFiles && \
|
|||||||
tini \
|
tini \
|
||||||
bash \
|
bash \
|
||||||
curl \
|
curl \
|
||||||
su-exec \
|
|
||||||
shadow \
|
|
||||||
openjdk17-jre && \
|
openjdk17-jre && \
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories
|
||||||
# User permissions
|
|
||||||
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
|
||||||
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /configs /customFiles /pipeline && \
|
|
||||||
chown stirlingpdfuser:stirlingpdfgroup /app.jar
|
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
| Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript |
|
||||||
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
|---------------------|---------|---------|----------|-------|------|--------|--------|-------------|----------|----------|------------|
|
||||||
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
| adjust-contrast | ✔️ | | | | | | | | | | ✔️ |
|
||||||
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
| auto-split-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| crop | ✔️ | | | | | | | | | ✔️ | |
|
| crop | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
| extract-page | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
| merge-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
| multi-page-layout | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
| pdf-organizer | ✔️ | | | | | | | | | ✔️ | ✔️ |
|
||||||
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
| pdf-to-single-page | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
| remove-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
| rotate-pdf | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
| scale-pages | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
| split-pdfs | ✔️ | | | | | | | | | ✔️ | |
|
||||||
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
||||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-text | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-word | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-xml | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| xlsx-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
| change-permissions | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
| remove-password | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
| sanitize-pdf | | | ✔️ | | | | | | | ✔️ | |
|
||||||
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
| add-image | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
| add-page-numbers | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
| auto-rename | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
| change-metadata | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| compare | | | | ✔️ | | | | | | | ✔️ |
|
| compare | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
| compress-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||||
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
| extract-image-scans | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||||
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
| extract-images | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
| flatten | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
| get-info-on-pdf | | | | ✔️ | | | | | | ✔️ | |
|
||||||
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
| ocr-pdf | | | | ✔️ | ✔️ | | | | ✔️ | | |
|
||||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
||||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| sign | | | | ✔️ | | | | | | | ✔️ |
|
| sign | | | | ✔️ | | | | | | | ✔️ |
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
## User Guide for Local Directory Scanning and File Processing
|
## User Guide for Local Directory Scanning and File Processing
|
||||||
|
|
||||||
|
### Whilst Pipelines are in alpha...
|
||||||
|
You must enable this alpha functionality by setting
|
||||||
|
```yaml
|
||||||
|
system:
|
||||||
|
enableAlphaFunctionality: true
|
||||||
|
```
|
||||||
|
To true like in the above for your `/config/settings.yml` file, after restarting Stirling-PDF you should see both UI and folder scanning enabled.
|
||||||
|
|
||||||
### Setting Up Watched Folders:
|
### Setting Up Watched Folders:
|
||||||
- Create a folder where you want your files to be monitored. This is your 'watched folder'.
|
- Create a folder where you want your files to be monitored. This is your 'watched folder'.
|
||||||
- The default directory for this is `./pipeline/watchedFolders/`
|
- The default directory for this is `./pipeline/watchedFolders/`
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ Fork Stirling-PDF and make a new branch out of Main
|
|||||||
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
Then add reference to the language in the navbar by adding a new language entry to the dropdown
|
||||||
|
|
||||||
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html
|
https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html
|
||||||
and add a flag svg file to
|
and add a flag svg file to
|
||||||
https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags
|
||||||
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
||||||
If your language isn't represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
If your language isnt represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
||||||
|
|
||||||
|
|
||||||
For example to add Polish you would add
|
For example to add Polish you would add
|
||||||
```html
|
```html
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
||||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||||
@@ -23,7 +23,7 @@ For example to add Polish you would add
|
|||||||
```
|
```
|
||||||
The data-language-code is the code used to reference the file in the next step.
|
The data-language-code is the code used to reference the file in the next step.
|
||||||
|
|
||||||
Start by copying the existing english property file
|
Start by copying the existing english property file
|
||||||
|
|
||||||
[https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
[https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ Copy and rename it to messages_{your data-language-code here}.properties, in the
|
|||||||
|
|
||||||
Then simply translate all property entries within that file and make a PR into main for others to use!
|
Then simply translate all property entries within that file and make a PR into main for others to use!
|
||||||
|
|
||||||
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but won't be able to verify the translations themselves)
|
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but wont be able to verify the translations themselves)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
This document provides instructions on how to add additional language packs for the OCR tab in Stirling-PDF, both inside and outside of Docker.
|
This document provides instructions on how to add additional language packs for the OCR tab in Stirling-PDF, both inside and outside of Docker.
|
||||||
|
|
||||||
## My OCR used to work and now doesn't!
|
## My OCR used to work and now doesnt!
|
||||||
The paths have changed for the tessadata locations on new docker images, please use ``/usr/share/tessdata`` (Others should still work for backwards compatibility but might not)
|
Please update your tesseract docker volume path version from 4.00 to 5
|
||||||
|
|
||||||
## How does the OCR Work
|
## How does the OCR Work
|
||||||
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition.
|
Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition.
|
||||||
All credit goes to them for this awesome work!
|
All credit goes to them for this awesome work!
|
||||||
|
|
||||||
## Language Packs
|
## Language Packs
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ Depending on your requirements, you can choose the appropriate language pack for
|
|||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
If you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.
|
||||||
#### Docker Compose
|
#### Docker Compose
|
||||||
Modify your `docker-compose.yml` file to include the following volume configuration:
|
Modify your `docker-compose.yml` file to include the following volume configuration:
|
||||||
|
|
||||||
|
|||||||
88
Jenkinsfile
vendored
88
Jenkinsfile
vendored
@@ -1,45 +1,45 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
stages {
|
stages {
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
steps {
|
steps {
|
||||||
sh 'chmod 755 gradlew'
|
sh 'chmod 755 gradlew'
|
||||||
sh './gradlew build'
|
sh './gradlew build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docker Build') {
|
stage('Docker Build') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||||
def image = "frooodle/s-pdf:$appVersion"
|
def image = "frooodle/s-pdf:$appVersion"
|
||||||
sh "docker build -t $image ."
|
sh "docker build -t $image ."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docker Push') {
|
stage('Docker Push') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
def appVersion = sh(returnStdout: true, script: './gradlew printVersion -q').trim()
|
||||||
def image = "frooodle/s-pdf:$appVersion"
|
def image = "frooodle/s-pdf:$appVersion"
|
||||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||||
sh "docker push $image"
|
sh "docker push $image"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Helm Push') {
|
stage('Helm Push') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
//TODO: Read chartVersion from Chart.yaml
|
//TODO: Read chartVersion from Chart.yaml
|
||||||
def chartVersion = '1.0.0'
|
def chartVersion = '1.0.0'
|
||||||
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
withCredentials([string(credentialsId: 'docker_hub_access_token', variable: 'DOCKER_HUB_ACCESS_TOKEN')]) {
|
||||||
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
sh "docker login --username frooodle --password $DOCKER_HUB_ACCESS_TOKEN"
|
||||||
sh "helm package chart/stirling-pdf"
|
sh "helm package chart/stirling-pdf"
|
||||||
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
sh "helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,10 +42,10 @@ For Debian-based systems, you can use the following command:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ openjdk-17-jdk python3 python3-pip
|
sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip
|
||||||
```
|
```
|
||||||
|
|
||||||
For Fedora-based systems use this command:
|
For Fedora-based systems use this command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-17-openjdk python3 python3-pip
|
sudo dnf install -y git automake autoconf libtool leptonica-devel pkg-config zlib-devel make gcc-c++ java-17-openjdk python3 python3-pip
|
||||||
@@ -65,7 +65,7 @@ sudo make install
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Install Additional Software
|
### Step 3: Install Additional Software
|
||||||
Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for pattern recognition functionality.
|
Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for patern recognition functionality.
|
||||||
|
|
||||||
Install the following software:
|
Install the following software:
|
||||||
|
|
||||||
@@ -95,14 +95,14 @@ For Debian-based systems, you can use the following command:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages
|
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||||
```
|
```
|
||||||
|
|
||||||
For Fedora:
|
For Fedora:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
sudo dnf install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf
|
||||||
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: Clone and Build Stirling-PDF
|
### Step 4: Clone and Build Stirling-PDF
|
||||||
@@ -140,7 +140,7 @@ Manual:
|
|||||||
|
|
||||||
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.
|
||||||
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata`
|
||||||
3.
|
3.
|
||||||
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
Please view [OCRmyPDF install guide](https://ocrmypdf.readthedocs.io/en/latest/installation.html) for more info.
|
||||||
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
**IMPORTANT:** DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.
|
||||||
|
|
||||||
@@ -264,10 +264,10 @@ sudo systemctl restart stirlingpdf.service
|
|||||||
|
|
||||||
Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme.
|
Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme.
|
||||||
|
|
||||||
You can do this in the terminal by using the `export` command or -D argument to java -jar command:
|
You can do this in the terminal by using the `export` command or -D arguements to java -jar command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export APP_HOME_NAME="Stirling PDF"
|
export APP_HOME_NAME="Stirling PDF"
|
||||||
or
|
or
|
||||||
-DAPP_HOME_NAME="Stirling PDF"
|
-DAPP_HOME_NAME="Stirling PDF"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
# Pipeline Configuration and Usage Tutorial
|
# Pipeline Configuration and Usage Tutorial
|
||||||
- Configure the pipeline config file and input files to run files against it
|
|
||||||
- For reuse, download the config file and re-upload it when needed, or place it in /pipeline/defaultWebUIConfigs/ to auto-load in the web UI for all users
|
## Whilst Pipelines are in alpha...
|
||||||
|
You must enable this alpha functionality by setting
|
||||||
|
```yaml
|
||||||
|
system:
|
||||||
|
enableAlphaFunctionality: true
|
||||||
|
```
|
||||||
|
To true like in the above for your `/config/settings.yml` file, after restarting Stirling-PDF you should see both UI and folder scanning enabled.
|
||||||
|
|
||||||
|
|
||||||
## Steps to Configure and Use Your Pipeline
|
## Steps to Configure and Use Your Pipeline
|
||||||
|
|
||||||
@@ -33,12 +40,3 @@
|
|||||||
|
|
||||||
10. **Note on Web UI Limitations**
|
10. **Note on Web UI Limitations**
|
||||||
- The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF.
|
- The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF.
|
||||||
|
|
||||||
|
|
||||||
### Current Limitations
|
|
||||||
- Cannot have more than one of the same operation
|
|
||||||
- Cannot input additional files via UI
|
|
||||||
- All files and operations run in serial mode
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
105
README.md
105
README.md
@@ -1,35 +1,34 @@
|
|||||||
<p align="center"><img src="https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png" width="80" ></p>
|
<p align="center"><img src="https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png" width="80" ><br><h1 align="center">Stirling-PDF</h1>
|
||||||
<h1 align="center">Stirling-PDF</h1>
|
</p>
|
||||||
|
|
||||||
[](https://hub.docker.com/r/frooodle/s-pdf)
|
[](https://hub.docker.com/r/frooodle/s-pdf)
|
||||||
[](https://discord.gg/Cn8pWhQRxZ)
|
[](https://discord.gg/Cn8pWhQRxZ)
|
||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
[](https://www.paypal.com/paypalme/froodleplex)
|
[](https://www.paypal.com/paypalme/froodleplex)
|
||||||
[](https://github.com/sponsors/Frooodle)
|
[](https://github.com/sponsors/Frooodle)
|
||||||
|
|
||||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
||||||
|
|
||||||
This is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. Originally developed entirely by ChatGPT, this locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs.
|
||||||
|
|
||||||
Stirling PDF does not initiate any outbound calls for record-keeping or tracking purposes.
|
Stirling PDF makes no outbound calls for any record keeping or tracking.
|
||||||
|
|
||||||
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Dark mode support.
|
- Dark mode support.
|
||||||
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
|
- Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example)
|
||||||
- Parallel file processing and downloads
|
- Parallel file processing and downloads
|
||||||
- API for integration with external scripts
|
- API for integration with external scripts
|
||||||
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
|
- Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation)
|
||||||
|
|
||||||
|
|
||||||
## **PDF Features**
|
## **PDF Features**
|
||||||
|
|
||||||
### **Page Operations**
|
### **Page Operations**
|
||||||
|
|
||||||
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
|
- View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts)
|
||||||
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
|
- Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages.
|
||||||
- Merge multiple PDFs together into a single resultant file.
|
- Merge multiple PDFs together into a single resultant file.
|
||||||
@@ -46,7 +45,6 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Convert PDF to a single page.
|
- Convert PDF to a single page.
|
||||||
|
|
||||||
### **Conversion Operations**
|
### **Conversion Operations**
|
||||||
|
|
||||||
- Convert PDFs to and from images.
|
- Convert PDFs to and from images.
|
||||||
- Convert any common file to PDF (using LibreOffice).
|
- Convert any common file to PDF (using LibreOffice).
|
||||||
- Convert PDF to Word/Powerpoint/Others (using LibreOffice).
|
- Convert PDF to Word/Powerpoint/Others (using LibreOffice).
|
||||||
@@ -55,7 +53,6 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Markdown to PDF.
|
- Markdown to PDF.
|
||||||
|
|
||||||
### **Security & Permissions**
|
### **Security & Permissions**
|
||||||
|
|
||||||
- Add and remove passwords.
|
- Add and remove passwords.
|
||||||
- Change/set PDF Permissions.
|
- Change/set PDF Permissions.
|
||||||
- Add watermark(s).
|
- Add watermark(s).
|
||||||
@@ -64,7 +61,6 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Auto-redact text.
|
- Auto-redact text.
|
||||||
|
|
||||||
### **Other Operations**
|
### **Other Operations**
|
||||||
|
|
||||||
- Add/Generate/Write signatures.
|
- Add/Generate/Write signatures.
|
||||||
- Repair PDFs.
|
- Repair PDFs.
|
||||||
- Detect and remove blank pages.
|
- Detect and remove blank pages.
|
||||||
@@ -81,11 +77,11 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Flatten PDFs.
|
- Flatten PDFs.
|
||||||
- Get all information on a PDF to view or export as JSON.
|
- Get all information on a PDF to view or export as JSON.
|
||||||
|
|
||||||
|
|
||||||
For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
||||||
Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo
|
Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo
|
||||||
|
|
||||||
## Technologies used
|
## Technologies used
|
||||||
|
|
||||||
- Spring Boot + Thymeleaf
|
- Spring Boot + Thymeleaf
|
||||||
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
|
- [PDFBox](https://github.com/apache/pdfbox/tree/trunk)
|
||||||
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
- [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions
|
||||||
@@ -98,21 +94,19 @@ Demo of the app is available [here](https://stirlingpdf.io). username: demo, pas
|
|||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
### Locally
|
### Locally
|
||||||
|
|
||||||
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md
|
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md
|
||||||
|
|
||||||
### Docker / Podman
|
### Docker / Podman
|
||||||
|
|
||||||
https://hub.docker.com/r/frooodle/s-pdf
|
https://hub.docker.com/r/frooodle/s-pdf
|
||||||
|
|
||||||
Stirling PDF has 2 different versions, a Full version and ultra-Lite version. Depending on the types of features you use you may want a smaller image to save on space.
|
Stirling PDF has 3 different versions, a Full version, Lite, and ultra-Lite. Depending on the types of features you use you may want a smaller image to save on space.
|
||||||
To see what the different versions offer please look at our [version mapping](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Version-groups.md)
|
To see what the different versions offer please look at our [version mapping](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Version-groups.md)
|
||||||
For people that don't mind about space optimization just use the latest tag.
|
For people that don't mind about space optimization just use the latest tag.
|
||||||

|

|
||||||
|

|
||||||

|

|
||||||
|
|
||||||
Docker Run
|
Docker Run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
@@ -120,7 +114,6 @@ docker run -d \
|
|||||||
-v /location/of/extraConfigs:/configs \
|
-v /location/of/extraConfigs:/configs \
|
||||||
-v /location/of/logs:/logs \
|
-v /location/of/logs:/logs \
|
||||||
-e DOCKER_ENABLE_SECURITY=false \
|
-e DOCKER_ENABLE_SECURITY=false \
|
||||||
-e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
|
||||||
--name stirling-pdf \
|
--name stirling-pdf \
|
||||||
frooodle/s-pdf:latest
|
frooodle/s-pdf:latest
|
||||||
|
|
||||||
@@ -129,9 +122,7 @@ docker run -d \
|
|||||||
|
|
||||||
-v /location/of/customFiles:/customFiles \
|
-v /location/of/customFiles:/customFiles \
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker Compose
|
Docker Compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
@@ -146,66 +137,59 @@ services:
|
|||||||
# - /location/of/logs:/logs/
|
# - /location/of/logs:/logs/
|
||||||
environment:
|
environment:
|
||||||
- DOCKER_ENABLE_SECURITY=false
|
- DOCKER_ENABLE_SECURITY=false
|
||||||
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
|
Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman".
|
||||||
|
|
||||||
## Enable OCR/Compression feature
|
## Enable OCR/Compression feature
|
||||||
|
|
||||||
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
|
Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
|
||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling PDF currently supports 26!
|
Stirling PDF currently supports 26!
|
||||||
|
- English (English) (en_GB)
|
||||||
| Language | Progress |
|
- English (US) (en_US)
|
||||||
| ------------------------------------------- | -------------------------------------- |
|
- Arabic (العربية) (ar_AR)
|
||||||
| English (English) (en_GB) |  |
|
- German (Deutsch) (de_DE)
|
||||||
| English (US) (en_US) |  |
|
- French (Français) (fr_FR)
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
- Spanish (Español) (es_ES)
|
||||||
| German (Deutsch) (de_DE) |  |
|
- Simplified Chinese (简体中文) (zh_CN)
|
||||||
| French (Français) (fr_FR) |  |
|
- Traditional Chinese (繁體中文) (zh_TW)
|
||||||
| Spanish (Español) (es_ES) |  |
|
- Catalan (Català) (ca_CA)
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
- Italian (Italiano) (it_IT)
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
- Swedish (Svenska) (sv_SE)
|
||||||
| Catalan (Català) (ca_CA) |  |
|
- Polish (Polski) (pl_PL)
|
||||||
| Italian (Italiano) (it_IT) |  |
|
- Romanian (Română) (ro_RO)
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
- Korean (한국어) (ko_KR)
|
||||||
| Polish (Polski) (pl_PL) |  |
|
- Portuguese Brazilian (Português) (pt_BR)
|
||||||
| Romanian (Română) (ro_RO) |  |
|
- Russian (Русский) (ru_RU)
|
||||||
| Korean (한국어) (ko_KR) |  |
|
- Basque (Euskara) (eu_ES)
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
- Japanese (日本語) (ja_JP)
|
||||||
| Russian (Русский) (ru_RU) |  |
|
- Dutch (Nederlands) (nl_NL)
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
- Greek (el_GR)
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
- Turkish (Türkçe) (tr_TR)
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
- Indonesia (Bahasa Indonesia) (id_ID)
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
- Hindi (हिंदी) (hi_IN)
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
- Hungarian (Magyar) (hu_HU)
|
||||||
| Indonesia (Bahasa Indonesia) (id_ID) |  |
|
- Bulgarian (Български) (bg_BG)
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
- Sebian Latin alphabet (Srpski) (sr-Latn-RS)
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
|
||||||
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
|
||||||
|
|
||||||
## Contributing (creating issues, translations, fixing bugs, etc.)
|
## Contributing (creating issues, translations, fixing bugs, etc.)
|
||||||
|
|
||||||
Please see our [Contributing Guide](CONTRIBUTING.md)!
|
Please see our [Contributing Guide](CONTRIBUTING.md)!
|
||||||
|
|
||||||
## Customisation
|
## Customisation
|
||||||
|
|
||||||
Stirling PDF allows easy customization of the app.
|
Stirling PDF allows easy customization of the app.
|
||||||
Includes things like
|
Includes things like
|
||||||
|
|
||||||
- Custom application name
|
- Custom application name
|
||||||
- Custom slogans, icons, images, and even custom HTML (via file overrides)
|
- Custom slogans, icons, images, and even custom HTML (via file overrides)
|
||||||
|
|
||||||
|
|
||||||
There are two options for this, either using the generated settings file ``settings.yml``
|
There are two options for this, either using the generated settings file ``settings.yml``
|
||||||
This file is located in the ``/configs`` directory and follows standard YAML formatting
|
This file is located in the ``/configs`` directory and follows standard YAML formatting
|
||||||
|
|
||||||
Environment variables are also supported and would override the settings file
|
Environment variables are also supported and would override the settings file
|
||||||
For example in the settings.yml you have
|
For example in the settings.yml you have
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
system:
|
system:
|
||||||
defaultLocale: 'en-US'
|
defaultLocale: 'en-US'
|
||||||
@@ -214,7 +198,6 @@ system:
|
|||||||
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
|
To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE``
|
||||||
|
|
||||||
The Current list of settings is
|
The Current list of settings is
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
@@ -237,31 +220,23 @@ endpoints:
|
|||||||
metrics:
|
metrics:
|
||||||
enabled: true # 'true' to enable Info APIs endpoints (view http://localhost:8080/swagger-ui/index.html#/API to learn more), 'false' to disable
|
enabled: true # 'true' to enable Info APIs endpoints (view http://localhost:8080/swagger-ui/index.html#/API to learn more), 'false' to disable
|
||||||
```
|
```
|
||||||
|
|
||||||
### Extra notes
|
### Extra notes
|
||||||
|
|
||||||
- Endpoints. Currently, the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma separate lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image-to-pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
- Endpoints. Currently, the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma separate lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image-to-pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md)
|
||||||
- customStaticFilePath. Customise static files such as the app logo by placing files in the /customFiles/static/ directory. An example of customising app logo is placing a /customFiles/static/favicon.svg to override current SVG. This can be used to change any images/icons/css/fonts/js etc in Stirling-PDF
|
- customStaticFilePath. Customise static files such as the app logo by placing files in the /customFiles/static/ directory. An example of customising app logo is placing a /customFiles/static/favicon.svg to override current SVG. This can be used to change any images/icons/css/fonts/js etc in Stirling-PDF
|
||||||
|
|
||||||
### Environment only parameters
|
### Environment only parameters
|
||||||
|
|
||||||
- ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app``
|
- ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app``
|
||||||
- ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values
|
- ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values
|
||||||
- ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login)
|
- ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login)
|
||||||
- ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS`` to download calibre onto stirling-pdf enabling pdf to/from book and advanced html conversion
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation
|
||||||
[here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF)
|
[here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF)
|
||||||
|
|
||||||
|
|
||||||
## Login authentication
|
## Login authentication
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Prerequisites:
|
### Prerequisites:
|
||||||
|
|
||||||
- User must have the folder ./configs volumed within docker so that it is retained during updates.
|
- User must have the folder ./configs volumed within docker so that it is retained during updates.
|
||||||
- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
|
- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables.
|
||||||
- Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true``
|
- Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true``
|
||||||
@@ -277,22 +252,20 @@ To add new users go to the bottom of Account settings and hit 'Admin Settings',
|
|||||||
|
|
||||||
For API usage you must provide a header with 'X-API-Key' and the associated API key for that user.
|
For API usage you must provide a header with 'X-API-Key' and the associated API key for that user.
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Q1: What are your planned features?
|
### Q1: What are your planned features?
|
||||||
|
|
||||||
- Progress bar/Tracking
|
- Progress bar/Tracking
|
||||||
- Full custom logic pipelines to combine multiple operations together.
|
- Full custom logic pipelines to combine multiple operations together.
|
||||||
- Folder support with auto scanning to perform operations on
|
- Folder support with auto scanning to perform operations on
|
||||||
- Redact text (Via UI not just automated way)
|
- Redact text (Via UI not just automated way)
|
||||||
- Add Forms
|
- Add Forms
|
||||||
- Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing
|
- Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing
|
||||||
- Fill forms manually or automatically
|
- Fill forms mannual and automatic
|
||||||
|
|
||||||
### Q2: Why is my application downloading .htm files?
|
### Q2: Why is my application downloading .htm files?
|
||||||
|
|
||||||
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files.
|
||||||
|
|
||||||
### Q3: Why is my download timing out
|
### Q3: Why is my download timing out
|
||||||
|
|
||||||
NGINX has timeout values by default so if you are running Stirling-PDF behind NGINX you may need to set a timeout value such as adding the config ``proxy_read_timeout 3600;``
|
NGINX has timeout values by default so if you are running Stirling-PDF behind NGINX you may need to set a timeout value such as adding the config ``proxy_read_timeout 3600;``
|
||||||
|
|||||||
@@ -1,52 +1,64 @@
|
|||||||
| Technology | Ultra-Lite | Full |
|
|Technology | Ultra-Lite | Lite | Full |
|
||||||
|----------------|:----------:|:----:|
|
|----------------|:----------:|:----:|:----:|
|
||||||
| Java | ✔️ | ✔️ |
|
| Java | ✔️ | ✔️ | ✔️ |
|
||||||
| JavaScript | ✔️ | ✔️ |
|
| JavaScript | ✔️ | ✔️ | ✔️ |
|
||||||
| Libre | | ✔️ |
|
| Libre | | ✔️ | ✔️ |
|
||||||
| Python | | ✔️ |
|
| Python | | | ✔️ |
|
||||||
| OpenCV | | ✔️ |
|
| OpenCV | | | ✔️ |
|
||||||
| OCRmyPDF | | ✔️ |
|
| OCRmyPDF | | | ✔️ |
|
||||||
|
|
||||||
Operation | Ultra-Lite | Full
|
|
||||||
-------------------------|------------|-----
|
|
||||||
add-page-numbers | ✔️ | ✔️
|
|
||||||
add-password | ✔️ | ✔️
|
|
||||||
add-image | ✔️ | ✔️
|
Operation | Ultra-Lite | Lite | Full
|
||||||
add-watermark | ✔️ | ✔️
|
--------------------|------------|------|-----
|
||||||
adjust-contrast | ✔️ | ✔️
|
add-page-numbers | ✔️ | ✔️ | ✔️
|
||||||
auto-split-pdf | ✔️ | ✔️
|
add-password | ✔️ | ✔️ | ✔️
|
||||||
auto-redact | ✔️ | ✔️
|
add-image | ✔️ | ✔️ | ✔️
|
||||||
auto-rename | ✔️ | ✔️
|
add-watermark | ✔️ | ✔️ | ✔️
|
||||||
cert-sign | ✔️ | ✔️
|
adjust-contrast | ✔️ | ✔️ | ✔️
|
||||||
crop | ✔️ | ✔️
|
auto-split-pdf | ✔️ | ✔️ | ✔️
|
||||||
change-metadata | ✔️ | ✔️
|
auto-redact | ✔️ | ✔️ | ✔️
|
||||||
change-permissions | ✔️ | ✔️
|
auto-rename | ✔️ | ✔️ | ✔️
|
||||||
compare | ✔️ | ✔️
|
cert-sign | ✔️ | ✔️ | ✔️
|
||||||
extract-page | ✔️ | ✔️
|
crop | ✔️ | ✔️ | ✔️
|
||||||
extract-images | ✔️ | ✔️
|
change-metadata | ✔️ | ✔️ | ✔️
|
||||||
flatten | ✔️ | ✔️
|
change-permissions | ✔️ | ✔️ | ✔️
|
||||||
get-info-on-pdf | ✔️ | ✔️
|
compare | ✔️ | ✔️ | ✔️
|
||||||
img-to-pdf | ✔️ | ✔️
|
extract-page | ✔️ | ✔️ | ✔️
|
||||||
markdown-to-pdf | ✔️ | ✔️
|
extract-images | ✔️ | ✔️ | ✔️
|
||||||
merge-pdfs | ✔️ | ✔️
|
flatten | ✔️ | ✔️ | ✔️
|
||||||
multi-page-layout | ✔️ | ✔️
|
get-info-on-pdf | ✔️ | ✔️ | ✔️
|
||||||
overlay-pdf | ✔️ | ✔️
|
img-to-pdf | ✔️ | ✔️ | ✔️
|
||||||
pdf-organizer | ✔️ | ✔️
|
markdown-to-pdf | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-csv | ✔️ | ✔️
|
merge-pdfs | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-img | ✔️ | ✔️
|
multi-page-layout | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-single-page | ✔️ | ✔️
|
overlay-pdf | ✔️ | ✔️ | ✔️
|
||||||
remove-pages | ✔️ | ✔️
|
pdf-organizer | ✔️ | ✔️ | ✔️
|
||||||
remove-password | ✔️ | ✔️
|
pdf-to-csv | ✔️ | ✔️ | ✔️
|
||||||
rotate-pdf | ✔️ | ✔️
|
pdf-to-img | ✔️ | ✔️ | ✔️
|
||||||
sanitize-pdf | ✔️ | ✔️
|
pdf-to-single-page | ✔️ | ✔️ | ✔️
|
||||||
scale-pages | ✔️ | ✔️
|
remove-pages | ✔️ | ✔️ | ✔️
|
||||||
sign | ✔️ | ✔️
|
remove-password | ✔️ | ✔️ | ✔️
|
||||||
show-javascript | ✔️ | ✔️
|
rotate-pdf | ✔️ | ✔️ | ✔️
|
||||||
split-by-size-or-count | ✔️ | ✔️
|
sanitize-pdf | ✔️ | ✔️ | ✔️
|
||||||
split-pdf-by-sections | ✔️ | ✔️
|
scale-pages | ✔️ | ✔️ | ✔️
|
||||||
split-pdfs | ✔️ | ✔️
|
sign | ✔️ | ✔️ | ✔️
|
||||||
compress-pdf | | ✔️
|
show-javascript | ✔️ | ✔️ | ✔️
|
||||||
extract-image-scans | | ✔️
|
split-by-size-or-count | ✔️ | ✔️ | ✔️
|
||||||
ocr-pdf | | ✔️
|
split-pdf-by-sections | ✔️ | ✔️ | ✔️
|
||||||
pdf-to-pdfa | | ✔️
|
split-pdfs | ✔️ | ✔️ | ✔️
|
||||||
remove-blanks | | ✔️
|
file-to-pdf | | ✔️ | ✔️
|
||||||
|
pdf-to-html | | ✔️ | ✔️
|
||||||
|
pdf-to-presentation | | ✔️ | ✔️
|
||||||
|
pdf-to-text | | ✔️ | ✔️
|
||||||
|
pdf-to-word | | ✔️ | ✔️
|
||||||
|
pdf-to-xml | | ✔️ | ✔️
|
||||||
|
repair | | ✔️ | ✔️
|
||||||
|
xlsx-to-pdf | | ✔️ | ✔️
|
||||||
|
compress-pdf | | | ✔️
|
||||||
|
extract-image-scans | | | ✔️
|
||||||
|
ocr-pdf | | | ✔️
|
||||||
|
pdf-to-pdfa | | | ✔️
|
||||||
|
remove-blanks | | | ✔️
|
||||||
|
|||||||
58
build.gradle
58
build.gradle
@@ -1,18 +1,18 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.2.4'
|
id 'org.springframework.boot' version '3.2.2'
|
||||||
id 'io.spring.dependency-management' version '1.1.3'
|
id 'io.spring.dependency-management' version '1.1.3'
|
||||||
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
id 'org.springdoc.openapi-gradle-plugin' version '1.8.0'
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id 'edu.sc.seis.launch4j' version '3.0.5'
|
id 'edu.sc.seis.launch4j' version '3.0.5'
|
||||||
id 'com.diffplug.spotless' version '6.25.0'
|
id 'com.diffplug.spotless' version '6.25.0'
|
||||||
id 'com.github.jk1.dependency-license-report' version '2.6'
|
id 'com.github.jk1.dependency-license-report' version '2.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '0.22.8'
|
version = '0.21.0'
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -20,6 +20,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
licenseReport {
|
licenseReport {
|
||||||
renderers = [new JsonReportRenderer()]
|
renderers = [new JsonReportRenderer()]
|
||||||
}
|
}
|
||||||
@@ -47,6 +48,7 @@ openApi {
|
|||||||
outputFileName = "SwaggerDoc.json"
|
outputFileName = "SwaggerDoc.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
launch4j {
|
launch4j {
|
||||||
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
||||||
|
|
||||||
@@ -85,26 +87,26 @@ spotless {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//security updates
|
//security updates
|
||||||
implementation 'ch.qos.logback:logback-classic:1.5.3'
|
implementation 'ch.qos.logback:logback-classic:1.4.14'
|
||||||
implementation 'ch.qos.logback:logback-core:1.5.3'
|
implementation 'ch.qos.logback:logback-core:1.4.14'
|
||||||
implementation 'org.springframework:spring-webmvc:6.1.5'
|
implementation 'org.springframework:spring-webmvc:6.1.2'
|
||||||
|
|
||||||
implementation("io.github.pixee:java-security-toolkit:1.1.3")
|
|
||||||
|
|
||||||
|
implementation("io.github.pixee:java-security-toolkit:1.1.2")
|
||||||
|
|
||||||
implementation 'org.yaml:snakeyaml:2.2'
|
implementation 'org.yaml:snakeyaml:2.2'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.4'
|
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.2'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.4'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.2'
|
||||||
|
|
||||||
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.4'
|
implementation 'org.springframework.boot:spring-boot-starter-security:3.2.2'
|
||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE'
|
||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.4"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.2"
|
||||||
|
|
||||||
//2.2.x requires rebuild of DB file.. need migration path
|
//2.2.x requires rebuild of DB file.. need migration path
|
||||||
implementation "com.h2database:h2:2.1.214"
|
implementation "com.h2database:h2:2.1.214"
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.4'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.2'
|
||||||
|
|
||||||
// Batik
|
// Batik
|
||||||
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
implementation 'org.apache.xmlgraphics:batik-all:1.17'
|
||||||
@@ -136,31 +138,29 @@ dependencies {
|
|||||||
implementation ('com.opencsv:opencsv:5.9') {
|
implementation ('com.opencsv:opencsv:5.9') {
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ('org.apache.pdfbox:pdfbox:3.0.2'){
|
implementation ('org.apache.pdfbox:pdfbox:3.0.1'){
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ('org.apache.pdfbox:xmpbox:3.0.2'){
|
implementation ('org.apache.pdfbox:xmpbox:3.0.1'){
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
|
||||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
implementation 'org.bouncycastle:bcpkix-jdk18on:1.77'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.4'
|
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.2'
|
||||||
implementation 'io.micrometer:micrometer-core:1.12.4'
|
implementation 'io.micrometer:micrometer-core:1.12.2'
|
||||||
implementation group: 'com.google.zxing', name: 'core', version: '3.5.3'
|
implementation group: 'com.google.zxing', name: 'core', version: '3.5.2'
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation 'org.commonmark:commonmark:0.22.0'
|
implementation 'org.commonmark:commonmark:0.21.0'
|
||||||
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.22.0'
|
implementation 'org.commonmark:commonmark-ext-gfm-tables:0.21.0'
|
||||||
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
// https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core
|
||||||
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0'
|
||||||
|
|
||||||
implementation 'com.fathzer:javaluator:3.0.3'
|
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.2")
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.4")
|
annotationProcessor 'org.projectlombok:lombok:1.18.28'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.32'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.32'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 0.22.8
|
appVersion: 0.14.2
|
||||||
description: locally hosted web application that allows you to perform various operations
|
description: locally hosted web application that allows you to perform various operations on PDF files
|
||||||
on PDF files
|
|
||||||
home: https://github.com/Stirling-Tools/Stirling-PDF
|
home: https://github.com/Stirling-Tools/Stirling-PDF
|
||||||
keywords:
|
keywords:
|
||||||
- stirling-pdf
|
- stirling-pdf
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ spec:
|
|||||||
name: http
|
name: http
|
||||||
{{- end }}
|
{{- end }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|
||||||
selector:
|
selector:
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ commonLabels: {}
|
|||||||
# team_name: dev
|
# team_name: dev
|
||||||
|
|
||||||
envs: []
|
envs: []
|
||||||
# - name: UI_APP_NAME
|
# - name: PP_HOME_NAME
|
||||||
# value: "Stirling PDF"
|
# value: "Stirling PDF"
|
||||||
# - name: UI_HOME_DESCRIPTION
|
# - name: APP_HOME_DESCRIPTION
|
||||||
# value: "Your locally hosted one-stop-shop for all your PDF needs."
|
# value: "Your locally hosted one-stop-shop for all your PDF needs."
|
||||||
# - name: UI_APP_NAVBAR_NAME
|
# - name: APP_NAVBAR_NAME
|
||||||
# value: "Stirling PDF"
|
# value: "Stirling PDF"
|
||||||
# - name: ALLOW_GOOGLE_VISIBILITY
|
# - name: ALLOW_GOOGLE_VISIBILITY
|
||||||
# value: "true"
|
# value: "true"
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
id="defs173">
|
id="defs173">
|
||||||
|
|
||||||
|
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="XMLID_5_"
|
id="XMLID_5_"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
style="stop-color:#C2C2C9"
|
style="stop-color:#C2C2C9"
|
||||||
id="stop158" />
|
id="stop158" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
|
|
||||||
</defs><sodipodi:namedview
|
</defs><sodipodi:namedview
|
||||||
id="namedview171"
|
id="namedview171"
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
31
exampleYmlFiles/docker-compose-latest-lite-security.yml
Normal file
31
exampleYmlFiles/docker-compose-latest-lite-security.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
stirling-pdf:
|
||||||
|
container_name: Stirling-PDF-Lite-Security
|
||||||
|
image: frooodle/s-pdf:latest-lite
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 16
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
||||||
|
- /stirling/latest/config:/configs:rw
|
||||||
|
- /stirling/latest/logs:/logs:rw
|
||||||
|
environment:
|
||||||
|
DOCKER_ENABLE_SECURITY: "true"
|
||||||
|
SECURITY_ENABLELOGIN: "true"
|
||||||
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
|
UI_APPNAME: Stirling-PDF-Lite
|
||||||
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security
|
||||||
|
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||||
|
SYSTEM_MAXFILESIZE: "100"
|
||||||
|
METRICS_ENABLED: "true"
|
||||||
|
SYSTEM_GOOGLEVISIBILITY: "true"
|
||||||
|
restart: on-failure:5
|
||||||
30
exampleYmlFiles/docker-compose-latest-lite.yml
Normal file
30
exampleYmlFiles/docker-compose-latest-lite.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
stirling-pdf:
|
||||||
|
container_name: Stirling-PDF-Lite
|
||||||
|
image: frooodle/s-pdf:latest-lite
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -qv 'Please sign in'"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 16
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- /stirling/latest/config:/configs:rw
|
||||||
|
- /stirling/latest/logs:/logs:rw
|
||||||
|
environment:
|
||||||
|
DOCKER_ENABLE_SECURITY: "false"
|
||||||
|
SECURITY_ENABLELOGIN: "false"
|
||||||
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
|
UI_APPNAME: Stirling-PDF-Lite
|
||||||
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest
|
||||||
|
UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest
|
||||||
|
SYSTEM_MAXFILESIZE: "100"
|
||||||
|
METRICS_ENABLED: "true"
|
||||||
|
SYSTEM_GOOGLEVISIBILITY: "true"
|
||||||
|
restart: on-failure:5
|
||||||
@@ -15,15 +15,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
DOCKER_ENABLE_SECURITY: "true"
|
DOCKER_ENABLE_SECURITY: "true"
|
||||||
SECURITY_ENABLELOGIN: "true"
|
SECURITY_ENABLELOGIN: "true"
|
||||||
PUID: 1002
|
|
||||||
PGID: 1002
|
|
||||||
UMASK: "022"
|
|
||||||
SYSTEM_DEFAULTLOCALE: en-US
|
SYSTEM_DEFAULTLOCALE: en-US
|
||||||
UI_APPNAME: Stirling-PDF
|
UI_APPNAME: Stirling-PDF
|
||||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
|
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
- /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw
|
||||||
- /stirling/latest/config:/configs:rw
|
- /stirling/latest/config:/configs:rw
|
||||||
- /stirling/latest/logs:/logs:rw
|
- /stirling/latest/logs:/logs:rw
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
182
gradlew.bat
vendored
182
gradlew.bat
vendored
@@ -1,91 +1,91 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@rem you may not use this file except in compliance with the License.
|
@rem you may not use this file except in compliance with the License.
|
||||||
@rem You may obtain a copy of the License at
|
@rem You may obtain a copy of the License at
|
||||||
@rem
|
@rem
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@rem
|
@rem
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo.
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation.
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo.
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation.
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
exit /b %EXIT_CODE%
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
:omega
|
:omega
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 203 KiB |
BIN
images/stirling-home.png
Normal file
BIN
images/stirling-home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
@@ -6,8 +6,7 @@
|
|||||||
"parameters": {
|
"parameters": {
|
||||||
"horizontalDivisions": 2,
|
"horizontalDivisions": 2,
|
||||||
"verticalDivisions": 2,
|
"verticalDivisions": 2,
|
||||||
"fileInput": "automated",
|
"fileInput": "automated"
|
||||||
"merge": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,4 +30,4 @@
|
|||||||
},
|
},
|
||||||
"outputDir": "{outputFolder}",
|
"outputDir": "{outputFolder}",
|
||||||
"outputFileName": "{filename}"
|
"outputFileName": "{filename}"
|
||||||
}
|
}
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
"""A script to update language progress status in README.md based on
|
|
||||||
properties file comparison.
|
|
||||||
|
|
||||||
This script compares default properties file with others in a directory to
|
|
||||||
determine language progress.
|
|
||||||
It then updates README.md based on provided progress list.
|
|
||||||
|
|
||||||
Author: Ludy87
|
|
||||||
|
|
||||||
Example:
|
|
||||||
To use this script, simply run it from command line:
|
|
||||||
$ python counter_translation.py
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import re
|
|
||||||
from typing import List, Tuple
|
|
||||||
|
|
||||||
|
|
||||||
def write_readme(progress_list: List[Tuple[str, int]]) -> None:
|
|
||||||
"""
|
|
||||||
Updates the progress status in the README.md file based
|
|
||||||
on the provided progress list.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
progress_list (List[Tuple[str, int]]): A list of tuples containing
|
|
||||||
language and progress percentage.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
with open("README.md", "r", encoding="utf-8") as file:
|
|
||||||
content = file.read()
|
|
||||||
|
|
||||||
lines = content.split("\n")
|
|
||||||
for i, line in enumerate(lines[2:], start=2):
|
|
||||||
for progress in progress_list:
|
|
||||||
language, value = progress
|
|
||||||
if language in line:
|
|
||||||
match = re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line)
|
|
||||||
if match:
|
|
||||||
lines[i] = line.replace(
|
|
||||||
match.group(0),
|
|
||||||
f"",
|
|
||||||
)
|
|
||||||
|
|
||||||
new_content = "\n".join(lines)
|
|
||||||
|
|
||||||
with open("README.md", "w", encoding="utf-8") as file:
|
|
||||||
file.write(new_content)
|
|
||||||
|
|
||||||
|
|
||||||
def compare_files(default_file_path, files_directory) -> List[Tuple[str, int]]:
|
|
||||||
"""
|
|
||||||
Compares the default properties file with other
|
|
||||||
properties files in the directory.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
default_file_path (str): The path to the default properties file.
|
|
||||||
files_directory (str): The directory containing other properties files.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Tuple[str, int]]: A list of tuples containing
|
|
||||||
language and progress percentage.
|
|
||||||
"""
|
|
||||||
file_paths = glob.glob(os.path.join(files_directory, "messages_*.properties"))
|
|
||||||
num_lines = sum(1 for _ in open(default_file_path, encoding="utf-8"))
|
|
||||||
|
|
||||||
result_list = []
|
|
||||||
|
|
||||||
for file_path in file_paths:
|
|
||||||
language = (
|
|
||||||
os.path.basename(file_path)
|
|
||||||
.split("messages_", 1)[1]
|
|
||||||
.split(".properties", 1)[0]
|
|
||||||
)
|
|
||||||
|
|
||||||
fails = 0
|
|
||||||
if "en_GB" in language or "en_US" in language:
|
|
||||||
result_list.append(("en_GB", 100))
|
|
||||||
result_list.append(("en_US", 100))
|
|
||||||
continue
|
|
||||||
|
|
||||||
with open(default_file_path, "r", encoding="utf-8") as default_file, open(
|
|
||||||
file_path, "r", encoding="utf-8"
|
|
||||||
) as file:
|
|
||||||
for _ in range(5):
|
|
||||||
next(default_file)
|
|
||||||
try:
|
|
||||||
next(file)
|
|
||||||
except StopIteration:
|
|
||||||
fails = num_lines
|
|
||||||
|
|
||||||
for _, (line_default, line_file) in enumerate(
|
|
||||||
zip(default_file, file), start=6
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
if (
|
|
||||||
line_default.split("=", 1)[1].strip()
|
|
||||||
== line_file.split("=", 1)[1].strip()
|
|
||||||
):
|
|
||||||
fails += 1
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
result_list.append(
|
|
||||||
(
|
|
||||||
language,
|
|
||||||
int((num_lines - fails) * 100 / num_lines),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
unique_data = list(set(result_list))
|
|
||||||
unique_data.sort(key=lambda x: x[1], reverse=True)
|
|
||||||
|
|
||||||
return unique_data
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
directory = os.path.join(os.getcwd(), "src", "main", "resources")
|
|
||||||
reference_file = os.path.join(directory, "messages_en_GB.properties")
|
|
||||||
write_readme(compare_files(reference_file, directory))
|
|
||||||
37
scripts/detect-blank-pages.py
Normal file
37
scripts/detect-blank-pages.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import cv2
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255, blur_size=5):
|
||||||
|
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
|
||||||
|
|
||||||
|
if image is None:
|
||||||
|
print(f"Error: Unable to read the image file: {image_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Apply Gaussian blur to reduce noise
|
||||||
|
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
||||||
|
|
||||||
|
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
|
# Calculate the percentage of white pixels in the thresholded image
|
||||||
|
white_pixels = np.sum(thresholded_image == white_value)
|
||||||
|
white_pixel_percentage = (white_pixels / thresholded_image.size) * 100
|
||||||
|
|
||||||
|
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
||||||
|
return white_pixel_percentage >= white_percent
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description='Detect if an image is considered blank or not.')
|
||||||
|
parser.add_argument('image_path', help='The path to the image file.')
|
||||||
|
parser.add_argument('-t', '--threshold', type=int, default=10, help='Threshold for determining white pixels. The default value is 10.')
|
||||||
|
parser.add_argument('-w', '--white_percent', type=float, default=99, help='The percentage of white pixels for an image to be considered blank. The default value is 99.')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
blank = is_blank_image(args.image_path, args.threshold, args.white_percent)
|
||||||
|
|
||||||
|
# Return code 1: The image is considered blank.
|
||||||
|
# Return code 0: The image is not considered blank.
|
||||||
|
sys.exit(int(blank))
|
||||||
@@ -4,7 +4,7 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
|||||||
if [ ! -f app-security.jar ]; then
|
if [ ! -f app-security.jar ]; then
|
||||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||||
curl -L -o app-security.jar https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
curl -L -o app-security.jar https://github.com/Stirling-Tools/Stirling-PDF/releases/download/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||||
|
|
||||||
# If the first download attempt failed, try with the 'v' prefix
|
# If the first download attempt failed, try with the 'v' prefix
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
echo "Trying to download from: https://github.com/Stirling-Tools/Stirling-PDF/releases/download/$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||||
@@ -14,8 +14,6 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
|||||||
if [ $? -eq 0 ]; then # checks if curl was successful
|
if [ $? -eq 0 ]; then # checks if curl was successful
|
||||||
rm -f app.jar
|
rm -f app.jar
|
||||||
ln -s app-security.jar app.jar
|
ln -s app-security.jar app.jar
|
||||||
chown stirlingpdfuser:stirlingpdfgroup app.jar || true
|
|
||||||
chmod 755 app.jar || true
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,29 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Update the user and group IDs as per environment variables
|
|
||||||
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
|
||||||
usermod -o -u "$PUID" stirlingpdfuser || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then
|
|
||||||
groupmod -o -g "$PGID" stirlingpdfgroup || true
|
|
||||||
fi
|
|
||||||
umask "$UMASK" || true
|
|
||||||
|
|
||||||
|
|
||||||
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
|
|
||||||
apk add --no-cache calibre@testing
|
|
||||||
fi
|
|
||||||
|
|
||||||
/scripts/download-security-jar.sh
|
/scripts/download-security-jar.sh
|
||||||
|
|
||||||
echo "Setting permissions and ownership for necessary directories..."
|
# Run the main command
|
||||||
if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar; then
|
exec "$@"
|
||||||
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar || true
|
|
||||||
# If chown succeeds, execute the command as stirlingpdfuser
|
|
||||||
exec su-exec stirlingpdfuser "$@"
|
|
||||||
else
|
|
||||||
# If chown fails, execute the command without changing the user context
|
|
||||||
echo "[WARN] Chown failed, running as host user"
|
|
||||||
exec "$@"
|
|
||||||
fi
|
|
||||||
@@ -13,47 +13,18 @@ if [ -d /usr/share/tesseract-ocr/5/tessdata ]; then
|
|||||||
cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true;
|
cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update the user and group IDs as per environment variables
|
|
||||||
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
|
||||||
usermod -o -u "$PUID" stirlingpdfuser || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then
|
|
||||||
groupmod -o -g "$PGID" stirlingpdfgroup || true
|
|
||||||
fi
|
|
||||||
umask "$UMASK" || true
|
|
||||||
|
|
||||||
|
|
||||||
# Check if TESSERACT_LANGS environment variable is set and is not empty
|
# Check if TESSERACT_LANGS environment variable is set and is not empty
|
||||||
if [[ -n "$TESSERACT_LANGS" ]]; then
|
if [[ -n "$TESSERACT_LANGS" ]]; then
|
||||||
# Convert comma-separated values to a space-separated list
|
# Convert comma-separated values to a space-separated list
|
||||||
LANGS=$(echo $TESSERACT_LANGS | tr ',' ' ')
|
LANGS=$(echo $TESSERACT_LANGS | tr ',' ' ')
|
||||||
pattern='^[a-zA-Z]{2,4}(_[a-zA-Z]{2,4})?$'
|
|
||||||
# Install each language pack
|
# Install each language pack
|
||||||
for LANG in $LANGS; do
|
for LANG in $LANGS; do
|
||||||
if [[ $LANG =~ $pattern ]]; then
|
apt-get install -y "tesseract-ocr-$LANG"
|
||||||
apk add --no-cache "tesseract-ocr-data-$LANG"
|
|
||||||
else
|
|
||||||
echo "Skipping invalid language code"
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then
|
|
||||||
apk add --no-cache calibre@testing
|
|
||||||
fi
|
|
||||||
|
|
||||||
/scripts/download-security-jar.sh
|
/scripts/download-security-jar.sh
|
||||||
|
|
||||||
echo "Setting permissions and ownership for necessary directories..."
|
# Run the main command
|
||||||
# Attempt to change ownership of directories and files
|
exec "$@"
|
||||||
if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar; then
|
|
||||||
chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles /pipeline /app.jar || true
|
|
||||||
# If chown succeeds, execute the command as stirlingpdfuser
|
|
||||||
exec su-exec stirlingpdfuser "$@"
|
|
||||||
else
|
|
||||||
# If chown fails, execute the command without changing the user context
|
|
||||||
echo "[WARN] Chown failed, running as host user"
|
|
||||||
exec "$@"
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import argparse
|
|||||||
import sys
|
import sys
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
def find_photo_boundaries(image, background_color, tolerance=30, min_area=10000, min_contour_area=500):
|
||||||
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
mask = cv2.inRange(image, background_color - tolerance, background_color + tolerance)
|
||||||
@@ -49,9 +49,9 @@ def auto_rotate(image, angle_threshold=1):
|
|||||||
angles = []
|
angles = []
|
||||||
for rho, theta in lines[:, 0]:
|
for rho, theta in lines[:, 0]:
|
||||||
angles.append((theta * 180) / np.pi - 90)
|
angles.append((theta * 180) / np.pi - 90)
|
||||||
|
|
||||||
angle = np.median(angles)
|
angle = np.median(angles)
|
||||||
|
|
||||||
if abs(angle) < angle_threshold:
|
if abs(angle) < angle_threshold:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
@@ -65,16 +65,16 @@ def auto_rotate(image, angle_threshold=1):
|
|||||||
|
|
||||||
def crop_borders(image, border_color, tolerance=30):
|
def crop_borders(image, border_color, tolerance=30):
|
||||||
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
mask = cv2.inRange(image, border_color - tolerance, border_color + tolerance)
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
if len(contours) == 0:
|
if len(contours) == 0:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
largest_contour = max(contours, key=cv2.contourArea)
|
largest_contour = max(contours, key=cv2.contourArea)
|
||||||
x, y, w, h = cv2.boundingRect(largest_contour)
|
x, y, w, h = cv2.boundingRect(largest_contour)
|
||||||
|
|
||||||
return image[y:y+h, x:x+w]
|
return image[y:y+h, x:x+w]
|
||||||
|
|
||||||
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
|
||||||
image = cv2.imread(input_file)
|
image = cv2.imread(input_file)
|
||||||
background_color = estimate_background_color(image)
|
background_color = estimate_background_color(image)
|
||||||
@@ -110,7 +110,7 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
|
parser.add_argument("--min_contour_area", type=int, default=500, help="Sets the minimum contour area threshold for a photo (default: 500).")
|
||||||
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
parser.add_argument("--angle_threshold", type=int, default=10, help="Sets the minimum absolute angle required for the image to be rotated (default: 10).")
|
||||||
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
parser.add_argument("--border_size", type=int, default=0, help="Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)
|
split_photos(args.input_file, args.output_directory, tolerance=args.tolerance, min_area=args.min_area, min_contour_area=args.min_contour_area, angle_threshold=args.angle_threshold, border_size=args.border_size)
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
@@ -19,25 +14,14 @@ import io.github.pixee.security.SystemCommand;
|
|||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.config.ConfigInitializer;
|
import stirling.software.SPDF.config.ConfigInitializer;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class);
|
|
||||||
|
|
||||||
@Autowired private Environment env;
|
@Autowired private Environment env;
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
|
||||||
|
|
||||||
private static String serverPortStatic;
|
|
||||||
|
|
||||||
@Value("${server.port:8080}")
|
|
||||||
public void setServerPortStatic(String port) {
|
|
||||||
SPdfApplication.serverPortStatic = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Check if the BROWSER_OPEN environment variable is set to true
|
// Check if the BROWSER_OPEN environment variable is set to true
|
||||||
@@ -46,7 +30,7 @@ public class SPdfApplication {
|
|||||||
|
|
||||||
if (browserOpen) {
|
if (browserOpen) {
|
||||||
try {
|
try {
|
||||||
String url = "http://localhost:" + getNonStaticPort();
|
String url = "http://localhost:" + getPort();
|
||||||
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
@@ -55,13 +39,12 @@ public class SPdfApplication {
|
|||||||
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error opening browser: {}", e.getMessage());
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info("Running configs {}", applicationProperties.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) {
|
||||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||||
app.addInitializers(new ConfigInitializer());
|
app.addInitializers(new ConfigInitializer());
|
||||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
||||||
@@ -69,7 +52,7 @@ public class SPdfApplication {
|
|||||||
Collections.singletonMap(
|
Collections.singletonMap(
|
||||||
"spring.config.additional-location", "file:configs/settings.yml"));
|
"spring.config.additional-location", "file:configs/settings.yml"));
|
||||||
} else {
|
} else {
|
||||||
logger.warn(
|
System.out.println(
|
||||||
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
"External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead.");
|
||||||
}
|
}
|
||||||
app.run(args);
|
app.run(args);
|
||||||
@@ -77,30 +60,24 @@ public class SPdfApplication {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
// TODO Auto-generated catch block
|
||||||
throw new RuntimeException("Thread interrupted while sleeping", e);
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
GeneralUtils.createDir("customFiles/static/");
|
||||||
Files.createDirectories(Path.of("customFiles/static/"));
|
GeneralUtils.createDir("customFiles/templates/");
|
||||||
Files.createDirectories(Path.of("customFiles/templates/"));
|
|
||||||
} catch (Exception e) {
|
System.out.println("Stirling-PDF Started.");
|
||||||
logger.error("Error creating directories: {}", e.getMessage());
|
|
||||||
|
String url = "http://localhost:" + getPort();
|
||||||
|
System.out.println("Navigate to " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPort() {
|
||||||
|
String port = System.getProperty("local.server.port");
|
||||||
|
if (port == null || port.isEmpty()) {
|
||||||
|
port = "8080";
|
||||||
}
|
}
|
||||||
printStartupLogs();
|
return port;
|
||||||
}
|
|
||||||
|
|
||||||
private static void printStartupLogs() {
|
|
||||||
logger.info("Stirling-PDF Started.");
|
|
||||||
String url = "http://localhost:" + getStaticPort();
|
|
||||||
logger.info("Navigate to {}", url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getStaticPort() {
|
|
||||||
return serverPortStatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNonStaticPort() {
|
|
||||||
return serverPortStatic;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,10 +79,9 @@ public class AppConfig {
|
|||||||
|
|
||||||
@Bean(name = "bookAndHtmlFormatsInstalled")
|
@Bean(name = "bookAndHtmlFormatsInstalled")
|
||||||
public boolean bookAndHtmlFormatsInstalled() {
|
public boolean bookAndHtmlFormatsInstalled() {
|
||||||
String installOps = System.getProperty("INSTALL_BOOK_AND_ADVANCED_HTML_OPS");
|
return applicationProperties
|
||||||
if (installOps == null) {
|
.getSystem()
|
||||||
installOps = System.getenv("INSTALL_BOOK_AND_ADVANCED_HTML_OPS");
|
.getCustomApplications()
|
||||||
}
|
.isInstallBookAndHtmlFormats();
|
||||||
return "true".equalsIgnoreCase(installOps);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,16 +79,6 @@ public class ConfigInitializer
|
|||||||
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
|
return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
Function<String, Integer> getIndentationLevel =
|
|
||||||
line -> {
|
|
||||||
int count = 0;
|
|
||||||
for (char ch : line.toCharArray()) {
|
|
||||||
if (ch == ' ') count++;
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
};
|
|
||||||
|
|
||||||
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
|
Set<String> userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet());
|
||||||
|
|
||||||
for (String line : templateLines) {
|
for (String line : templateLines) {
|
||||||
@@ -144,77 +134,10 @@ public class ConfigInitializer
|
|||||||
.map(extractKey)
|
.map(extractKey)
|
||||||
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
|
.anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey));
|
||||||
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
|
if (!isPresentInTemplate && !isCommented.apply(userLine)) {
|
||||||
if (!childOfTemplateEntry(
|
mergedLines.add(userLine);
|
||||||
isCommented,
|
|
||||||
extractKey,
|
|
||||||
getIndentationLevel,
|
|
||||||
userLines,
|
|
||||||
userLine,
|
|
||||||
templateLines)) {
|
|
||||||
// check if userLine is a child of a entry within templateLines or not, if child
|
|
||||||
// of parent in templateLines then dont add to mergedLines, if anything else
|
|
||||||
// then add
|
|
||||||
mergedLines.add(userLine);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
|
Files.write(outputPath, mergedLines, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method to check if a userLine is a child of an entry in templateLines
|
|
||||||
boolean childOfTemplateEntry(
|
|
||||||
Function<String, Boolean> isCommented,
|
|
||||||
Function<String, String> extractKey,
|
|
||||||
Function<String, Integer> getIndentationLevel,
|
|
||||||
List<String> userLines,
|
|
||||||
String userLine,
|
|
||||||
List<String> templateLines) {
|
|
||||||
String userKey = extractKey.apply(userLine).trim();
|
|
||||||
int userIndentation = getIndentationLevel.apply(userLine);
|
|
||||||
|
|
||||||
// Start by assuming the line is not a child of an entry in templateLines
|
|
||||||
boolean isChild = false;
|
|
||||||
|
|
||||||
// Iterate backwards through userLines from the current line to find any parent
|
|
||||||
for (int i = userLines.indexOf(userLine) - 1; i >= 0; i--) {
|
|
||||||
String potentialParentLine = userLines.get(i);
|
|
||||||
int parentIndentation = getIndentationLevel.apply(potentialParentLine);
|
|
||||||
|
|
||||||
// Check if we've reached a potential parent based on indentation
|
|
||||||
if (parentIndentation < userIndentation) {
|
|
||||||
String parentKey = extractKey.apply(potentialParentLine).trim();
|
|
||||||
|
|
||||||
// Now, check if this potential parent or any of its parents exist in templateLines
|
|
||||||
boolean parentExistsInTemplate =
|
|
||||||
templateLines.stream()
|
|
||||||
.filter(line -> !isCommented.apply(line)) // Skip commented lines
|
|
||||||
.anyMatch(
|
|
||||||
templateLine -> {
|
|
||||||
String templateKey =
|
|
||||||
extractKey.apply(templateLine).trim();
|
|
||||||
return parentKey.equalsIgnoreCase(templateKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!parentExistsInTemplate) {
|
|
||||||
// If the parent does not exist in template, check the next level parent
|
|
||||||
userIndentation =
|
|
||||||
parentIndentation; // Update userIndentation to the parent's indentation
|
|
||||||
// for next iteration
|
|
||||||
if (parentIndentation == 0) {
|
|
||||||
// If we've reached the top-level parent and it's not in template, the
|
|
||||||
// original line is considered not a child
|
|
||||||
isChild = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If any parent exists in template, the original line is considered a child
|
|
||||||
isChild = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isChild; // Return true if the line is not a child of any entry in templateLines
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Other", "sign");
|
addEndpointToGroup("Other", "sign");
|
||||||
addEndpointToGroup("Other", "flatten");
|
addEndpointToGroup("Other", "flatten");
|
||||||
addEndpointToGroup("Other", "repair");
|
addEndpointToGroup("Other", "repair");
|
||||||
addEndpointToGroup("Other", REMOVE_BLANKS);
|
addEndpointToGroup("Other", "remove-blanks");
|
||||||
addEndpointToGroup("Other", "remove-annotations");
|
addEndpointToGroup("Other", "remove-annotations");
|
||||||
addEndpointToGroup("Other", "compare");
|
addEndpointToGroup("Other", "compare");
|
||||||
addEndpointToGroup("Other", "add-page-numbers");
|
addEndpointToGroup("Other", "add-page-numbers");
|
||||||
@@ -140,6 +140,7 @@ public class EndpointConfiguration {
|
|||||||
// CLI
|
// CLI
|
||||||
addEndpointToGroup("CLI", "compress-pdf");
|
addEndpointToGroup("CLI", "compress-pdf");
|
||||||
addEndpointToGroup("CLI", "extract-image-scans");
|
addEndpointToGroup("CLI", "extract-image-scans");
|
||||||
|
addEndpointToGroup("CLI", "remove-blanks");
|
||||||
addEndpointToGroup("CLI", "repair");
|
addEndpointToGroup("CLI", "repair");
|
||||||
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
addEndpointToGroup("CLI", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("CLI", "file-to-pdf");
|
addEndpointToGroup("CLI", "file-to-pdf");
|
||||||
@@ -161,13 +162,13 @@ public class EndpointConfiguration {
|
|||||||
|
|
||||||
// python
|
// python
|
||||||
addEndpointToGroup("Python", "extract-image-scans");
|
addEndpointToGroup("Python", "extract-image-scans");
|
||||||
addEndpointToGroup("Python", REMOVE_BLANKS);
|
addEndpointToGroup("Python", "remove-blanks");
|
||||||
addEndpointToGroup("Python", "html-to-pdf");
|
addEndpointToGroup("Python", "html-to-pdf");
|
||||||
addEndpointToGroup("Python", "url-to-pdf");
|
addEndpointToGroup("Python", "url-to-pdf");
|
||||||
|
|
||||||
// openCV
|
// openCV
|
||||||
addEndpointToGroup("OpenCV", "extract-image-scans");
|
addEndpointToGroup("OpenCV", "extract-image-scans");
|
||||||
addEndpointToGroup("OpenCV", REMOVE_BLANKS);
|
addEndpointToGroup("OpenCV", "remove-blanks");
|
||||||
|
|
||||||
// LibreOffice
|
// LibreOffice
|
||||||
addEndpointToGroup("LibreOffice", "repair");
|
addEndpointToGroup("LibreOffice", "repair");
|
||||||
@@ -217,7 +218,6 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Java", "split-by-size-or-count");
|
addEndpointToGroup("Java", "split-by-size-or-count");
|
||||||
addEndpointToGroup("Java", "overlay-pdf");
|
addEndpointToGroup("Java", "overlay-pdf");
|
||||||
addEndpointToGroup("Java", "split-pdf-by-sections");
|
addEndpointToGroup("Java", "split-pdf-by-sections");
|
||||||
addEndpointToGroup("Java", REMOVE_BLANKS);
|
|
||||||
|
|
||||||
// Javascript
|
// Javascript
|
||||||
addEndpointToGroup("Javascript", "pdf-organizer");
|
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||||
@@ -244,6 +244,4 @@ public class EndpointConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String REMOVE_BLANKS = "remove-blanks";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class PostStartupProcesses {
|
||||||
|
|
||||||
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("RunningInDocker")
|
||||||
|
private boolean runningInDocker;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(PostStartupProcesses.class);
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void runInstallCommandBasedOnEnvironment() throws IOException, InterruptedException {
|
||||||
|
List<List<String>> commands = new ArrayList<>();
|
||||||
|
// Checking for DOCKER_INSTALL_BOOK_FORMATS environment variable
|
||||||
|
if (bookAndHtmlFormatsInstalled) {
|
||||||
|
List<String> tmpList = new ArrayList<>();
|
||||||
|
|
||||||
|
tmpList = new ArrayList<>();
|
||||||
|
tmpList.addAll(Arrays.asList("apk add --no-cache calibre"));
|
||||||
|
commands.add(tmpList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!commands.isEmpty()) {
|
||||||
|
// Run the command
|
||||||
|
if (runningInDocker) {
|
||||||
|
List<String> tmpList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (List<String> list : commands) {
|
||||||
|
ProcessExecutorResult returnCode =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.INSTALL_APP, true)
|
||||||
|
.runCommandWithOutputHandling(list);
|
||||||
|
logger.info("RC for app installs {}", returnCode.getRc());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Not running inside Docker so skipping automated install process with command.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (runningInDocker) {
|
||||||
|
logger.info("No custom apps to install.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
@@ -13,19 +12,15 @@ import org.springframework.stereotype.Component;
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.model.User;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
@Autowired private final LoginAttemptService loginAttemptService;
|
@Autowired private final LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Autowired private final UserService userService; // Inject the UserService
|
@Autowired
|
||||||
|
public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) {
|
||||||
public CustomAuthenticationFailureHandler(
|
|
||||||
LoginAttemptService loginAttemptService, UserService userService) {
|
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
this.userService = userService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -38,27 +33,17 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
|||||||
logger.error("Failed login attempt from IP: " + ip);
|
logger.error("Failed login attempt from IP: " + ip);
|
||||||
|
|
||||||
String username = request.getParameter("username");
|
String username = request.getParameter("username");
|
||||||
if (!isDemoUser(username)) {
|
if (loginAttemptService.loginAttemptCheck(username)) {
|
||||||
if (loginAttemptService.loginAttemptCheck(username)) {
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
||||||
setDefaultFailureUrl("/login?error=locked");
|
setDefaultFailureUrl("/login?error=badcredentials");
|
||||||
}
|
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
|
||||||
|
setDefaultFailureUrl("/login?error=locked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) {
|
|
||||||
setDefaultFailureUrl("/login?error=badcredentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onAuthenticationFailure(request, response, exception);
|
super.onAuthenticationFailure(request, response, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDemoUser(String username) {
|
|
||||||
Optional<User> user = userService.findByUsername(username);
|
|
||||||
return user.isPresent()
|
|
||||||
&& user.get().getAuthorities().stream()
|
|
||||||
.anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
|||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
|
||||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
@@ -21,7 +18,6 @@ import org.springframework.security.web.authentication.rememberme.PersistentToke
|
|||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpSession;
|
|
||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -48,11 +44,6 @@ public class SecurityConfiguration {
|
|||||||
|
|
||||||
@Autowired private FirstLoginFilter firstLoginFilter;
|
@Autowired private FirstLoginFilter firstLoginFilter;
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SessionRegistry sessionRegistry() {
|
|
||||||
return new SessionRegistryImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
@@ -62,15 +53,6 @@ public class SecurityConfiguration {
|
|||||||
http.csrf(csrf -> csrf.disable());
|
http.csrf(csrf -> csrf.disable());
|
||||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
http.sessionManagement(
|
|
||||||
sessionManagement ->
|
|
||||||
sessionManagement
|
|
||||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
|
||||||
.maximumSessions(10)
|
|
||||||
.maxSessionsPreventsLogin(false)
|
|
||||||
.sessionRegistry(sessionRegistry())
|
|
||||||
.expiredUrl("/login?logout=true"));
|
|
||||||
|
|
||||||
http.formLogin(
|
http.formLogin(
|
||||||
formLogin ->
|
formLogin ->
|
||||||
formLogin
|
formLogin
|
||||||
@@ -80,7 +62,7 @@ public class SecurityConfiguration {
|
|||||||
.defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomAuthenticationFailureHandler(
|
new CustomAuthenticationFailureHandler(
|
||||||
loginAttemptService, userService))
|
loginAttemptService))
|
||||||
.permitAll())
|
.permitAll())
|
||||||
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()))
|
||||||
.logout(
|
.logout(
|
||||||
@@ -89,18 +71,7 @@ public class SecurityConfiguration {
|
|||||||
new AntPathRequestMatcher("/logout"))
|
new AntPathRequestMatcher("/logout"))
|
||||||
.logoutSuccessUrl("/login?logout=true")
|
.logoutSuccessUrl("/login?logout=true")
|
||||||
.invalidateHttpSession(true) // Invalidate session
|
.invalidateHttpSession(true) // Invalidate session
|
||||||
.deleteCookies("JSESSIONID", "remember-me")
|
.deleteCookies("JSESSIONID", "remember-me"))
|
||||||
.addLogoutHandler(
|
|
||||||
(request, response, authentication) -> {
|
|
||||||
HttpSession session =
|
|
||||||
request.getSession(false);
|
|
||||||
if (session != null) {
|
|
||||||
String sessionId = session.getId();
|
|
||||||
sessionRegistry()
|
|
||||||
.removeSessionInformation(
|
|
||||||
sessionId);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.rememberMe(
|
.rememberMe(
|
||||||
rememberMeConfigurer ->
|
rememberMeConfigurer ->
|
||||||
rememberMeConfigurer // Use the configurator directly
|
rememberMeConfigurer // Use the configurator directly
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter()
|
response.getWriter()
|
||||||
.write(
|
.write(
|
||||||
"Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternatively you can disable authentication if this is unexpected");
|
"Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternativly you can disable authentication if this is unexpected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,10 +176,6 @@ public class UserService implements UserServiceInterface {
|
|||||||
return userRepository.findByUsername(username);
|
return userRepository.findByUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<User> findByUsernameIgnoreCase(String username) {
|
|
||||||
return userRepository.findByUsernameIgnoreCase(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void changeUsername(User user, String newUsername) {
|
public void changeUsername(User user, String newUsername) {
|
||||||
user.setUsername(newUsername);
|
user.setUsername(newUsername);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
@@ -198,8 +194,4 @@ public class UserService implements UserServiceInterface {
|
|||||||
public boolean isPasswordCorrect(User user, String currentPassword) {
|
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||||
return passwordEncoder.matches(currentPassword, user.getPassword());
|
return passwordEncoder.matches(currentPassword, user.getPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsernameValid(String username) {
|
|
||||||
return username.matches("[a-zA-Z0-9]+");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class MergeController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
|
||||||
|
|
||||||
public PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
|
||||||
PDDocument mergedDoc = new PDDocument();
|
PDDocument mergedDoc = new PDDocument();
|
||||||
for (PDDocument doc : documents) {
|
for (PDDocument doc : documents) {
|
||||||
for (PDPage page : doc.getPages()) {
|
for (PDPage page : doc.getPages()) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.api;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
@@ -51,9 +50,7 @@ public class RearrangePagesPDFController {
|
|||||||
String[] pageOrderArr = pagesToDelete.split(",");
|
String[] pageOrderArr = pagesToDelete.split(",");
|
||||||
|
|
||||||
List<Integer> pagesToRemove =
|
List<Integer> pagesToRemove =
|
||||||
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages(), false);
|
GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages());
|
||||||
|
|
||||||
Collections.sort(pagesToRemove);
|
|
||||||
|
|
||||||
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
|
||||||
int pageIndex = pagesToRemove.get(i);
|
int pageIndex = pagesToRemove.get(i);
|
||||||
@@ -195,7 +192,7 @@ public class RearrangePagesPDFController {
|
|||||||
if (sortType != null && sortType.length() > 0) {
|
if (sortType != null && sortType.length() > 0) {
|
||||||
newPageOrder = processSortTypes(sortType, totalPages);
|
newPageOrder = processSortTypes(sortType, totalPages);
|
||||||
} else {
|
} else {
|
||||||
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages);
|
||||||
}
|
}
|
||||||
logger.info("newPageOrder = " + newPageOrder);
|
logger.info("newPageOrder = " + newPageOrder);
|
||||||
logger.info("totalPages = " + totalPages);
|
logger.info("totalPages = " + totalPages);
|
||||||
|
|||||||
@@ -49,16 +49,10 @@ public class SplitPDFController {
|
|||||||
// open the pdf document
|
// open the pdf document
|
||||||
|
|
||||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
int totalPages = document.getNumberOfPages();
|
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
|
||||||
System.out.println(
|
|
||||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
|
||||||
if (!pageNumbers.contains(totalPages - 1)) {
|
|
||||||
// Create a mutable ArrayList so we can add to it
|
|
||||||
pageNumbers = new ArrayList<>(pageNumbers);
|
|
||||||
pageNumbers.add(totalPages - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
List<Integer> pageNumbers = request.getPageNumbersList(document, true);
|
||||||
|
if (!pageNumbers.contains(document.getNumberOfPages() - 1))
|
||||||
|
pageNumbers.add(document.getNumberOfPages() - 1);
|
||||||
logger.info(
|
logger.info(
|
||||||
"Splitting PDF into pages: {}",
|
"Splitting PDF into pages: {}",
|
||||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
@@ -71,7 +65,7 @@ public class SplitPDFController {
|
|||||||
for (int i = previousPageNumber; i <= splitPoint; i++) {
|
for (int i = previousPageNumber; i <= splitPoint; i++) {
|
||||||
PDPage page = document.getPage(i);
|
PDPage page = document.getPage(i);
|
||||||
splitDocument.addPage(page);
|
splitDocument.addPage(page);
|
||||||
logger.info("Adding page {} to split document", i);
|
logger.debug("Adding page {} to split document", i);
|
||||||
}
|
}
|
||||||
previousPageNumber = splitPoint + 1;
|
previousPageNumber = splitPoint + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -53,21 +53,8 @@ public class SplitPdfBySectionsController {
|
|||||||
// Process the PDF based on split parameters
|
// Process the PDF based on split parameters
|
||||||
int horiz = request.getHorizontalDivisions() + 1;
|
int horiz = request.getHorizontalDivisions() + 1;
|
||||||
int verti = request.getVerticalDivisions() + 1;
|
int verti = request.getVerticalDivisions() + 1;
|
||||||
boolean merge = request.isMerge();
|
|
||||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
|
||||||
|
|
||||||
String filename =
|
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
||||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
|
||||||
.replaceFirst("[.][^.]+$", "");
|
|
||||||
if (merge) {
|
|
||||||
MergeController mergeController = new MergeController();
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
mergeController.mergeDocuments(splitDocuments).save(baos);
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
baos.toByteArray(),
|
|
||||||
filename + "_split.pdf",
|
|
||||||
MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
}
|
|
||||||
for (PDDocument doc : splitDocuments) {
|
for (PDDocument doc : splitDocuments) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
doc.save(baos);
|
doc.save(baos);
|
||||||
@@ -78,6 +65,9 @@ public class SplitPdfBySectionsController {
|
|||||||
sourceDocument.close();
|
sourceDocument.close();
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
|
String filename =
|
||||||
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
|
.replaceFirst("[.][^.]+$", "");
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.session.SessionInformation;
|
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
@@ -31,6 +28,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
import stirling.software.SPDF.model.api.user.UpdateUserDetails;
|
||||||
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@@ -53,62 +51,18 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/change-username")
|
@PostMapping("/change-username-and-password")
|
||||||
public RedirectView changeUsername(
|
public RedirectView changeUsernameAndPassword(
|
||||||
Principal principal,
|
Principal principal,
|
||||||
@RequestParam(name = "currentPassword") String currentPassword,
|
@ModelAttribute UpdateUserDetails requestModel,
|
||||||
@RequestParam(name = "newUsername") String newUsername,
|
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
RedirectAttributes redirectAttributes) {
|
RedirectAttributes redirectAttributes) {
|
||||||
|
|
||||||
if (!userService.isUsernameValid(newUsername)) {
|
String currentPassword = requestModel.getPassword();
|
||||||
return new RedirectView("/account?messageType=invalidUsername");
|
String newPassword = requestModel.getNewPassword();
|
||||||
}
|
String newUsername = requestModel.getNewUsername();
|
||||||
|
|
||||||
if (principal == null) {
|
|
||||||
return new RedirectView("/account?messageType=notAuthenticated");
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(principal.getName());
|
|
||||||
|
|
||||||
if (userOpt == null || userOpt.isEmpty()) {
|
|
||||||
return new RedirectView("/account?messageType=userNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
User user = userOpt.get();
|
|
||||||
|
|
||||||
if (user.getUsername().equals(newUsername)) {
|
|
||||||
return new RedirectView("/account?messageType=usernameExists");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userService.isPasswordCorrect(user, currentPassword)) {
|
|
||||||
return new RedirectView("/account?messageType=incorrectPassword");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) {
|
|
||||||
return new RedirectView("/account?messageType=usernameExists");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newUsername != null && newUsername.length() > 0) {
|
|
||||||
userService.changeUsername(user, newUsername);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logout using Spring's utility
|
|
||||||
new SecurityContextLogoutHandler().logout(request, response, null);
|
|
||||||
|
|
||||||
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
|
||||||
@PostMapping("/change-password-on-login")
|
|
||||||
public RedirectView changePasswordOnLogin(
|
|
||||||
Principal principal,
|
|
||||||
@RequestParam(name = "currentPassword") String currentPassword,
|
|
||||||
@RequestParam(name = "newPassword") String newPassword,
|
|
||||||
HttpServletRequest request,
|
|
||||||
HttpServletResponse response,
|
|
||||||
RedirectAttributes redirectAttributes) {
|
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
return new RedirectView("/change-creds?messageType=notAuthenticated");
|
return new RedirectView("/change-creds?messageType=notAuthenticated");
|
||||||
}
|
}
|
||||||
@@ -125,20 +79,69 @@ public class UserController {
|
|||||||
return new RedirectView("/change-creds?messageType=incorrectPassword");
|
return new RedirectView("/change-creds?messageType=incorrectPassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) {
|
||||||
|
return new RedirectView("/change-creds?messageType=usernameExists");
|
||||||
|
}
|
||||||
|
|
||||||
userService.changePassword(user, newPassword);
|
userService.changePassword(user, newPassword);
|
||||||
|
if (newUsername != null
|
||||||
|
&& newUsername.length() > 0
|
||||||
|
&& !user.getUsername().equals(newUsername)) {
|
||||||
|
userService.changeUsername(user, newUsername);
|
||||||
|
}
|
||||||
userService.changeFirstUse(user, false);
|
userService.changeFirstUse(user, false);
|
||||||
|
|
||||||
// Logout using Spring's utility
|
// Logout using Spring's utility
|
||||||
new SecurityContextLogoutHandler().logout(request, response, null);
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
|
return new RedirectView("/login?messageType=credsUpdated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
|
@PostMapping("/change-username")
|
||||||
|
public RedirectView changeUsername(
|
||||||
|
Principal principal,
|
||||||
|
@RequestParam String currentPassword,
|
||||||
|
@RequestParam String newUsername,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RedirectAttributes redirectAttributes) {
|
||||||
|
if (principal == null) {
|
||||||
|
return new RedirectView("/account?messageType=notAuthenticated");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<User> userOpt = userService.findByUsername(principal.getName());
|
||||||
|
|
||||||
|
if (userOpt == null || userOpt.isEmpty()) {
|
||||||
|
return new RedirectView("/account?messageType=userNotFound");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userOpt.get();
|
||||||
|
|
||||||
|
if (!userService.isPasswordCorrect(user, currentPassword)) {
|
||||||
|
return new RedirectView("/account?messageType=incorrectPassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) {
|
||||||
|
return new RedirectView("/account?messageType=usernameExists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newUsername != null && newUsername.length() > 0) {
|
||||||
|
userService.changeUsername(user, newUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout using Spring's utility
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
|
return new RedirectView("/login?messageType=credsUpdated");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/change-password")
|
@PostMapping("/change-password")
|
||||||
public RedirectView changePassword(
|
public RedirectView changePassword(
|
||||||
Principal principal,
|
Principal principal,
|
||||||
@RequestParam(name = "currentPassword") String currentPassword,
|
@RequestParam String currentPassword,
|
||||||
@RequestParam(name = "newPassword") String newPassword,
|
@RequestParam String newPassword,
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
RedirectAttributes redirectAttributes) {
|
RedirectAttributes redirectAttributes) {
|
||||||
@@ -163,7 +166,7 @@ public class UserController {
|
|||||||
// Logout using Spring's utility
|
// Logout using Spring's utility
|
||||||
new SecurityContextLogoutHandler().logout(request, response, null);
|
new SecurityContextLogoutHandler().logout(request, response, null);
|
||||||
|
|
||||||
return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED);
|
return new RedirectView("/login?messageType=credsUpdated");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@@ -189,24 +192,12 @@ public class UserController {
|
|||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/saveUser")
|
@PostMapping("/admin/saveUser")
|
||||||
public RedirectView saveUser(
|
public RedirectView saveUser(
|
||||||
@RequestParam(name = "username") String username,
|
@RequestParam String username,
|
||||||
@RequestParam(name = "password") String password,
|
@RequestParam String password,
|
||||||
@RequestParam(name = "role") String role,
|
@RequestParam String role,
|
||||||
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
||||||
boolean forceChange) {
|
boolean forceChange) {
|
||||||
|
|
||||||
if (!userService.isUsernameValid(username)) {
|
|
||||||
return new RedirectView("/addUsers?messageType=invalidUsername");
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
|
||||||
|
|
||||||
if (userOpt.isPresent()) {
|
|
||||||
User user = userOpt.get();
|
|
||||||
if (user != null && user.getUsername().equalsIgnoreCase(username)) {
|
|
||||||
return new RedirectView("/addUsers?messageType=usernameExists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (userService.usernameExists(username)) {
|
if (userService.usernameExists(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=usernameExists");
|
return new RedirectView("/addUsers?messageType=usernameExists");
|
||||||
}
|
}
|
||||||
@@ -228,39 +219,18 @@ public class UserController {
|
|||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/deleteUser/{username}")
|
@PostMapping("/admin/deleteUser/{username}")
|
||||||
public RedirectView deleteUser(
|
public String deleteUser(@PathVariable String username, Authentication authentication) {
|
||||||
@PathVariable(name = "username") String username, Authentication authentication) {
|
|
||||||
|
|
||||||
if (!userService.usernameExists(username)) {
|
|
||||||
return new RedirectView("/addUsers?messageType=deleteUsernameExists");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the currently authenticated username
|
// Get the currently authenticated username
|
||||||
String currentUsername = authentication.getName();
|
String currentUsername = authentication.getName();
|
||||||
|
|
||||||
// Check if the provided username matches the current session's username
|
// Check if the provided username matches the current session's username
|
||||||
if (currentUsername.equals(username)) {
|
if (currentUsername.equals(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=deleteCurrentUser");
|
throw new IllegalArgumentException("Cannot delete currently logined in user.");
|
||||||
}
|
}
|
||||||
invalidateUserSessions(username);
|
|
||||||
userService.deleteUser(username);
|
userService.deleteUser(username);
|
||||||
return new RedirectView("/addUsers");
|
return "redirect:/addUsers";
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired private SessionRegistry sessionRegistry;
|
|
||||||
|
|
||||||
private void invalidateUserSessions(String username) {
|
|
||||||
for (Object principal : sessionRegistry.getAllPrincipals()) {
|
|
||||||
if (principal instanceof UserDetails) {
|
|
||||||
UserDetails userDetails = (UserDetails) principal;
|
|
||||||
if (userDetails.getUsername().equals(username)) {
|
|
||||||
for (SessionInformation session :
|
|
||||||
sessionRegistry.getAllSessions(principal, false)) {
|
|
||||||
session.expireNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@@ -291,6 +261,4 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
return ResponseEntity.ok(apiKey);
|
return ResponseEntity.ok(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String LOGIN_MESSAGETYPE_CREDSUPDATED = "/login?messageType=credsUpdated";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class ConvertBookToPDFController {
|
|||||||
|
|
||||||
if (!bookAndHtmlFormatsInstalled) {
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"bookAndHtmlFormatsInstalled flag is False, this functionality is not available");
|
"bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileInput == null) {
|
if (fileInput == null) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class ConvertPDFToBookController {
|
|||||||
|
|
||||||
if (!bookAndHtmlFormatsInstalled) {
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"bookAndHtmlFormatsInstalled flag is False, this functionality is not available");
|
"bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileInput == null) {
|
if (fileInput == null) {
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@Tag(name = "Convert", description = "Convert APIs")
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
public class ConvertPDFToOffice {
|
public class ConvertPDFToOffice {
|
||||||
|
|
||||||
|
@PostMapping(consumes = "multipart/form-data", value = "/pdf/html")
|
||||||
|
@Operation(
|
||||||
|
summary = "Convert PDF to HTML",
|
||||||
|
description =
|
||||||
|
"This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO")
|
||||||
|
public ResponseEntity<byte[]> processPdfToHTML(@ModelAttribute PDFFile request)
|
||||||
|
throws Exception {
|
||||||
|
MultipartFile inputFile = request.getFileInput();
|
||||||
|
PDFToFile pdfToFile = new PDFToFile();
|
||||||
|
return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import");
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/pdf/presentation")
|
@PostMapping(consumes = "multipart/form-data", value = "/pdf/presentation")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert PDF to Presentation format",
|
summary = "Convert PDF to Presentation format",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -26,6 +28,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@RequestMapping("/api/v1/convert")
|
@RequestMapping("/api/v1/convert")
|
||||||
public class ConvertWebsiteToPDF {
|
public class ConvertWebsiteToPDF {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("bookAndHtmlFormatsInstalled")
|
||||||
|
private boolean bookAndHtmlFormatsInstalled;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert a URL to a PDF",
|
summary = "Convert a URL to a PDF",
|
||||||
@@ -47,7 +53,11 @@ public class ConvertWebsiteToPDF {
|
|||||||
|
|
||||||
// Prepare the OCRmyPDF command
|
// Prepare the OCRmyPDF command
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
command.add("weasyprint");
|
if (!bookAndHtmlFormatsInstalled) {
|
||||||
|
command.add("weasyprint");
|
||||||
|
} else {
|
||||||
|
command.add("wkhtmltopdf");
|
||||||
|
}
|
||||||
command.add(URL);
|
command.add(URL);
|
||||||
command.add(tempOutputFile.toString());
|
command.add(tempOutputFile.toString());
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public class AutoRenameController {
|
|||||||
|
|
||||||
// Sanitize the header string by removing characters not allowed in a filename.
|
// Sanitize the header string by removing characters not allowed in a filename.
|
||||||
if (header != null && header.length() < 255) {
|
if (header != null && header.length() < 255) {
|
||||||
header = header.replaceAll("[/\\\\?%*:|\"<>]", "").trim();
|
header = header.replaceAll("[/\\\\?%*:|\"<>]", "");
|
||||||
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
||||||
} else {
|
} else {
|
||||||
logger.info("File has no good title to be found");
|
logger.info("File has no good title to be found");
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class AutoSplitPdfController {
|
|||||||
|
|
||||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
List<PDDocument> splitDocuments = new ArrayList<>();
|
List<PDDocument> splitDocuments = new ArrayList<>();
|
||||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class BlankPageController {
|
|||||||
List<Integer> pagesToKeepIndex = new ArrayList<>();
|
List<Integer> pagesToKeepIndex = new ArrayList<>();
|
||||||
int pageIndex = 0;
|
int pageIndex = 0;
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
for (PDPage page : pages) {
|
for (PDPage page : pages) {
|
||||||
logger.info("checking page " + pageIndex);
|
logger.info("checking page " + pageIndex);
|
||||||
textStripper.setStartPage(pageIndex + 1);
|
textStripper.setStartPage(pageIndex + 1);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package stirling.software.SPDF.controller.api.misc;
|
|||||||
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -73,208 +75,194 @@ public class CompressController {
|
|||||||
long inputFileSize = Files.size(tempInputFile);
|
long inputFileSize = Files.size(tempInputFile);
|
||||||
|
|
||||||
// Prepare the output file path
|
// Prepare the output file path
|
||||||
|
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
|
||||||
|
|
||||||
Path tempOutputFile = null;
|
// Determine initial optimization level based on expected size reduction, only if in
|
||||||
byte[] pdfBytes;
|
// autoMode
|
||||||
try {
|
if (autoMode) {
|
||||||
tempOutputFile = Files.createTempFile("output_", ".pdf");
|
double sizeReductionRatio = expectedOutputSize / (double) inputFileSize;
|
||||||
// Determine initial optimization level based on expected size reduction, only if in
|
if (sizeReductionRatio > 0.7) {
|
||||||
// autoMode
|
optimizeLevel = 1;
|
||||||
if (autoMode) {
|
} else if (sizeReductionRatio > 0.5) {
|
||||||
double sizeReductionRatio = expectedOutputSize / (double) inputFileSize;
|
optimizeLevel = 2;
|
||||||
if (sizeReductionRatio > 0.7) {
|
} else if (sizeReductionRatio > 0.35) {
|
||||||
optimizeLevel = 1;
|
optimizeLevel = 3;
|
||||||
} else if (sizeReductionRatio > 0.5) {
|
} else {
|
||||||
optimizeLevel = 2;
|
optimizeLevel = 3;
|
||||||
} else if (sizeReductionRatio > 0.35) {
|
}
|
||||||
optimizeLevel = 3;
|
}
|
||||||
} else {
|
|
||||||
optimizeLevel = 3;
|
boolean sizeMet = false;
|
||||||
}
|
while (!sizeMet && optimizeLevel <= 4) {
|
||||||
|
// Prepare the Ghostscript command
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add("gs");
|
||||||
|
command.add("-sDEVICE=pdfwrite");
|
||||||
|
command.add("-dCompatibilityLevel=1.4");
|
||||||
|
|
||||||
|
switch (optimizeLevel) {
|
||||||
|
case 1:
|
||||||
|
command.add("-dPDFSETTINGS=/prepress");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
command.add("-dPDFSETTINGS=/printer");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
command.add("-dPDFSETTINGS=/ebook");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
command.add("-dPDFSETTINGS=/screen");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
command.add("-dPDFSETTINGS=/default");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean sizeMet = false;
|
command.add("-dNOPAUSE");
|
||||||
while (!sizeMet && optimizeLevel <= 4) {
|
command.add("-dQUIET");
|
||||||
// Prepare the Ghostscript command
|
command.add("-dBATCH");
|
||||||
List<String> command = new ArrayList<>();
|
command.add("-sOutputFile=" + tempOutputFile.toString());
|
||||||
command.add("gs");
|
command.add(tempInputFile.toString());
|
||||||
command.add("-sDEVICE=pdfwrite");
|
|
||||||
command.add("-dCompatibilityLevel=1.4");
|
|
||||||
|
|
||||||
switch (optimizeLevel) {
|
ProcessExecutorResult returnCode =
|
||||||
case 1:
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
|
||||||
command.add("-dPDFSETTINGS=/prepress");
|
.runCommandWithOutputHandling(command);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
command.add("-dPDFSETTINGS=/printer");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
command.add("-dPDFSETTINGS=/ebook");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
command.add("-dPDFSETTINGS=/screen");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
command.add("-dPDFSETTINGS=/default");
|
|
||||||
}
|
|
||||||
|
|
||||||
command.add("-dNOPAUSE");
|
// Check if file size is within expected size or not auto mode so instantly finish
|
||||||
command.add("-dQUIET");
|
long outputFileSize = Files.size(tempOutputFile);
|
||||||
command.add("-dBATCH");
|
if (outputFileSize <= expectedOutputSize || !autoMode) {
|
||||||
command.add("-sOutputFile=" + tempOutputFile.toString());
|
sizeMet = true;
|
||||||
command.add(tempInputFile.toString());
|
} else {
|
||||||
|
// Increase optimization level for next iteration
|
||||||
ProcessExecutorResult returnCode =
|
optimizeLevel++;
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT)
|
if (autoMode && optimizeLevel > 3) {
|
||||||
.runCommandWithOutputHandling(command);
|
System.out.println("Skipping level 4 due to bad results in auto mode");
|
||||||
|
|
||||||
// Check if file size is within expected size or not auto mode so instantly finish
|
|
||||||
long outputFileSize = Files.size(tempOutputFile);
|
|
||||||
if (outputFileSize <= expectedOutputSize || !autoMode) {
|
|
||||||
sizeMet = true;
|
sizeMet = true;
|
||||||
|
} else if (optimizeLevel == 5) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Increase optimization level for next iteration
|
System.out.println(
|
||||||
optimizeLevel++;
|
"Increasing ghostscript optimisation level to " + optimizeLevel);
|
||||||
if (autoMode && optimizeLevel > 4) {
|
|
||||||
System.out.println("Skipping level 5 due to bad results in auto mode");
|
|
||||||
sizeMet = true;
|
|
||||||
} else {
|
|
||||||
System.out.println(
|
|
||||||
"Increasing ghostscript optimisation level to " + optimizeLevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (expectedOutputSize != null && autoMode) {
|
if (expectedOutputSize != null && autoMode) {
|
||||||
long outputFileSize = Files.size(tempOutputFile);
|
long outputFileSize = Files.size(tempOutputFile);
|
||||||
byte[] fileBytes = Files.readAllBytes(tempOutputFile);
|
if (outputFileSize > expectedOutputSize) {
|
||||||
if (outputFileSize > expectedOutputSize) {
|
try (PDDocument doc = Loader.loadPDF(new File(tempOutputFile.toString()))) {
|
||||||
try (PDDocument doc = Loader.loadPDF(fileBytes)) {
|
long previousFileSize = 0;
|
||||||
long previousFileSize = 0;
|
double scaleFactor = 1.0;
|
||||||
double scaleFactorConst = 0.9f;
|
while (true) {
|
||||||
double scaleFactor = 0.9f;
|
for (PDPage page : doc.getPages()) {
|
||||||
while (true) {
|
PDResources res = page.getResources();
|
||||||
for (PDPage page : doc.getPages()) {
|
|
||||||
PDResources res = page.getResources();
|
|
||||||
if (res != null && res.getXObjectNames() != null) {
|
|
||||||
for (COSName name : res.getXObjectNames()) {
|
|
||||||
PDXObject xobj = res.getXObject(name);
|
|
||||||
if (xobj != null && xobj instanceof PDImageXObject) {
|
|
||||||
PDImageXObject image = (PDImageXObject) xobj;
|
|
||||||
|
|
||||||
// Get the image in BufferedImage format
|
for (COSName name : res.getXObjectNames()) {
|
||||||
BufferedImage bufferedImage = image.getImage();
|
PDXObject xobj = res.getXObject(name);
|
||||||
|
if (xobj instanceof PDImageXObject) {
|
||||||
|
PDImageXObject image = (PDImageXObject) xobj;
|
||||||
|
|
||||||
// Calculate the new dimensions
|
// Get the image in BufferedImage format
|
||||||
int newWidth =
|
BufferedImage bufferedImage = image.getImage();
|
||||||
(int)
|
|
||||||
(bufferedImage.getWidth()
|
|
||||||
* scaleFactorConst);
|
|
||||||
int newHeight =
|
|
||||||
(int)
|
|
||||||
(bufferedImage.getHeight()
|
|
||||||
* scaleFactorConst);
|
|
||||||
|
|
||||||
// If the new dimensions are zero, skip this iteration
|
// Calculate the new dimensions
|
||||||
if (newWidth == 0 || newHeight == 0) {
|
int newWidth = (int) (bufferedImage.getWidth() * scaleFactor);
|
||||||
continue;
|
int newHeight = (int) (bufferedImage.getHeight() * scaleFactor);
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, proceed with the scaling
|
// If the new dimensions are zero, skip this iteration
|
||||||
Image scaledImage =
|
if (newWidth == 0 || newHeight == 0) {
|
||||||
bufferedImage.getScaledInstance(
|
continue;
|
||||||
newWidth,
|
|
||||||
newHeight,
|
|
||||||
Image.SCALE_SMOOTH);
|
|
||||||
|
|
||||||
// Convert the scaled image back to a BufferedImage
|
|
||||||
BufferedImage scaledBufferedImage =
|
|
||||||
new BufferedImage(
|
|
||||||
newWidth,
|
|
||||||
newHeight,
|
|
||||||
BufferedImage.TYPE_INT_RGB);
|
|
||||||
scaledBufferedImage
|
|
||||||
.getGraphics()
|
|
||||||
.drawImage(scaledImage, 0, 0, null);
|
|
||||||
|
|
||||||
// Compress the scaled image
|
|
||||||
ByteArrayOutputStream compressedImageStream =
|
|
||||||
new ByteArrayOutputStream();
|
|
||||||
ImageIO.write(
|
|
||||||
scaledBufferedImage,
|
|
||||||
"jpeg",
|
|
||||||
compressedImageStream);
|
|
||||||
byte[] imageBytes = compressedImageStream.toByteArray();
|
|
||||||
compressedImageStream.close();
|
|
||||||
|
|
||||||
PDImageXObject compressedImage =
|
|
||||||
PDImageXObject.createFromByteArray(
|
|
||||||
doc,
|
|
||||||
imageBytes,
|
|
||||||
image.getCOSObject().toString());
|
|
||||||
|
|
||||||
// Replace the image in the resources with the
|
|
||||||
// compressed
|
|
||||||
// version
|
|
||||||
res.put(name, compressedImage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise, proceed with the scaling
|
||||||
|
Image scaledImage =
|
||||||
|
bufferedImage.getScaledInstance(
|
||||||
|
newWidth, newHeight, Image.SCALE_SMOOTH);
|
||||||
|
|
||||||
|
// Convert the scaled image back to a BufferedImage
|
||||||
|
BufferedImage scaledBufferedImage =
|
||||||
|
new BufferedImage(
|
||||||
|
newWidth,
|
||||||
|
newHeight,
|
||||||
|
BufferedImage.TYPE_INT_RGB);
|
||||||
|
scaledBufferedImage
|
||||||
|
.getGraphics()
|
||||||
|
.drawImage(scaledImage, 0, 0, null);
|
||||||
|
|
||||||
|
// Compress the scaled image
|
||||||
|
ByteArrayOutputStream compressedImageStream =
|
||||||
|
new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(
|
||||||
|
scaledBufferedImage, "jpeg", compressedImageStream);
|
||||||
|
byte[] imageBytes = compressedImageStream.toByteArray();
|
||||||
|
compressedImageStream.close();
|
||||||
|
|
||||||
|
// Convert compressed image back to PDImageXObject
|
||||||
|
ByteArrayInputStream bais =
|
||||||
|
new ByteArrayInputStream(imageBytes);
|
||||||
|
PDImageXObject compressedImage =
|
||||||
|
PDImageXObject.createFromByteArray(
|
||||||
|
doc,
|
||||||
|
imageBytes,
|
||||||
|
image.getCOSObject().toString());
|
||||||
|
|
||||||
|
// Replace the image in the resources with the compressed
|
||||||
|
// version
|
||||||
|
res.put(name, compressedImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// save the document to tempOutputFile again
|
// save the document to tempOutputFile again
|
||||||
doc.save(tempOutputFile.toString());
|
doc.save(tempOutputFile.toString());
|
||||||
|
|
||||||
long currentSize = Files.size(tempOutputFile);
|
long currentSize = Files.size(tempOutputFile);
|
||||||
// Check if the overall PDF size is still larger than expectedOutputSize
|
// Check if the overall PDF size is still larger than expectedOutputSize
|
||||||
if (currentSize > expectedOutputSize) {
|
if (currentSize > expectedOutputSize) {
|
||||||
// Log the current file size and scaleFactor
|
// Log the current file size and scaleFactor
|
||||||
|
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"Current file size: "
|
"Current file size: "
|
||||||
+ FileUtils.byteCountToDisplaySize(currentSize));
|
+ FileUtils.byteCountToDisplaySize(currentSize));
|
||||||
System.out.println("Current scale factor: " + scaleFactor);
|
System.out.println("Current scale factor: " + scaleFactor);
|
||||||
|
|
||||||
// The file is still too large, reduce scaleFactor and try again
|
// The file is still too large, reduce scaleFactor and try again
|
||||||
scaleFactor *= 0.9f; // reduce scaleFactor by 10%
|
scaleFactor *= 0.9; // reduce scaleFactor by 10%
|
||||||
// Avoid scaleFactor being too small, causing the image to shrink to
|
// Avoid scaleFactor being too small, causing the image to shrink to 0
|
||||||
// 0
|
if (scaleFactor < 0.2 || previousFileSize == currentSize) {
|
||||||
if (scaleFactor < 0.2f || previousFileSize == currentSize) {
|
throw new RuntimeException(
|
||||||
throw new RuntimeException(
|
"Could not reach the desired size without excessively degrading image quality, lowest size recommended is "
|
||||||
"Could not reach the desired size without excessively degrading image quality, lowest size recommended is "
|
+ FileUtils.byteCountToDisplaySize(currentSize)
|
||||||
+ FileUtils.byteCountToDisplaySize(currentSize)
|
+ ", "
|
||||||
+ ", "
|
+ currentSize
|
||||||
+ currentSize
|
+ " bytes");
|
||||||
+ " bytes");
|
|
||||||
}
|
|
||||||
previousFileSize = currentSize;
|
|
||||||
} else {
|
|
||||||
// The file is small enough, break the loop
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
previousFileSize = currentSize;
|
||||||
|
} else {
|
||||||
|
// The file is small enough, break the loop
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the optimized PDF file
|
|
||||||
pdfBytes = Files.readAllBytes(tempOutputFile);
|
|
||||||
|
|
||||||
// Check if optimized file is larger than the original
|
|
||||||
if (pdfBytes.length > inputFileSize) {
|
|
||||||
// Log the occurrence
|
|
||||||
logger.warn(
|
|
||||||
"Optimized file is larger than the original. Returning the original file instead.");
|
|
||||||
|
|
||||||
// Read the original file again
|
|
||||||
pdfBytes = Files.readAllBytes(tempInputFile);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Clean up the temporary files
|
|
||||||
Files.delete(tempInputFile);
|
|
||||||
Files.delete(tempOutputFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the optimized PDF file
|
||||||
|
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
|
||||||
|
|
||||||
|
// Check if optimized file is larger than the original
|
||||||
|
if (pdfBytes.length > inputFileSize) {
|
||||||
|
// Log the occurrence
|
||||||
|
logger.warn(
|
||||||
|
"Optimized file is larger than the original. Returning the original file instead.");
|
||||||
|
|
||||||
|
// Read the original file again
|
||||||
|
pdfBytes = Files.readAllBytes(tempInputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the temporary files
|
||||||
|
Files.delete(tempInputFile);
|
||||||
|
Files.delete(tempOutputFile);
|
||||||
|
|
||||||
// Return the optimized PDF as a response
|
// Return the optimized PDF as a response
|
||||||
String outputFilename =
|
String outputFilename =
|
||||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.nio.file.StandardCopyOption;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@@ -72,151 +73,111 @@ public class ExtractImageScansController {
|
|||||||
|
|
||||||
List<String> images = new ArrayList<>();
|
List<String> images = new ArrayList<>();
|
||||||
|
|
||||||
List<Path> tempImageFiles = new ArrayList<>();
|
// Check if input file is a PDF
|
||||||
Path tempInputFile = null;
|
if ("pdf".equalsIgnoreCase(extension)) {
|
||||||
Path tempZipFile = null;
|
// Load PDF document
|
||||||
List<Path> tempDirs = new ArrayList<>();
|
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
||||||
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
|
int pageCount = document.getNumberOfPages();
|
||||||
|
images = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
// Create images of all pages
|
||||||
// Check if input file is a PDF
|
for (int i = 0; i < pageCount; i++) {
|
||||||
if ("pdf".equalsIgnoreCase(extension)) {
|
// Create temp file to save the image
|
||||||
// Load PDF document
|
Path tempFile = Files.createTempFile("image_", ".png");
|
||||||
try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) {
|
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
int pageCount = document.getNumberOfPages();
|
|
||||||
images = new ArrayList<>();
|
|
||||||
|
|
||||||
// Create images of all pages
|
// Render image and save as temp file
|
||||||
for (int i = 0; i < pageCount; i++) {
|
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
|
||||||
// Create temp file to save the image
|
ImageIO.write(image, "png", tempFile.toFile());
|
||||||
Path tempFile = Files.createTempFile("image_", ".png");
|
|
||||||
|
|
||||||
// Render image and save as temp file
|
// Add temp file path to images list
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
|
images.add(tempFile.toString());
|
||||||
ImageIO.write(image, "png", tempFile.toFile());
|
|
||||||
|
|
||||||
// Add temp file path to images list
|
|
||||||
images.add(tempFile.toString());
|
|
||||||
tempImageFiles.add(tempFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tempInputFile = Files.createTempFile("input_", "." + extension);
|
|
||||||
Files.copy(
|
|
||||||
form.getFileInput().getInputStream(),
|
|
||||||
tempInputFile,
|
|
||||||
StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
// Add input file path to images list
|
|
||||||
images.add(tempInputFile.toString());
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Path tempInputFile = Files.createTempFile("input_", "." + extension);
|
||||||
|
Files.copy(
|
||||||
|
form.getFileInput().getInputStream(),
|
||||||
|
tempInputFile,
|
||||||
|
StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
// Add input file path to images list
|
||||||
|
images.add(tempInputFile.toString());
|
||||||
|
}
|
||||||
|
|
||||||
List<byte[]> processedImageBytes = new ArrayList<>();
|
List<byte[]> processedImageBytes = new ArrayList<>();
|
||||||
|
|
||||||
// Process each image
|
// Process each image
|
||||||
for (int i = 0; i < images.size(); i++) {
|
for (int i = 0; i < images.size(); i++) {
|
||||||
|
|
||||||
Path tempDir = Files.createTempDirectory("openCV_output");
|
Path tempDir = Files.createTempDirectory("openCV_output");
|
||||||
tempDirs.add(tempDir);
|
List<String> command =
|
||||||
List<String> command =
|
new ArrayList<>(
|
||||||
new ArrayList<>(
|
Arrays.asList(
|
||||||
Arrays.asList(
|
"python3",
|
||||||
"python3",
|
"./scripts/split_photos.py",
|
||||||
"./scripts/split_photos.py",
|
images.get(i),
|
||||||
images.get(i),
|
tempDir.toString(),
|
||||||
tempDir.toString(),
|
"--angle_threshold",
|
||||||
"--angle_threshold",
|
String.valueOf(form.getAngleThreshold()),
|
||||||
String.valueOf(form.getAngleThreshold()),
|
"--tolerance",
|
||||||
"--tolerance",
|
String.valueOf(form.getTolerance()),
|
||||||
String.valueOf(form.getTolerance()),
|
"--min_area",
|
||||||
"--min_area",
|
String.valueOf(form.getMinArea()),
|
||||||
String.valueOf(form.getMinArea()),
|
"--min_contour_area",
|
||||||
"--min_contour_area",
|
String.valueOf(form.getMinContourArea()),
|
||||||
String.valueOf(form.getMinContourArea()),
|
"--border_size",
|
||||||
"--border_size",
|
String.valueOf(form.getBorderSize())));
|
||||||
String.valueOf(form.getBorderSize())));
|
|
||||||
|
|
||||||
// Run CLI command
|
// Run CLI command
|
||||||
ProcessExecutorResult returnCode =
|
ProcessExecutorResult returnCode =
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||||
.runCommandWithOutputHandling(command);
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
// Read the output photos in temp directory
|
// Read the output photos in temp directory
|
||||||
List<Path> tempOutputFiles = Files.list(tempDir).sorted().toList();
|
List<Path> tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList());
|
||||||
for (Path tempOutputFile : tempOutputFiles) {
|
for (Path tempOutputFile : tempOutputFiles) {
|
||||||
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
|
byte[] imageBytes = Files.readAllBytes(tempOutputFile);
|
||||||
processedImageBytes.add(imageBytes);
|
processedImageBytes.add(imageBytes);
|
||||||
}
|
|
||||||
// Clean up the temporary directory
|
|
||||||
FileUtils.deleteDirectory(tempDir.toFile());
|
|
||||||
}
|
}
|
||||||
|
// Clean up the temporary directory
|
||||||
|
FileUtils.deleteDirectory(tempDir.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
// Create zip file if multiple images
|
// Create zip file if multiple images
|
||||||
if (processedImageBytes.size() > 1) {
|
if (processedImageBytes.size() > 1) {
|
||||||
String outputZipFilename =
|
String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip";
|
||||||
fileName.replaceFirst(REPLACEFIRST, "") + "_processed.zip";
|
Path tempZipFile = Files.createTempFile("output_", ".zip");
|
||||||
tempZipFile = Files.createTempFile("output_", ".zip");
|
|
||||||
|
|
||||||
try (ZipOutputStream zipOut =
|
try (ZipOutputStream zipOut =
|
||||||
new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
|
||||||
// Add processed images to the zip
|
// Add processed images to the zip
|
||||||
for (int i = 0; i < processedImageBytes.size(); i++) {
|
for (int i = 0; i < processedImageBytes.size(); i++) {
|
||||||
ZipEntry entry =
|
ZipEntry entry =
|
||||||
new ZipEntry(
|
new ZipEntry(
|
||||||
fileName.replaceFirst(REPLACEFIRST, "")
|
fileName.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_"
|
+ "_"
|
||||||
+ (i + 1)
|
+ (i + 1)
|
||||||
+ ".png");
|
+ ".png");
|
||||||
zipOut.putNextEntry(entry);
|
zipOut.putNextEntry(entry);
|
||||||
zipOut.write(processedImageBytes.get(i));
|
zipOut.write(processedImageBytes.get(i));
|
||||||
zipOut.closeEntry();
|
zipOut.closeEntry();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
|
||||||
|
|
||||||
// Clean up the temporary zip file
|
|
||||||
Files.delete(tempZipFile);
|
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
} else {
|
|
||||||
// Return the processed image as a response
|
|
||||||
byte[] imageBytes = processedImageBytes.get(0);
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
imageBytes,
|
|
||||||
fileName.replaceFirst(REPLACEFIRST, "") + ".png",
|
|
||||||
MediaType.IMAGE_PNG);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Cleanup logic for all temporary files and directories
|
|
||||||
tempImageFiles.forEach(
|
|
||||||
path -> {
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Failed to delete temporary image file: " + path, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tempZipFile != null && Files.exists(tempZipFile)) {
|
|
||||||
try {
|
|
||||||
Files.delete(tempZipFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Failed to delete temporary zip file: " + tempZipFile, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempDirs.forEach(
|
byte[] zipBytes = Files.readAllBytes(tempZipFile);
|
||||||
dir -> {
|
|
||||||
try {
|
// Clean up the temporary zip file
|
||||||
FileUtils.deleteDirectory(dir.toFile());
|
Files.delete(tempZipFile);
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Failed to delete temporary directory: " + dir, e);
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
}
|
zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
});
|
} else {
|
||||||
|
// Return the processed image as a response
|
||||||
|
byte[] imageBytes = processedImageBytes.get(0);
|
||||||
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
|
imageBytes,
|
||||||
|
fileName.replaceFirst("[.][^.]+$", "") + ".png",
|
||||||
|
MediaType.IMAGE_PNG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String REPLACEFIRST = "[.][^.]+$";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ public class FakeScanControllerWIP {
|
|||||||
|
|
||||||
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||||
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
||||||
ImageIO.write(image, "png", new File("scanned-" + (page + 1) + ".png"));
|
ImageIO.write(image, "png", new File("scanned-" + (page + 1) + ".png"));
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class OCRController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tessdata";
|
String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata";
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class StampController {
|
|||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
|
||||||
|
|
||||||
List<Integer> pageNumbers = request.getPageNumbersList(document, true);
|
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||||
|
|
||||||
for (int pageIndex : pageNumbers) {
|
for (int pageIndex : pageNumbers) {
|
||||||
int zeroBasedIndex = pageIndex - 1;
|
int zeroBasedIndex = pageIndex - 1;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class ApiDocService {
|
|||||||
|
|
||||||
private String getApiDocsUrl() {
|
private String getApiDocsUrl() {
|
||||||
String contextPath = servletContext.getContextPath();
|
String contextPath = servletContext.getContextPath();
|
||||||
String port = SPdfApplication.getStaticPort();
|
String port = SPdfApplication.getPort();
|
||||||
|
|
||||||
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
@@ -49,6 +50,9 @@ public class PipelineController {
|
|||||||
@PostMapping("/handleData")
|
@PostMapping("/handleData")
|
||||||
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request)
|
public ResponseEntity<byte[]> handleData(@ModelAttribute HandleDataRequest request)
|
||||||
throws JsonMappingException, JsonProcessingException {
|
throws JsonMappingException, JsonProcessingException {
|
||||||
|
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
MultipartFile[] files = request.getFileInput();
|
MultipartFile[] files = request.getFileInput();
|
||||||
String jsonString = request.getJson();
|
String jsonString = request.getJson();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.PipelineOperation;
|
import stirling.software.SPDF.model.PipelineOperation;
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ public class PipelineDirectoryProcessor {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class);
|
private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class);
|
||||||
@Autowired private ObjectMapper objectMapper;
|
@Autowired private ObjectMapper objectMapper;
|
||||||
@Autowired private ApiDocService apiDocService;
|
@Autowired private ApiDocService apiDocService;
|
||||||
|
@Autowired private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||||
@@ -43,6 +45,9 @@ public class PipelineDirectoryProcessor {
|
|||||||
|
|
||||||
@Scheduled(fixedRate = 60000)
|
@Scheduled(fixedRate = 60000)
|
||||||
public void scanFolders() {
|
public void scanFolders() {
|
||||||
|
if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
||||||
if (!Files.exists(watchedFolderPath)) {
|
if (!Files.exists(watchedFolderPath)) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class PipelineProcessor {
|
|||||||
|
|
||||||
private String getBaseUrl() {
|
private String getBaseUrl() {
|
||||||
String contextPath = servletContext.getContextPath();
|
String contextPath = servletContext.getContextPath();
|
||||||
String port = SPdfApplication.getStaticPort();
|
String port = SPdfApplication.getPort();
|
||||||
|
|
||||||
return "http://localhost:" + port + contextPath + "/";
|
return "http://localhost:" + port + contextPath + "/";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ public class RedactController {
|
|||||||
if (convertPDFToImage) {
|
if (convertPDFToImage) {
|
||||||
PDDocument imageDocument = new PDDocument();
|
PDDocument imageDocument = new PDDocument();
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
for (int page = 0; page < document.getNumberOfPages(); ++page) {
|
||||||
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
|
||||||
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
|
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
|
||||||
|
|||||||
@@ -54,32 +54,33 @@ public class SanitizeController {
|
|||||||
boolean removeLinks = request.isRemoveLinks();
|
boolean removeLinks = request.isRemoveLinks();
|
||||||
boolean removeFonts = request.isRemoveFonts();
|
boolean removeFonts = request.isRemoveFonts();
|
||||||
|
|
||||||
PDDocument document = Loader.loadPDF(inputFile.getBytes());
|
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
|
||||||
if (removeJavaScript) {
|
if (removeJavaScript) {
|
||||||
sanitizeJavaScript(document);
|
sanitizeJavaScript(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeEmbeddedFiles) {
|
if (removeEmbeddedFiles) {
|
||||||
sanitizeEmbeddedFiles(document);
|
sanitizeEmbeddedFiles(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeMetadata) {
|
if (removeMetadata) {
|
||||||
sanitizeMetadata(document);
|
sanitizeMetadata(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeLinks) {
|
if (removeLinks) {
|
||||||
sanitizeLinks(document);
|
sanitizeLinks(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeFonts) {
|
if (removeFonts) {
|
||||||
sanitizeFonts(document);
|
sanitizeFonts(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(
|
return WebResponseUtils.pdfDocToWebResponse(
|
||||||
document,
|
document,
|
||||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", "")
|
.replaceFirst("[.][^.]+$", "")
|
||||||
+ "_sanitized.pdf");
|
+ "_sanitized.pdf");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeJavaScript(PDDocument document) throws IOException {
|
private void sanitizeJavaScript(PDDocument document) throws IOException {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.web;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -34,8 +33,6 @@ public class AccountWebController {
|
|||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("currentPage", "login");
|
|
||||||
|
|
||||||
if (request.getParameter("error") != null) {
|
if (request.getParameter("error") != null) {
|
||||||
|
|
||||||
model.addAttribute("error", request.getParameter("error"));
|
model.addAttribute("error", request.getParameter("error"));
|
||||||
@@ -56,7 +53,6 @@ public class AccountWebController {
|
|||||||
public String showAddUserForm(Model model, Authentication authentication) {
|
public String showAddUserForm(Model model, Authentication authentication) {
|
||||||
List<User> allUsers = userRepository.findAll();
|
List<User> allUsers = userRepository.findAll();
|
||||||
Iterator<User> iterator = allUsers.iterator();
|
Iterator<User> iterator = allUsers.iterator();
|
||||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
User user = iterator.next();
|
User user = iterator.next();
|
||||||
@@ -64,7 +60,6 @@ public class AccountWebController {
|
|||||||
for (Authority authority : user.getAuthorities()) {
|
for (Authority authority : user.getAuthorities()) {
|
||||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
roleDetails.remove(Role.INTERNAL_API_USER.getRoleId());
|
|
||||||
break; // Break out of the inner loop once the user is removed
|
break; // Break out of the inner loop once the user is removed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +68,6 @@ public class AccountWebController {
|
|||||||
|
|
||||||
model.addAttribute("users", allUsers);
|
model.addAttribute("users", allUsers);
|
||||||
model.addAttribute("currentUsername", authentication.getName());
|
model.addAttribute("currentUsername", authentication.getName());
|
||||||
model.addAttribute("roleDetails", roleDetails);
|
|
||||||
return "addUsers";
|
return "addUsers";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +112,6 @@ public class AccountWebController {
|
|||||||
model.addAttribute("role", user.get().getRolesAsString());
|
model.addAttribute("role", user.get().getRolesAsString());
|
||||||
model.addAttribute("settings", settingsJson);
|
model.addAttribute("settings", settingsJson);
|
||||||
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
|
||||||
model.addAttribute("currentPage", "account");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class OtherWebController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tessdata";
|
String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata";
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ public class ApplicationProperties {
|
|||||||
private String rootURIPath;
|
private String rootURIPath;
|
||||||
private String customStaticFilePath;
|
private String customStaticFilePath;
|
||||||
private Integer maxFileSize;
|
private Integer maxFileSize;
|
||||||
|
private CustomApplications customApplications;
|
||||||
|
|
||||||
private Boolean enableAlphaFunctionality;
|
private Boolean enableAlphaFunctionality;
|
||||||
|
|
||||||
@@ -261,6 +262,14 @@ public class ApplicationProperties {
|
|||||||
this.maxFileSize = maxFileSize;
|
this.maxFileSize = maxFileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CustomApplications getCustomApplications() {
|
||||||
|
return customApplications != null ? customApplications : new CustomApplications();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomApplications(CustomApplications customApplications) {
|
||||||
|
this.customApplications = customApplications;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "System [defaultLocale="
|
return "System [defaultLocale="
|
||||||
@@ -273,10 +282,31 @@ public class ApplicationProperties {
|
|||||||
+ customStaticFilePath
|
+ customStaticFilePath
|
||||||
+ ", maxFileSize="
|
+ ", maxFileSize="
|
||||||
+ maxFileSize
|
+ maxFileSize
|
||||||
|
+ ", customApplications="
|
||||||
|
+ customApplications
|
||||||
+ ", enableAlphaFunctionality="
|
+ ", enableAlphaFunctionality="
|
||||||
+ enableAlphaFunctionality
|
+ enableAlphaFunctionality
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CustomApplications {
|
||||||
|
private boolean installBookAndHtmlFormats;
|
||||||
|
|
||||||
|
public boolean isInstallBookAndHtmlFormats() {
|
||||||
|
return installBookAndHtmlFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstallBookAndHtmlFormats(boolean installBookAndHtmlFormats) {
|
||||||
|
this.installBookAndHtmlFormats = installBookAndHtmlFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CustomApplications [installBookAndHtmlFormats="
|
||||||
|
+ installBookAndHtmlFormats
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Ui {
|
public static class Ui {
|
||||||
|
|||||||
@@ -1,43 +1,34 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
|
|
||||||
// Unlimited access
|
// Unlimited access
|
||||||
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.admin"),
|
ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
|
|
||||||
// Unlimited access
|
// Unlimited access
|
||||||
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.user"),
|
USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
|
|
||||||
// 40 API calls Per Day, 40 web calls
|
// 40 API calls Per Day, 40 web calls
|
||||||
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40, "adminUserSettings.apiUser"),
|
LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40),
|
||||||
|
|
||||||
// 20 API calls Per Day, 20 web calls
|
// 20 API calls Per Day, 20 web calls
|
||||||
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20, "adminUserSettings.extraApiUser"),
|
EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20),
|
||||||
|
|
||||||
// 0 API calls per day and 20 web calls
|
// 0 API calls per day and 20 web calls
|
||||||
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20, "adminUserSettings.webOnlyUser"),
|
WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20),
|
||||||
|
|
||||||
INTERNAL_API_USER(
|
INTERNAL_API_USER("STIRLING-PDF-BACKEND-API-USER", Integer.MAX_VALUE, Integer.MAX_VALUE),
|
||||||
"STIRLING-PDF-BACKEND-API-USER",
|
|
||||||
Integer.MAX_VALUE,
|
|
||||||
Integer.MAX_VALUE,
|
|
||||||
"adminUserSettings.internalApiUser"),
|
|
||||||
|
|
||||||
DEMO_USER("ROLE_DEMO_USER", 100, 100, "adminUserSettings.demoUser");
|
DEMO_USER("ROLE_DEMO_USER", 100, 100);
|
||||||
|
|
||||||
private final String roleId;
|
private final String roleId;
|
||||||
private final int apiCallsPerDay;
|
private final int apiCallsPerDay;
|
||||||
private final int webCallsPerDay;
|
private final int webCallsPerDay;
|
||||||
private final String roleName;
|
|
||||||
|
|
||||||
Role(String roleId, int apiCallsPerDay, int webCallsPerDay, String roleName) {
|
Role(String roleId, int apiCallsPerDay, int webCallsPerDay) {
|
||||||
this.roleId = roleId;
|
this.roleId = roleId;
|
||||||
this.apiCallsPerDay = apiCallsPerDay;
|
this.apiCallsPerDay = apiCallsPerDay;
|
||||||
this.webCallsPerDay = webCallsPerDay;
|
this.webCallsPerDay = webCallsPerDay;
|
||||||
this.roleName = roleName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRoleId() {
|
public String getRoleId() {
|
||||||
@@ -52,27 +43,6 @@ public enum Role {
|
|||||||
return webCallsPerDay;
|
return webCallsPerDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRoleName() {
|
|
||||||
return roleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getRoleNameByRoleId(String roleId) {
|
|
||||||
// Using the fromString method to get the Role enum based on the roleId
|
|
||||||
Role role = fromString(roleId);
|
|
||||||
// Return the roleName of the found Role enum
|
|
||||||
return role.getRoleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to retrieve all role IDs and role names
|
|
||||||
public static Map<String, String> getAllRoleDetails() {
|
|
||||||
// Using LinkedHashMap to preserve order
|
|
||||||
Map<String, String> roleDetails = new LinkedHashMap<>();
|
|
||||||
for (Role role : Role.values()) {
|
|
||||||
roleDetails.put(role.getRoleId(), role.getRoleName());
|
|
||||||
}
|
|
||||||
return roleDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Role fromString(String roleId) {
|
public static Role fromString(String roleId) {
|
||||||
for (Role role : Role.values()) {
|
for (Role role : Role.values()) {
|
||||||
if (role.getRoleId().equalsIgnoreCase(roleId)) {
|
if (role.getRoleId().equalsIgnoreCase(roleId)) {
|
||||||
|
|||||||
@@ -44,9 +44,6 @@ public class User {
|
|||||||
@Column(name = "isFirstLogin")
|
@Column(name = "isFirstLogin")
|
||||||
private Boolean isFirstLogin = false;
|
private Boolean isFirstLogin = false;
|
||||||
|
|
||||||
@Column(name = "roleName")
|
|
||||||
private String roleName;
|
|
||||||
|
|
||||||
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
||||||
private Set<Authority> authorities = new HashSet<>();
|
private Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
@@ -56,10 +53,6 @@ public class User {
|
|||||||
@CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "user_id"))
|
@CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "user_id"))
|
||||||
private Map<String, String> settings = new HashMap<>(); // Key-value pairs of settings.
|
private Map<String, String> settings = new HashMap<>(); // Key-value pairs of settings.
|
||||||
|
|
||||||
public String getRoleName() {
|
|
||||||
return Role.getRoleNameByRoleId(getRolesAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFirstLogin() {
|
public boolean isFirstLogin() {
|
||||||
return isFirstLogin != null && isFirstLogin;
|
return isFirstLogin != null && isFirstLogin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ public class PDFWithPageNums extends PDFFile {
|
|||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount);
|
return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Hidden
|
@Hidden
|
||||||
public List<Integer> getPageNumbersList(PDDocument doc, boolean zeroCount) {
|
public List<Integer> getPageNumbersList(PDDocument doc, boolean zeroCount) {
|
||||||
int pageCount = 0;
|
int pageCount = 0;
|
||||||
pageCount = doc.getNumberOfPages();
|
pageCount = doc.getNumberOfPages();
|
||||||
return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount);
|
return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,4 @@ public class SplitPdfBySectionsRequest extends PDFFile {
|
|||||||
|
|
||||||
@Schema(description = "Number of vertical divisions for each PDF page", example = "2")
|
@Schema(description = "Number of vertical divisions for each PDF page", example = "2")
|
||||||
private int verticalDivisions;
|
private int verticalDivisions;
|
||||||
|
|
||||||
@Schema(description = "Merge the split documents into a single PDF", example = "true")
|
|
||||||
private boolean merge;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ public class PdfToTextOrRTFRequest extends PDFFile {
|
|||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description = "The output Text or RTF format",
|
description = "The output Text or RTF format",
|
||||||
allowableValues = {"rtf", "txt"})
|
allowableValues = {"rtf", "txt:Text"})
|
||||||
private String outputFormat;
|
private String outputFormat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,6 @@ public class TextFinder extends PDFTextStripper {
|
|||||||
private final boolean wholeWordSearch;
|
private final boolean wholeWordSearch;
|
||||||
private final List<PDFText> textOccurrences = new ArrayList<>();
|
private final List<PDFText> textOccurrences = new ArrayList<>();
|
||||||
|
|
||||||
private class MatchInfo {
|
|
||||||
int startIndex;
|
|
||||||
int matchLength;
|
|
||||||
|
|
||||||
MatchInfo(int startIndex, int matchLength) {
|
|
||||||
this.startIndex = startIndex;
|
|
||||||
this.matchLength = matchLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextFinder(String searchText, boolean useRegex, boolean wholeWordSearch)
|
public TextFinder(String searchText, boolean useRegex, boolean wholeWordSearch)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this.searchText = searchText.toLowerCase();
|
this.searchText = searchText.toLowerCase();
|
||||||
@@ -37,37 +27,36 @@ public class TextFinder extends PDFTextStripper {
|
|||||||
setSortByPosition(true);
|
setSortByPosition(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MatchInfo> findOccurrencesInText(String searchText, String content) {
|
private List<Integer> findOccurrencesInText(String searchText, String content) {
|
||||||
List<MatchInfo> matches = new ArrayList<>();
|
List<Integer> indexes = new ArrayList<>();
|
||||||
|
|
||||||
Pattern pattern;
|
Pattern pattern;
|
||||||
|
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
// Use regex-based search
|
// Use regex-based search
|
||||||
pattern =
|
pattern =
|
||||||
wholeWordSearch
|
wholeWordSearch
|
||||||
? Pattern.compile("\\b" + searchText + "\\b")
|
? Pattern.compile("(\\b|_|\\.)" + searchText + "(\\b|_|\\.)")
|
||||||
: Pattern.compile(searchText);
|
: Pattern.compile(searchText);
|
||||||
} else {
|
} else {
|
||||||
// Use normal text search
|
// Use normal text search
|
||||||
pattern =
|
pattern =
|
||||||
wholeWordSearch
|
wholeWordSearch
|
||||||
? Pattern.compile("\\b" + Pattern.quote(searchText) + "\\b")
|
? Pattern.compile(
|
||||||
|
"(\\b|_|\\.)" + Pattern.quote(searchText) + "(\\b|_|\\.)")
|
||||||
: Pattern.compile(Pattern.quote(searchText));
|
: Pattern.compile(Pattern.quote(searchText));
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher matcher = pattern.matcher(content);
|
Matcher matcher = pattern.matcher(content);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
matches.add(new MatchInfo(matcher.start(), matcher.end() - matcher.start()));
|
indexes.add(matcher.start());
|
||||||
}
|
}
|
||||||
return matches;
|
return indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeString(String text, List<TextPosition> textPositions) {
|
protected void writeString(String text, List<TextPosition> textPositions) {
|
||||||
for (MatchInfo match : findOccurrencesInText(searchText, text.toLowerCase())) {
|
for (Integer index : findOccurrencesInText(searchText, text.toLowerCase())) {
|
||||||
int index = match.startIndex;
|
if (index + searchText.length() <= textPositions.size()) {
|
||||||
if (index + match.matchLength <= textPositions.size()) {
|
|
||||||
// Initial values based on the first character
|
// Initial values based on the first character
|
||||||
TextPosition first = textPositions.get(index);
|
TextPosition first = textPositions.get(index);
|
||||||
float minX = first.getX();
|
float minX = first.getX();
|
||||||
@@ -76,7 +65,7 @@ public class TextFinder extends PDFTextStripper {
|
|||||||
float maxY = first.getY() + first.getHeight();
|
float maxY = first.getY() + first.getHeight();
|
||||||
|
|
||||||
// Loop over the rest of the characters and adjust bounding box values
|
// Loop over the rest of the characters and adjust bounding box values
|
||||||
for (int i = index; i < index + match.matchLength; i++) {
|
for (int i = index; i < index + searchText.length(); i++) {
|
||||||
TextPosition position = textPositions.get(i);
|
TextPosition position = textPositions.get(i);
|
||||||
minX = Math.min(minX, position.getX());
|
minX = Math.min(minX, position.getX());
|
||||||
minY = Math.min(minY, position.getY());
|
minY = Math.min(minY, position.getY());
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, String> {
|
public interface UserRepository extends JpaRepository<User, String> {
|
||||||
Optional<User> findByUsernameIgnoreCase(String username);
|
|
||||||
|
|
||||||
Optional<User> findByUsername(String username);
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
User findByApiKey(String apiKey);
|
User findByApiKey(String apiKey);
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ import java.nio.file.Paths;
|
|||||||
import java.nio.file.SimpleFileVisitor;
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import com.fathzer.soft.javaluator.DoubleEvaluator;
|
|
||||||
|
|
||||||
import io.github.pixee.security.HostValidator;
|
import io.github.pixee.security.HostValidator;
|
||||||
import io.github.pixee.security.Urls;
|
import io.github.pixee.security.Urls;
|
||||||
|
|
||||||
@@ -116,115 +115,91 @@ public class GeneralUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> parsePageList(String pages, int totalPages, boolean oneBased) {
|
public static List<Integer> parsePageString(String pageOrder, int totalPages) {
|
||||||
if (pages == null) {
|
return parsePageString(pageOrder, totalPages, false);
|
||||||
return List.of(1); // Default to first page if input is null
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return parsePageList(pages.split(","), totalPages, oneBased);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return List.of(1); // Default to first page if input is invalid
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> parsePageList(String[] pages, int totalPages) {
|
public static List<Integer> parsePageString(
|
||||||
return parsePageList(pages, totalPages, false);
|
String pageOrder, int totalPages, boolean isOneBased) {
|
||||||
|
if (pageOrder == null || pageOrder.isEmpty()) {
|
||||||
|
return Collections.singletonList(1);
|
||||||
|
}
|
||||||
|
if (pageOrder.matches("\\d+")) {
|
||||||
|
// Convert the single number string to an integer and return it in a list
|
||||||
|
return Collections.singletonList(Integer.parseInt(pageOrder));
|
||||||
|
}
|
||||||
|
return parsePageList(pageOrder.split(","), totalPages, isOneBased);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> parsePageList(String[] pages, int totalPages, boolean oneBased) {
|
public static List<Integer> parsePageList(String[] pageOrderArr, int totalPages) {
|
||||||
List<Integer> result = new ArrayList<>();
|
return parsePageList(pageOrderArr, totalPages, false);
|
||||||
int offset = oneBased ? 1 : 0;
|
}
|
||||||
for (String page : pages) {
|
|
||||||
if ("all".equalsIgnoreCase(page)) {
|
|
||||||
|
|
||||||
|
public static List<Integer> parsePageList(
|
||||||
|
String[] pageOrderArr, int totalPages, boolean isOneBased) {
|
||||||
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
|
|
||||||
|
int adjustmentFactor = isOneBased ? 1 : 0;
|
||||||
|
|
||||||
|
// loop through the page order array
|
||||||
|
for (String element : pageOrderArr) {
|
||||||
|
if ("all".equalsIgnoreCase(element)) {
|
||||||
for (int i = 0; i < totalPages; i++) {
|
for (int i = 0; i < totalPages; i++) {
|
||||||
result.add(i + offset);
|
newPageOrder.add(i + adjustmentFactor);
|
||||||
}
|
}
|
||||||
} else if (page.contains(",")) {
|
// As all pages are already added, no need to check further
|
||||||
// Split the string into parts, could be single pages or ranges
|
break;
|
||||||
String[] parts = page.split(",");
|
} else if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
||||||
for (String part : parts) {
|
// Handle page order as a function
|
||||||
result.addAll(handlePart(part, totalPages, offset));
|
int coefficient = 0;
|
||||||
|
int constant = 0;
|
||||||
|
boolean coefficientExists = false;
|
||||||
|
boolean constantExists = false;
|
||||||
|
|
||||||
|
if (element.contains("n")) {
|
||||||
|
String[] parts = element.split("n");
|
||||||
|
if (!"".equals(parts[0]) && parts[0] != null) {
|
||||||
|
coefficient = Integer.parseInt(parts[0]);
|
||||||
|
coefficientExists = true;
|
||||||
|
}
|
||||||
|
if (parts.length > 1 && !"".equals(parts[1]) && parts[1] != null) {
|
||||||
|
constant = Integer.parseInt(parts[1]);
|
||||||
|
constantExists = true;
|
||||||
|
}
|
||||||
|
} else if (element.contains("+")) {
|
||||||
|
constant = Integer.parseInt(element.replace("+", ""));
|
||||||
|
constantExists = true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result.addAll(handlePart(page, totalPages, offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ArrayList<>(
|
|
||||||
new java.util.LinkedHashSet<>(result)); // Remove duplicates and maintain order
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Integer> evaluateNFunc(String expression, int maxValue) {
|
for (int i = 1; i <= totalPages; i++) {
|
||||||
List<Integer> results = new ArrayList<>();
|
int pageNum = coefficientExists ? coefficient * i : i;
|
||||||
DoubleEvaluator evaluator = new DoubleEvaluator();
|
pageNum += constantExists ? constant : 0;
|
||||||
|
|
||||||
// Validate the expression
|
if (pageNum <= totalPages && pageNum > 0) {
|
||||||
if (!expression.matches("[0-9n+\\-*/() ]+")) {
|
newPageOrder.add(pageNum - adjustmentFactor);
|
||||||
throw new IllegalArgumentException("Invalid expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
while (true) {
|
|
||||||
// Replace 'n' with the current value of n, correctly handling numbers before 'n'
|
|
||||||
String sanitizedExpression = insertMultiplicationBeforeN(expression, n);
|
|
||||||
Double result = evaluator.evaluate(sanitizedExpression);
|
|
||||||
|
|
||||||
// Check if the result is null or not within bounds
|
|
||||||
if (result == null || result <= 0 || result.intValue() > maxValue) {
|
|
||||||
if (n != 0) break;
|
|
||||||
} else {
|
|
||||||
results.add(result.intValue());
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String insertMultiplicationBeforeN(String expression, int nValue) {
|
|
||||||
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
|
||||||
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
|
||||||
// Now replace 'n' with its current value
|
|
||||||
return withMultiplication.replaceAll("n", String.valueOf(nValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
|
||||||
List<Integer> partResult = new ArrayList<>();
|
|
||||||
|
|
||||||
// First check for n-syntax because it should not be processed as a range
|
|
||||||
if (part.contains("n")) {
|
|
||||||
partResult = evaluateNFunc(part, totalPages);
|
|
||||||
// Adjust the results according to the offset
|
|
||||||
for (int i = 0; i < partResult.size(); i++) {
|
|
||||||
int adjustedValue = partResult.get(i) - 1 + offset;
|
|
||||||
partResult.set(i, adjustedValue);
|
|
||||||
}
|
|
||||||
} else if (part.contains("-")) {
|
|
||||||
// Process ranges only if it's not n-syntax
|
|
||||||
String[] rangeParts = part.split("-");
|
|
||||||
try {
|
|
||||||
int start = Integer.parseInt(rangeParts[0]);
|
|
||||||
int end = Integer.parseInt(rangeParts[1]);
|
|
||||||
for (int i = start; i <= end; i++) {
|
|
||||||
if (i >= 1 && i <= totalPages) {
|
|
||||||
partResult.add(i - 1 + offset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} else if (element.contains("-")) {
|
||||||
// Range is invalid, ignore this part
|
// split the range into start and end page
|
||||||
}
|
String[] range = element.split("-");
|
||||||
} else {
|
int start = Integer.parseInt(range[0]);
|
||||||
// This is a single page number
|
int end = Integer.parseInt(range[1]);
|
||||||
try {
|
// check if the end page is greater than total pages
|
||||||
int pageNum = Integer.parseInt(part.trim());
|
if (end > totalPages) {
|
||||||
if (pageNum >= 1 && pageNum <= totalPages) {
|
end = totalPages;
|
||||||
partResult.add(pageNum - 1 + offset);
|
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignored) {
|
// loop through the range of pages
|
||||||
// Ignore invalid numbers
|
for (int j = start; j <= end; j++) {
|
||||||
|
// print the current index
|
||||||
|
newPageOrder.add(j - adjustmentFactor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the element is a single page
|
||||||
|
newPageOrder.add(Integer.parseInt(element) - adjustmentFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return partResult;
|
|
||||||
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean createDir(String path) {
|
public static boolean createDir(String path) {
|
||||||
|
|||||||
@@ -25,71 +25,6 @@ import io.github.pixee.security.Filenames;
|
|||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
public class PDFToFile {
|
public class PDFToFile {
|
||||||
|
|
||||||
public ResponseEntity<byte[]> processPdfToHtml(MultipartFile inputFile)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
if (!"application/pdf".equals(inputFile.getContentType())) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the original PDF file name without the extension
|
|
||||||
String originalPdfFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
|
|
||||||
String pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.'));
|
|
||||||
|
|
||||||
Path tempInputFile = null;
|
|
||||||
Path tempOutputDir = null;
|
|
||||||
byte[] fileBytes;
|
|
||||||
String fileName = "temp.file";
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Save the uploaded file to a temporary location
|
|
||||||
tempInputFile = Files.createTempFile("input_", ".pdf");
|
|
||||||
Files.copy(
|
|
||||||
inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
|
|
||||||
// Prepare the output directory
|
|
||||||
tempOutputDir = Files.createTempDirectory("output_");
|
|
||||||
|
|
||||||
// Run the pdftohtml command with complex output
|
|
||||||
List<String> command =
|
|
||||||
new ArrayList<>(
|
|
||||||
Arrays.asList(
|
|
||||||
"pdftohtml", "-c", tempInputFile.toString(), pdfBaseName));
|
|
||||||
|
|
||||||
ProcessExecutorResult returnCode =
|
|
||||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.PDFTOHTML)
|
|
||||||
.runCommandWithOutputHandling(command, tempOutputDir.toFile());
|
|
||||||
|
|
||||||
// Get output files
|
|
||||||
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
|
|
||||||
|
|
||||||
// Return output files in a ZIP archive
|
|
||||||
fileName = pdfBaseName + "ToHtml.zip";
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
|
|
||||||
|
|
||||||
for (File outputFile : outputFiles) {
|
|
||||||
ZipEntry entry = new ZipEntry(outputFile.getName());
|
|
||||||
zipOutputStream.putNextEntry(entry);
|
|
||||||
FileInputStream fis = new FileInputStream(outputFile);
|
|
||||||
IOUtils.copy(fis, zipOutputStream);
|
|
||||||
fis.close();
|
|
||||||
zipOutputStream.closeEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
zipOutputStream.close();
|
|
||||||
fileBytes = byteArrayOutputStream.toByteArray();
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// Clean up the temporary files
|
|
||||||
if (tempInputFile != null) Files.delete(tempInputFile);
|
|
||||||
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
|
||||||
fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResponseEntity<byte[]> processPdfToOfficeFormat(
|
public ResponseEntity<byte[]> processPdfToOfficeFormat(
|
||||||
MultipartFile inputFile, String outputFormat, String libreOfficeFilter)
|
MultipartFile inputFile, String outputFormat, String libreOfficeFilter)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
@@ -104,7 +39,17 @@ public class PDFToFile {
|
|||||||
|
|
||||||
// Validate output format
|
// Validate output format
|
||||||
List<String> allowedFormats =
|
List<String> allowedFormats =
|
||||||
Arrays.asList("doc", "docx", "odt", "ppt", "pptx", "odp", "rtf", "xml", "txt:Text");
|
Arrays.asList(
|
||||||
|
"doc",
|
||||||
|
"docx",
|
||||||
|
"odt",
|
||||||
|
"ppt",
|
||||||
|
"pptx",
|
||||||
|
"odp",
|
||||||
|
"rtf",
|
||||||
|
"html",
|
||||||
|
"xml",
|
||||||
|
"txt:Text");
|
||||||
if (!allowedFormats.contains(outputFormat)) {
|
if (!allowedFormats.contains(outputFormat)) {
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ public class PdfUtils {
|
|||||||
throws IOException, Exception {
|
throws IOException, Exception {
|
||||||
try (PDDocument document = Loader.loadPDF(inputStream)) {
|
try (PDDocument document = Loader.loadPDF(inputStream)) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
||||||
pdfRenderer.setSubsamplingAllowed(true);
|
|
||||||
int pageCount = document.getNumberOfPages();
|
int pageCount = document.getNumberOfPages();
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to save the image(s) to
|
// Create a ByteArrayOutputStream to save the image(s) to
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ public class ProcessExecutor {
|
|||||||
|
|
||||||
public enum Processes {
|
public enum Processes {
|
||||||
LIBRE_OFFICE,
|
LIBRE_OFFICE,
|
||||||
PDFTOHTML,
|
|
||||||
OCR_MY_PDF,
|
OCR_MY_PDF,
|
||||||
PYTHON_OPENCV,
|
PYTHON_OPENCV,
|
||||||
GHOSTSCRIPT,
|
GHOSTSCRIPT,
|
||||||
@@ -46,7 +45,6 @@ public class ProcessExecutor {
|
|||||||
int semaphoreLimit =
|
int semaphoreLimit =
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case LIBRE_OFFICE -> 1;
|
case LIBRE_OFFICE -> 1;
|
||||||
case PDFTOHTML -> 1;
|
|
||||||
case OCR_MY_PDF -> 2;
|
case OCR_MY_PDF -> 2;
|
||||||
case PYTHON_OPENCV -> 8;
|
case PYTHON_OPENCV -> 8;
|
||||||
case GHOSTSCRIPT -> 16;
|
case GHOSTSCRIPT -> 16;
|
||||||
@@ -58,7 +56,6 @@ public class ProcessExecutor {
|
|||||||
long timeoutMinutes =
|
long timeoutMinutes =
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case LIBRE_OFFICE -> 30;
|
case LIBRE_OFFICE -> 30;
|
||||||
case PDFTOHTML -> 5;
|
|
||||||
case OCR_MY_PDF -> 30;
|
case OCR_MY_PDF -> 30;
|
||||||
case PYTHON_OPENCV -> 30;
|
case PYTHON_OPENCV -> 30;
|
||||||
case GHOSTSCRIPT -> 5;
|
case GHOSTSCRIPT -> 5;
|
||||||
@@ -172,35 +169,27 @@ public class ProcessExecutor {
|
|||||||
errorReaderThread.join();
|
errorReaderThread.join();
|
||||||
outputReaderThread.join();
|
outputReaderThread.join();
|
||||||
|
|
||||||
if (outputLines.size() > 0) {
|
if (!liveUpdates) {
|
||||||
String outputMessage = String.join("\n", outputLines);
|
if (outputLines.size() > 0) {
|
||||||
messages += outputMessage;
|
String outputMessage = String.join("\n", outputLines);
|
||||||
if (!liveUpdates) {
|
messages += outputMessage;
|
||||||
logger.info("Command output:\n" + outputMessage);
|
logger.info("Command output:\n" + outputMessage);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (errorLines.size() > 0) {
|
if (errorLines.size() > 0) {
|
||||||
String errorMessage = String.join("\n", errorLines);
|
String errorMessage = String.join("\n", errorLines);
|
||||||
messages += errorMessage;
|
messages += errorMessage;
|
||||||
if (!liveUpdates) {
|
|
||||||
logger.warn("Command error output:\n" + errorMessage);
|
logger.warn("Command error output:\n" + errorMessage);
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new IOException(
|
||||||
|
"Command process failed with exit code "
|
||||||
|
+ exitCode
|
||||||
|
+ ". Error message: "
|
||||||
|
+ errorMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (exitCode != 0) {
|
} else if (exitCode != 0) {
|
||||||
throw new IOException(
|
throw new IOException("Command process failed with exit code " + exitCode);
|
||||||
"Command process failed with exit code "
|
|
||||||
+ exitCode
|
|
||||||
+ ". Error message: "
|
|
||||||
+ errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitCode != 0) {
|
|
||||||
throw new IOException(
|
|
||||||
"Command process failed with exit code "
|
|
||||||
+ exitCode
|
|
||||||
+ "\nLogs: "
|
|
||||||
+ messages);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
semaphore.release();
|
semaphore.release();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____
|
||||||
/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___|
|
/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___|
|
||||||
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_
|
||||||
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _|
|
||||||
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_|
|
||||||
Powered by Spring Boot ${spring-boot.version}
|
Powered by Spring Boot ${spring-boot.version}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileToPDF.fileTypesList=Microsoft Word: (DOC, DOCX, DOT, DOTX) <br> \
|
||||||
|
Microsoft Excel: (CSV, XLS, XLSX, XLT, XLTX, SLK, DIF) <br> \
|
||||||
|
Microsoft PowerPoint: (PPT, PPTX) <br> \
|
||||||
|
OpenDocument Formats: (ODT, OTT, ODS, OTS, ODP, OTP, ODG, OTG) <br> \
|
||||||
|
Plain Text: (TXT, TEXT, XML) <br> \
|
||||||
|
Rich Text Format: (RTF) <br> \
|
||||||
|
Images: (BMP, GIF, JPEG, PNG, TIF, PBM, PGM, PPM, RAS, XBM, XPM, SVG, SVM, WMF) <br> \
|
||||||
|
HTML: (HTML) <br> \
|
||||||
|
Lotus Word Pro: (LWP) <br> \
|
||||||
|
StarOffice formats: (SDA, SDC, SDD, SDW, STC, STD, STI, STW, SXD, SXG, SXI, SXW) <br> \
|
||||||
|
Other formats: (DBF, FODS, VSD, VOR, VOR3, VOR4, UOP, PCT, PS, PDF)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -11,19 +11,16 @@ imgPrompt=اختر صورة
|
|||||||
genericSubmit=إرسال
|
genericSubmit=إرسال
|
||||||
processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية ما يصل إلى دقيقة حسب حجم الملف
|
processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية ما يصل إلى دقيقة حسب حجم الملف
|
||||||
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
||||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
|
||||||
goToPage=اذهب
|
goToPage=اذهب
|
||||||
true=صحيح
|
true=\u0635\u062D\u064A\u062D
|
||||||
false=خطأ
|
false=\u062E\u0637\u0623
|
||||||
unknown=غير معروف
|
unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641
|
||||||
save=حفظ
|
save=\u062D\u0641\u0638
|
||||||
saveToBrowser=Save to Browser
|
close=\u0625\u063A\u0644\u0627\u0642
|
||||||
close=إغلاق
|
|
||||||
filesSelected=الملفات المحددة
|
filesSelected=الملفات المحددة
|
||||||
noFavourites=لم تتم إضافة أي مفضلات
|
noFavourites=لم تتم إضافة أي مفضلات
|
||||||
downloadComplete=Download Complete
|
|
||||||
bored=الانتظار بالملل؟
|
bored=الانتظار بالملل؟
|
||||||
alphabet=الأبجدية
|
alphabet=\u0627\u0644\u0623\u0628\u062C\u062F\u064A\u0629
|
||||||
downloadPdf=تنزيل PDF
|
downloadPdf=تنزيل PDF
|
||||||
text=نص
|
text=نص
|
||||||
font=الخط
|
font=الخط
|
||||||
@@ -54,32 +51,16 @@ notAuthenticatedMessage=User not authenticated.
|
|||||||
userNotFoundMessage=User not found.
|
userNotFoundMessage=User not found.
|
||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
usernameExistsMessage=New Username already exists.
|
||||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline Menu (Beta)
|
pipeline.header=Pipeline Menu (Alpha)
|
||||||
pipeline.uploadButton=Upload Custom
|
pipeline.uploadButton=Upload Custom
|
||||||
pipeline.configureButton=Configure
|
pipeline.configureButton=Configure
|
||||||
pipeline.defaultOption=Custom
|
pipeline.defaultOption=Custom
|
||||||
pipeline.submitButton=Submit
|
pipeline.submitButton=Submit
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@@ -105,19 +86,19 @@ navbar.security=الأمان
|
|||||||
navbar.other=أخرى
|
navbar.other=أخرى
|
||||||
navbar.darkmode=الوضع الداكن
|
navbar.darkmode=الوضع الداكن
|
||||||
navbar.pageOps=عمليات الصفحة
|
navbar.pageOps=عمليات الصفحة
|
||||||
navbar.settings=إعدادات
|
navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
#############
|
#############
|
||||||
settings.title=الإعدادات
|
settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||||
settings.update=التحديث متاح
|
settings.update=\u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D
|
||||||
settings.appVersion=إصدار التطبيق:
|
settings.appVersion=\u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642:
|
||||||
settings.downloadOption.title=تحديد خيار التنزيل (للتنزيلات ذات الملف الواحد غير المضغوط):
|
settings.downloadOption.title=\u062A\u062D\u062F\u064A\u062F \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0646\u0632\u064A\u0644 (\u0644\u0644\u062A\u0646\u0632\u064A\u0644\u0627\u062A \u0630\u0627\u062A \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0648\u0627\u062D\u062F \u063A\u064A\u0631 \u0627\u0644\u0645\u0636\u063A\u0648\u0637):
|
||||||
settings.downloadOption.1=فتح في نفس النافذة
|
settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0627\u0644\u0646\u0627\u0641\u0630\u0629
|
||||||
settings.downloadOption.2=فتح في نافذة جديدة
|
settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629
|
||||||
settings.downloadOption.3=تنزيل الملف
|
settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641
|
||||||
settings.zipThreshold=ملفات مضغوطة عند تجاوز عدد الملفات التي تم تنزيلها
|
settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627
|
||||||
settings.signOut=Sign Out
|
settings.signOut=Sign Out
|
||||||
settings.accountSettings=Account Settings
|
settings.accountSettings=Account Settings
|
||||||
|
|
||||||
@@ -125,7 +106,7 @@ settings.accountSettings=Account Settings
|
|||||||
|
|
||||||
changeCreds.title=Change Credentials
|
changeCreds.title=Change Credentials
|
||||||
changeCreds.header=Update Your Account Details
|
changeCreds.header=Update Your Account Details
|
||||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted)
|
||||||
changeCreds.newUsername=New Username
|
changeCreds.newUsername=New Username
|
||||||
changeCreds.oldPassword=Current Password
|
changeCreds.oldPassword=Current Password
|
||||||
changeCreds.newPassword=New Password
|
changeCreds.newPassword=New Password
|
||||||
@@ -160,16 +141,13 @@ adminUserSettings.header=Admin User Control Settings
|
|||||||
adminUserSettings.admin=Admin
|
adminUserSettings.admin=Admin
|
||||||
adminUserSettings.user=User
|
adminUserSettings.user=User
|
||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
|
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
adminUserSettings.actions=Actions
|
adminUserSettings.actions=Actions
|
||||||
adminUserSettings.apiUser=Limited API User
|
adminUserSettings.apiUser=Limited API User
|
||||||
adminUserSettings.extraApiUser=Additional Limited API User
|
|
||||||
adminUserSettings.webOnlyUser=Web Only User
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||||
adminUserSettings.internalApiUser=Internal API User
|
adminUserSettings.forceChange=Force user to change username/password on login
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
@@ -243,25 +221,25 @@ home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
|
|||||||
compressPdfs.tags=squish,small,tiny
|
compressPdfs.tags=squish,small,tiny
|
||||||
|
|
||||||
|
|
||||||
home.changeMetadata.title=تغيير البيانات الوصفية
|
home.changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||||
home.changeMetadata.desc=تغيير / إزالة / إضافة بيانات أولية من مستند PDF
|
home.changeMetadata.desc=\u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=تحويل الملف إلى PDF
|
home.fileToPDF.title=\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0625\u0644\u0649 PDF
|
||||||
home.fileToPDF.desc=تحويل أي ملف تقريبا إلى PDF (DOCX وPNG وXLS وPPT وTXT والمزيد)
|
home.fileToPDF.desc=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u062A\u0642\u0631\u064A\u0628\u0627 \u0625\u0644\u0649 PDF (DOCX \u0648PNG \u0648XLS \u0648PPT \u0648TXT \u0648\u0627\u0644\u0645\u0632\u064A\u062F)
|
||||||
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
|
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
|
||||||
|
|
||||||
home.ocr.title=تشغيل OCR على PDF و / أو مسح ضوئي
|
home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF \u0648 / \u0623\u0648 \u0645\u0633\u062D \u0636\u0648\u0626\u064A
|
||||||
home.ocr.desc=يقوم برنامج التنظيف بمسح واكتشاف النص من الصور داخل ملف PDF ويعيد إضافته كنص
|
home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0628\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF \u0648\u064A\u0639\u064A\u062F \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635
|
||||||
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
|
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
|
||||||
|
|
||||||
|
|
||||||
home.extractImages.title=استخراج الصور
|
home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||||
home.extractImages.desc=يستخرج جميع الصور من ملف PDF ويحفظها في الرمز البريدي
|
home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A
|
||||||
extractImages.tags=picture,photo,save,archive,zip,capture,grab
|
extractImages.tags=picture,photo,save,archive,zip,capture,grab
|
||||||
|
|
||||||
home.pdfToPDFA.title=تحويل ملفات PDF إلى PDF / A
|
home.pdfToPDFA.title=\u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A
|
||||||
home.pdfToPDFA.desc=تحويل PDF إلى PDF / A للتخزين طويل المدى
|
home.pdfToPDFA.desc=\u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649
|
||||||
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
|
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
|
||||||
|
|
||||||
home.PDFToWord.title=تحويل PDF إلى Word
|
home.PDFToWord.title=تحويل PDF إلى Word
|
||||||
@@ -413,15 +391,6 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations
|
|||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
|
||||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
home.BookToPDF.title=Book to PDF
|
|
||||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
# WEB PAGES #
|
# WEB PAGES #
|
||||||
@@ -429,7 +398,6 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Sign in
|
login.title=Sign in
|
||||||
login.header=Sign in
|
|
||||||
login.signin=Sign in
|
login.signin=Sign in
|
||||||
login.rememberme=Remember me
|
login.rememberme=Remember me
|
||||||
login.invalid=Invalid username or password.
|
login.invalid=Invalid username or password.
|
||||||
@@ -467,7 +435,6 @@ pdfToSinglePage.submit=Convert To Single Page
|
|||||||
pageExtracter.title=Extract Pages
|
pageExtracter.title=Extract Pages
|
||||||
pageExtracter.header=Extract Pages
|
pageExtracter.header=Extract Pages
|
||||||
pageExtracter.submit=Extract
|
pageExtracter.submit=Extract
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -655,18 +622,6 @@ compare.document.1=المستند 1
|
|||||||
compare.document.2=المستند 2
|
compare.document.2=المستند 2
|
||||||
compare.submit=يقارن
|
compare.submit=يقارن
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Books and Comics to PDF
|
|
||||||
BookToPDF.header=Book to PDF
|
|
||||||
BookToPDF.credit=Uses Calibre
|
|
||||||
BookToPDF.submit=Convert
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF to Book
|
|
||||||
PDFToBook.header=PDF to Book
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Uses Calibre
|
|
||||||
PDFToBook.submit=Convert
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=تسجيل الدخول
|
sign.title=تسجيل الدخول
|
||||||
@@ -704,38 +659,38 @@ ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة وا
|
|||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
ocr.title=التعرف الضوئي على الحروف / تنظيف المسح الضوئي
|
ocr.title=\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 / \u062A\u0646\u0638\u064A\u0641 \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A
|
||||||
ocr.header=مسح المسح الضوئي / التعرف الضوئي على الحروف (التعرف الضوئي على الحروف)
|
ocr.header=\u0645\u0633\u062D \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A / \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641)
|
||||||
ocr.selectText.1=حدد اللغات التي سيتم اكتشافها داخل ملف PDF (اللغات المدرجة هي تلك التي تم اكتشافها حاليًا):
|
ocr.selectText.1=\u062D\u062F\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u062A\u064A \u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062D\u0627\u0644\u064A\u064B\u0627):
|
||||||
ocr.selectText.2=إنتاج ملف نصي يحتوي على نص OCR بجانب ملف PDF الذي تم إعداده بواسطة OCR
|
ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u0628\u062C\u0627\u0646\u0628 \u0645\u0644\u0641 PDF \u0627\u0644\u0630\u064A \u062A\u0645 \u0625\u0639\u062F\u0627\u062F\u0647 \u0628\u0648\u0627\u0633\u0637\u0629 OCR
|
||||||
ocr.selectText.3=تم مسح الصفحات الصحيحة ضوئيًا بزاوية منحرفة عن طريق تدويرها مرة أخرى في مكانها
|
ocr.selectText.3=\u062A\u0645 \u0645\u0633\u062D \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0636\u0648\u0626\u064A\u064B\u0627 \u0628\u0632\u0627\u0648\u064A\u0629 \u0645\u0646\u062D\u0631\u0641\u0629 \u0639\u0646 \u0637\u0631\u064A\u0642 \u062A\u062F\u0648\u064A\u0631\u0647\u0627 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0641\u064A \u0645\u0643\u0627\u0646\u0647\u0627
|
||||||
ocr.selectText.4=صفحة نظيفة لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية. (لا يوجد تغيير في الإخراج)
|
ocr.selectText.4=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629. (\u0644\u0627 \u064A\u0648\u062C\u062F \u062A\u063A\u064A\u064A\u0631 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C)
|
||||||
ocr.selectText.5=صفحة نظيفة ، لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية ، ويحافظ على التنظيف في الإخراج.
|
ocr.selectText.5=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u060C \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629 \u060C \u0648\u064A\u062D\u0627\u0641\u0638 \u0639\u0644\u0649 \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C.
|
||||||
ocr.selectText.6=يتجاهل الصفحات التي تحتوي على نص تفاعلي ، فقط صفحات OCRs التي هي صور
|
ocr.selectText.6=\u064A\u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 \u062A\u0641\u0627\u0639\u0644\u064A \u060C \u0641\u0642\u0637 \u0635\u0641\u062D\u0627\u062A OCRs \u0627\u0644\u062A\u064A \u0647\u064A \u0635\u0648\u0631
|
||||||
ocr.selectText.7=فرض التعرف الضوئي على الحروف ، سيؤدي التعرف الضوئي على الحروف على كل صفحة إلى إزالة جميع عناصر النص الأصلي
|
ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u060C \u0633\u064A\u0624\u062F\u064A \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u0639\u0644\u0649 \u0643\u0644 \u0635\u0641\u062D\u0629 \u0625\u0644\u0649 \u0625\u0632\u0627\u0644\u0629 \u062C\u0645\u064A\u0639 \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u0646\u0635 \u0627\u0644\u0623\u0635\u0644\u064A
|
||||||
ocr.selectText.8=عادي (خطأ إذا كان PDF يحتوي على نص)
|
ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635)
|
||||||
ocr.selectText.9=إعدادات إضافية
|
ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629
|
||||||
ocr.selectText.10=وضع التعرف الضوئي على الحروف
|
ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641
|
||||||
ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
|
ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
|
||||||
ocr.selectText.12=نوع العرض (متقدم)
|
ocr.selectText.12=نوع العرض (متقدم)
|
||||||
ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و / أو الاستخدام ليس في عامل الإرساء
|
ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621
|
||||||
ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract ل OCR.
|
ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR.
|
||||||
ocr.submit=معالجة PDF باستخدام OCR
|
ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR
|
||||||
|
|
||||||
|
|
||||||
#extractImages
|
#extractImages
|
||||||
extractImages.title=استخراج الصور
|
extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||||
extractImages.header=استخراج الصور
|
extractImages.header=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||||
extractImages.selectText=حدد تنسيق الصورة لتحويل الصور المستخرجة إلى
|
extractImages.selectText=\u062D\u062F\u062F \u062A\u0646\u0633\u064A\u0642 \u0627\u0644\u0635\u0648\u0631\u0629 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0635\u0648\u0631 \u0627\u0644\u0645\u0633\u062A\u062E\u0631\u062C\u0629 \u0625\u0644\u0649
|
||||||
extractImages.submit=استخراج
|
extractImages.submit=\u0627\u0633\u062A\u062E\u0631\u0627\u062C
|
||||||
|
|
||||||
|
|
||||||
#File to PDF
|
#File to PDF
|
||||||
fileToPDF.title=ملف إلى PDF
|
fileToPDF.title=\u0645\u0644\u0641 \u0625\u0644\u0649 PDF
|
||||||
fileToPDF.header=تحويل أي ملف إلى PDF
|
fileToPDF.header=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u0625\u0644\u0649 PDF
|
||||||
fileToPDF.credit=تستخدم هذه الخدمة ليبر أوفيس وأونوكونف لتحويل الملفات.
|
fileToPDF.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 \u0644\u064A\u0628\u0631 \u0623\u0648\u0641\u064A\u0633 \u0648\u0623\u0648\u0646\u0648\u0643\u0648\u0646\u0641 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641\u0627\u062A.
|
||||||
fileToPDF.supportedFileTypes=يجب أن تتضمن أنواع الملفات المدعومة ما يلي ولكن للحصول على قائمة محدثة كاملة بالتنسيقات المدعومة ، يرجى الرجوع إلى وثائق LibreOffice
|
fileToPDF.supportedFileTypes=\u064A\u062C\u0628 \u0623\u0646 \u062A\u062A\u0636\u0645\u0646 \u0623\u0646\u0648\u0627\u0639 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u0645\u0627 \u064A\u0644\u064A \u0648\u0644\u0643\u0646 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0642\u0627\u0626\u0645\u0629 \u0645\u062D\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629 \u0628\u0627\u0644\u062A\u0646\u0633\u064A\u0642\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u060C \u064A\u0631\u062C\u0649 \u0627\u0644\u0631\u062C\u0648\u0639 \u0625\u0644\u0649 \u0648\u062B\u0627\u0626\u0642 LibreOffice
|
||||||
fileToPDF.submit=تحويل إلى PDF
|
fileToPDF.submit=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 PDF
|
||||||
|
|
||||||
|
|
||||||
#compress
|
#compress
|
||||||
@@ -770,23 +725,11 @@ merge.submit=دمج
|
|||||||
pdfOrganiser.title=منظم الصفحة
|
pdfOrganiser.title=منظم الصفحة
|
||||||
pdfOrganiser.header=منظم صفحات PDF
|
pdfOrganiser.header=منظم صفحات PDF
|
||||||
pdfOrganiser.submit=إعادة ترتيب الصفحات
|
pdfOrganiser.submit=إعادة ترتيب الصفحات
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=أداة متعددة PDF
|
multiTool.title=أداة متعددة PDF
|
||||||
multiTool.header=أداة متعددة PDF
|
multiTool.header=أداة متعددة PDF
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
@@ -797,7 +740,6 @@ pageRemover.title=مزيل الصفحة
|
|||||||
pageRemover.header=مزيل صفحة PDF
|
pageRemover.header=مزيل صفحة PDF
|
||||||
pageRemover.pagesToDelete=الصفحات المراد حذفها (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
pageRemover.pagesToDelete=الصفحات المراد حذفها (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
|
||||||
pageRemover.submit=حذف الصفحات
|
pageRemover.submit=حذف الصفحات
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
@@ -807,17 +749,17 @@ rotate.selectAngle=حدد زاوية الدوران (بمضاعفات 90 درج
|
|||||||
rotate.submit=استدارة
|
rotate.submit=استدارة
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=انقسام PDF
|
split.title=انقسام PDF
|
||||||
split.header=تقسيم PDF
|
split.header=تقسيم PDF
|
||||||
split.desc.1=الأرقام التي تحددها هي رقم الصفحة التي تريد تقسيمها
|
split.desc.1=الأرقام التي تحددها هي رقم الصفحة التي تريد تقسيمها
|
||||||
split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-9 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع:
|
split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-8 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع:
|
||||||
split.desc.3=المستند رقم 1: الصفحة 1
|
split.desc.3=المستند رقم 1: الصفحة 1
|
||||||
split.desc.4=المستند رقم 2: الصفحتان 2 و 3
|
split.desc.4=المستند رقم 2: الصفحتان 2 و 3
|
||||||
split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6 و 7
|
split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6
|
||||||
split.desc.6=المستند رقم 4: الصفحة 8
|
split.desc.6=المستند رقم 4: الصفحة 7
|
||||||
split.desc.7=المستند رقم 5: الصفحة 9
|
split.desc.7=المستند رقم 5: الصفحة 8
|
||||||
split.desc.8=المستند رقم 6: الصفحة 10
|
split.desc.8=المستند رقم 6: الصفحتان 9 و 10
|
||||||
split.splitPages=أدخل الصفحات المراد تقسيمها:
|
split.splitPages=أدخل الصفحات المراد تقسيمها:
|
||||||
split.submit=Split
|
split.submit=Split
|
||||||
|
|
||||||
@@ -830,23 +772,23 @@ imageToPDF.selectLabel=Image Fit Options
|
|||||||
imageToPDF.fillPage=Fill Page
|
imageToPDF.fillPage=Fill Page
|
||||||
imageToPDF.fitDocumentToImage=Fit Page to Image
|
imageToPDF.fitDocumentToImage=Fit Page to Image
|
||||||
imageToPDF.maintainAspectRatio=Maintain Aspect Ratios
|
imageToPDF.maintainAspectRatio=Maintain Aspect Ratios
|
||||||
imageToPDF.selectText.2=دوران PDF تلقائيًا
|
imageToPDF.selectText.2=\u062F\u0648\u0631\u0627\u0646 PDF \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627
|
||||||
imageToPDF.selectText.3=المنطق المتعدد للملفات (مفعّل فقط إذا كنت تعمل مع صور متعددة)
|
imageToPDF.selectText.3=\u0627\u0644\u0645\u0646\u0637\u0642 \u0627\u0644\u0645\u062A\u0639\u062F\u062F \u0644\u0644\u0645\u0644\u0641\u0627\u062A (\u0645\u0641\u0639\u0651\u0644 \u0641\u0642\u0637 \u0625\u0630\u0627 \u0643\u0646\u062A \u062A\u0639\u0645\u0644 \u0645\u0639 \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629)
|
||||||
imageToPDF.selectText.4=دمج في ملف PDF واحد
|
imageToPDF.selectText.4=\u062F\u0645\u062C \u0641\u064A \u0645\u0644\u0641 PDF \u0648\u0627\u062D\u062F
|
||||||
imageToPDF.selectText.5=تحويل إلى ملفات PDF منفصلة
|
imageToPDF.selectText.5=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 \u0645\u0644\u0641\u0627\u062A PDF \u0645\u0646\u0641\u0635\u0644\u0629
|
||||||
|
|
||||||
|
|
||||||
#pdfToImage
|
#pdfToImage
|
||||||
pdfToImage.title=تحويل PDF إلى صورة
|
pdfToImage.title=تحويل PDF إلى صورة
|
||||||
pdfToImage.header=تحويل PDF إلى صورة
|
pdfToImage.header=تحويل PDF إلى صورة
|
||||||
pdfToImage.selectText=تنسيق الصورة
|
pdfToImage.selectText=تنسيق الصورة
|
||||||
pdfToImage.singleOrMultiple=نوع نتيجة الصورة
|
pdfToImage.singleOrMultiple=\u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
|
||||||
pdfToImage.single=صورة واحدة كبيرة
|
pdfToImage.single=\u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
|
||||||
pdfToImage.multi=صور متعددة
|
pdfToImage.multi=\u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
|
||||||
pdfToImage.colorType=نوع اللون
|
pdfToImage.colorType=\u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
|
||||||
pdfToImage.color=اللون
|
pdfToImage.color=\u0627\u0644\u0644\u0648\u0646
|
||||||
pdfToImage.grey=تدرج الرمادي
|
pdfToImage.grey=\u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
|
||||||
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
|
pdfToImage.blackwhite=\u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
|
||||||
pdfToImage.submit=تحول
|
pdfToImage.submit=تحول
|
||||||
|
|
||||||
|
|
||||||
@@ -881,12 +823,10 @@ watermark.selectText.3=حجم الخط:
|
|||||||
watermark.selectText.4=دوران (0-360):
|
watermark.selectText.4=دوران (0-360):
|
||||||
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
|
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
|
||||||
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
|
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
|
||||||
watermark.selectText.7=التعتيم (0٪ - 100٪):
|
watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A):
|
||||||
watermark.selectText.8=Watermark Type:
|
watermark.selectText.8=Watermark Type:
|
||||||
watermark.selectText.9=Watermark Image:
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=إضافة علامة مائية
|
watermark.submit=إضافة علامة مائية
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -915,30 +855,29 @@ removePassword.submit=إزالة
|
|||||||
|
|
||||||
|
|
||||||
#changeMetadata
|
#changeMetadata
|
||||||
changeMetadata.title=العنوان:
|
changeMetadata.title=\u0627\u0644\u0639\u0646\u0648\u0627\u0646:
|
||||||
changeMetadata.header=تغيير البيانات الوصفية
|
changeMetadata.header=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
|
||||||
changeMetadata.selectText.1=يرجى تعديل المتغيرات التي ترغب في تغييرها
|
changeMetadata.selectText.1=\u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
|
||||||
changeMetadata.selectText.2=حذف كل البيانات الأولية
|
changeMetadata.selectText.2=\u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
|
||||||
changeMetadata.selectText.3=إظهار البيانات الأولية المخصصة:
|
changeMetadata.selectText.3=\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
|
||||||
changeMetadata.author=المؤلف:
|
changeMetadata.author=\u0627\u0644\u0645\u0624\u0644\u0641:
|
||||||
changeMetadata.creationDate=تاريخ الإنشاء (yyyy / MM / dd HH: mm: ss):
|
changeMetadata.creationDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
|
||||||
changeMetadata.creator=المنشئ:
|
changeMetadata.creator=\u0627\u0644\u0645\u0646\u0634\u0626:
|
||||||
changeMetadata.keywords=الكلمات الرئيسية:
|
changeMetadata.keywords=\u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
|
||||||
changeMetadata.modDate=تاريخ التعديل (yyyy / MM / dd HH: mm: ss):
|
changeMetadata.modDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
|
||||||
changeMetadata.producer=المنتج:
|
changeMetadata.producer=\u0627\u0644\u0645\u0646\u062A\u062C:
|
||||||
changeMetadata.subject=الموضوع:
|
changeMetadata.subject=\u0627\u0644\u0645\u0648\u0636\u0648\u0639:
|
||||||
changeMetadata.trapped=محاصر:
|
changeMetadata.trapped=\u0645\u062D\u0627\u0635\u0631:
|
||||||
changeMetadata.selectText.4=بيانات وصفية أخرى:
|
changeMetadata.selectText.4=\u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
|
||||||
changeMetadata.selectText.5=إضافة إدخال بيانات أولية مخصص
|
changeMetadata.selectText.5=\u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
|
||||||
changeMetadata.submit=تغيير
|
changeMetadata.submit=\u062A\u063A\u064A\u064A\u0631
|
||||||
|
|
||||||
|
|
||||||
#pdfToPDFA
|
#pdfToPDFA
|
||||||
pdfToPDFA.title=PDF إلى PDF / A
|
pdfToPDFA.title=PDF \u0625\u0644\u0649 PDF / A
|
||||||
pdfToPDFA.header=PDF إلى PDF / A
|
pdfToPDFA.header=PDF \u0625\u0644\u0649 PDF / A
|
||||||
pdfToPDFA.credit=تستخدم هذه الخدمة OCRmyPDF لتحويل PDF / A.
|
pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u062A\u062D\u0648\u064A\u0644 PDF / A.
|
||||||
pdfToPDFA.submit=تحويل
|
pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -968,7 +907,7 @@ PDFToText.submit=تحويل
|
|||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF إلى HTML
|
PDFToHTML.title=PDF إلى HTML
|
||||||
PDFToHTML.header=PDF إلى HTML
|
PDFToHTML.header=PDF إلى HTML
|
||||||
PDFToHTML.credit=تستخدم هذه الخدمة pdftohtml لتحويل الملفات.
|
PDFToHTML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||||
PDFToHTML.submit=تحويل
|
PDFToHTML.submit=تحويل
|
||||||
|
|
||||||
|
|
||||||
@@ -985,7 +924,6 @@ PDFToCSV.prompt=Choose page to extract table
|
|||||||
PDFToCSV.submit=??????
|
PDFToCSV.submit=??????
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=Split PDF by Size or Count
|
|
||||||
split-by-size-or-count.header=Split PDF by Size or Count
|
split-by-size-or-count.header=Split PDF by Size or Count
|
||||||
split-by-size-or-count.type.label=Select Split Type
|
split-by-size-or-count.type.label=Select Split Type
|
||||||
split-by-size-or-count.type.size=By Size
|
split-by-size-or-count.type.size=By Size
|
||||||
@@ -1020,7 +958,7 @@ split-by-sections.vertical.label=Vertical Divisions
|
|||||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||||
split-by-sections.submit=Split PDF
|
split-by-sections.submit=Split PDF
|
||||||
split-by-sections.merge=Merge Into One PDF
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Licenses
|
licenses.nav=Licenses
|
||||||
@@ -1031,16 +969,3 @@ licenses.version=Version
|
|||||||
licenses.license=License
|
licenses.license=License
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -11,17 +11,14 @@ imgPrompt=Изберете изображение(я)
|
|||||||
genericSubmit=Подайте
|
genericSubmit=Подайте
|
||||||
processTimeWarning=Предупреждение: Този процес може да отнеме до минута в зависимост от размера на файла
|
processTimeWarning=Предупреждение: Този процес може да отнеме до минута в зависимост от размера на файла
|
||||||
pageOrderPrompt=Персонализиран ред на страниците (Въведете разделен със запетаи списък с номера на страници или функции като 2n+1):
|
pageOrderPrompt=Персонализиран ред на страниците (Въведете разделен със запетаи списък с номера на страници или функции като 2n+1):
|
||||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
|
||||||
goToPage=Давай
|
goToPage=Давай
|
||||||
true=Вярно
|
true=Вярно
|
||||||
false=Невярно
|
false=Невярно
|
||||||
unknown=Непознат
|
unknown=Непознат
|
||||||
save=Съхранете
|
save=Съхранете
|
||||||
saveToBrowser=Save to Browser
|
|
||||||
close=Затворете
|
close=Затворете
|
||||||
filesSelected=избрани файлове
|
filesSelected=избрани файлове
|
||||||
noFavourites=Няма добавени любими
|
noFavourites=Няма добавени любими
|
||||||
downloadComplete=Download Complete
|
|
||||||
bored=Отекчени сте да чакате?
|
bored=Отекчени сте да чакате?
|
||||||
alphabet=Азбука
|
alphabet=Азбука
|
||||||
downloadPdf=Изтеглете PDF
|
downloadPdf=Изтеглете PDF
|
||||||
@@ -54,32 +51,16 @@ notAuthenticatedMessage=Потребителят не е автентикира
|
|||||||
userNotFoundMessage=Потребителят не е намерен
|
userNotFoundMessage=Потребителят не е намерен
|
||||||
incorrectPasswordMessage=Текущата парола е неправилна.
|
incorrectPasswordMessage=Текущата парола е неправилна.
|
||||||
usernameExistsMessage=Новият потребител вече съществува.
|
usernameExistsMessage=Новият потребител вече съществува.
|
||||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline Menu (Beta)
|
pipeline.header=Pipeline Menu (Alpha)
|
||||||
pipeline.uploadButton=Upload Custom
|
pipeline.uploadButton=Upload Custom
|
||||||
pipeline.configureButton=Configure
|
pipeline.configureButton=Configure
|
||||||
pipeline.defaultOption=Custom
|
pipeline.defaultOption=Custom
|
||||||
pipeline.submitButton=Submit
|
pipeline.submitButton=Submit
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@@ -125,7 +106,7 @@ settings.accountSettings=Настройки на акаунта
|
|||||||
|
|
||||||
changeCreds.title=Промяна на идентификационните данни
|
changeCreds.title=Промяна на идентификационните данни
|
||||||
changeCreds.header=Актуализирайте данните за акаунта си
|
changeCreds.header=Актуализирайте данните за акаунта си
|
||||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
changeCreds.changeUserAndPassword=Използвате идентификационни данни за вход по подразбиране. Моля, въведете нова парола (и потребителско име, ако искате)
|
||||||
changeCreds.newUsername=Ново потребителско име
|
changeCreds.newUsername=Ново потребителско име
|
||||||
changeCreds.oldPassword=Текуща парола
|
changeCreds.oldPassword=Текуща парола
|
||||||
changeCreds.newPassword=Нова парола
|
changeCreds.newPassword=Нова парола
|
||||||
@@ -160,15 +141,12 @@ adminUserSettings.header=Настройки за администраторск
|
|||||||
adminUserSettings.admin=Администратор
|
adminUserSettings.admin=Администратор
|
||||||
adminUserSettings.user=Потребител
|
adminUserSettings.user=Потребител
|
||||||
adminUserSettings.addUser=Добавяне на нов потребител
|
adminUserSettings.addUser=Добавяне на нов потребител
|
||||||
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
|
|
||||||
adminUserSettings.roles=Роли
|
adminUserSettings.roles=Роли
|
||||||
adminUserSettings.role=Роля
|
adminUserSettings.role=Роля
|
||||||
adminUserSettings.actions=Действия
|
adminUserSettings.actions=Действия
|
||||||
adminUserSettings.apiUser=Ограничен API потребител
|
adminUserSettings.apiUser=Ограничен API потребител
|
||||||
adminUserSettings.extraApiUser=Additional Limited API User
|
|
||||||
adminUserSettings.webOnlyUser=Само за уеб-потребител
|
adminUserSettings.webOnlyUser=Само за уеб-потребител
|
||||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||||
adminUserSettings.internalApiUser=Internal API User
|
|
||||||
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
|
||||||
adminUserSettings.submit=Съхранете потребителя
|
adminUserSettings.submit=Съхранете потребителя
|
||||||
|
|
||||||
@@ -413,15 +391,6 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations
|
|||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
|
||||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
home.BookToPDF.title=Book to PDF
|
|
||||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
# WEB PAGES #
|
# WEB PAGES #
|
||||||
@@ -429,7 +398,6 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Вход
|
login.title=Вход
|
||||||
login.header=Вход
|
|
||||||
login.signin=Впишете се
|
login.signin=Впишете се
|
||||||
login.rememberme=Запомни ме
|
login.rememberme=Запомни ме
|
||||||
login.invalid=Невалидно потребителско име или парола.
|
login.invalid=Невалидно потребителско име или парола.
|
||||||
@@ -467,7 +435,6 @@ pdfToSinglePage.submit=Преобразуване към единична стр
|
|||||||
pageExtracter.title=Extract Pages
|
pageExtracter.title=Extract Pages
|
||||||
pageExtracter.header=Extract Pages
|
pageExtracter.header=Extract Pages
|
||||||
pageExtracter.submit=Extract
|
pageExtracter.submit=Extract
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -655,18 +622,6 @@ compare.document.1=Документ 1
|
|||||||
compare.document.2=Документ 2
|
compare.document.2=Документ 2
|
||||||
compare.submit=Сравнявай
|
compare.submit=Сравнявай
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Books and Comics to PDF
|
|
||||||
BookToPDF.header=Book to PDF
|
|
||||||
BookToPDF.credit=Uses Calibre
|
|
||||||
BookToPDF.submit=Convert
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF to Book
|
|
||||||
PDFToBook.header=PDF to Book
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Uses Calibre
|
|
||||||
PDFToBook.submit=Convert
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Подпишете
|
sign.title=Подпишете
|
||||||
@@ -770,23 +725,11 @@ merge.submit=Обединяване
|
|||||||
pdfOrganiser.title=Организатор на страници
|
pdfOrganiser.title=Организатор на страници
|
||||||
pdfOrganiser.header=Организатор на PDF страници
|
pdfOrganiser.header=Организатор на PDF страници
|
||||||
pdfOrganiser.submit=Пренареждане на страниците
|
pdfOrganiser.submit=Пренареждане на страниците
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Мулти инструмент
|
multiTool.title=PDF Мулти инструмент
|
||||||
multiTool.header=PDF Мулти инструмент
|
multiTool.header=PDF Мулти инструмент
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
@@ -797,7 +740,6 @@ pageRemover.title=Премахване на страници
|
|||||||
pageRemover.header=Премахване на PDF страници
|
pageRemover.header=Премахване на PDF страници
|
||||||
pageRemover.pagesToDelete=Страници за изтриване (Въведете списък с номера на страници, разделени със запетая) :
|
pageRemover.pagesToDelete=Страници за изтриване (Въведете списък с номера на страници, разделени със запетая) :
|
||||||
pageRemover.submit=Изтриване на страници
|
pageRemover.submit=Изтриване на страници
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
@@ -807,17 +749,17 @@ rotate.selectAngle=Изберете ъгъл на въртене (кратно
|
|||||||
rotate.submit=Завъртане
|
rotate.submit=Завъртане
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=Разделяне на PDF
|
split.title=Разделяне на PDF
|
||||||
split.header=Разделяне на PDF
|
split.header=Разделяне на PDF
|
||||||
split.desc.1=Числата, които избирате, са номера на страницата, на която искате да направите разделяне
|
split.desc.1=Числата, които избирате, са номера на страницата, на която искате да направите разделяне
|
||||||
split.desc.2=Така че избирането на 1,3,7-9 ще раздели документ от 10 страници на 6 отделни PDF файла с:
|
split.desc.2=Така че избирането на 1,3,7-8 ще раздели документ от 10 страници на 6 отделни PDF файла с:
|
||||||
split.desc.3=Документ #1: Страница 1
|
split.desc.3=Документ #1: Страница 1
|
||||||
split.desc.4=Документ #2: Страница 2 и 3
|
split.desc.4=Документ #2: Страница 2 и 3
|
||||||
split.desc.5=Документ #3: Страница 4, 5, 6 и 7
|
split.desc.5=Документ #3: Страница 4, 5 и 6
|
||||||
split.desc.6=Документ #4: Страница 8
|
split.desc.6=Документ #4: Страница 7
|
||||||
split.desc.7=Документ #5: Страница 9
|
split.desc.7=Документ #5: Страница 8
|
||||||
split.desc.8=Документ #6: Страница 10
|
split.desc.8=Документ #6: Страница 9 и 10
|
||||||
split.splitPages=Въведете страници за разделяне:
|
split.splitPages=Въведете страници за разделяне:
|
||||||
split.submit=Разделяне
|
split.submit=Разделяне
|
||||||
|
|
||||||
@@ -885,8 +827,6 @@ watermark.selectText.7=Непрозрачност (0% - 100%):
|
|||||||
watermark.selectText.8=Тип воден знак:
|
watermark.selectText.8=Тип воден знак:
|
||||||
watermark.selectText.9=Изображение за воден знак:
|
watermark.selectText.9=Изображение за воден знак:
|
||||||
watermark.submit=Добавяне на воден знак
|
watermark.submit=Добавяне на воден знак
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -938,7 +878,6 @@ pdfToPDFA.title=PDF към PDF/A
|
|||||||
pdfToPDFA.header=PDF към PDF/A
|
pdfToPDFA.header=PDF към PDF/A
|
||||||
pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A преобразуване.
|
pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A преобразуване.
|
||||||
pdfToPDFA.submit=Преобразуване
|
pdfToPDFA.submit=Преобразуване
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -968,7 +907,7 @@ PDFToText.submit=Преобразуване
|
|||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF към HTML
|
PDFToHTML.title=PDF към HTML
|
||||||
PDFToHTML.header=PDF към HTML
|
PDFToHTML.header=PDF към HTML
|
||||||
PDFToHTML.credit=Тази услуга използва pdftohtml за преобразуване на файлове.
|
PDFToHTML.credit=Тази услуга използва LibreOffice за преобразуване на файлове.
|
||||||
PDFToHTML.submit=Преобразуване
|
PDFToHTML.submit=Преобразуване
|
||||||
|
|
||||||
|
|
||||||
@@ -985,7 +924,6 @@ PDFToCSV.prompt=Choose page to extract table
|
|||||||
PDFToCSV.submit=????????
|
PDFToCSV.submit=????????
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=Split PDF by Size or Count
|
|
||||||
split-by-size-or-count.header=Split PDF by Size or Count
|
split-by-size-or-count.header=Split PDF by Size or Count
|
||||||
split-by-size-or-count.type.label=Select Split Type
|
split-by-size-or-count.type.label=Select Split Type
|
||||||
split-by-size-or-count.type.size=By Size
|
split-by-size-or-count.type.size=By Size
|
||||||
@@ -1020,7 +958,7 @@ split-by-sections.vertical.label=Vertical Divisions
|
|||||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||||
split-by-sections.submit=Split PDF
|
split-by-sections.submit=Split PDF
|
||||||
split-by-sections.merge=Merge Into One PDF
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Licenses
|
licenses.nav=Licenses
|
||||||
@@ -1031,16 +969,3 @@ licenses.version=Version
|
|||||||
licenses.license=License
|
licenses.license=License
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -11,17 +11,14 @@ imgPrompt=Selecciona Imatge(s)
|
|||||||
genericSubmit=Envia
|
genericSubmit=Envia
|
||||||
processTimeWarning=Alerta: Aquest procés pot tardar 1 minut depenent de la mida de l'arxiu
|
processTimeWarning=Alerta: Aquest procés pot tardar 1 minut depenent de la mida de l'arxiu
|
||||||
pageOrderPrompt=Ordre de Pàgines (Llista separada per comes) :
|
pageOrderPrompt=Ordre de Pàgines (Llista separada per comes) :
|
||||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
|
||||||
goToPage=Anar
|
goToPage=Anar
|
||||||
true=Verdader
|
true=Verdader
|
||||||
false=Fals
|
false=Fals
|
||||||
unknown=Desconegut
|
unknown=Desconegut
|
||||||
save=Desa
|
save=Desa
|
||||||
saveToBrowser=Save to Browser
|
|
||||||
close=Tanca
|
close=Tanca
|
||||||
filesSelected=fitxers seleccionats
|
filesSelected=fitxers seleccionats
|
||||||
noFavourites=No s'ha afegit cap favorit
|
noFavourites=No s'ha afegit cap favorit
|
||||||
downloadComplete=Download Complete
|
|
||||||
bored=Avorrit esperant?
|
bored=Avorrit esperant?
|
||||||
alphabet=Alfabet
|
alphabet=Alfabet
|
||||||
downloadPdf=Descarregueu PDF
|
downloadPdf=Descarregueu PDF
|
||||||
@@ -54,32 +51,16 @@ notAuthenticatedMessage=User not authenticated.
|
|||||||
userNotFoundMessage=User not found.
|
userNotFoundMessage=User not found.
|
||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
usernameExistsMessage=New Username already exists.
|
||||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline Menu (Beta)
|
pipeline.header=Pipeline Menu (Alpha)
|
||||||
pipeline.uploadButton=Upload Custom
|
pipeline.uploadButton=Upload Custom
|
||||||
pipeline.configureButton=Configure
|
pipeline.configureButton=Configure
|
||||||
pipeline.defaultOption=Custom
|
pipeline.defaultOption=Custom
|
||||||
pipeline.submitButton=Submit
|
pipeline.submitButton=Submit
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@@ -125,7 +106,7 @@ settings.accountSettings=Account Settings
|
|||||||
|
|
||||||
changeCreds.title=Change Credentials
|
changeCreds.title=Change Credentials
|
||||||
changeCreds.header=Update Your Account Details
|
changeCreds.header=Update Your Account Details
|
||||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted)
|
||||||
changeCreds.newUsername=New Username
|
changeCreds.newUsername=New Username
|
||||||
changeCreds.oldPassword=Current Password
|
changeCreds.oldPassword=Current Password
|
||||||
changeCreds.newPassword=New Password
|
changeCreds.newPassword=New Password
|
||||||
@@ -160,16 +141,13 @@ adminUserSettings.header=Usuari Admin Opcions Control
|
|||||||
adminUserSettings.admin=Admin
|
adminUserSettings.admin=Admin
|
||||||
adminUserSettings.user=Usuari
|
adminUserSettings.user=Usuari
|
||||||
adminUserSettings.addUser=Afegir Usuari
|
adminUserSettings.addUser=Afegir Usuari
|
||||||
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
|
|
||||||
adminUserSettings.roles=Rols
|
adminUserSettings.roles=Rols
|
||||||
adminUserSettings.role=Rol
|
adminUserSettings.role=Rol
|
||||||
adminUserSettings.actions=Accions
|
adminUserSettings.actions=Accions
|
||||||
adminUserSettings.apiUser=Usuari amb API limitada
|
adminUserSettings.apiUser=Usuari amb API limitada
|
||||||
adminUserSettings.extraApiUser=Additional Limited API User
|
|
||||||
adminUserSettings.webOnlyUser=Usuari només WEB
|
adminUserSettings.webOnlyUser=Usuari només WEB
|
||||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||||
adminUserSettings.internalApiUser=Internal API User
|
adminUserSettings.forceChange=Force user to change username/password on login
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
|
||||||
adminUserSettings.submit=Desar Usuari
|
adminUserSettings.submit=Desar Usuari
|
||||||
|
|
||||||
#############
|
#############
|
||||||
@@ -413,15 +391,6 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations
|
|||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
|
||||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
home.BookToPDF.title=Book to PDF
|
|
||||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
# WEB PAGES #
|
# WEB PAGES #
|
||||||
@@ -429,7 +398,6 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Accedir
|
login.title=Accedir
|
||||||
login.header=Accedir
|
|
||||||
login.signin=Accedir
|
login.signin=Accedir
|
||||||
login.rememberme=Recordar
|
login.rememberme=Recordar
|
||||||
login.invalid=Nom usuari / password no vàlid
|
login.invalid=Nom usuari / password no vàlid
|
||||||
@@ -467,7 +435,6 @@ pdfToSinglePage.submit=Convert To Single Page
|
|||||||
pageExtracter.title=Extract Pages
|
pageExtracter.title=Extract Pages
|
||||||
pageExtracter.header=Extract Pages
|
pageExtracter.header=Extract Pages
|
||||||
pageExtracter.submit=Extract
|
pageExtracter.submit=Extract
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -655,18 +622,6 @@ compare.document.1=Document 1
|
|||||||
compare.document.2=Document 2
|
compare.document.2=Document 2
|
||||||
compare.submit=Comparar
|
compare.submit=Comparar
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Books and Comics to PDF
|
|
||||||
BookToPDF.header=Book to PDF
|
|
||||||
BookToPDF.credit=Uses Calibre
|
|
||||||
BookToPDF.submit=Convert
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF to Book
|
|
||||||
PDFToBook.header=PDF to Book
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Uses Calibre
|
|
||||||
PDFToBook.submit=Convert
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Sign
|
sign.title=Sign
|
||||||
@@ -770,23 +725,11 @@ merge.submit=Fusiona
|
|||||||
pdfOrganiser.title=Organitzador de pàgines
|
pdfOrganiser.title=Organitzador de pàgines
|
||||||
pdfOrganiser.header=Organitzador de pàgines PDF
|
pdfOrganiser.header=Organitzador de pàgines PDF
|
||||||
pdfOrganiser.submit=Reorganitza Pàgines
|
pdfOrganiser.submit=Reorganitza Pàgines
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
@@ -797,7 +740,6 @@ pageRemover.title=Eliminació Pàgines
|
|||||||
pageRemover.header=Eliminació Pàgines PDF
|
pageRemover.header=Eliminació Pàgines PDF
|
||||||
pageRemover.pagesToDelete=Pàgines a esborrar (Números de pàgina) :
|
pageRemover.pagesToDelete=Pàgines a esborrar (Números de pàgina) :
|
||||||
pageRemover.submit=Esborra Pàgines
|
pageRemover.submit=Esborra Pàgines
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
@@ -807,17 +749,17 @@ rotate.selectAngle=Selecciona l'angle de gir (en múltiples de 90 graus):
|
|||||||
rotate.submit=Rota
|
rotate.submit=Rota
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=Divideix PDF
|
split.title=Divideix PDF
|
||||||
split.header=Divideix PDF
|
split.header=Divideix PDF
|
||||||
split.desc.1=Els números seleccionats són el número de pàgina en què voleu fer la divisió
|
split.desc.1=Els números seleccionats són el número de pàgina en què voleu fer la divisió
|
||||||
split.desc.2=Per tant, seleccionant 1,3,7-9 dividiria un document de 10 pàgines en 6 PDFS separats amb:
|
split.desc.2=Per tant, seleccionant 1,3,7-8 dividiria un document de 10 pàgines en 6 PDFS separats amb:
|
||||||
split.desc.3=Document #1: Pàgina 1
|
split.desc.3=Document #1: Pàgina 1
|
||||||
split.desc.4=Document #2: Pàgina 2 i 3
|
split.desc.4=Document #2: Pàgina 2 i 3
|
||||||
split.desc.5=Document #3: Pàgina 4, 5, 6 i 7
|
split.desc.5=Document #3: Pàgina 4, 5 i 6
|
||||||
split.desc.6=Document #4: Pàgina 8
|
split.desc.6=Document #4: Pàgina 7
|
||||||
split.desc.7=Document #5: Pàgina 9
|
split.desc.7=Document #5: Pàgina 8
|
||||||
split.desc.8=Document #6: Pàgina 10
|
split.desc.8=Document #6: Pàgina 9 i 10
|
||||||
split.splitPages=Introdueix pàgines per dividir-les:
|
split.splitPages=Introdueix pàgines per dividir-les:
|
||||||
split.submit=Divideix
|
split.submit=Divideix
|
||||||
|
|
||||||
@@ -885,8 +827,6 @@ watermark.selectText.7=Opacitat (0% - 100%):
|
|||||||
watermark.selectText.8=Watermark Type:
|
watermark.selectText.8=Watermark Type:
|
||||||
watermark.selectText.9=Watermark Image:
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Afegir Marca d'Aigua
|
watermark.submit=Afegir Marca d'Aigua
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -938,7 +878,6 @@ pdfToPDFA.title=PDF a PDF/A
|
|||||||
pdfToPDFA.header=PDF a PDF/A
|
pdfToPDFA.header=PDF a PDF/A
|
||||||
pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
|
pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A
|
||||||
pdfToPDFA.submit=Converteix
|
pdfToPDFA.submit=Converteix
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -968,7 +907,7 @@ PDFToText.submit=Converteix
|
|||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF a HTML
|
PDFToHTML.title=PDF a HTML
|
||||||
PDFToHTML.header=PDF a HTML
|
PDFToHTML.header=PDF a HTML
|
||||||
PDFToHTML.credit=Utilitza pdftohtml per a la conversió d'Arxius.
|
PDFToHTML.credit=Utilitza LibreOffice per a la conversió d'Arxius.
|
||||||
PDFToHTML.submit=Converteix
|
PDFToHTML.submit=Converteix
|
||||||
|
|
||||||
|
|
||||||
@@ -985,7 +924,6 @@ PDFToCSV.prompt=Choose page to extract table
|
|||||||
PDFToCSV.submit=Extracte
|
PDFToCSV.submit=Extracte
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=Split PDF by Size or Count
|
|
||||||
split-by-size-or-count.header=Split PDF by Size or Count
|
split-by-size-or-count.header=Split PDF by Size or Count
|
||||||
split-by-size-or-count.type.label=Select Split Type
|
split-by-size-or-count.type.label=Select Split Type
|
||||||
split-by-size-or-count.type.size=By Size
|
split-by-size-or-count.type.size=By Size
|
||||||
@@ -1020,7 +958,7 @@ split-by-sections.vertical.label=Vertical Divisions
|
|||||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||||
split-by-sections.submit=Split PDF
|
split-by-sections.submit=Split PDF
|
||||||
split-by-sections.merge=Merge Into One PDF
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Licenses
|
licenses.nav=Licenses
|
||||||
@@ -1031,16 +969,3 @@ licenses.version=Version
|
|||||||
licenses.license=License
|
licenses.license=License
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -8,20 +8,17 @@ pdfPrompt=PDF auswählen
|
|||||||
multiPdfPrompt=PDFs auswählen(2+)
|
multiPdfPrompt=PDFs auswählen(2+)
|
||||||
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
|
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
|
||||||
imgPrompt=Wählen Sie ein Bild
|
imgPrompt=Wählen Sie ein Bild
|
||||||
genericSubmit=Absenden
|
genericSubmit=Einreichen
|
||||||
processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern
|
processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern
|
||||||
pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein):
|
pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein):
|
||||||
pageSelectionPrompt=Benutzerdefinierte Seitenauswahl (Geben Sie eine durch Kommas getrennte Liste von Seitenzahlen 1,5,6 oder Funktionen wie 2n+1 ein):
|
|
||||||
goToPage=Los
|
goToPage=Los
|
||||||
true=Wahr
|
true=Wahr
|
||||||
false=Falsch
|
false=Falsch
|
||||||
unknown=Unbekannt
|
unknown=Unbekannt
|
||||||
save=Speichern
|
save=Speichern
|
||||||
saveToBrowser=Im Browser speichern
|
|
||||||
close=Schließen
|
close=Schließen
|
||||||
filesSelected=Dateien ausgewählt
|
filesSelected=Dateien ausgewählt
|
||||||
noFavourites=Keine Favoriten hinzugefügt
|
noFavourites=Keine Favoriten hinzugefügt
|
||||||
downloadComplete=Download abgeschlossen
|
|
||||||
bored=Langeweile beim Warten?
|
bored=Langeweile beim Warten?
|
||||||
alphabet=Alphabet
|
alphabet=Alphabet
|
||||||
downloadPdf=PDF herunterladen
|
downloadPdf=PDF herunterladen
|
||||||
@@ -54,44 +51,28 @@ notAuthenticatedMessage=Benutzer nicht authentifiziert.
|
|||||||
userNotFoundMessage=Benutzer nicht gefunden.
|
userNotFoundMessage=Benutzer nicht gefunden.
|
||||||
incorrectPasswordMessage=Das Passwort ist falsch.
|
incorrectPasswordMessage=Das Passwort ist falsch.
|
||||||
usernameExistsMessage=Neuer Benutzername existiert bereits.
|
usernameExistsMessage=Neuer Benutzername existiert bereits.
|
||||||
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten.
|
|
||||||
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
|
|
||||||
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline-Menü (Beta)
|
pipeline.header=Pipeline-Menü (Alpha)
|
||||||
pipeline.uploadButton=Benutzerdefinierter Upload
|
pipeline.uploadButton=Benutzerdefinierter Upload
|
||||||
pipeline.configureButton=Konfigurieren
|
pipeline.configureButton=Konfigurieren
|
||||||
pipeline.defaultOption=Benutzerdefiniert
|
pipeline.defaultOption=Benutzerdefiniert
|
||||||
pipeline.submitButton=Speichern
|
pipeline.submitButton=Speichern
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
######################
|
######################
|
||||||
pipelineOptions.header=Pipeline-Konfiguration
|
pipelineOptions.header=Pipeline-Konfiguration
|
||||||
pipelineOptions.pipelineNameLabel=Pipeline-Name
|
pipelineOptions.pipelineNameLabel=Pipeline-Name
|
||||||
pipelineOptions.saveSettings=Operations-Einstellungen speichern
|
pipelineOptions.saveSettings=Save Operation Settings
|
||||||
pipelineOptions.pipelineNamePrompt=Geben Sie hier den Namen der Pipeline ein
|
pipelineOptions.pipelineNamePrompt=Geben Sie hier den Namen der Pipeline ein
|
||||||
pipelineOptions.selectOperation=Vorgang auswählen
|
pipelineOptions.selectOperation=Vorgang auswählen
|
||||||
pipelineOptions.addOperationButton=Vorgang hinzufügen
|
pipelineOptions.addOperationButton=Vorgang hinzufügen
|
||||||
pipelineOptions.pipelineHeader=Pipeline:
|
pipelineOptions.pipelineHeader=Pipeline:
|
||||||
pipelineOptions.saveButton=Herunterladen
|
pipelineOptions.saveButton=Downloaden
|
||||||
pipelineOptions.validateButton=Validieren
|
pipelineOptions.validateButton=Validieren
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +84,7 @@ pipelineOptions.validateButton=Validieren
|
|||||||
navbar.convert=Konvertieren
|
navbar.convert=Konvertieren
|
||||||
navbar.security=Sicherheit
|
navbar.security=Sicherheit
|
||||||
navbar.other=Anderes
|
navbar.other=Anderes
|
||||||
navbar.darkmode=Dunkler Modus
|
navbar.darkmode=Dark Mode
|
||||||
navbar.pageOps=Seitenoperationen
|
navbar.pageOps=Seitenoperationen
|
||||||
navbar.settings=Einstellungen
|
navbar.settings=Einstellungen
|
||||||
|
|
||||||
@@ -125,7 +106,7 @@ settings.accountSettings=Kontoeinstellungen
|
|||||||
|
|
||||||
changeCreds.title=Anmeldeinformationen ändern
|
changeCreds.title=Anmeldeinformationen ändern
|
||||||
changeCreds.header=Aktualisieren Sie Ihre Kontodaten
|
changeCreds.header=Aktualisieren Sie Ihre Kontodaten
|
||||||
changeCreds.changePassword=Sie verwenden die Standard-Zugangsdaten. Bitte geben Sie ein neues Passwort ein.
|
changeCreds.changeUserAndPassword=Sie verwenden Standard-Anmeldeinformationen. Bitte geben Sie ein neues Passwort (und ggf. einen Benutzernamen) ein.
|
||||||
changeCreds.newUsername=Neuer Benutzername
|
changeCreds.newUsername=Neuer Benutzername
|
||||||
changeCreds.oldPassword=Aktuelles Passwort
|
changeCreds.oldPassword=Aktuelles Passwort
|
||||||
changeCreds.newPassword=Neues Passwort
|
changeCreds.newPassword=Neues Passwort
|
||||||
@@ -146,7 +127,7 @@ account.newPassword=Neues Passwort
|
|||||||
account.changePassword=Passwort ändern
|
account.changePassword=Passwort ändern
|
||||||
account.confirmNewPassword=Neues Passwort bestätigen
|
account.confirmNewPassword=Neues Passwort bestätigen
|
||||||
account.signOut=Abmelden
|
account.signOut=Abmelden
|
||||||
account.yourApiKey=Dein API-Schlüssel
|
account.yourApiKey=Dein API Schlüssel
|
||||||
account.syncTitle=Browsereinstellungen mit Konto synchronisieren
|
account.syncTitle=Browsereinstellungen mit Konto synchronisieren
|
||||||
account.settingsCompare=Einstellungen vergleichen:
|
account.settingsCompare=Einstellungen vergleichen:
|
||||||
account.property=Eigenschaft
|
account.property=Eigenschaft
|
||||||
@@ -157,18 +138,15 @@ account.syncToAccount=Synchronisiere Konto <- Browser
|
|||||||
|
|
||||||
adminUserSettings.title=Benutzerkontrolle
|
adminUserSettings.title=Benutzerkontrolle
|
||||||
adminUserSettings.header=Administrator-Benutzerkontrolle
|
adminUserSettings.header=Administrator-Benutzerkontrolle
|
||||||
adminUserSettings.admin=Administrator
|
adminUserSettings.admin=Admin
|
||||||
adminUserSettings.user=Benutzer
|
adminUserSettings.user=Benutzer
|
||||||
adminUserSettings.addUser=Neuen Benutzer hinzufügen
|
adminUserSettings.addUser=Neuen Benutzer hinzufügen
|
||||||
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen oder Sonderzeichen.
|
|
||||||
adminUserSettings.roles=Rollen
|
adminUserSettings.roles=Rollen
|
||||||
adminUserSettings.role=Rolle
|
adminUserSettings.role=Rolle
|
||||||
adminUserSettings.actions=Aktion
|
adminUserSettings.actions=Aktion
|
||||||
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
||||||
adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer
|
|
||||||
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
||||||
adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellungen)
|
adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellungen)
|
||||||
adminUserSettings.internalApiUser=Interner API-Benutzer
|
|
||||||
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
|
||||||
adminUserSettings.submit=Benutzer speichern
|
adminUserSettings.submit=Benutzer speichern
|
||||||
|
|
||||||
@@ -181,7 +159,7 @@ home.searchBar=Suche nach Funktionen...
|
|||||||
|
|
||||||
home.viewPdf.title=PDF anzeigen
|
home.viewPdf.title=PDF anzeigen
|
||||||
home.viewPdf.desc=Anzeigen, Kommentieren, Text oder Bilder hinzufügen
|
home.viewPdf.desc=Anzeigen, Kommentieren, Text oder Bilder hinzufügen
|
||||||
viewPdf.tags=anzeigen,lesen,kommentieren,text,bild
|
viewPdf.tags=view,read,annotate,text,image
|
||||||
|
|
||||||
home.multiTool.title=PDF-Multitool
|
home.multiTool.title=PDF-Multitool
|
||||||
home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
|
home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen
|
||||||
@@ -189,88 +167,88 @@ multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side
|
|||||||
|
|
||||||
home.merge.title=Zusammenführen
|
home.merge.title=Zusammenführen
|
||||||
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
|
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
|
||||||
merge.tags=zusammenführen,seitenvorgänge,back end,serverseite
|
merge.tags=merge,Page operations,Back end,server side
|
||||||
|
|
||||||
home.split.title=Aufteilen
|
home.split.title=Aufteilen
|
||||||
home.split.desc=PDFs in mehrere Dokumente aufteilen.
|
home.split.desc=PDFs in mehrere Dokumente aufteilen.
|
||||||
split.tags=seitenoperationen,teilen,mehrseitig,ausschneiden,serverseitig
|
split.tags=Page operations,divide,Multi Page,cut,server side
|
||||||
|
|
||||||
home.rotate.title=Drehen
|
home.rotate.title=Drehen
|
||||||
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
|
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
|
||||||
rotate.tags=serverseitig
|
rotate.tags=server side
|
||||||
|
|
||||||
|
|
||||||
home.imageToPdf.title=Bild zu PDF
|
home.imageToPdf.title=Bild zu PDF
|
||||||
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
|
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
|
||||||
imageToPdf.tags=konvertierung,img,jpg,bild,foto
|
imageToPdf.tags=conversion,img,jpg,picture,photo
|
||||||
|
|
||||||
home.pdfToImage.title=PDF zu Bild
|
home.pdfToImage.title=PDF zu Bild
|
||||||
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
|
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
|
||||||
pdfToImage.tags=konvertierung,img,jpg,bild,foto
|
pdfToImage.tags=conversion,img,jpg,picture,photo
|
||||||
|
|
||||||
home.pdfOrganiser.title=Organisieren
|
home.pdfOrganiser.title=Organisieren
|
||||||
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
|
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
|
||||||
pdfOrganiser.tags=duplex,gerade,ungerade,sortieren,verschieben
|
pdfOrganiser.tags=duplex,even,odd,sort,move
|
||||||
|
|
||||||
|
|
||||||
home.addImage.title=Bild einfügen
|
home.addImage.title=Bild einfügen
|
||||||
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit).
|
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit).
|
||||||
addImage.tags=img,jpg,bild,foto
|
addImage.tags=img,jpg,picture,photo
|
||||||
|
|
||||||
home.watermark.title=Wasserzeichen hinzufügen
|
home.watermark.title=Wasserzeichen hinzufügen
|
||||||
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu.
|
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu.
|
||||||
watermark.tags=text,wiederholend,beschriftung,besitzen,urheberrecht,marke,img,jpg,bild,foto
|
watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo
|
||||||
|
|
||||||
home.permissions.title=Berechtigungen ändern
|
home.permissions.title=Berechtigungen ändern
|
||||||
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
|
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
|
||||||
permissions.tags=lesen,schreiben,bearbeiten,drucken
|
permissions.tags=read,write,edit,print
|
||||||
|
|
||||||
|
|
||||||
home.removePages.title=Entfernen
|
home.removePages.title=Entfernen
|
||||||
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
|
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
|
||||||
removePages.tags=seiten entfernen,seiten löschen
|
removePages.tags=Remove pages,delete pages
|
||||||
|
|
||||||
home.addPassword.title=Passwort hinzufügen
|
home.addPassword.title=Passwort hinzufügen
|
||||||
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
|
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
|
||||||
addPassword.tags=sicher,sicherheit
|
addPassword.tags=secure,security
|
||||||
|
|
||||||
home.removePassword.title=Passwort entfernen
|
home.removePassword.title=Passwort entfernen
|
||||||
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
|
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
|
||||||
removePassword.tags=sichern,entschlüsseln,sicherheit,passwort aufheben,passwort löschen
|
removePassword.tags=secure,Decrypt,security,unpassword,delete password
|
||||||
|
|
||||||
home.compressPdfs.title=Komprimieren
|
home.compressPdfs.title=Komprimieren
|
||||||
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
|
||||||
compressPdfs.tags=komprimieren,verkleinern,minimieren
|
compressPdfs.tags=squish,small,tiny
|
||||||
|
|
||||||
|
|
||||||
home.changeMetadata.title=Metadaten ändern
|
home.changeMetadata.title=Metadaten ändern
|
||||||
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
||||||
changeMetadata.tags==titel,autor,datum,erstellung,uhrzeit,herausgeber,produzent,statistiken
|
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Datei in PDF konvertieren
|
home.fileToPDF.title=Datei in PDF konvertieren
|
||||||
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
|
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
|
||||||
fileToPDF.tags=transformation,format,dokument,bild,folie,text,konvertierung,büro,dokumente,word,excel,powerpoint
|
fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint
|
||||||
|
|
||||||
home.ocr.title=Führe OCR/Cleanup-Scans aus
|
home.ocr.title=Führe OCR/Cleanup-Scans aus
|
||||||
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
|
home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu.
|
||||||
ocr.tags=erkennung,text,bild,scannen,lesen,identifizieren,erkennung,bearbeitbar
|
ocr.tags=recognition,text,image,scan,read,identify,detection,editable
|
||||||
|
|
||||||
|
|
||||||
home.extractImages.title=Bilder extrahieren
|
home.extractImages.title=Bilder extrahieren
|
||||||
home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv
|
home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv
|
||||||
extractImages.tags=bild,foto,speichern,archivieren,zippen,erfassen,greifen
|
extractImages.tags=picture,photo,save,archive,zip,capture,grab
|
||||||
|
|
||||||
home.pdfToPDFA.title=PDF zu PDF/A konvertieren
|
home.pdfToPDFA.title=PDF zu PDF/A konvertieren
|
||||||
home.pdfToPDFA.desc=PDF zu PDF/A für Langzeitarchivierung konvertieren
|
home.pdfToPDFA.desc=PDF zu PDF/A für Langzeitarchivierung konvertieren
|
||||||
pdfToPDFA.tags=archiv,langfristig,standard,konvertierung,speicherung,aufbewahrung
|
pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation
|
||||||
|
|
||||||
home.PDFToWord.title=PDF zu Word
|
home.PDFToWord.title=PDF zu Word
|
||||||
home.PDFToWord.desc=PDF in Word-Formate konvertieren (DOC, DOCX und ODT)
|
home.PDFToWord.desc=PDF in Word-Formate konvertieren (DOC, DOCX und ODT)
|
||||||
PDFToWord.tags=doc,docx,odt,word,transformation,format,konvertierung,office,microsoft,docfile
|
PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile
|
||||||
|
|
||||||
home.PDFToPresentation.title=PDF zu Präsentation
|
home.PDFToPresentation.title=PDF zu Präsentation
|
||||||
home.PDFToPresentation.desc=PDF in Präsentationsformate konvertieren (PPT, PPTX und ODP)
|
home.PDFToPresentation.desc=PDF in Präsentationsformate konvertieren (PPT, PPTX und ODP)
|
||||||
PDFToPresentation.tags=folien,show,büro,microsoft
|
PDFToPresentation.tags=slides,show,office,microsoft
|
||||||
|
|
||||||
home.PDFToText.title=PDF in Text/RTF
|
home.PDFToText.title=PDF in Text/RTF
|
||||||
home.PDFToText.desc=PDF in Text- oder RTF-Format konvertieren
|
home.PDFToText.desc=PDF in Text- oder RTF-Format konvertieren
|
||||||
@@ -278,88 +256,88 @@ PDFToText.tags=richformat,richtextformat,rich text format
|
|||||||
|
|
||||||
home.PDFToHTML.title=PDF in HTML
|
home.PDFToHTML.title=PDF in HTML
|
||||||
home.PDFToHTML.desc=PDF in HTML-Format konvertieren
|
home.PDFToHTML.desc=PDF in HTML-Format konvertieren
|
||||||
PDFToHTML.tags=webinhalte,browserfreundlich
|
PDFToHTML.tags=web content,browser friendly
|
||||||
|
|
||||||
|
|
||||||
home.PDFToXML.title=PDF in XML
|
home.PDFToXML.title=PDF in XML
|
||||||
home.PDFToXML.desc=PDF in XML-Format konvertieren
|
home.PDFToXML.desc=PDF in XML-Format konvertieren
|
||||||
PDFToXML.tags=datenextraktion,strukturierter inhalt,interop,transformation,konvertierung
|
PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert
|
||||||
|
|
||||||
home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen
|
home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen
|
||||||
home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF
|
home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF
|
||||||
ScannerImageSplit.tags=separat,automatische erkennung,scans,mehrere fotos,organisieren
|
ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize
|
||||||
|
|
||||||
home.sign.title=Signieren
|
home.sign.title=Signieren
|
||||||
home.sign.desc=Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu
|
home.sign.desc=Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu
|
||||||
sign.tags=autorisieren,initialen,gezeichnete signatur,textzeichen,bildsignatur
|
sign.tags=authorize,initials,drawn-signature,text-sign,image-signature
|
||||||
|
|
||||||
home.flatten.title=Abflachen
|
home.flatten.title=Abflachen
|
||||||
home.flatten.desc=Alle interaktiven Elemente und Formulare aus einem PDF entfernen
|
home.flatten.desc=Alle interaktiven Elemente und Formulare aus einem PDF entfernen
|
||||||
flatten.tags=statisch,deaktivieren,nicht interaktiv,optimieren
|
flatten.tags=static,deactivate,non-interactive,streamline
|
||||||
|
|
||||||
home.repair.title=Reparatur
|
home.repair.title=Reparatur
|
||||||
home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
|
home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren
|
||||||
repair.tags=reparieren,wiederherstellen,korrigieren,wiederherstellen
|
repair.tags=fix,restore,correction,recover
|
||||||
|
|
||||||
home.removeBlanks.title=Leere Seiten entfernen
|
home.removeBlanks.title=Leere Seiten entfernen
|
||||||
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
|
home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument
|
||||||
removeBlanks.tags=aufräumen,rationalisieren,nicht inhaltsreich,organisieren
|
removeBlanks.tags=cleanup,streamline,non-content,organize
|
||||||
|
|
||||||
home.removeAnnotations.title=Anmerkungen entfernen
|
home.removeAnnotations.title=Anmerkungen entfernen
|
||||||
home.removeAnnotations.desc=Entfernt alle Kommentare/Anmerkungen aus einem PDF
|
home.removeAnnotations.desc=Entfernt alle Kommentare/Anmerkungen aus einem PDF
|
||||||
removeAnnotations.tags=kommentare,hervorheben,notizen,markieren,entfernen
|
removeAnnotations.tags=comments,highlight,notes,markup,remove
|
||||||
|
|
||||||
home.compare.title=Vergleichen
|
home.compare.title=Vergleichen
|
||||||
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
|
home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an
|
||||||
compare.tags=differenzieren,kontrastieren,verändern,analysieren
|
compare.tags=differentiate,contrast,changes,analysis
|
||||||
|
|
||||||
home.certSign.title=Mit Zertifikat signieren
|
home.certSign.title=Mit Zertifikat signieren
|
||||||
home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren
|
home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren
|
||||||
certSign.tags=authentifizieren,pem,p12,offiziell,verschlüsseln
|
certSign.tags=authenticate,PEM,P12,official,encrypt
|
||||||
|
|
||||||
home.pageLayout.title=Mehrseitiges Layout
|
home.pageLayout.title=Mehrseitiges Layout
|
||||||
home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen
|
home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen
|
||||||
pageLayout.tags=zusammenführen,zusammensetzen,einzelansicht,organisieren
|
pageLayout.tags=merge,composite,single-view,organize
|
||||||
|
|
||||||
home.scalePages.title=Seitengröße/Skalierung anpassen
|
home.scalePages.title=Seitengröße/Skalierung anpassen
|
||||||
home.scalePages.desc=Größe/Skalierung der Seite und/oder des Inhalts ändern
|
home.scalePages.desc=Größe/Skalierung der Seite und/oder des Inhalts ändern
|
||||||
scalePages.tags=größe ändern,ändern,dimensionieren,anpassen
|
scalePages.tags=resize,modify,dimension,adapt
|
||||||
|
|
||||||
home.pipeline.title=Pipeline (Fortgeschritten)
|
home.pipeline.title=Pipeline (Fortgeschritten)
|
||||||
home.pipeline.desc=Mehrere Aktionen auf ein PDF anwenden, definiert durch ein Pipeline Skript
|
home.pipeline.desc=Mehrere Aktionen auf ein PDF anwenden, definiert durch ein Pipeline Skript
|
||||||
pipeline.tags=automatisieren,sequenzieren,skriptgesteuert,batch prozess
|
pipeline.tags=automate,sequence,scripted,batch-process
|
||||||
|
|
||||||
home.add-page-numbers.title=Seitenzahlen hinzufügen
|
home.add-page-numbers.title=Seitenzahlen hinzufügen
|
||||||
home.add-page-numbers.desc=Hinzufügen von Seitenzahlen an einer bestimmten Stelle
|
home.add-page-numbers.desc=Hinzufügen von Seitenzahlen an einer bestimmten Stelle
|
||||||
add-page-numbers.tags=paginieren,beschriften,organisieren,indizieren
|
add-page-numbers.tags=paginate,label,organize,index
|
||||||
|
|
||||||
home.auto-rename.title=PDF automatisch umbenennen
|
home.auto-rename.title=PDF automatisch umbenennen
|
||||||
home.auto-rename.desc=PDF-Datei anhand von erkannten Kopfzeilen umbenennen
|
home.auto-rename.desc=PDF-Datei anhand von erkannten Kopfzeilen umbenennen
|
||||||
auto-rename.tags=automatisch erkennen,header basiert,organisieren,neu kennzeichnen
|
auto-rename.tags=auto-detect,header-based,organize,relabel
|
||||||
|
|
||||||
home.adjust-contrast.title=Farben/Kontrast anpassen
|
home.adjust-contrast.title=Farben/Kontrast anpassen
|
||||||
home.adjust-contrast.desc=Kontrast, Sättigung und Helligkeit einer PDF anpassen
|
home.adjust-contrast.desc=Kontrast, Sättigung und Helligkeit einer PDF anpassen
|
||||||
adjust-contrast.tags=farbkorrektur,abstimmung,änderung,verbesserung
|
adjust-contrast.tags=color-correction,tune,modify,enhance
|
||||||
|
|
||||||
home.crop.title=PDF zuschneiden
|
home.crop.title=PDF zuschneiden
|
||||||
home.crop.desc=PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!)
|
home.crop.desc=PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!)
|
||||||
crop.tags=trimmen,verkleinern,bearbeiten,formen
|
crop.tags=trim,shrink,edit,shape
|
||||||
|
|
||||||
home.autoSplitPDF.title=PDF automatisch teilen
|
home.autoSplitPDF.title=PDF automatisch teilen
|
||||||
home.autoSplitPDF.desc=Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen
|
home.autoSplitPDF.desc=Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen
|
||||||
autoSplitPDF.tags=qr basiert,trennen,segment scannen,organisieren
|
autoSplitPDF.tags=QR-based,separate,scan-segment,organize
|
||||||
|
|
||||||
home.sanitizePdf.title=PDF Bereinigen
|
home.sanitizePdf.title=PDF Bereinigen
|
||||||
home.sanitizePdf.desc=Entfernen von Skripten und anderen Elementen aus PDF-Dateien
|
home.sanitizePdf.desc=Entfernen von Skripten und anderen Elementen aus PDF-Dateien
|
||||||
sanitizePdf.tags=sauber,sicher,sicher,bedrohungen entfernen
|
sanitizePdf.tags=clean,secure,safe,remove-threats
|
||||||
|
|
||||||
home.URLToPDF.title=URL/Website zu PDF
|
home.URLToPDF.title=URL/Website zu PDF
|
||||||
home.URLToPDF.desc=Konvertiert jede http(s)URL zu PDF
|
home.URLToPDF.desc=Konvertiert jede http(s)URL zu PDF
|
||||||
URLToPDF.tags=web capture,seite speichern,web to doc,archiv
|
URLToPDF.tags=web-capture,save-page,web-to-doc,archive
|
||||||
|
|
||||||
home.HTMLToPDF.title=HTML zu PDF
|
home.HTMLToPDF.title=HTML zu PDF
|
||||||
home.HTMLToPDF.desc=Konvertiert jede HTML-Datei oder Zip-Archiv zu PDF
|
home.HTMLToPDF.desc=Konvertiert jede HTML-Datei oder Zip-Archiv zu PDF
|
||||||
HTMLToPDF.tags=markup,webinhalt,transformation,konvertierung
|
HTMLToPDF.tags=markup,web-content,transformation,convert
|
||||||
|
|
||||||
|
|
||||||
home.MarkdownToPDF.title=Markdown zu PDF
|
home.MarkdownToPDF.title=Markdown zu PDF
|
||||||
@@ -384,7 +362,7 @@ PdfToSinglePage.tags=einzelseite
|
|||||||
|
|
||||||
home.showJS.title=Javascript anzeigen
|
home.showJS.title=Javascript anzeigen
|
||||||
home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen
|
home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen
|
||||||
showJS.tags=js
|
showJS.tags=JS
|
||||||
|
|
||||||
home.autoRedact.title=Automatisch zensieren/schwärzen
|
home.autoRedact.title=Automatisch zensieren/schwärzen
|
||||||
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
|
home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text
|
||||||
@@ -410,16 +388,7 @@ split-by-sections.tags=abschnitte,teilen,bearbeiten
|
|||||||
|
|
||||||
home.AddStampRequest.title=Stempel zu PDF hinzufügen
|
home.AddStampRequest.title=Stempel zu PDF hinzufügen
|
||||||
home.AddStampRequest.desc=Fügen Sie an festgelegten Stellen Text oder Bildstempel hinzu
|
home.AddStampRequest.desc=Fügen Sie an festgelegten Stellen Text oder Bildstempel hinzu
|
||||||
AddStampRequest.tags=stempeln,bild hinzufügen,bild zentrieren,wasserzeichen,pdf,einbetten,anpassen
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF zum Buch
|
|
||||||
home.PDFToBook.desc=Konvertiert PDF mit Calibre in Buch-/Comic-Formate
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
home.BookToPDF.title=Buch als PDF
|
|
||||||
home.BookToPDF.desc=Konvertiert Buch-/Comic-Formate mithilfe von Calibre in PDF
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
@@ -429,12 +398,11 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Anmelden
|
login.title=Anmelden
|
||||||
login.header=Anmelden
|
|
||||||
login.signin=Anmelden
|
login.signin=Anmelden
|
||||||
login.rememberme=Angemeldet bleiben
|
login.rememberme=Angemeldet bleiben
|
||||||
login.invalid=Benutzername oder Passwort ungültig.
|
login.invalid=Ungültiger Benutzername oder Passwort.
|
||||||
login.locked=Ihr Konto wurde gesperrt.
|
login.locked=Ihr Konto wurde gesperrt.
|
||||||
login.signinTitle=Bitte melden Sie sich an.
|
login.signinTitle=Bitte melden Sie sich an
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -467,7 +435,6 @@ pdfToSinglePage.submit=Zusammenfassen
|
|||||||
pageExtracter.title=Seiten extrahieren
|
pageExtracter.title=Seiten extrahieren
|
||||||
pageExtracter.header=Seiten extrahieren
|
pageExtracter.header=Seiten extrahieren
|
||||||
pageExtracter.submit=Extrahieren
|
pageExtracter.submit=Extrahieren
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -522,7 +489,7 @@ AddStampRequest.stampText=Stempeltext
|
|||||||
AddStampRequest.stampImage=Stampelbild
|
AddStampRequest.stampImage=Stampelbild
|
||||||
AddStampRequest.alphabet=Alphabet
|
AddStampRequest.alphabet=Alphabet
|
||||||
AddStampRequest.fontSize=Schriftart/Bildgröße
|
AddStampRequest.fontSize=Schriftart/Bildgröße
|
||||||
AddStampRequest.rotation=Drehung
|
AddStampRequest.rotation=Rotation
|
||||||
AddStampRequest.opacity=Deckkraft
|
AddStampRequest.opacity=Deckkraft
|
||||||
AddStampRequest.position=Position
|
AddStampRequest.position=Position
|
||||||
AddStampRequest.overrideX=X-Koordinate überschreiben
|
AddStampRequest.overrideX=X-Koordinate überschreiben
|
||||||
@@ -655,18 +622,6 @@ compare.document.1=Dokument 1
|
|||||||
compare.document.2=Dokument 2
|
compare.document.2=Dokument 2
|
||||||
compare.submit=Vergleichen
|
compare.submit=Vergleichen
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Bücher und Comics zu PDF
|
|
||||||
BookToPDF.header=Buch zu PDF
|
|
||||||
BookToPDF.credit=Verwendet Calibre
|
|
||||||
BookToPDF.submit=Konvertieren
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF zu Buch
|
|
||||||
PDFToBook.header=PDF zu Buch
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Verwendet Calibre
|
|
||||||
PDFToBook.submit=Konvertieren
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Signieren
|
sign.title=Signieren
|
||||||
@@ -770,23 +725,11 @@ merge.submit=Zusammenführen
|
|||||||
pdfOrganiser.title=Seiten anordnen
|
pdfOrganiser.title=Seiten anordnen
|
||||||
pdfOrganiser.header=PDF Seitenorganisation
|
pdfOrganiser.header=PDF Seitenorganisation
|
||||||
pdfOrganiser.submit=Seiten anordnen
|
pdfOrganiser.submit=Seiten anordnen
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF-Multitool
|
multiTool.title=PDF-Multitool
|
||||||
multiTool.header=PDF-Multitool
|
multiTool.header=PDF-Multitool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF anzeigen
|
viewPdf.title=PDF anzeigen
|
||||||
@@ -797,27 +740,26 @@ pageRemover.title=Seiten entfernen
|
|||||||
pageRemover.header=PDF Seiten entfernen
|
pageRemover.header=PDF Seiten entfernen
|
||||||
pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an):
|
pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an):
|
||||||
pageRemover.submit=Seiten löschen
|
pageRemover.submit=Seiten löschen
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
rotate.title=PDF drehen
|
rotate.title=PDF drehen
|
||||||
rotate.header=PDF drehen
|
rotate.header=PDF drehen
|
||||||
rotate.selectAngle=Wählen Sie den Winkel (in Vielfachen von 90 Grad):
|
rotate.selectAngle=Wählen Sie den Winkel (in Vielfachen von 90 Grad):
|
||||||
rotate.submit=Herunterladen
|
rotate.submit=Drehen
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=PDF aufteilen
|
split.title=PDF aufteilen
|
||||||
split.header=PDF aufteilen
|
split.header=PDF aufteilen
|
||||||
split.desc.1=Die Nummern, die Sie auswählen, sind die Seitenzahlen, an denen Sie aufteilen möchten.
|
split.desc.1=Die Nummern, die Sie auswählen, sind die Seitenzahlen, an denen Sie aufteilen möchten.
|
||||||
split.desc.2=So würde die Auswahl von 1,3,7-9 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit:
|
split.desc.2=So würde die Auswahl von 1,3,7-8 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit:
|
||||||
split.desc.3=Dokument #1: Seite 1
|
split.desc.3=Dokument #1: Seite 1
|
||||||
split.desc.4=Dokument #2: Seite 2 und 3
|
split.desc.4=Dokument #2: Seite 2 und 3
|
||||||
split.desc.5=Dokument #3: Seite 4, 5, 6 und 7
|
split.desc.5=Dokument #3: Seite 4, 5 und 6
|
||||||
split.desc.6=Dokument #4: Seite 8
|
split.desc.6=Dokument #4: Seite 7
|
||||||
split.desc.7=Dokument #5: Seite 9
|
split.desc.7=Dokument #5: Seite 8
|
||||||
split.desc.8=Dokument #6: Seite 10
|
split.desc.8=Dokument #6: Seite 9 und 10
|
||||||
split.splitPages=Geben Sie die Seiten an, an denen aufgeteilt werden soll:
|
split.splitPages=Geben Sie die Seiten an, an denen aufgeteilt werden soll:
|
||||||
split.submit=Aufteilen
|
split.submit=Aufteilen
|
||||||
|
|
||||||
@@ -885,8 +827,6 @@ watermark.selectText.7=Deckkraft (0% - 100 %):
|
|||||||
watermark.selectText.8=Wasserzeichen Typ:
|
watermark.selectText.8=Wasserzeichen Typ:
|
||||||
watermark.selectText.9=Wasserzeichen-Bild:
|
watermark.selectText.9=Wasserzeichen-Bild:
|
||||||
watermark.submit=Wasserzeichen hinzufügen
|
watermark.submit=Wasserzeichen hinzufügen
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -938,7 +878,6 @@ pdfToPDFA.title=PDF zu PDF/A
|
|||||||
pdfToPDFA.header=PDF zu PDF/A
|
pdfToPDFA.header=PDF zu PDF/A
|
||||||
pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
|
pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung
|
||||||
pdfToPDFA.submit=Konvertieren
|
pdfToPDFA.submit=Konvertieren
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -966,15 +905,15 @@ PDFToText.submit=Konvertieren
|
|||||||
|
|
||||||
|
|
||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF zu HTML
|
PDFToHTML.title=PDF in HTML
|
||||||
PDFToHTML.header=PDF zu HTML
|
PDFToHTML.header=PDF in HTML
|
||||||
PDFToHTML.credit=Dieser Dienst verwendet pdftohtml für die Dateikonvertierung.
|
PDFToHTML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||||
PDFToHTML.submit=Konvertieren
|
PDFToHTML.submit=Konvertieren
|
||||||
|
|
||||||
|
|
||||||
#PDFToXML
|
#PDFToXML
|
||||||
PDFToXML.title=PDF zu XML
|
PDFToXML.title=PDF in XML
|
||||||
PDFToXML.header=PDF zu XML
|
PDFToXML.header=PDF in XML
|
||||||
PDFToXML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
PDFToXML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung.
|
||||||
PDFToXML.submit=Konvertieren
|
PDFToXML.submit=Konvertieren
|
||||||
|
|
||||||
@@ -985,7 +924,6 @@ PDFToCSV.prompt=Seite mit der zu extrahierenden Tabelle wählen
|
|||||||
PDFToCSV.submit=Extrahieren
|
PDFToCSV.submit=Extrahieren
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=PDF nach Größe oder Anzahl teilen
|
|
||||||
split-by-size-or-count.header=PDF nach Größe oder Anzahl teilen
|
split-by-size-or-count.header=PDF nach Größe oder Anzahl teilen
|
||||||
split-by-size-or-count.type.label=Teil-Modus wählen
|
split-by-size-or-count.type.label=Teil-Modus wählen
|
||||||
split-by-size-or-count.type.size=Nach Größe
|
split-by-size-or-count.type.size=Nach Größe
|
||||||
@@ -1020,7 +958,7 @@ split-by-sections.vertical.label=Vertikale Teiler
|
|||||||
split-by-sections.horizontal.placeholder=Anzahl horizontaler Teiler eingeben
|
split-by-sections.horizontal.placeholder=Anzahl horizontaler Teiler eingeben
|
||||||
split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben
|
split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben
|
||||||
split-by-sections.submit=PDF teilen
|
split-by-sections.submit=PDF teilen
|
||||||
split-by-sections.merge=In eine PDF zusammenfügen
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Lizenzen
|
licenses.nav=Lizenzen
|
||||||
@@ -1031,16 +969,3 @@ licenses.version=Version
|
|||||||
licenses.license=Lizenz
|
licenses.license=Lizenz
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -17,11 +17,9 @@ true=True
|
|||||||
false=False
|
false=False
|
||||||
unknown=Unknown
|
unknown=Unknown
|
||||||
save=Save
|
save=Save
|
||||||
saveToBrowser=Save to Browser
|
|
||||||
close=Close
|
close=Close
|
||||||
filesSelected=files selected
|
filesSelected=files selected
|
||||||
noFavourites=No favourites added
|
noFavourites=No favourites added
|
||||||
downloadComplete=Download Complete
|
|
||||||
bored=Bored Waiting?
|
bored=Bored Waiting?
|
||||||
alphabet=Alphabet
|
alphabet=Alphabet
|
||||||
downloadPdf=Download PDF
|
downloadPdf=Download PDF
|
||||||
@@ -54,32 +52,16 @@ notAuthenticatedMessage=User not authenticated.
|
|||||||
userNotFoundMessage=User not found.
|
userNotFoundMessage=User not found.
|
||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
usernameExistsMessage=New Username already exists.
|
||||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline Menu (Beta)
|
pipeline.header=Pipeline Menu (Alpha)
|
||||||
pipeline.uploadButton=Upload Custom
|
pipeline.uploadButton=Upload Custom
|
||||||
pipeline.configureButton=Configure
|
pipeline.configureButton=Configure
|
||||||
pipeline.defaultOption=Custom
|
pipeline.defaultOption=Custom
|
||||||
pipeline.submitButton=Submit
|
pipeline.submitButton=Submit
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@@ -125,7 +107,7 @@ settings.accountSettings=Account Settings
|
|||||||
|
|
||||||
changeCreds.title=Change Credentials
|
changeCreds.title=Change Credentials
|
||||||
changeCreds.header=Update Your Account Details
|
changeCreds.header=Update Your Account Details
|
||||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted)
|
||||||
changeCreds.newUsername=New Username
|
changeCreds.newUsername=New Username
|
||||||
changeCreds.oldPassword=Current Password
|
changeCreds.oldPassword=Current Password
|
||||||
changeCreds.newPassword=New Password
|
changeCreds.newPassword=New Password
|
||||||
@@ -160,16 +142,13 @@ adminUserSettings.header=Admin User Control Settings
|
|||||||
adminUserSettings.admin=Admin
|
adminUserSettings.admin=Admin
|
||||||
adminUserSettings.user=User
|
adminUserSettings.user=User
|
||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
|
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
adminUserSettings.actions=Actions
|
adminUserSettings.actions=Actions
|
||||||
adminUserSettings.apiUser=Limited API User
|
adminUserSettings.apiUser=Limited API User
|
||||||
adminUserSettings.extraApiUser=Additional Limited API User
|
|
||||||
adminUserSettings.webOnlyUser=Web Only User
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||||
adminUserSettings.internalApiUser=Internal API User
|
adminUserSettings.forceChange = Force user to change username/password on login
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
@@ -413,15 +392,6 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations
|
|||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
|
||||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
|
|
||||||
|
|
||||||
home.BookToPDF.title=Book to PDF
|
|
||||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
# WEB PAGES #
|
# WEB PAGES #
|
||||||
@@ -429,7 +399,6 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,doc
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Sign in
|
login.title=Sign in
|
||||||
login.header=Sign in
|
|
||||||
login.signin=Sign in
|
login.signin=Sign in
|
||||||
login.rememberme=Remember me
|
login.rememberme=Remember me
|
||||||
login.invalid=Invalid username or password.
|
login.invalid=Invalid username or password.
|
||||||
@@ -467,7 +436,6 @@ pdfToSinglePage.submit=Convert To Single Page
|
|||||||
pageExtracter.title=Extract Pages
|
pageExtracter.title=Extract Pages
|
||||||
pageExtracter.header=Extract Pages
|
pageExtracter.header=Extract Pages
|
||||||
pageExtracter.submit=Extract
|
pageExtracter.submit=Extract
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -655,18 +623,6 @@ compare.document.1=Document 1
|
|||||||
compare.document.2=Document 2
|
compare.document.2=Document 2
|
||||||
compare.submit=Compare
|
compare.submit=Compare
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Books and Comics to PDF
|
|
||||||
BookToPDF.header=Book to PDF
|
|
||||||
BookToPDF.credit=Uses Calibre
|
|
||||||
BookToPDF.submit=Convert
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF to Book
|
|
||||||
PDFToBook.header=PDF to Book
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Uses Calibre
|
|
||||||
PDFToBook.submit=Convert
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Sign
|
sign.title=Sign
|
||||||
@@ -770,23 +726,11 @@ merge.submit=Merge
|
|||||||
pdfOrganiser.title=Page Organiser
|
pdfOrganiser.title=Page Organiser
|
||||||
pdfOrganiser.header=PDF Page Organiser
|
pdfOrganiser.header=PDF Page Organiser
|
||||||
pdfOrganiser.submit=Rearrange Pages
|
pdfOrganiser.submit=Rearrange Pages
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
@@ -797,7 +741,6 @@ pageRemover.title=Page Remover
|
|||||||
pageRemover.header=PDF Page remover
|
pageRemover.header=PDF Page remover
|
||||||
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
||||||
pageRemover.submit=Delete Pages
|
pageRemover.submit=Delete Pages
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
@@ -807,17 +750,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
|
|||||||
rotate.submit=Rotate
|
rotate.submit=Rotate
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=Split PDF
|
split.title=Split PDF
|
||||||
split.header=Split PDF
|
split.header=Split PDF
|
||||||
split.desc.1=The numbers you select are the page number you wish to do a split on
|
split.desc.1=The numbers you select are the page number you wish to do a split on
|
||||||
split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with:
|
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
|
||||||
split.desc.3=Document #1: Page 1
|
split.desc.3=Document #1: Page 1
|
||||||
split.desc.4=Document #2: Page 2 and 3
|
split.desc.4=Document #2: Page 2 and 3
|
||||||
split.desc.5=Document #3: Page 4, 5, 6 and 7
|
split.desc.5=Document #3: Page 4, 5 and 6
|
||||||
split.desc.6=Document #4: Page 8
|
split.desc.6=Document #4: Page 7
|
||||||
split.desc.7=Document #5: Page 9
|
split.desc.7=Document #5: Page 8
|
||||||
split.desc.8=Document #6: Page 10
|
split.desc.8=Document #6: Page 9 and 10
|
||||||
split.splitPages=Enter pages to split on:
|
split.splitPages=Enter pages to split on:
|
||||||
split.submit=Split
|
split.submit=Split
|
||||||
|
|
||||||
@@ -885,8 +828,6 @@ watermark.selectText.7=Opacity (0% - 100%):
|
|||||||
watermark.selectText.8=Watermark Type:
|
watermark.selectText.8=Watermark Type:
|
||||||
watermark.selectText.9=Watermark Image:
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Add Watermark
|
watermark.submit=Add Watermark
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -938,7 +879,6 @@ pdfToPDFA.title=PDF To PDF/A
|
|||||||
pdfToPDFA.header=PDF To PDF/A
|
pdfToPDFA.header=PDF To PDF/A
|
||||||
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
||||||
pdfToPDFA.submit=Convert
|
pdfToPDFA.submit=Convert
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -968,7 +908,7 @@ PDFToText.submit=Convert
|
|||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF to HTML
|
PDFToHTML.title=PDF to HTML
|
||||||
PDFToHTML.header=PDF to HTML
|
PDFToHTML.header=PDF to HTML
|
||||||
PDFToHTML.credit=This service uses pdftohtml for file conversion.
|
PDFToHTML.credit=This service uses LibreOffice for file conversion.
|
||||||
PDFToHTML.submit=Convert
|
PDFToHTML.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
@@ -985,7 +925,6 @@ PDFToCSV.prompt=Choose page to extract table
|
|||||||
PDFToCSV.submit=Extract
|
PDFToCSV.submit=Extract
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=Split PDF by Size or Count
|
|
||||||
split-by-size-or-count.header=Split PDF by Size or Count
|
split-by-size-or-count.header=Split PDF by Size or Count
|
||||||
split-by-size-or-count.type.label=Select Split Type
|
split-by-size-or-count.type.label=Select Split Type
|
||||||
split-by-size-or-count.type.size=By Size
|
split-by-size-or-count.type.size=By Size
|
||||||
@@ -1020,7 +959,7 @@ split-by-sections.vertical.label=Vertical Divisions
|
|||||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||||
split-by-sections.submit=Split PDF
|
split-by-sections.submit=Split PDF
|
||||||
split-by-sections.merge=Merge Into One PDF
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Licenses
|
licenses.nav=Licenses
|
||||||
@@ -1031,16 +970,3 @@ licenses.version=Version
|
|||||||
licenses.license=License
|
licenses.license=License
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# 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)
|
||||||
@@ -11,17 +11,14 @@ imgPrompt=Select Image(s)
|
|||||||
genericSubmit=Submit
|
genericSubmit=Submit
|
||||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||||
pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers or Functions like 2n+1) :
|
pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers or Functions like 2n+1) :
|
||||||
pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) :
|
|
||||||
goToPage=Go
|
goToPage=Go
|
||||||
true=True
|
true=True
|
||||||
false=False
|
false=False
|
||||||
unknown=Unknown
|
unknown=Unknown
|
||||||
save=Save
|
save=Save
|
||||||
saveToBrowser=Save to Browser
|
|
||||||
close=Close
|
close=Close
|
||||||
filesSelected=files selected
|
filesSelected=files selected
|
||||||
noFavourites=No favorites added
|
noFavourites=No favorites added
|
||||||
downloadComplete=Download Complete
|
|
||||||
bored=Bored Waiting?
|
bored=Bored Waiting?
|
||||||
alphabet=Alphabet
|
alphabet=Alphabet
|
||||||
downloadPdf=Download PDF
|
downloadPdf=Download PDF
|
||||||
@@ -54,32 +51,16 @@ notAuthenticatedMessage=User not authenticated.
|
|||||||
userNotFoundMessage=User not found.
|
userNotFoundMessage=User not found.
|
||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
usernameExistsMessage=New Username already exists.
|
||||||
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers.
|
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
|
||||||
error=Error
|
|
||||||
oops=Oops!
|
|
||||||
help=Help
|
|
||||||
goHomepage=Go to Homepage
|
|
||||||
joinDiscord=Join our Discord server
|
|
||||||
seeDockerHub=See Docker Hub
|
|
||||||
visitGithub=Visit Github Repository
|
|
||||||
donate=Donate
|
|
||||||
color=Color
|
|
||||||
sponsor=Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
###############
|
###############
|
||||||
pipeline.header=Pipeline Menu (Beta)
|
pipeline.header=Pipeline Menu (Alpha)
|
||||||
pipeline.uploadButton=Upload Custom
|
pipeline.uploadButton=Upload Custom
|
||||||
pipeline.configureButton=Configure
|
pipeline.configureButton=Configure
|
||||||
pipeline.defaultOption=Custom
|
pipeline.defaultOption=Custom
|
||||||
pipeline.submitButton=Submit
|
pipeline.submitButton=Submit
|
||||||
pipeline.help=Pipeline Help
|
|
||||||
pipeline.scanHelp=Folder Scanning Help
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@@ -125,7 +106,7 @@ settings.accountSettings=Account Settings
|
|||||||
|
|
||||||
changeCreds.title=Change Credentials
|
changeCreds.title=Change Credentials
|
||||||
changeCreds.header=Update Your Account Details
|
changeCreds.header=Update Your Account Details
|
||||||
changeCreds.changePassword=You are using default login credentials. Please enter a new password
|
changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted)
|
||||||
changeCreds.newUsername=New Username
|
changeCreds.newUsername=New Username
|
||||||
changeCreds.oldPassword=Current Password
|
changeCreds.oldPassword=Current Password
|
||||||
changeCreds.newPassword=New Password
|
changeCreds.newPassword=New Password
|
||||||
@@ -160,16 +141,13 @@ adminUserSettings.header=Admin User Control Settings
|
|||||||
adminUserSettings.admin=Admin
|
adminUserSettings.admin=Admin
|
||||||
adminUserSettings.user=User
|
adminUserSettings.user=User
|
||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters.
|
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
adminUserSettings.actions=Actions
|
adminUserSettings.actions=Actions
|
||||||
adminUserSettings.apiUser=Limited API User
|
adminUserSettings.apiUser=Limited API User
|
||||||
adminUserSettings.extraApiUser=Additional Limited API User
|
|
||||||
adminUserSettings.webOnlyUser=Web Only User
|
adminUserSettings.webOnlyUser=Web Only User
|
||||||
adminUserSettings.demoUser=Demo User (No custom settings)
|
adminUserSettings.demoUser=Demo User (No custom settings)
|
||||||
adminUserSettings.internalApiUser=Internal API User
|
adminUserSettings.forceChange=Force user to change username/password on login
|
||||||
adminUserSettings.forceChange=Force user to change password on login
|
|
||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
|
|
||||||
#############
|
#############
|
||||||
@@ -413,15 +391,6 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations
|
|||||||
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize
|
||||||
|
|
||||||
|
|
||||||
home.PDFToBook.title=PDF to Book
|
|
||||||
home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre
|
|
||||||
PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
home.BookToPDF.title=Book to PDF
|
|
||||||
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
|
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# #
|
# #
|
||||||
# WEB PAGES #
|
# WEB PAGES #
|
||||||
@@ -429,7 +398,6 @@ BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
|||||||
###########################
|
###########################
|
||||||
#login
|
#login
|
||||||
login.title=Sign in
|
login.title=Sign in
|
||||||
login.header=Sign in
|
|
||||||
login.signin=Sign in
|
login.signin=Sign in
|
||||||
login.rememberme=Remember me
|
login.rememberme=Remember me
|
||||||
login.invalid=Invalid username or password.
|
login.invalid=Invalid username or password.
|
||||||
@@ -467,7 +435,6 @@ pdfToSinglePage.submit=Convert To Single Page
|
|||||||
pageExtracter.title=Extract Pages
|
pageExtracter.title=Extract Pages
|
||||||
pageExtracter.header=Extract Pages
|
pageExtracter.header=Extract Pages
|
||||||
pageExtracter.submit=Extract
|
pageExtracter.submit=Extract
|
||||||
pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#getPdfInfo
|
#getPdfInfo
|
||||||
@@ -655,18 +622,6 @@ compare.document.1=Document 1
|
|||||||
compare.document.2=Document 2
|
compare.document.2=Document 2
|
||||||
compare.submit=Compare
|
compare.submit=Compare
|
||||||
|
|
||||||
#BookToPDF
|
|
||||||
BookToPDF.title=Books and Comics to PDF
|
|
||||||
BookToPDF.header=Book to PDF
|
|
||||||
BookToPDF.credit=Uses Calibre
|
|
||||||
BookToPDF.submit=Convert
|
|
||||||
|
|
||||||
#PDFToBook
|
|
||||||
PDFToBook.title=PDF to Book
|
|
||||||
PDFToBook.header=PDF to Book
|
|
||||||
PDFToBook.selectText.1=Format
|
|
||||||
PDFToBook.credit=Uses Calibre
|
|
||||||
PDFToBook.submit=Convert
|
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Sign
|
sign.title=Sign
|
||||||
@@ -770,23 +725,11 @@ merge.submit=Merge
|
|||||||
pdfOrganiser.title=Page Organizer
|
pdfOrganiser.title=Page Organizer
|
||||||
pdfOrganiser.header=PDF Page Organizer
|
pdfOrganiser.header=PDF Page Organizer
|
||||||
pdfOrganiser.submit=Rearrange Pages
|
pdfOrganiser.submit=Rearrange Pages
|
||||||
pdfOrganiser.mode=Mode
|
|
||||||
pdfOrganiser.mode.1=Custom Page Order
|
|
||||||
pdfOrganiser.mode.2=Reverse Order
|
|
||||||
pdfOrganiser.mode.3=Duplex Sort
|
|
||||||
pdfOrganiser.mode.4=Booklet Sort
|
|
||||||
pdfOrganiser.mode.5=Side Stitch Booklet Sort
|
|
||||||
pdfOrganiser.mode.6=Odd-Even Split
|
|
||||||
pdfOrganiser.mode.7=Remove First
|
|
||||||
pdfOrganiser.mode.8=Remove Last
|
|
||||||
pdfOrganiser.mode.9=Remove First and Last
|
|
||||||
pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|
||||||
|
|
||||||
|
|
||||||
#multiTool
|
#multiTool
|
||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=Please Upload PDF
|
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
@@ -797,7 +740,6 @@ pageRemover.title=Page Remover
|
|||||||
pageRemover.header=PDF Page remover
|
pageRemover.header=PDF Page remover
|
||||||
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
|
||||||
pageRemover.submit=Delete Pages
|
pageRemover.submit=Delete Pages
|
||||||
pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30)
|
|
||||||
|
|
||||||
|
|
||||||
#rotate
|
#rotate
|
||||||
@@ -807,17 +749,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
|
|||||||
rotate.submit=Rotate
|
rotate.submit=Rotate
|
||||||
|
|
||||||
|
|
||||||
#split-pdfs
|
#merge
|
||||||
split.title=Split PDF
|
split.title=Split PDF
|
||||||
split.header=Split PDF
|
split.header=Split PDF
|
||||||
split.desc.1=The numbers you select are the page number you wish to do a split on
|
split.desc.1=The numbers you select are the page number you wish to do a split on
|
||||||
split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with:
|
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
|
||||||
split.desc.3=Document #1: Page 1
|
split.desc.3=Document #1: Page 1
|
||||||
split.desc.4=Document #2: Page 2 and 3
|
split.desc.4=Document #2: Page 2 and 3
|
||||||
split.desc.5=Document #3: Page 4, 5, 6, 7
|
split.desc.5=Document #3: Page 4, 5 and 6
|
||||||
split.desc.6=Document #4: Page 8
|
split.desc.6=Document #4: Page 7
|
||||||
split.desc.7=Document #5: Page 9
|
split.desc.7=Document #5: Page 8
|
||||||
split.desc.8=Document #6: Page 10
|
split.desc.8=Document #6: Page 9 and 10
|
||||||
split.splitPages=Enter pages to split on:
|
split.splitPages=Enter pages to split on:
|
||||||
split.submit=Split
|
split.submit=Split
|
||||||
|
|
||||||
@@ -885,8 +827,6 @@ watermark.selectText.7=Opacity (0% - 100%):
|
|||||||
watermark.selectText.8=Watermark Type:
|
watermark.selectText.8=Watermark Type:
|
||||||
watermark.selectText.9=Watermark Image:
|
watermark.selectText.9=Watermark Image:
|
||||||
watermark.submit=Add Watermark
|
watermark.submit=Add Watermark
|
||||||
watermark.type.1=Text
|
|
||||||
watermark.type.2=Image
|
|
||||||
|
|
||||||
|
|
||||||
#Change permissions
|
#Change permissions
|
||||||
@@ -938,7 +878,6 @@ pdfToPDFA.title=PDF To PDF/A
|
|||||||
pdfToPDFA.header=PDF To PDF/A
|
pdfToPDFA.header=PDF To PDF/A
|
||||||
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion
|
||||||
pdfToPDFA.submit=Convert
|
pdfToPDFA.submit=Convert
|
||||||
pdfToPDFA.tip=Currently does not work for multiple inputs at once
|
|
||||||
|
|
||||||
|
|
||||||
#PDFToWord
|
#PDFToWord
|
||||||
@@ -968,7 +907,7 @@ PDFToText.submit=Convert
|
|||||||
#PDFToHTML
|
#PDFToHTML
|
||||||
PDFToHTML.title=PDF to HTML
|
PDFToHTML.title=PDF to HTML
|
||||||
PDFToHTML.header=PDF to HTML
|
PDFToHTML.header=PDF to HTML
|
||||||
PDFToHTML.credit=This service uses pdftohtml for file conversion.
|
PDFToHTML.credit=This service uses LibreOffice for file conversion.
|
||||||
PDFToHTML.submit=Convert
|
PDFToHTML.submit=Convert
|
||||||
|
|
||||||
|
|
||||||
@@ -985,7 +924,6 @@ PDFToCSV.prompt=Choose page to extract table
|
|||||||
PDFToCSV.submit=Extract
|
PDFToCSV.submit=Extract
|
||||||
|
|
||||||
#split-by-size-or-count
|
#split-by-size-or-count
|
||||||
split-by-size-or-count.title=Split PDF by Size or Count
|
|
||||||
split-by-size-or-count.header=Split PDF by Size or Count
|
split-by-size-or-count.header=Split PDF by Size or Count
|
||||||
split-by-size-or-count.type.label=Select Split Type
|
split-by-size-or-count.type.label=Select Split Type
|
||||||
split-by-size-or-count.type.size=By Size
|
split-by-size-or-count.type.size=By Size
|
||||||
@@ -1020,7 +958,7 @@ split-by-sections.vertical.label=Vertical Divisions
|
|||||||
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
split-by-sections.horizontal.placeholder=Enter number of horizontal divisions
|
||||||
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
split-by-sections.vertical.placeholder=Enter number of vertical divisions
|
||||||
split-by-sections.submit=Split PDF
|
split-by-sections.submit=Split PDF
|
||||||
split-by-sections.merge=Merge Into One PDF
|
|
||||||
|
|
||||||
#licenses
|
#licenses
|
||||||
licenses.nav=Licenses
|
licenses.nav=Licenses
|
||||||
@@ -1031,16 +969,3 @@ licenses.version=Version
|
|||||||
licenses.license=License
|
licenses.license=License
|
||||||
|
|
||||||
|
|
||||||
# error
|
|
||||||
error.sorry=Sorry for the issue!
|
|
||||||
error.needHelp=Need help / Found an issue?
|
|
||||||
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:
|
|
||||||
error.404.head=404 - Page Not Found | Oops, we tripped in the code!
|
|
||||||
error.404.1=We can't seem to find the page you're looking for.
|
|
||||||
error.404.2=Something went wrong
|
|
||||||
error.github=Submit a ticket on GitHub
|
|
||||||
error.showStack=Show Stack Trace
|
|
||||||
error.copyStack=Copy Stack Trace
|
|
||||||
error.githubSubmit=GitHub - Submit a ticket
|
|
||||||
error.discordSubmit=Discord - Submit Support post
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user