Compare commits
52 Commits
stirling-p
...
bug/rememb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2062c19ab | ||
|
|
6760fddd6f | ||
|
|
1833d7cd73 | ||
|
|
fd93dad9a5 | ||
|
|
20a75d577d | ||
|
|
559581c59d | ||
|
|
ef18b17890 | ||
|
|
d3ae9f9a81 | ||
|
|
4a70d680a4 | ||
|
|
82ebd3dba9 | ||
|
|
15848e3de6 | ||
|
|
ea0d9301ff | ||
|
|
b27e1f254c | ||
|
|
7f30882e5e | ||
|
|
26c0a92e30 | ||
|
|
5cf53e39d0 | ||
|
|
7f566d5de8 | ||
|
|
caa32c5bae | ||
|
|
41c41cc88c | ||
|
|
c2acd74447 | ||
|
|
4d5d0e3cef | ||
|
|
df6af8766f | ||
|
|
0dd4456ae8 | ||
|
|
b0c8912742 | ||
|
|
467be09749 | ||
|
|
ceabcf2b3d | ||
|
|
361a0c9be8 | ||
|
|
128ca8e224 | ||
|
|
7d1d6d1f12 | ||
|
|
645c786d95 | ||
|
|
862a88e2e9 | ||
|
|
2f92aa90ef | ||
|
|
ba8dd04086 | ||
|
|
c13509cf67 | ||
|
|
0ab02e6ceb | ||
|
|
af52652aee | ||
|
|
e534f022f5 | ||
|
|
84867a7ad7 | ||
|
|
e97cb9d49e | ||
|
|
1b0c1b6cff | ||
|
|
e7356a1d38 | ||
|
|
1405e4f5ee | ||
|
|
322e7dee0d | ||
|
|
7eea7fb3cb | ||
|
|
c921b5d76f | ||
|
|
26ec0c5d77 | ||
|
|
404e31468e | ||
|
|
0c0f61aa0d | ||
|
|
918e977c6a | ||
|
|
a31633e5b8 | ||
|
|
ed551cec91 | ||
|
|
3f14e77725 |
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -10,5 +10,6 @@ Closes #(issue_number)
|
|||||||
- [ ] I have performed a self-review of my own code
|
- [ ] I have performed a self-review of my own code
|
||||||
- [ ] I have attached images of the change if it is UI based
|
- [ ] I have attached images of the change if it is UI based
|
||||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||||
|
- [ ] If my code has heavily changed functionality I have updated relevant docs on [Stirling-PDFs doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
|
||||||
- [ ] My changes generate no new warnings
|
- [ ] My changes generate no new warnings
|
||||||
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
|
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
|
||||||
|
|||||||
100
.github/scripts/gradle_to_chart.py
vendored
100
.github/scripts/gradle_to_chart.py
vendored
@@ -1,100 +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 version and the appVersion from Chart.yaml.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): The file path to the Chart.yaml.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: The version under "chart" key and the appVersion under "app" key.
|
|
||||||
"""
|
|
||||||
with open(path, encoding="utf-8") as file:
|
|
||||||
chart_yaml = yaml.safe_load(file)
|
|
||||||
return {
|
|
||||||
"chart": chart_yaml["version"],
|
|
||||||
"app": chart_yaml["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 get_new_chart_version(chart_version, old_app_version, new_app_version):
|
|
||||||
"""
|
|
||||||
Get the new chart version from
|
|
||||||
|
|
||||||
Args:
|
|
||||||
str: The current chart version.
|
|
||||||
str: The current app version.
|
|
||||||
str: The new app version.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: The new chart version to update to.
|
|
||||||
"""
|
|
||||||
chart_major, chart_minor, chart_patch = chart_version.split(".")
|
|
||||||
|
|
||||||
old_major, old_minor, old_patch = old_app_version.split(".")
|
|
||||||
new_major, new_minor, new_patch = new_app_version.split(".")
|
|
||||||
|
|
||||||
if old_major != new_major:
|
|
||||||
new_chart_version = f"{int(chart_major)+1}.0.0"
|
|
||||||
elif old_minor != new_minor:
|
|
||||||
new_chart_version = f"{chart_major}.{int(chart_minor)+1}.0"
|
|
||||||
elif old_patch != new_patch:
|
|
||||||
new_chart_version = f"{chart_major}.{chart_minor}.{int(chart_patch)+1}"
|
|
||||||
|
|
||||||
return new_chart_version
|
|
||||||
|
|
||||||
|
|
||||||
def update_chart_version(path, new_chart_version, new_app_version):
|
|
||||||
"""
|
|
||||||
Updates the version and the appVersion in Chart.yaml with a new version.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): The file path to the Chart.yaml.
|
|
||||||
new_chart_version (str): The new chart version to update to.
|
|
||||||
new_app_version (str): The new app version to update to.
|
|
||||||
"""
|
|
||||||
with open(path, encoding="utf-8") as file:
|
|
||||||
chart_yaml = yaml.safe_load(file)
|
|
||||||
chart_yaml["version"] = new_chart_version
|
|
||||||
chart_yaml["appVersion"] = new_app_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["app"] != gradle_version:
|
|
||||||
new_chart_version = get_new_chart_version(chart_version["chart"], chart_version["app"], gradle_version, )
|
|
||||||
print(
|
|
||||||
f"Versions do not match. Updating Chart.yaml from {chart_version['chart']} to {new_chart_version}."
|
|
||||||
)
|
|
||||||
update_chart_version(chart_yaml_path, new_chart_version, gradle_version)
|
|
||||||
else:
|
|
||||||
print("Versions match. No update required.")
|
|
||||||
47
.github/workflows/lint-helm-charts.yml
vendored
47
.github/workflows/lint-helm-charts.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: Lint and Test Helm Charts
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
|
||||||
branches: ["main"]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Helm
|
|
||||||
uses: azure/setup-helm@v4
|
|
||||||
|
|
||||||
- name: Set up python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Run pre-commit
|
|
||||||
uses: pre-commit/action@v3.0.1
|
|
||||||
with:
|
|
||||||
extra_args: helm-docs-built
|
|
||||||
|
|
||||||
- name: Set up chart-testing
|
|
||||||
uses: helm/chart-testing-action@v2
|
|
||||||
|
|
||||||
- name: Run chart-testing (list-changed)
|
|
||||||
id: list-changed
|
|
||||||
run: |
|
|
||||||
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
|
|
||||||
if [[ -n "$changed" ]]; then
|
|
||||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run chart-testing
|
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
|
||||||
run: ct lint --target-branch ${{ github.event.repository.default_branch }} --validate-maintainers=false
|
|
||||||
4
.github/workflows/push-docker.yml
vendored
4
.github/workflows/push-docker.yml
vendored
@@ -10,6 +10,7 @@ on:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -66,6 +67,7 @@ jobs:
|
|||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
|
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
@@ -93,6 +95,7 @@ jobs:
|
|||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
|
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
|
||||||
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' }}
|
||||||
@@ -119,6 +122,7 @@ jobs:
|
|||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
|
${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|||||||
31
.github/workflows/release-helm-charts.yml
vendored
31
.github/workflows/release-helm-charts.yml
vendored
@@ -1,31 +0,0 @@
|
|||||||
name: Release Helm charts
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up git config
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
|
|
||||||
- name: Run chart-releaser
|
|
||||||
uses: helm/chart-releaser-action@v1.6.0
|
|
||||||
with:
|
|
||||||
config: "./cr.yaml"
|
|
||||||
charts_dir: "chart"
|
|
||||||
env:
|
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
42
.github/workflows/sync_files.yml
vendored
42
.github/workflows/sync_files.yml
vendored
@@ -14,48 +14,6 @@ permissions:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-versions:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pip install pyyaml
|
|
||||||
- name: Sync versions
|
|
||||||
run: python .github/scripts/gradle_to_chart.py
|
|
||||||
- name: Run pre-commit helm-docs-built
|
|
||||||
uses: pre-commit/action@v3.0.1
|
|
||||||
with:
|
|
||||||
extra_args: helm-docs-built
|
|
||||||
- name: Set up git config
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
- name: Run git add
|
|
||||||
run: |
|
|
||||||
git add .
|
|
||||||
git diff --staged --quiet || git commit -m ":floppy_disk: Sync Versions
|
|
||||||
> Made via sync_files.yml" || echo "no changes"
|
|
||||||
- name: Create Pull Request
|
|
||||||
uses: peter-evans/create-pull-request@v6
|
|
||||||
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
|
|
||||||
labels: github-actions
|
|
||||||
sync-readme:
|
sync-readme:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -37,9 +37,3 @@ repos:
|
|||||||
language: python
|
language: python
|
||||||
exclude: ^(src/main/resources/static/pdfjs|src/main/resources/static/pdfjs-legacy)
|
exclude: ^(src/main/resources/static/pdfjs|src/main/resources/static/pdfjs-legacy)
|
||||||
files: ^.*(\.html|\.css|\.js)$
|
files: ^.*(\.html|\.css|\.js)$
|
||||||
- repo: https://github.com/norwoodj/helm-docs
|
|
||||||
rev: v1.14.2
|
|
||||||
hooks:
|
|
||||||
- id: helm-docs-built
|
|
||||||
args:
|
|
||||||
- --chart-search-root=chart
|
|
||||||
|
|||||||
@@ -1,47 +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 | Unoconv | Ghostscript |
|
||||||
| ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- |
|
| ------------------- | ------- | ------- | -------- | ----- | --- | ------ | ------ | ----------- | -------- | ---- | ---------- | ------- | ----------- |
|
||||||
| 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 | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| add-password | | | ✔️ | | | | | | | ✔️ | | | |
|
||||||
| add-password | | | ✔️ | | | | | | | ✔️ | |
|
| add-watermark | | | ✔️ | | | | | | | ✔️ | | | |
|
||||||
| add-watermark | | | ✔️ | | | | | | | ✔️ | |
|
| cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
|
||||||
| cert-sign | | | ✔️ | | | | | | | ✔️ | |
|
| remove-cert-sign | | | ✔️ | | | | | | | ✔️ | | | |
|
||||||
| remove-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 | | | | ✔️ | | | | | | | ✔️ |
|
|
||||||
|
|||||||
@@ -80,3 +80,23 @@ dnf search -C tesseract-langpack-
|
|||||||
# View installed languages:
|
# View installed languages:
|
||||||
rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
|
rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For Windows:
|
||||||
|
|
||||||
|
Ensure ocrmypdf in installed with
|
||||||
|
``pip install ocrmypdf``
|
||||||
|
|
||||||
|
Additional languages must be downloaded manually:
|
||||||
|
Download desired .traineddata files from tessdata or tessdata_fast
|
||||||
|
Place them in the tessdata folder within your Tesseract installation directory
|
||||||
|
(e.g., C:\Program Files\Tesseract-OCR\tessdata)
|
||||||
|
|
||||||
|
Verify installation:
|
||||||
|
``tesseract --list-langs``
|
||||||
|
|
||||||
|
You must then edit your ``/configs/settings.yml`` and change the system.tessdataDir to match the directory containing lang files
|
||||||
|
```
|
||||||
|
system:
|
||||||
|
tessdataDir: C:/Program Files/Tesseract-OCR/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
95
README.md
95
README.md
@@ -7,9 +7,8 @@
|
|||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
|
|
||||||
[](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)
|
||||||
[<img src="https://www.ssdnodes.com/wp-content/uploads/2023/11/footer-logo.svg" alt="Name" height="40">](https://www.ssdnodes.com/manage/aff.php?aff=2216®ister=true)
|
|
||||||
|
|
||||||
Stirling-PDF 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. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
[Stirling-PDF](https://www.stirlingpdf.com) 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. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements.
|
||||||
|
|
||||||
Stirling-PDF does not initiate any outbound calls for record-keeping or tracking purposes.
|
Stirling-PDF does not initiate any outbound calls for record-keeping or tracking purposes.
|
||||||
|
|
||||||
@@ -19,6 +18,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- Enterprise features like SSO Check [here](https://docs.stirlingpdf.com/Enterprise%20Edition)
|
||||||
- Dark mode support
|
- Dark mode support
|
||||||
- Custom download options
|
- Custom download options
|
||||||
- Parallel file processing and downloads
|
- Parallel file processing and downloads
|
||||||
@@ -27,6 +27,7 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- 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)
|
||||||
- Database Backup and Import (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DATABASE.md) for documentation)
|
- Database Backup and Import (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DATABASE.md) for documentation)
|
||||||
|
|
||||||
|
|
||||||
## PDF Features
|
## PDF Features
|
||||||
|
|
||||||
### Page Operations
|
### Page Operations
|
||||||
@@ -46,6 +47,8 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- Extract page(s)
|
- Extract page(s)
|
||||||
- Convert PDF to a single page
|
- Convert PDF to a single page
|
||||||
- Overlay PDFs on top of each other
|
- Overlay PDFs on top of each other
|
||||||
|
- PDF to single page
|
||||||
|
- Split PDF by sections
|
||||||
|
|
||||||
### Conversion Operations
|
### Conversion Operations
|
||||||
|
|
||||||
@@ -53,6 +56,8 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
- 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)
|
||||||
- Convert HTML to PDF
|
- Convert HTML to PDF
|
||||||
|
- Convert PDF to xml
|
||||||
|
- Convert PDF to CSV
|
||||||
- URL to PDF
|
- URL to PDF
|
||||||
- Markdown to PDF
|
- Markdown to PDF
|
||||||
|
|
||||||
@@ -68,13 +73,16 @@ All files and PDFs exist either exclusively on the client side, reside in server
|
|||||||
### Other Operations
|
### Other Operations
|
||||||
|
|
||||||
- Add/generate/write signatures
|
- Add/generate/write signatures
|
||||||
|
- Split by Size or PDF
|
||||||
- Repair PDFs
|
- Repair PDFs
|
||||||
- Detect and remove blank pages
|
- Detect and remove blank pages
|
||||||
- Compare two PDFs and show differences in text
|
- Compare two PDFs and show differences in text
|
||||||
- Add images to PDFs
|
- Add images to PDFs
|
||||||
- Compress PDFs to decrease their filesize (using OCRMyPDF)
|
- Compress PDFs to decrease their filesize (using OCRMyPDF)
|
||||||
- Extract images from PDF
|
- Extract images from PDF
|
||||||
|
- Remove images from PDF
|
||||||
- Extract images from scans
|
- Extract images from scans
|
||||||
|
- Remove annotations
|
||||||
- Add page numbers
|
- Add page numbers
|
||||||
- Auto rename file by detecting PDF header text
|
- Auto rename file by detecting PDF header text
|
||||||
- OCR on PDF (using OCRMyPDF)
|
- OCR on PDF (using OCRMyPDF)
|
||||||
@@ -161,6 +169,10 @@ services:
|
|||||||
|
|
||||||
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".
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
See the kubernetes helm chart [here](https://github.com/Stirling-Tools/Stirling-PDF-chart)
|
||||||
|
|
||||||
## Enable OCR/Compression Feature
|
## Enable OCR/Compression Feature
|
||||||
|
|
||||||
Please view the [HowToUseOCR.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md).
|
Please view the [HowToUseOCR.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md).
|
||||||
@@ -179,46 +191,63 @@ Stirling-PDF currently supports 36 languages!
|
|||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
## 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).
|
||||||
|
|
||||||
|
## Stirling PDF Enterprise
|
||||||
|
|
||||||
|
Stirling PDF offers a Enterprise edition of its software, This is the same great software but with added features and comforts
|
||||||
|
|
||||||
|
### Whats included
|
||||||
|
|
||||||
|
- Prioritised Support tickets via support@stirlingpdf.com to reach directly to Stirling-PDF team for support and 1:1 meetings where applicable (Provided they come from same email domain registered with us)
|
||||||
|
- Prioritised Enhancements to Stirling-PDF where applicable
|
||||||
|
- Base SSO support
|
||||||
|
- Advanced SSO such as automated login handling (Coming very soon)
|
||||||
|
- SAML SSO (Coming very soon)
|
||||||
|
- Custom automated metadata handling
|
||||||
|
- Advanced user configurations (Coming soon)
|
||||||
|
- Plus other exciting features to come
|
||||||
|
|
||||||
|
Check out of [docs](https://docs.stirlingpdf.com/Enterprise%20Edition) on it or our official [website](https://www.stirlingpdf.com)
|
||||||
|
|
||||||
## Customization
|
## Customization
|
||||||
|
|
||||||
Stirling-PDF allows easy customization of the app, including things like:
|
Stirling-PDF allows easy customization of the app, including things like:
|
||||||
@@ -335,6 +364,8 @@ AutomaticallyGenerated:
|
|||||||
|
|
||||||
There is an additional config file `/configs/custom_settings.yml` where users familiar with Java and Spring `application.properties` can input their own settings on top of Stirling-PDF's existing ones.
|
There is an additional config file `/configs/custom_settings.yml` where users familiar with Java and Spring `application.properties` can input their own settings on top of Stirling-PDF's existing ones.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Extra Notes
|
### Extra Notes
|
||||||
|
|
||||||
- **Endpoints**: Currently, the `ENDPOINTS_TO_REMOVE` and `GROUPS_TO_REMOVE` endpoints can include comma-separated lists of endpoints and groups to disable. For example, `ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages` would disable both image-to-pdf and remove pages, while `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_TO_REMOVE` and `GROUPS_TO_REMOVE` endpoints can include comma-separated lists of endpoints and groups to disable. For example, `ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages` would disable both image-to-pdf and remove pages, while `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).
|
||||||
@@ -385,7 +416,7 @@ For API usage, you must provide a header with `X-API-Key` and the associated API
|
|||||||
- Multi-page layout (stitch PDF pages together) support x rows y columns and custom page sizing
|
- Multi-page layout (stitch PDF pages together) support x rows y columns and custom page sizing
|
||||||
- Fill forms manually or automatically
|
- Fill forms manually or automatically
|
||||||
|
|
||||||
### Q2: Why is my application downloading .htm files?
|
### Q2: Why is my application downloading .htm files? Why am i getting HTTP error 413?
|
||||||
|
|
||||||
This is an issue commonly caused 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 commonly caused 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).
|
||||||
|
|
||||||
|
|||||||
@@ -54,3 +54,15 @@ The 'Fat' container contains all those found in 'Full' with security jar along w
|
|||||||
| ocr-pdf | | ✔️ |
|
| ocr-pdf | | ✔️ |
|
||||||
| pdf-to-pdfa | | ✔️ |
|
| pdf-to-pdfa | | ✔️ |
|
||||||
| remove-blanks | | ✔️ |
|
| remove-blanks | | ✔️ |
|
||||||
|
pdf-to-text | ✔️ | ✔️
|
||||||
|
pdf-to-html | | ✔️
|
||||||
|
pdf-to-word | | ✔️
|
||||||
|
pdf-to-presentation | | ✔️
|
||||||
|
pdf-to-xml | | ✔️
|
||||||
|
remove-annotations | ✔️ | ✔️
|
||||||
|
remove-cert-sign | ✔️ | ✔️
|
||||||
|
remove-image-pdf | ✔️ | ✔️
|
||||||
|
file-to-pdf | | ✔️
|
||||||
|
html-to-pdf | | ✔️
|
||||||
|
url-to-pdf | | ✔️
|
||||||
|
repair | | ✔️
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.31.1"
|
version = "0.32.0"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
@@ -78,7 +78,7 @@ launch4j {
|
|||||||
|
|
||||||
errTitle="Encountered error, Do you have Java 21?"
|
errTitle="Encountered error, Do you have Java 21?"
|
||||||
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
||||||
variables=["BROWSER_OPEN=true", "ENDPOINTS_GROUPS_TO_REMOVE=CLI"]
|
variables=["BROWSER_OPEN=true"]
|
||||||
jreMinVersion="17"
|
jreMinVersion="17"
|
||||||
|
|
||||||
mutexName="Stirling-PDF"
|
mutexName="Stirling-PDF"
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: v2
|
|
||||||
appVersion: 0.31.1
|
|
||||||
description: locally hosted web application that allows you to perform various operations
|
|
||||||
on PDF files
|
|
||||||
home: https://github.com/Stirling-Tools/Stirling-PDF
|
|
||||||
keywords:
|
|
||||||
- stirling-pdf
|
|
||||||
- helm
|
|
||||||
- charts repo
|
|
||||||
maintainers:
|
|
||||||
- name: Stirling-Tools
|
|
||||||
url: https://github.com/Stirling-Tools/Stirling-PDF
|
|
||||||
name: stirling-pdf-chart
|
|
||||||
sources:
|
|
||||||
- https://github.com/Stirling-Tools/Stirling-PDF
|
|
||||||
version: 1.0.1
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
# stirling-pdf-chart
|
|
||||||
|
|
||||||
 
|
|
||||||
|
|
||||||
locally hosted web application that allows you to perform various operations on PDF files
|
|
||||||
|
|
||||||
**Homepage:** <https://github.com/Stirling-Tools/Stirling-PDF>
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
|
|
||||||
| Name | Email | Url |
|
|
||||||
| ---- | ------ | --- |
|
|
||||||
| Stirling-Tools | | <https://github.com/Stirling-Tools/Stirling-PDF> |
|
|
||||||
|
|
||||||
## Source Code
|
|
||||||
|
|
||||||
* <https://github.com/Stirling-Tools/Stirling-PDF>
|
|
||||||
|
|
||||||
## Chart Repo
|
|
||||||
|
|
||||||
Add the following repo to use the chart:
|
|
||||||
|
|
||||||
```console
|
|
||||||
helm repo add stirling-pdf https://stirling-tools.github.io/Stirling-PDF
|
|
||||||
```
|
|
||||||
|
|
||||||
## Values
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
|-----|------|---------|-------------|
|
|
||||||
| affinity | object | `{}` | |
|
|
||||||
| commonLabels | object | `{}` | Labels to apply to all resources |
|
|
||||||
| containerSecurityContext | object | `{}` | |
|
|
||||||
| deployment.annotations | object | `{}` | Stirling-pdf Deployment annotations |
|
|
||||||
| deployment.extraVolumeMounts | list | `[]` | Additional volumes to mount |
|
|
||||||
| deployment.extraVolumes | list | `[]` | Additional volumes |
|
|
||||||
| deployment.labels | object | `{}` | |
|
|
||||||
| deployment.sidecarContainers | object | `{}` | of the chart's content, send notifications... |
|
|
||||||
| envs | list | `[]` | |
|
|
||||||
| extraArgs | list | `[]` | |
|
|
||||||
| image.pullPolicy | string | `"IfNotPresent"` | |
|
|
||||||
| image.repository | string | `"frooodle/s-pdf"` | |
|
|
||||||
| image.tag | string | `nil` | |
|
|
||||||
| ingress | object | `{"annotations":{},"enabled":false,"hosts":[],"ingressClassName":null,"labels":{},"pathType":"ImplementationSpecific"}` | Ingress for load balancer |
|
|
||||||
| ingress.annotations | object | `{}` | Stirling-pdf Ingress annotations |
|
|
||||||
| ingress.hosts | list | `[]` | Must be provided if Ingress is enabled |
|
|
||||||
| ingress.ingressClassName | string | `nil` | See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress |
|
|
||||||
| ingress.labels | object | `{}` | Stirling-pdf Ingress labels |
|
|
||||||
| nodeSelector | object | `{}` | |
|
|
||||||
| persistence.accessMode | string | `"ReadWriteOnce"` | |
|
|
||||||
| persistence.enabled | bool | `false` | |
|
|
||||||
| persistence.labels | object | `{}` | |
|
|
||||||
| persistence.path | string | `"/tmp"` | |
|
|
||||||
| persistence.pv | object | `{"accessMode":"ReadWriteOnce","capacity":{"storage":"8Gi"},"enabled":false,"nfs":{"path":null,"server":null},"pvname":null}` | stirling-pdf data Persistent Volume Storage Class If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) storageClass: "-" volumeName: |
|
|
||||||
| persistence.size | string | `"8Gi"` | |
|
|
||||||
| podAnnotations | object | `{}` | Read more about kube2iam to provide access to s3 https://github.com/jtblin/kube2iam |
|
|
||||||
| podLabels | object | `{}` | ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ |
|
|
||||||
| priorityClassName | string | `""` | |
|
|
||||||
| probes.liveness.failureThreshold | int | `3` | |
|
|
||||||
| probes.liveness.initialDelaySeconds | int | `5` | |
|
|
||||||
| probes.liveness.periodSeconds | int | `10` | |
|
|
||||||
| probes.liveness.successThreshold | int | `1` | |
|
|
||||||
| probes.liveness.timeoutSeconds | int | `1` | |
|
|
||||||
| probes.livenessHttpGetConfig.scheme | string | `"HTTP"` | |
|
|
||||||
| probes.readiness.failureThreshold | int | `3` | |
|
|
||||||
| probes.readiness.initialDelaySeconds | int | `5` | |
|
|
||||||
| probes.readiness.periodSeconds | int | `10` | |
|
|
||||||
| probes.readiness.successThreshold | int | `1` | |
|
|
||||||
| probes.readiness.timeoutSeconds | int | `1` | |
|
|
||||||
| probes.readinessHttpGetConfig.scheme | string | `"HTTP"` | |
|
|
||||||
| replicaCount | int | `1` | |
|
|
||||||
| resources | object | `{}` | |
|
|
||||||
| rootPath | string | `"/"` | Rootpath for the application |
|
|
||||||
| secret.labels | object | `{}` | |
|
|
||||||
| securityContext | object | `{"enabled":true,"fsGroup":1000}` | does not allow this, try setting securityContext: {} |
|
|
||||||
| service.annotations | object | `{}` | |
|
|
||||||
| service.externalPort | int | `8080` | |
|
|
||||||
| service.externalTrafficPolicy | string | `"Local"` | |
|
|
||||||
| service.labels | object | `{}` | |
|
|
||||||
| service.loadBalancerIP | string | `nil` | Only valid if service.type: LoadBalancer |
|
|
||||||
| service.loadBalancerSourceRanges | list | `[]` | Only valid if service.type: LoadBalancer |
|
|
||||||
| service.nodePort | string | `nil` | |
|
|
||||||
| service.servicename | string | `nil` | |
|
|
||||||
| service.targetPort | string | `nil` | from deployment above. Leave empty to use stirling-pdf directly. |
|
|
||||||
| service.type | string | `"ClusterIP"` | |
|
|
||||||
| serviceAccount.annotations | object | `{}` | |
|
|
||||||
| serviceAccount.automountServiceAccountToken | bool | `false` | |
|
|
||||||
| serviceAccount.create | bool | `true` | |
|
|
||||||
| serviceAccount.name | string | `""` | |
|
|
||||||
| serviceMonitor.enabled | bool | `false` | |
|
|
||||||
| serviceMonitor.labels | object | `{}` | |
|
|
||||||
| serviceMonitor.metricsPath | string | `"/metrics"` | |
|
|
||||||
| strategy.type | string | `"RollingUpdate"` | |
|
|
||||||
| tolerations | list | `[]` | |
|
|
||||||
| volumePermissions | object | `{"image":{"pullPolicy":"Always","registry":"docker.io","repository":"bitnami/minideb","tag":"buster"}}` | volumePermissions: Change the owner of the persistent volume mountpoint to RunAsUser:fsGroup |
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{{ template "chart.header" . }}
|
|
||||||
|
|
||||||
{{ template "chart.deprecationWarning" . }}
|
|
||||||
|
|
||||||
{{ template "chart.badgesSection" . }}
|
|
||||||
|
|
||||||
{{ template "chart.description" . }}
|
|
||||||
|
|
||||||
{{ template "chart.homepageLine" . }}
|
|
||||||
|
|
||||||
{{ template "chart.maintainersSection" . }}
|
|
||||||
|
|
||||||
{{ template "chart.sourcesSection" . }}
|
|
||||||
|
|
||||||
{{ template "chart.requirementsSection" . }}
|
|
||||||
|
|
||||||
## Chart Repo
|
|
||||||
|
|
||||||
Add the following repo to use the chart:
|
|
||||||
|
|
||||||
```console
|
|
||||||
helm repo add stirling-pdf https://docs.stirlingpdf.com/Stirling-PDF/
|
|
||||||
```
|
|
||||||
|
|
||||||
{{ template "chart.valuesSection" . }}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
** Please be patient while the chart is being deployed **
|
|
||||||
|
|
||||||
Get the stirlingpdf URL by running:
|
|
||||||
|
|
||||||
{{- if contains "NodePort" .Values.service.type }}
|
|
||||||
|
|
||||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "stirlingpdf.fullname" . }})
|
|
||||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
|
||||||
echo http://$NODE_IP:$NODE_PORT/
|
|
||||||
|
|
||||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
|
||||||
|
|
||||||
** Please ensure an external IP is associated to the {{ template "stirlingpdf.fullname" . }} service before proceeding **
|
|
||||||
** Watch the status using: kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "stirlingpdf.fullname" . }} **
|
|
||||||
|
|
||||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "stirlingpdf.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
|
||||||
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}/
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
export SERVICE_HOST=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "stirlingpdf.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
|
|
||||||
echo http://$SERVICE_HOST:{{ .Values.service.externalPort }}/
|
|
||||||
|
|
||||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
|
||||||
|
|
||||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "stirlingpdf.name" . }}" -l "release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
|
||||||
echo http://127.0.0.1:8080/
|
|
||||||
kubectl port-forward $POD_NAME 8080:8080 --namespace {{ .Release.Namespace }}
|
|
||||||
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
{{/*
|
|
||||||
Expand the name of the chart.
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.name" -}}
|
|
||||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create a default fully qualified app name.
|
|
||||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
|
||||||
If release name contains chart name it will be used as a full name.
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.fullname" -}}
|
|
||||||
{{- if .Values.fullnameOverride }}
|
|
||||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- else }}
|
|
||||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
|
||||||
{{- if contains $name .Release.Name }}
|
|
||||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- else }}
|
|
||||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- /*
|
|
||||||
Create chart name and version as used by the chart label.
|
|
||||||
|
|
||||||
It does minimal escaping for use in Kubernetes labels.
|
|
||||||
|
|
||||||
Example output:
|
|
||||||
|
|
||||||
stirlingpdf-0.4.5
|
|
||||||
*/ -}}
|
|
||||||
{{- define "stirlingpdf.chart" -}}
|
|
||||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Common labels
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.labels" -}}
|
|
||||||
helm.sh/chart: {{ include "stirlingpdf.chart" . }}
|
|
||||||
{{ include "stirlingpdf.selectorLabels" . }}
|
|
||||||
{{- if .Chart.AppVersion }}
|
|
||||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.commonLabels}}
|
|
||||||
{{ toYaml .Values.commonLabels }}
|
|
||||||
{{- end }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Selector labels
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.selectorLabels" -}}
|
|
||||||
app.kubernetes.io/name: {{ include "stirlingpdf.name" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the service account to use
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.serviceAccountName" -}}
|
|
||||||
{{- if .Values.serviceAccount.create }}
|
|
||||||
{{- default (include "stirlingpdf.fullname" .) .Values.serviceAccount.name }}
|
|
||||||
{{- else }}
|
|
||||||
{{- default "default" .Values.serviceAccount.name }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Return the proper image name to change the volume permissions
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.volumePermissions.image" -}}
|
|
||||||
{{- $registryName := .Values.volumePermissions.image.registry -}}
|
|
||||||
{{- $repositoryName := .Values.volumePermissions.image.repository -}}
|
|
||||||
{{- $tag := .Values.volumePermissions.image.tag | toString -}}
|
|
||||||
{{/*
|
|
||||||
Helm 2.11 supports the assignment of a value to a variable defined in a different scope,
|
|
||||||
but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic.
|
|
||||||
Also, we can't use a single if because lazy evaluation is not an option
|
|
||||||
*/}}
|
|
||||||
{{- if .Values.global }}
|
|
||||||
{{- if .Values.global.imageRegistry }}
|
|
||||||
{{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}}
|
|
||||||
{{- else -}}
|
|
||||||
{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{- else -}}
|
|
||||||
{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Return the proper Docker Image Registry Secret Names
|
|
||||||
*/}}
|
|
||||||
{{- define "stirlingpdf.imagePullSecrets" -}}
|
|
||||||
{{/*
|
|
||||||
Helm 2.11 supports the assignment of a value to a variable defined in a different scope,
|
|
||||||
but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic.
|
|
||||||
Also, we can not use a single if because lazy evaluation is not an option
|
|
||||||
*/}}
|
|
||||||
{{- if .Values.global }}
|
|
||||||
{{- if .Values.global.imagePullSecrets }}
|
|
||||||
imagePullSecrets:
|
|
||||||
{{- range .Values.global.imagePullSecrets }}
|
|
||||||
- name: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- else if or .Values.image.pullSecrets .Values.volumePermissions.image.pullSecrets }}
|
|
||||||
imagePullSecrets:
|
|
||||||
{{- range .Values.image.pullSecrets }}
|
|
||||||
- name: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- range .Values.volumePermissions.image.pullSecrets }}
|
|
||||||
- name: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end -}}
|
|
||||||
{{- else if or .Values.image.pullSecrets .Values.volumePermissions.image.pullSecrets }}
|
|
||||||
imagePullSecrets:
|
|
||||||
{{- range .Values.image.pullSecrets }}
|
|
||||||
- name: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- range .Values.volumePermissions.image.pullSecrets }}
|
|
||||||
- name: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ include "stirlingpdf.fullname" . }}
|
|
||||||
{{- with .Values.deployment.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- if .Values.deployment.labels }}
|
|
||||||
{{- toYaml .Values.deployment.labels | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 6 }}
|
|
||||||
replicas: {{ .Values.replicaCount }}
|
|
||||||
strategy:
|
|
||||||
{{ toYaml .Values.strategy | indent 4 }}
|
|
||||||
revisionHistoryLimit: 10
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
{{- with .Values.podAnnotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 8 }}
|
|
||||||
{{- if .Values.podLabels }}
|
|
||||||
{{- toYaml .Values.podLabels | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
{{- if .Values.priorityClassName }}
|
|
||||||
priorityClassName: "{{ .Values.priorityClassName }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.securityContext.enabled }}
|
|
||||||
securityContext:
|
|
||||||
fsGroup: {{ .Values.securityContext.fsGroup }}
|
|
||||||
{{- if .Values.securityContext.runAsNonRoot }}
|
|
||||||
runAsNonRoot: {{ .Values.securityContext.runAsNonRoot }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.securityContext.supplementalGroups }}
|
|
||||||
supplementalGroups: {{ .Values.securityContext.supplementalGroups }}
|
|
||||||
{{- end }}
|
|
||||||
{{- else if .Values.persistence.enabled }}
|
|
||||||
initContainers:
|
|
||||||
- name: volume-permissions
|
|
||||||
image: {{ template "stirlingpdf.volumePermissions.image" . }}
|
|
||||||
imagePullPolicy: "{{ .Values.volumePermissions.image.pullPolicy }}"
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.containerSecurityContext | nindent 10 }}
|
|
||||||
command: ['sh', '-c', 'chown -R {{ .Values.securityContext.fsGroup }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.path }}']
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: {{ .Values.persistence.path }}
|
|
||||||
name: storage-volume
|
|
||||||
{{- end }}
|
|
||||||
{{- include "stirlingpdf.imagePullSecrets" . | indent 6 }}
|
|
||||||
containers:
|
|
||||||
- name: {{ .Chart.Name }}
|
|
||||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
|
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.containerSecurityContext | nindent 10 }}
|
|
||||||
env:
|
|
||||||
- name: SYSTEM_ROOTURIPATH
|
|
||||||
value: {{ .Values.rootPath}}
|
|
||||||
{{- if .Values.envs }}
|
|
||||||
{{ toYaml .Values.envs | indent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.extraArgs }}
|
|
||||||
args:
|
|
||||||
{{ toYaml .Values.extraArgs | indent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: {{ .Values.rootPath}}
|
|
||||||
port: http
|
|
||||||
{{ toYaml .Values.probes.livenessHttpGetConfig | indent 12 }}
|
|
||||||
{{ toYaml .Values.probes.liveness | indent 10 }}
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: {{ .Values.rootPath}}
|
|
||||||
port: http
|
|
||||||
{{ toYaml .Values.probes.readinessHttpGetConfig | indent 12 }}
|
|
||||||
{{ toYaml .Values.probes.readiness | indent 10 }}
|
|
||||||
volumeMounts:
|
|
||||||
{{- if .Values.deployment.extraVolumeMounts }}
|
|
||||||
{{- toYaml .Values.deployment.extraVolumeMounts | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.deployment.sidecarContainers }}
|
|
||||||
{{- range $name, $spec := .Values.deployment.sidecarContainers }}
|
|
||||||
- name: {{ $name }}
|
|
||||||
{{- toYaml $spec | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.resources }}
|
|
||||||
resources:
|
|
||||||
{{ toYaml . | indent 10 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.nodeSelector }}
|
|
||||||
nodeSelector:
|
|
||||||
{{ toYaml . | indent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.affinity }}
|
|
||||||
affinity:
|
|
||||||
{{ toYaml . | indent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.tolerations }}
|
|
||||||
tolerations:
|
|
||||||
{{ toYaml . | indent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.schedulerName }}
|
|
||||||
schedulerName: {{ .Values.schedulerName }}
|
|
||||||
{{- end }}
|
|
||||||
serviceAccountName: {{ include "stirlingpdf.serviceAccountName" . }}
|
|
||||||
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
|
|
||||||
volumes:
|
|
||||||
{{- if .Values.deployment.extraVolumes }}
|
|
||||||
{{- toYaml .Values.deployment.extraVolumes | nindent 6 }}
|
|
||||||
{{- end }}
|
|
||||||
- name: storage-volume
|
|
||||||
{{- if .Values.persistence.enabled }}
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: {{ .Values.persistence.existingClaim | default (include "stirlingpdf.fullname" .) }}
|
|
||||||
{{- else }}
|
|
||||||
emptyDir: {}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
{{- if .Values.ingress.enabled }}
|
|
||||||
{{- $servicePort := .Values.service.externalPort -}}
|
|
||||||
{{- $serviceName := include "stirlingpdf.fullname" . -}}
|
|
||||||
{{- $ingressExtraPaths := .Values.ingress.extraPaths -}}
|
|
||||||
---
|
|
||||||
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion }}
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
{{- else if semverCompare "<1.19-0" .Capabilities.KubeVersion.GitVersion }}
|
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
|
||||||
{{- else }}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
{{- end }}
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: {{ include "stirlingpdf.fullname" . }}
|
|
||||||
{{- with .Values.ingress.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.ingress.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
{{- with .Values.ingress.ingressClassName }}
|
|
||||||
ingressClassName: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
rules:
|
|
||||||
{{- range .Values.ingress.hosts }}
|
|
||||||
- host: {{ .name }}
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
{{- range $ingressExtraPaths }}
|
|
||||||
- path: {{ default "/" .path | quote }}
|
|
||||||
backend:
|
|
||||||
{{- if semverCompare "<1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
|
||||||
{{- if $.Values.service.servicename }}
|
|
||||||
serviceName: {{ $.Values.service.servicename }}
|
|
||||||
{{- else }}
|
|
||||||
serviceName: {{ default $serviceName .service }}
|
|
||||||
{{- end }}
|
|
||||||
servicePort: {{ default $servicePort .port }}
|
|
||||||
{{- else }}
|
|
||||||
service:
|
|
||||||
{{- if $.Values.service.servicename }}
|
|
||||||
name: {{ $.Values.service.servicename }}
|
|
||||||
{{- else }}
|
|
||||||
name: {{ default $serviceName .service }}
|
|
||||||
{{- end }}
|
|
||||||
port:
|
|
||||||
number: {{ default $servicePort .port }}
|
|
||||||
pathType: {{ default $.Values.ingress.pathType .pathType }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
- path: {{ default "/" .path | quote }}
|
|
||||||
backend:
|
|
||||||
{{- if semverCompare "<1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
|
||||||
{{- if $.Values.service.servicename }}
|
|
||||||
serviceName: {{ $.Values.service.servicename }}
|
|
||||||
{{- else }}
|
|
||||||
serviceName: {{ default $serviceName .service }}
|
|
||||||
{{- end }}
|
|
||||||
servicePort: {{ default $servicePort .servicePort }}
|
|
||||||
{{- else }}
|
|
||||||
service:
|
|
||||||
{{- if $.Values.service.servicename }}
|
|
||||||
name: {{ $.Values.service.servicename }}
|
|
||||||
{{- else }}
|
|
||||||
name: {{ default $serviceName .service }}
|
|
||||||
{{- end }}
|
|
||||||
port:
|
|
||||||
number: {{ default $servicePort .port }}
|
|
||||||
pathType: {{ $.Values.ingress.pathType }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
tls:
|
|
||||||
{{- range .Values.ingress.hosts }}
|
|
||||||
{{- if .tls }}
|
|
||||||
- hosts:
|
|
||||||
- {{ .name }}
|
|
||||||
secretName: {{ .tlsSecret }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end -}}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{{- if .Values.persistence.pv.enabled -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolume
|
|
||||||
metadata:
|
|
||||||
name: {{ .Values.persistence.pv.pvname | default (include "stirlingpdf.fullname" .) }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
capacity:
|
|
||||||
storage: {{ .Values.persistence.pv.capacity.storage }}
|
|
||||||
accessModes:
|
|
||||||
- {{ .Values.persistence.pv.accessMode | quote }}
|
|
||||||
nfs:
|
|
||||||
server: {{ .Values.persistence.pv.nfs.server }}
|
|
||||||
path: {{ .Values.persistence.pv.nfs.path | quote }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}}
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: {{ include "stirlingpdf.fullname" . }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.persistence.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- {{ .Values.persistence.accessMode | quote }}
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: {{ .Values.persistence.size | quote }}
|
|
||||||
{{- if .Values.persistence.storageClass }}
|
|
||||||
{{- if (eq "-" .Values.persistence.storageClass) }}
|
|
||||||
storageClassName: ""
|
|
||||||
{{- else }}
|
|
||||||
storageClassName: "{{ .Values.persistence.storageClass }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.persistence.volumeName }}
|
|
||||||
volumeName: "{{ .Values.persistence.volumeName }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Values.service.servicename | default (include "stirlingpdf.fullname" .) }}
|
|
||||||
{{- with .Values.service.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.service.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.service.type }}
|
|
||||||
{{- if (or (eq .Values.service.type "LoadBalancer") (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort)))) }}
|
|
||||||
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if (and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP) }}
|
|
||||||
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if (and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges) }}
|
|
||||||
loadBalancerSourceRanges:
|
|
||||||
{{- with .Values.service.loadBalancerSourceRanges }}
|
|
||||||
{{ toYaml . | indent 2 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if eq .Values.service.type "ClusterIP" }}
|
|
||||||
{{- if .Values.service.clusterIP }}
|
|
||||||
clusterIP: {{ .Values.service.clusterIP }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.service.externalPort }}
|
|
||||||
{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
|
|
||||||
nodePort: {{.Values.service.nodePort}}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.service.targetPort }}
|
|
||||||
targetPort: {{ .Values.service.targetPort }}
|
|
||||||
name: {{ .Values.service.targetPort }}
|
|
||||||
{{- else }}
|
|
||||||
targetPort: http
|
|
||||||
name: http
|
|
||||||
{{- end }}
|
|
||||||
protocol: TCP
|
|
||||||
|
|
||||||
selector:
|
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 4 }}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{{- if .Values.serviceAccount.create -}}
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: {{ include "stirlingpdf.serviceAccountName" . }}
|
|
||||||
{{- with .Values.serviceAccount.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{ toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled ) }}
|
|
||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: ServiceMonitor
|
|
||||||
metadata:
|
|
||||||
name: {{ include "stirlingpdf.fullname" . }}
|
|
||||||
namespace: {{ .Values.serviceMonitor.namespace | default .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "stirlingpdf.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.serviceMonitor.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
endpoints:
|
|
||||||
- targetPort: 8080
|
|
||||||
{{- if .Values.serviceMonitor.interval }}
|
|
||||||
interval: {{ .Values.serviceMonitor.interval }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.serviceMonitor.metricsPath }}
|
|
||||||
path: {{ .Values.serviceMonitor.metricsPath }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.serviceMonitor.timeout }}
|
|
||||||
scrapeTimeout: {{ .Values.serviceMonitor.timeout }}
|
|
||||||
{{- end }}
|
|
||||||
jobLabel: {{ include "stirlingpdf.fullname" . }}
|
|
||||||
namespaceSelector:
|
|
||||||
matchNames:
|
|
||||||
- {{ .Release.Namespace }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "stirlingpdf.selectorLabels" . | nindent 6 }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
extraArgs:
|
|
||||||
[]
|
|
||||||
# - --storage-timestamp-tolerance 1s
|
|
||||||
replicaCount: 1
|
|
||||||
strategy:
|
|
||||||
type: RollingUpdate
|
|
||||||
image:
|
|
||||||
repository: frooodle/s-pdf
|
|
||||||
# took Chart appVersion by default
|
|
||||||
tag: ~
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
secret:
|
|
||||||
labels: {}
|
|
||||||
# -- Labels to apply to all resources
|
|
||||||
commonLabels: {}
|
|
||||||
# team_name: dev
|
|
||||||
|
|
||||||
# -- Rootpath for the application
|
|
||||||
rootPath: /
|
|
||||||
|
|
||||||
envs: []
|
|
||||||
# - name: UI_APP_NAME
|
|
||||||
# value: "Stirling PDF"
|
|
||||||
# - name: UI_HOME_DESCRIPTION
|
|
||||||
# value: "Your locally hosted one-stop-shop for all your PDF needs."
|
|
||||||
# - name: UI_APP_NAVBAR_NAME
|
|
||||||
# value: "Stirling PDF"
|
|
||||||
# - name: ALLOW_GOOGLE_VISIBILITY
|
|
||||||
# value: "true"
|
|
||||||
# - name: APP_LOCALE
|
|
||||||
# value: "en_GB"
|
|
||||||
|
|
||||||
deployment:
|
|
||||||
# -- Stirling-pdf Deployment annotations
|
|
||||||
annotations: {}
|
|
||||||
# name: value
|
|
||||||
labels: {}
|
|
||||||
# name: value
|
|
||||||
# -- Additional volumes
|
|
||||||
extraVolumes: []
|
|
||||||
# - name: nginx-config
|
|
||||||
# secret:
|
|
||||||
# secretName: nginx-config
|
|
||||||
# -- Additional volumes to mount
|
|
||||||
extraVolumeMounts: []
|
|
||||||
# -- sidecarContainers for the stirling-pdf
|
|
||||||
# -- Can be used to add a proxy to the pod that does
|
|
||||||
# -- scanning for secrets, signing, authentication, validation
|
|
||||||
# -- of the chart's content, send notifications...
|
|
||||||
sidecarContainers: {}
|
|
||||||
## Example sidecarContainer which uses an extraVolume from above and
|
|
||||||
## a named port that can be referenced in the service as targetPort.
|
|
||||||
# proxy:
|
|
||||||
# image: nginx:latest
|
|
||||||
# ports:
|
|
||||||
# - name: proxy
|
|
||||||
# containerPort: 8081
|
|
||||||
# volumeMounts:
|
|
||||||
# - name: nginx-config
|
|
||||||
# readOnly: true
|
|
||||||
# mountPath: /etc/nginx
|
|
||||||
|
|
||||||
# -- Pod annotations
|
|
||||||
# -- ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
|
||||||
# -- Read more about kube2iam to provide access to s3 https://github.com/jtblin/kube2iam
|
|
||||||
podAnnotations:
|
|
||||||
{}
|
|
||||||
# iam.amazonaws.com/role: role-arn
|
|
||||||
|
|
||||||
# -- Pod labels
|
|
||||||
# -- ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
|
||||||
podLabels:
|
|
||||||
{}
|
|
||||||
# name: value
|
|
||||||
|
|
||||||
service:
|
|
||||||
servicename:
|
|
||||||
type: ClusterIP
|
|
||||||
externalTrafficPolicy: Local
|
|
||||||
# -- Uses pre-assigned IP address from cloud provider
|
|
||||||
# -- Only valid if service.type: LoadBalancer
|
|
||||||
loadBalancerIP:
|
|
||||||
# -- Limits which cidr blocks can connect to service's load balancer
|
|
||||||
# -- Only valid if service.type: LoadBalancer
|
|
||||||
loadBalancerSourceRanges: []
|
|
||||||
# clusterIP: None
|
|
||||||
externalPort: 8080
|
|
||||||
# -- targetPort of the container to use. If a sidecar should handle the
|
|
||||||
# -- requests first, use the named port from the sidecar. See sidecar example
|
|
||||||
# -- from deployment above. Leave empty to use stirling-pdf directly.
|
|
||||||
targetPort:
|
|
||||||
nodePort:
|
|
||||||
annotations: {}
|
|
||||||
labels: {}
|
|
||||||
|
|
||||||
serviceMonitor:
|
|
||||||
enabled: false
|
|
||||||
# namespace: prometheus
|
|
||||||
labels: {}
|
|
||||||
metricsPath: "/metrics"
|
|
||||||
# timeout: 60
|
|
||||||
# interval: 60
|
|
||||||
|
|
||||||
resources: {}
|
|
||||||
# limits:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
# requests:
|
|
||||||
# cpu: 80m
|
|
||||||
# memory: 64Mi
|
|
||||||
|
|
||||||
probes:
|
|
||||||
liveness:
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 1
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessHttpGetConfig:
|
|
||||||
scheme: HTTP
|
|
||||||
readiness:
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 1
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
readinessHttpGetConfig:
|
|
||||||
scheme: HTTP
|
|
||||||
|
|
||||||
serviceAccount:
|
|
||||||
create: true
|
|
||||||
name: ""
|
|
||||||
automountServiceAccountToken: false
|
|
||||||
## Annotations for the Service Account
|
|
||||||
annotations: {}
|
|
||||||
|
|
||||||
# -- UID/GID 1000 is the default user "stirling-pdf" used in
|
|
||||||
# -- the container image starting in v0.8.0 and above. This
|
|
||||||
# -- is required for local persistent storage. If your cluster
|
|
||||||
# -- does not allow this, try setting securityContext: {}
|
|
||||||
securityContext:
|
|
||||||
enabled: true
|
|
||||||
fsGroup: 1000
|
|
||||||
## Optionally, specify supplementalGroups and/or
|
|
||||||
## runAsNonRoot for security purposes
|
|
||||||
# runAsNonRoot: true
|
|
||||||
# supplementalGroups: [1000]
|
|
||||||
|
|
||||||
containerSecurityContext: {}
|
|
||||||
|
|
||||||
priorityClassName: ""
|
|
||||||
|
|
||||||
nodeSelector: {}
|
|
||||||
|
|
||||||
tolerations: []
|
|
||||||
|
|
||||||
affinity: {}
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
enabled: false
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 8Gi
|
|
||||||
labels:
|
|
||||||
{}
|
|
||||||
# name: value
|
|
||||||
path: /tmp
|
|
||||||
## A manually managed Persistent Volume and Claim
|
|
||||||
## Requires persistence.enabled: true
|
|
||||||
## If defined, PVC must be created manually before volume will be bound
|
|
||||||
# existingClaim:
|
|
||||||
|
|
||||||
# -- stirling-pdf data Persistent Volume Storage Class
|
|
||||||
# If defined, storageClassName: <storageClass>
|
|
||||||
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
|
||||||
# If undefined (the default) or set to null, no storageClassName spec is
|
|
||||||
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
|
||||||
# GKE, AWS & OpenStack)
|
|
||||||
# storageClass: "-"
|
|
||||||
# volumeName:
|
|
||||||
pv:
|
|
||||||
enabled: false
|
|
||||||
pvname:
|
|
||||||
capacity:
|
|
||||||
storage: 8Gi
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
nfs:
|
|
||||||
server:
|
|
||||||
path:
|
|
||||||
|
|
||||||
# -- Init containers parameters:
|
|
||||||
# -- volumePermissions: Change the owner of the persistent volume mountpoint to RunAsUser:fsGroup
|
|
||||||
volumePermissions:
|
|
||||||
image:
|
|
||||||
registry: docker.io
|
|
||||||
repository: bitnami/minideb
|
|
||||||
tag: buster
|
|
||||||
pullPolicy: Always
|
|
||||||
## Optionally specify an array of imagePullSecrets.
|
|
||||||
## Secrets must be manually created in the namespace.
|
|
||||||
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
|
||||||
##
|
|
||||||
# pullSecrets:
|
|
||||||
# - myRegistryKeySecretName
|
|
||||||
|
|
||||||
# -- Ingress for load balancer
|
|
||||||
ingress:
|
|
||||||
enabled: false
|
|
||||||
pathType: "ImplementationSpecific"
|
|
||||||
# -- Stirling-pdf Ingress labels
|
|
||||||
labels:
|
|
||||||
{}
|
|
||||||
# dns: "route53"
|
|
||||||
|
|
||||||
# -- Stirling-pdf Ingress annotations
|
|
||||||
annotations:
|
|
||||||
{}
|
|
||||||
# kubernetes.io/ingress.class: nginx
|
|
||||||
# kubernetes.io/tls-acme: "true"
|
|
||||||
|
|
||||||
# -- Stirling-pdf Ingress hostnames
|
|
||||||
# -- Must be provided if Ingress is enabled
|
|
||||||
hosts:
|
|
||||||
[]
|
|
||||||
# - name: stirling-pdf.domain1.com
|
|
||||||
# path: /
|
|
||||||
# tls: false
|
|
||||||
# - name: stirling-pdf.domain2.com
|
|
||||||
# path: /
|
|
||||||
#
|
|
||||||
# ## Set this to true in order to enable TLS on the ingress record
|
|
||||||
# tls: true
|
|
||||||
#
|
|
||||||
# ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS
|
|
||||||
# ## Secrets must be added manually to the namespace
|
|
||||||
# tlsSecret: stirling-pdf.domain2-tls
|
|
||||||
|
|
||||||
# -- For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName
|
|
||||||
# -- See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress
|
|
||||||
ingressClassName:
|
|
||||||
@@ -13,7 +13,6 @@ ignore = [
|
|||||||
'PDFToText.tags',
|
'PDFToText.tags',
|
||||||
'adminUserSettings.admin',
|
'adminUserSettings.admin',
|
||||||
'language.direction',
|
'language.direction',
|
||||||
'survey.button',
|
|
||||||
'watermark.type.1',
|
'watermark.type.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ ignore = [
|
|||||||
ignore = [
|
ignore = [
|
||||||
'AddStampRequest.alphabet',
|
'AddStampRequest.alphabet',
|
||||||
'AddStampRequest.position',
|
'AddStampRequest.position',
|
||||||
|
'home.pipeline.title'
|
||||||
'PDFToBook.selectText.1',
|
'PDFToBook.selectText.1',
|
||||||
'PDFToText.tags',
|
'PDFToText.tags',
|
||||||
'addPageNumbers.selectText.3',
|
'addPageNumbers.selectText.3',
|
||||||
@@ -42,9 +42,11 @@ ignore = [
|
|||||||
'licenses.version',
|
'licenses.version',
|
||||||
'pipeline.title',
|
'pipeline.title',
|
||||||
'pipelineOptions.pipelineHeader',
|
'pipelineOptions.pipelineHeader',
|
||||||
|
'pro',
|
||||||
'sponsor',
|
'sponsor',
|
||||||
'text',
|
'text',
|
||||||
'watermark.type.1',
|
'watermark.type.1',
|
||||||
|
'certSign.name',
|
||||||
]
|
]
|
||||||
|
|
||||||
[el_GR]
|
[el_GR]
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Convert", "img-to-pdf");
|
addEndpointToGroup("Convert", "img-to-pdf");
|
||||||
addEndpointToGroup("Convert", "pdf-to-pdfa");
|
addEndpointToGroup("Convert", "pdf-to-pdfa");
|
||||||
addEndpointToGroup("Convert", "file-to-pdf");
|
addEndpointToGroup("Convert", "file-to-pdf");
|
||||||
addEndpointToGroup("Convert", "xlsx-to-pdf");
|
|
||||||
addEndpointToGroup("Convert", "pdf-to-word");
|
addEndpointToGroup("Convert", "pdf-to-word");
|
||||||
addEndpointToGroup("Convert", "pdf-to-presentation");
|
addEndpointToGroup("Convert", "pdf-to-presentation");
|
||||||
addEndpointToGroup("Convert", "pdf-to-text");
|
addEndpointToGroup("Convert", "pdf-to-text");
|
||||||
@@ -163,7 +162,6 @@ public class EndpointConfiguration {
|
|||||||
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");
|
||||||
addEndpointToGroup("CLI", "xlsx-to-pdf");
|
|
||||||
addEndpointToGroup("CLI", "pdf-to-word");
|
addEndpointToGroup("CLI", "pdf-to-word");
|
||||||
addEndpointToGroup("CLI", "pdf-to-presentation");
|
addEndpointToGroup("CLI", "pdf-to-presentation");
|
||||||
addEndpointToGroup("CLI", "pdf-to-html");
|
addEndpointToGroup("CLI", "pdf-to-html");
|
||||||
@@ -184,6 +182,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Python", "html-to-pdf");
|
addEndpointToGroup("Python", "html-to-pdf");
|
||||||
addEndpointToGroup("Python", "url-to-pdf");
|
addEndpointToGroup("Python", "url-to-pdf");
|
||||||
addEndpointToGroup("Python", "pdf-to-img");
|
addEndpointToGroup("Python", "pdf-to-img");
|
||||||
|
addEndpointToGroup("Python", "file-to-pdf");
|
||||||
|
|
||||||
// openCV
|
// openCV
|
||||||
addEndpointToGroup("OpenCV", "extract-image-scans");
|
addEndpointToGroup("OpenCV", "extract-image-scans");
|
||||||
@@ -191,13 +190,15 @@ public class EndpointConfiguration {
|
|||||||
// LibreOffice
|
// LibreOffice
|
||||||
addEndpointToGroup("LibreOffice", "repair");
|
addEndpointToGroup("LibreOffice", "repair");
|
||||||
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
||||||
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
|
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-word");
|
addEndpointToGroup("LibreOffice", "pdf-to-word");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
|
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-rtf");
|
addEndpointToGroup("LibreOffice", "pdf-to-rtf");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-html");
|
addEndpointToGroup("LibreOffice", "pdf-to-html");
|
||||||
addEndpointToGroup("LibreOffice", "pdf-to-xml");
|
addEndpointToGroup("LibreOffice", "pdf-to-xml");
|
||||||
|
|
||||||
|
// Unoconv
|
||||||
|
addEndpointToGroup("Unoconv", "file-to-pdf");
|
||||||
|
|
||||||
// OCRmyPDF
|
// OCRmyPDF
|
||||||
addEndpointToGroup("OCRmyPDF", "compress-pdf");
|
addEndpointToGroup("OCRmyPDF", "compress-pdf");
|
||||||
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
|
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
|
||||||
@@ -250,6 +251,7 @@ public class EndpointConfiguration {
|
|||||||
// Ghostscript dependent endpoints
|
// Ghostscript dependent endpoints
|
||||||
addEndpointToGroup("Ghostscript", "compress-pdf");
|
addEndpointToGroup("Ghostscript", "compress-pdf");
|
||||||
addEndpointToGroup("Ghostscript", "pdf-to-pdfa");
|
addEndpointToGroup("Ghostscript", "pdf-to-pdfa");
|
||||||
|
addEndpointToGroup("Ghostscript", "repair");
|
||||||
|
|
||||||
// Weasyprint dependent endpoints
|
// Weasyprint dependent endpoints
|
||||||
addEndpointToGroup("Weasyprint", "html-to-pdf");
|
addEndpointToGroup("Weasyprint", "html-to-pdf");
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class ExternalAppDepConfig {
|
|||||||
put("ocrmypdf", List.of("OCRmyPDF"));
|
put("ocrmypdf", List.of("OCRmyPDF"));
|
||||||
put("weasyprint", List.of("Weasyprint"));
|
put("weasyprint", List.of("Weasyprint"));
|
||||||
put("pdftohtml", List.of("Pdftohtml"));
|
put("pdftohtml", List.of("Pdftohtml"));
|
||||||
|
put("unoconv", List.of("Unoconv"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ public class ExternalAppDepConfig {
|
|||||||
checkDependencyAndDisableGroup("ocrmypdf");
|
checkDependencyAndDisableGroup("ocrmypdf");
|
||||||
checkDependencyAndDisableGroup("weasyprint");
|
checkDependencyAndDisableGroup("weasyprint");
|
||||||
checkDependencyAndDisableGroup("pdftohtml");
|
checkDependencyAndDisableGroup("pdftohtml");
|
||||||
|
checkDependencyAndDisableGroup("unoconv");
|
||||||
|
|
||||||
// Special handling for Python/OpenCV dependencies
|
// Special handling for Python/OpenCV dependencies
|
||||||
boolean pythonAvailable = isCommandAvailable("python3") || isCommandAvailable("python");
|
boolean pythonAvailable = isCommandAvailable("python3") || isCommandAvailable("python");
|
||||||
|
|||||||
@@ -156,10 +156,14 @@ public class SecurityConfiguration {
|
|||||||
http.rememberMe(
|
http.rememberMe(
|
||||||
rememberMeConfigurer ->
|
rememberMeConfigurer ->
|
||||||
rememberMeConfigurer // Use the configurator directly
|
rememberMeConfigurer // Use the configurator directly
|
||||||
.key("uniqueAndSecret")
|
|
||||||
.tokenRepository(persistentTokenRepository())
|
.tokenRepository(persistentTokenRepository())
|
||||||
.tokenValiditySeconds(1209600) // 2 weeks
|
.tokenValiditySeconds(14 * 24 * 60 * 60) // 14 days
|
||||||
);
|
.userDetailsService(
|
||||||
|
userDetailsService) // Your existing UserDetailsService
|
||||||
|
.useSecureCookie(true) // Enable secure cookie
|
||||||
|
.rememberMeParameter("remember-me") // Form parameter name
|
||||||
|
.rememberMeCookieName("remember-me") // Cookie name
|
||||||
|
.alwaysRemember(false));
|
||||||
http.authorizeHttpRequests(
|
http.authorizeHttpRequests(
|
||||||
authz ->
|
authz ->
|
||||||
authz.requestMatchers(
|
authz.requestMatchers(
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.AuthenticationType;
|
import stirling.software.SPDF.model.AuthenticationType;
|
||||||
import stirling.software.SPDF.model.Authority;
|
import stirling.software.SPDF.model.Authority;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
@@ -31,6 +33,7 @@ import stirling.software.SPDF.repository.AuthorityRepository;
|
|||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class UserService implements UserServiceInterface {
|
public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
@@ -45,6 +48,8 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
@Autowired DatabaseBackupInterface databaseBackupHelper;
|
@Autowired DatabaseBackupInterface databaseBackupHelper;
|
||||||
|
|
||||||
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
// Handle OAUTH2 login and user auto creation.
|
// Handle OAUTH2 login and user auto creation.
|
||||||
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
|
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
|
||||||
throws IllegalArgumentException, IOException {
|
throws IllegalArgumentException, IOException {
|
||||||
@@ -299,7 +304,13 @@ public class UserService implements UserServiceInterface {
|
|||||||
boolean isValidEmail =
|
boolean isValidEmail =
|
||||||
username.matches(
|
username.matches(
|
||||||
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
|
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
|
||||||
return isValidSimpleUsername || isValidEmail;
|
|
||||||
|
List<String> notAllowedUserList = new ArrayList<>();
|
||||||
|
notAllowedUserList.add("ALL_USERS".toLowerCase());
|
||||||
|
|
||||||
|
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
|
||||||
|
|
||||||
|
return (isValidSimpleUsername || isValidEmail) && !notAllowedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getInvalidUsernameMessage() {
|
private String getInvalidUsernameMessage() {
|
||||||
@@ -354,6 +365,14 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
if (principal instanceof UserDetails) {
|
if (principal instanceof UserDetails) {
|
||||||
return ((UserDetails) principal).getUsername();
|
return ((UserDetails) principal).getUsername();
|
||||||
|
} else if (principal instanceof OAuth2User) {
|
||||||
|
return ((OAuth2User) principal)
|
||||||
|
.getAttribute(
|
||||||
|
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
|
||||||
|
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
||||||
|
return ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
||||||
|
} else if (principal instanceof String) {
|
||||||
|
return (String) principal;
|
||||||
} else {
|
} else {
|
||||||
return principal.toString();
|
return principal.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
package stirling.software.SPDF.config.security.saml2;
|
package stirling.software.SPDF.config.security.saml2;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
import org.bouncycastle.util.io.pem.PemObject;
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
import org.bouncycastle.util.io.pem.PemReader;
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.UrlResource;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CertificateUtils {
|
public class CertificateUtils {
|
||||||
|
|
||||||
public static X509Certificate readCertificate(Resource certificateResource) throws Exception {
|
public static X509Certificate readCertificate(Resource certificateResource) throws Exception {
|
||||||
@@ -39,4 +46,84 @@ public class CertificateUtils {
|
|||||||
.generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
|
.generatePrivate(new PKCS8EncodedKeySpec(decodedKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static X509Certificate getIdPCertificate(Resource certificateResource) throws Exception {
|
||||||
|
|
||||||
|
if (certificateResource instanceof UrlResource) {
|
||||||
|
return extractCertificateFromMetadata(certificateResource);
|
||||||
|
} else {
|
||||||
|
// Treat as file resource
|
||||||
|
return readCertificate(certificateResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static X509Certificate extractCertificateFromMetadata(Resource metadataResource) throws Exception {
|
||||||
|
log.info("Attempting to extract certificate from metadata resource: {}", metadataResource.getDescription());
|
||||||
|
|
||||||
|
try (InputStream is = metadataResource.getInputStream()) {
|
||||||
|
String content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
log.info("Retrieved metadata content, length: {}", content.length());
|
||||||
|
|
||||||
|
// Find the certificate data
|
||||||
|
int startIndex = content.indexOf("<ds:X509Certificate>");
|
||||||
|
int endIndex = content.indexOf("</ds:X509Certificate>");
|
||||||
|
|
||||||
|
if (startIndex == -1 || endIndex == -1) {
|
||||||
|
log.error("Certificate tags not found in metadata");
|
||||||
|
throw new Exception("Certificate tags not found in metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract certificate data
|
||||||
|
String certData = content.substring(
|
||||||
|
startIndex + "<ds:X509Certificate>".length(),
|
||||||
|
endIndex
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
log.info("Found certificate data, length: {}", certData.length());
|
||||||
|
|
||||||
|
// Remove any whitespace and newlines from cert data
|
||||||
|
certData = certData.replaceAll("\\s+", "");
|
||||||
|
|
||||||
|
// Reconstruct PEM format with proper line breaks
|
||||||
|
StringBuilder pemBuilder = new StringBuilder();
|
||||||
|
pemBuilder.append("-----BEGIN CERTIFICATE-----\n");
|
||||||
|
|
||||||
|
// Insert line breaks every 64 characters
|
||||||
|
int lineLength = 64;
|
||||||
|
for (int i = 0; i < certData.length(); i += lineLength) {
|
||||||
|
int end = Math.min(i + lineLength, certData.length());
|
||||||
|
pemBuilder.append(certData, i, end).append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
pemBuilder.append("-----END CERTIFICATE-----");
|
||||||
|
String pemCert = pemBuilder.toString();
|
||||||
|
|
||||||
|
log.debug("Reconstructed PEM certificate:\n{}", pemCert);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream pemStream = new ByteArrayInputStream(pemCert.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
X509Certificate cert = (X509Certificate) cf.generateCertificate(pemStream);
|
||||||
|
|
||||||
|
log.info("Successfully parsed certificate. Subject: {}", cert.getSubjectX500Principal());
|
||||||
|
|
||||||
|
// Optional: check validity dates
|
||||||
|
cert.checkValidity(); // Throws CertificateExpiredException if expired
|
||||||
|
log.info("Certificate is valid (not expired)");
|
||||||
|
|
||||||
|
return cert;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to parse certificate", e);
|
||||||
|
throw new Exception("Failed to parse X509 certificate from metadata", e);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error processing metadata resource", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,23 +16,35 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationFailure(
|
public void onAuthenticationFailure(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AuthenticationException exception)
|
AuthenticationException exception)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
if (exception instanceof Saml2AuthenticationException) {
|
|
||||||
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
|
if (exception instanceof Saml2AuthenticationException saml2Exception) {
|
||||||
getRedirectStrategy()
|
Saml2Error error = saml2Exception.getSaml2Error();
|
||||||
.sendRedirect(request, response, "/login?erroroauth=" + error.getErrorCode());
|
|
||||||
} else if (exception instanceof ProviderNotFoundException) {
|
// Log detailed information about the SAML error
|
||||||
getRedirectStrategy()
|
log.error("SAML Authentication failed with error code: {}", error.getErrorCode());
|
||||||
.sendRedirect(
|
log.error("Error description: {}", error.getDescription());
|
||||||
request,
|
|
||||||
response,
|
// Redirect to login with specific error code
|
||||||
"/login?erroroauth=not_authentication_provider_found");
|
getRedirectStrategy()
|
||||||
}
|
.sendRedirect(request, response, "/login?erroroauth=" + error.getErrorCode());
|
||||||
log.error("AuthenticationException: " + exception);
|
} else if (exception instanceof ProviderNotFoundException) {
|
||||||
}
|
log.error("Authentication failed: No authentication provider found");
|
||||||
|
|
||||||
|
getRedirectStrategy()
|
||||||
|
.sendRedirect(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
"/login?erroroauth=not_authentication_provider_found");
|
||||||
|
} else {
|
||||||
|
log.error("Unknown AuthenticationException: {}", exception.getMessage());
|
||||||
|
getRedirectStrategy().sendRedirect(request, response, "/login?erroroauth=unknown_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package stirling.software.SPDF.config.security.saml2;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||||
|
|
||||||
|
public class LoggingSamlAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(LoggingSamlAuthenticationProvider.class);
|
||||||
|
private final OpenSaml4AuthenticationProvider delegate;
|
||||||
|
|
||||||
|
public LoggingSamlAuthenticationProvider(OpenSaml4AuthenticationProvider delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
|
if (authentication instanceof Saml2AuthenticationToken token) {
|
||||||
|
String samlResponse = token.getSaml2Response();
|
||||||
|
|
||||||
|
// Log the raw SAML response
|
||||||
|
log.info("Raw SAML Response (Base64): {}", samlResponse);
|
||||||
|
|
||||||
|
// Decode and log the SAML response XML
|
||||||
|
try {
|
||||||
|
String decodedResponse = new String(Base64.getDecoder().decode(samlResponse), StandardCharsets.UTF_8);
|
||||||
|
log.info("Decoded SAML Response XML:\n{}", decodedResponse);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// If decoding fails, it’s likely already plain XML
|
||||||
|
log.warn("SAML Response appears to be different format, not Base64-encoded.");
|
||||||
|
log.debug("SAML Response XML:\n{}", samlResponse);
|
||||||
|
}
|
||||||
|
// Delegate the actual authentication to the wrapped OpenSaml4AuthenticationProvider
|
||||||
|
try {
|
||||||
|
return delegate.authenticate(authentication);
|
||||||
|
} catch (Saml2AuthenticationException e) {
|
||||||
|
log.error("SAML authentication failed: {}");
|
||||||
|
log.error("Detailed error message: {}", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> authentication) {
|
||||||
|
// Only support Saml2AuthenticationToken
|
||||||
|
return Saml2AuthenticationToken.class.isAssignableFrom(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,12 +13,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest;
|
import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest;
|
||||||
import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService;
|
import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/misc")
|
@RequestMapping("/api/v1/misc")
|
||||||
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
public class ReplaceAndInvertColorController {
|
public class ReplaceAndInvertColorController {
|
||||||
|
|
||||||
private ReplaceAndInvertColorService replaceAndInvertColorService;
|
private ReplaceAndInvertColorService replaceAndInvertColorService;
|
||||||
|
|||||||
@@ -187,18 +187,31 @@ public class WatermarkController {
|
|||||||
float watermarkHeight = heightSpacer + fontSize * textLines.length;
|
float watermarkHeight = heightSpacer + fontSize * textLines.length;
|
||||||
float pageWidth = page.getMediaBox().getWidth();
|
float pageWidth = page.getMediaBox().getWidth();
|
||||||
float pageHeight = page.getMediaBox().getHeight();
|
float pageHeight = page.getMediaBox().getHeight();
|
||||||
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
|
|
||||||
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
|
// Calculating the new width and height depending on the angle.
|
||||||
|
float radians = (float) Math.toRadians(rotation);
|
||||||
|
float newWatermarkWidth =
|
||||||
|
(float)
|
||||||
|
(Math.abs(watermarkWidth * Math.cos(radians))
|
||||||
|
+ Math.abs(watermarkHeight * Math.sin(radians)));
|
||||||
|
float newWatermarkHeight =
|
||||||
|
(float)
|
||||||
|
(Math.abs(watermarkWidth * Math.sin(radians))
|
||||||
|
+ Math.abs(watermarkHeight * Math.cos(radians)));
|
||||||
|
|
||||||
|
// Calculating the number of rows and columns.
|
||||||
|
int watermarkRows = (int) (pageHeight / newWatermarkHeight + 1);
|
||||||
|
int watermarkCols = (int) (pageWidth / newWatermarkWidth + 1);
|
||||||
|
|
||||||
// Add the text watermark
|
// Add the text watermark
|
||||||
for (int i = 0; i < watermarkRows; i++) {
|
for (int i = 0; i <= watermarkRows; i++) {
|
||||||
for (int j = 0; j < watermarkCols; j++) {
|
for (int j = 0; j <= watermarkCols; j++) {
|
||||||
contentStream.beginText();
|
contentStream.beginText();
|
||||||
contentStream.setTextMatrix(
|
contentStream.setTextMatrix(
|
||||||
Matrix.getRotateInstance(
|
Matrix.getRotateInstance(
|
||||||
(float) Math.toRadians(rotation),
|
(float) Math.toRadians(rotation),
|
||||||
j * watermarkWidth,
|
j * newWatermarkWidth,
|
||||||
i * watermarkHeight));
|
i * newWatermarkHeight));
|
||||||
|
|
||||||
for (int k = 0; k < textLines.length; ++k) {
|
for (int k = 0; k < textLines.length; ++k) {
|
||||||
contentStream.showText(textLines[k]);
|
contentStream.showText(textLines[k]);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
|||||||
import stirling.software.SPDF.service.SignatureService;
|
import stirling.software.SPDF.service.SignatureService;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/api/v1/general/")
|
@RequestMapping("/api/v1/general")
|
||||||
public class SignatureController {
|
public class SignatureController {
|
||||||
|
|
||||||
@Autowired private SignatureService signatureService;
|
@Autowired private SignatureService signatureService;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.springframework.core.annotation.Order;
|
|||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.UrlResource;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -30,6 +31,7 @@ import stirling.software.SPDF.model.provider.GithubProvider;
|
|||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||||
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "")
|
@ConfigurationProperties(prefix = "")
|
||||||
@@ -47,6 +49,7 @@ public class ApplicationProperties {
|
|||||||
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
|
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
|
||||||
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
|
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
|
||||||
private AutoPipeline autoPipeline = new AutoPipeline();
|
private AutoPipeline autoPipeline = new AutoPipeline();
|
||||||
|
private ProcessExecutor processExecutor = new ProcessExecutor();
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class AutoPipeline {
|
public static class AutoPipeline {
|
||||||
@@ -133,44 +136,20 @@ public class ApplicationProperties {
|
|||||||
private String privateKey;
|
private String privateKey;
|
||||||
private String spCert;
|
private String spCert;
|
||||||
|
|
||||||
public InputStream getIdpMetadataUri() throws IOException {
|
public Resource getIdpMetadataUri() throws IOException {
|
||||||
if (idpMetadataUri.startsWith("classpath:")) {
|
return GeneralUtils.filePathToResource(idpMetadataUri);
|
||||||
return new ClassPathResource(idpMetadataUri.substring("classpath".length()))
|
|
||||||
.getInputStream();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
URI uri = new URI(idpMetadataUri);
|
|
||||||
URL url = uri.toURL();
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.setRequestMethod("GET");
|
|
||||||
return connection.getInputStream();
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new IOException("Invalid URI format: " + idpMetadataUri, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource getSpCert() {
|
public Resource getSpCert() {
|
||||||
if (spCert.startsWith("classpath:")) {
|
return GeneralUtils.filePathToResource(spCert);
|
||||||
return new ClassPathResource(spCert.substring("classpath:".length()));
|
|
||||||
} else {
|
|
||||||
return new FileSystemResource(spCert);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource getidpCert() {
|
public Resource getidpCert() {
|
||||||
if (idpCert.startsWith("classpath:")) {
|
return GeneralUtils.filePathToResource(idpCert);
|
||||||
return new ClassPathResource(idpCert.substring("classpath:".length()));
|
|
||||||
} else {
|
|
||||||
return new FileSystemResource(idpCert);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource getPrivateKey() {
|
public Resource getPrivateKey() {
|
||||||
if (privateKey.startsWith("classpath:")) {
|
return GeneralUtils.filePathToResource(privateKey);
|
||||||
return new ClassPathResource(privateKey.substring("classpath:".length()));
|
|
||||||
} else {
|
|
||||||
return new FileSystemResource(privateKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,4 +288,98 @@ public class ApplicationProperties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ProcessExecutor {
|
||||||
|
private SessionLimit sessionLimit = new SessionLimit();
|
||||||
|
private TimeoutMinutes timeoutMinutes = new TimeoutMinutes();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class SessionLimit {
|
||||||
|
private int libreOfficeSessionLimit;
|
||||||
|
private int pdfToHtmlSessionLimit;
|
||||||
|
private int ocrMyPdfSessionLimit;
|
||||||
|
private int pythonOpenCvSessionLimit;
|
||||||
|
private int ghostScriptSessionLimit;
|
||||||
|
private int weasyPrintSessionLimit;
|
||||||
|
private int installAppSessionLimit;
|
||||||
|
private int calibreSessionLimit;
|
||||||
|
|
||||||
|
public int getLibreOfficeSessionLimit() {
|
||||||
|
return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPdfToHtmlSessionLimit() {
|
||||||
|
return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOcrMyPdfSessionLimit() {
|
||||||
|
return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPythonOpenCvSessionLimit() {
|
||||||
|
return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGhostScriptSessionLimit() {
|
||||||
|
return ghostScriptSessionLimit > 0 ? ghostScriptSessionLimit : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWeasyPrintSessionLimit() {
|
||||||
|
return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInstallAppSessionLimit() {
|
||||||
|
return installAppSessionLimit > 0 ? installAppSessionLimit : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCalibreSessionLimit() {
|
||||||
|
return calibreSessionLimit > 0 ? calibreSessionLimit : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class TimeoutMinutes {
|
||||||
|
private long libreOfficeTimeoutMinutes;
|
||||||
|
private long pdfToHtmlTimeoutMinutes;
|
||||||
|
private long ocrMyPdfTimeoutMinutes;
|
||||||
|
private long pythonOpenCvTimeoutMinutes;
|
||||||
|
private long ghostScriptTimeoutMinutes;
|
||||||
|
private long weasyPrintTimeoutMinutes;
|
||||||
|
private long installAppTimeoutMinutes;
|
||||||
|
private long calibreTimeoutMinutes;
|
||||||
|
|
||||||
|
public long getLibreOfficeTimeoutMinutes() {
|
||||||
|
return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPdfToHtmlTimeoutMinutes() {
|
||||||
|
return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOcrMyPdfTimeoutMinutes() {
|
||||||
|
return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPythonOpenCvTimeoutMinutes() {
|
||||||
|
return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getGhostScriptTimeoutMinutes() {
|
||||||
|
return ghostScriptTimeoutMinutes > 0 ? ghostScriptTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getWeasyPrintTimeoutMinutes() {
|
||||||
|
return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getInstallAppTimeoutMinutes() {
|
||||||
|
return installAppTimeoutMinutes > 0 ? installAppTimeoutMinutes : 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCalibreTimeoutMinutes() {
|
||||||
|
return calibreTimeoutMinutes > 0 ? calibreTimeoutMinutes : 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.Date;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
|
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.PersistentLogin;
|
import stirling.software.SPDF.model.PersistentLogin;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
|||||||
@Autowired private PersistentLoginRepository persistentLoginRepository;
|
@Autowired private PersistentLoginRepository persistentLoginRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void createNewToken(PersistentRememberMeToken token) {
|
public void createNewToken(PersistentRememberMeToken token) {
|
||||||
PersistentLogin newToken = new PersistentLogin();
|
PersistentLogin newToken = new PersistentLogin();
|
||||||
newToken.setSeries(token.getSeries());
|
newToken.setSeries(token.getSeries());
|
||||||
@@ -23,6 +25,7 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void updateToken(String series, String tokenValue, Date lastUsed) {
|
public void updateToken(String series, String tokenValue, Date lastUsed) {
|
||||||
PersistentLogin existingToken = persistentLoginRepository.findById(series).orElse(null);
|
PersistentLogin existingToken = persistentLoginRepository.findById(series).orElse(null);
|
||||||
if (existingToken != null) {
|
if (existingToken != null) {
|
||||||
@@ -43,11 +46,11 @@ public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void removeUserTokens(String username) {
|
public void removeUserTokens(String username) {
|
||||||
for (PersistentLogin token : persistentLoginRepository.findAll()) {
|
try {
|
||||||
if (token.getUsername().equals(username)) {
|
persistentLoginRepository.deleteByUsername(username);
|
||||||
persistentLoginRepository.delete(token);
|
} catch (Exception e) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ import org.springframework.stereotype.Repository;
|
|||||||
import stirling.software.SPDF.model.PersistentLogin;
|
import stirling.software.SPDF.model.PersistentLogin;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PersistentLoginRepository extends JpaRepository<PersistentLogin, String> {}
|
public interface PersistentLoginRepository extends JpaRepository<PersistentLogin, String> {
|
||||||
|
void deleteByUsername(String username);
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
|||||||
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.UrlResource;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import com.fathzer.soft.javaluator.DoubleEvaluator;
|
import com.fathzer.soft.javaluator.DoubleEvaluator;
|
||||||
@@ -349,4 +353,23 @@ public class GeneralUtils {
|
|||||||
return "GenericID";
|
return "GenericID";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Resource filePathToResource(String resourceFile) {
|
||||||
|
if (resourceFile == null) {
|
||||||
|
throw new IllegalStateException("file is not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceFile.startsWith("classpath:")) {
|
||||||
|
return new ClassPathResource(resourceFile.substring("classpath:".length()));
|
||||||
|
} else if (resourceFile.startsWith("http://") || resourceFile.startsWith("https://")) {
|
||||||
|
try {
|
||||||
|
return new UrlResource(resourceFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to create URL resource: " + resourceFile, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new FileSystemResource(resourceFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import io.github.pixee.security.BoundedLineReader;
|
import io.github.pixee.security.BoundedLineReader;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
public class ProcessExecutor {
|
public class ProcessExecutor {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);
|
private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);
|
||||||
|
|
||||||
|
private static ApplicationProperties applicationProperties = new ApplicationProperties();
|
||||||
|
|
||||||
public enum Processes {
|
public enum Processes {
|
||||||
LIBRE_OFFICE,
|
LIBRE_OFFICE,
|
||||||
PDFTOHTML,
|
PDFTOHTML,
|
||||||
@@ -45,26 +49,90 @@ public class ProcessExecutor {
|
|||||||
key -> {
|
key -> {
|
||||||
int semaphoreLimit =
|
int semaphoreLimit =
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case LIBRE_OFFICE -> 1;
|
case LIBRE_OFFICE ->
|
||||||
case PDFTOHTML -> 1;
|
applicationProperties
|
||||||
case OCR_MY_PDF -> 2;
|
.getProcessExecutor()
|
||||||
case PYTHON_OPENCV -> 8;
|
.getSessionLimit()
|
||||||
case GHOSTSCRIPT -> 16;
|
.getLibreOfficeSessionLimit();
|
||||||
case WEASYPRINT -> 16;
|
case PDFTOHTML ->
|
||||||
case INSTALL_APP -> 1;
|
applicationProperties
|
||||||
case CALIBRE -> 1;
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getPdfToHtmlSessionLimit();
|
||||||
|
case OCR_MY_PDF ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getOcrMyPdfSessionLimit();
|
||||||
|
case PYTHON_OPENCV ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getPythonOpenCvSessionLimit();
|
||||||
|
case GHOSTSCRIPT ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getGhostScriptSessionLimit();
|
||||||
|
case WEASYPRINT ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getWeasyPrintSessionLimit();
|
||||||
|
case INSTALL_APP ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getInstallAppSessionLimit();
|
||||||
|
case CALIBRE ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getSessionLimit()
|
||||||
|
.getCalibreSessionLimit();
|
||||||
};
|
};
|
||||||
|
|
||||||
long timeoutMinutes =
|
long timeoutMinutes =
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case LIBRE_OFFICE -> 30;
|
case LIBRE_OFFICE ->
|
||||||
case PDFTOHTML -> 20;
|
applicationProperties
|
||||||
case OCR_MY_PDF -> 30;
|
.getProcessExecutor()
|
||||||
case PYTHON_OPENCV -> 30;
|
.getTimeoutMinutes()
|
||||||
case GHOSTSCRIPT -> 30;
|
.getLibreOfficeTimeoutMinutes();
|
||||||
case WEASYPRINT -> 30;
|
case PDFTOHTML ->
|
||||||
case INSTALL_APP -> 60;
|
applicationProperties
|
||||||
case CALIBRE -> 30;
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getPdfToHtmlTimeoutMinutes();
|
||||||
|
case OCR_MY_PDF ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getOcrMyPdfTimeoutMinutes();
|
||||||
|
case PYTHON_OPENCV ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getPythonOpenCvTimeoutMinutes();
|
||||||
|
case GHOSTSCRIPT ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getGhostScriptTimeoutMinutes();
|
||||||
|
case WEASYPRINT ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getWeasyPrintTimeoutMinutes();
|
||||||
|
case INSTALL_APP ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getInstallAppTimeoutMinutes();
|
||||||
|
case CALIBRE ->
|
||||||
|
applicationProperties
|
||||||
|
.getProcessExecutor()
|
||||||
|
.getTimeoutMinutes()
|
||||||
|
.getCalibreTimeoutMinutes();
|
||||||
};
|
};
|
||||||
return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes);
|
return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,4 +50,4 @@ springdoc.swagger-ui.url=/v1/api-docs
|
|||||||
|
|
||||||
|
|
||||||
posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq
|
posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq
|
||||||
posthog.host=https://eu.i.posthog.com
|
posthog.host=https://eu.i.posthog.com
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=اللغات
|
|||||||
navbar.settings=إعدادات
|
navbar.settings=إعدادات
|
||||||
navbar.allTools=أدوات
|
navbar.allTools=أدوات
|
||||||
navbar.multiTool=أدوات متعددة
|
navbar.multiTool=أدوات متعددة
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=تنظيم
|
navbar.sections.organize=تنظيم
|
||||||
navbar.sections.convertTo=تحويل الى PDF
|
navbar.sections.convertTo=تحويل الى PDF
|
||||||
navbar.sections.convertFrom=تحويل من PDF
|
navbar.sections.convertFrom=تحويل من PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(مثال: 1,3,2 أو 4-8,2,10-12 أو 2n-1)
|
|||||||
multiTool.title=أداة متعددة PDF
|
multiTool.title=أداة متعددة PDF
|
||||||
multiTool.header=أداة متعددة PDF
|
multiTool.header=أداة متعددة PDF
|
||||||
multiTool.uploadPrompts=اسم الملف
|
multiTool.uploadPrompts=اسم الملف
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=عرض PDF
|
viewPdf.title=عرض PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Езици
|
|||||||
navbar.settings=Настройки
|
navbar.settings=Настройки
|
||||||
navbar.allTools=Инструменти
|
navbar.allTools=Инструменти
|
||||||
navbar.multiTool=Мулти инструменти
|
navbar.multiTool=Мулти инструменти
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Организирайте
|
navbar.sections.organize=Организирайте
|
||||||
navbar.sections.convertTo=Преобразуване в PDF
|
navbar.sections.convertTo=Преобразуване в PDF
|
||||||
navbar.sections.convertFrom=Преобразуване от PDF
|
navbar.sections.convertFrom=Преобразуване от PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(напр. 1,3,2 или 4-8,2,10-12 или 2n-1)
|
|||||||
multiTool.title=PDF Мулти инструмент
|
multiTool.title=PDF Мулти инструмент
|
||||||
multiTool.header=PDF Мулти инструмент
|
multiTool.header=PDF Мулти инструмент
|
||||||
multiTool.uploadPrompts=Име на файл
|
multiTool.uploadPrompts=Име на файл
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Преглед на PDF
|
viewPdf.title=Преглед на PDF
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -141,6 +141,7 @@ navbar.language=Jazyky
|
|||||||
navbar.settings=Nastavení
|
navbar.settings=Nastavení
|
||||||
navbar.allTools=Nástroje
|
navbar.allTools=Nástroje
|
||||||
navbar.multiTool=Multifunkční nástroje
|
navbar.multiTool=Multifunkční nástroje
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizovat
|
navbar.sections.organize=Organizovat
|
||||||
navbar.sections.convertTo=Převést do PDF
|
navbar.sections.convertTo=Převést do PDF
|
||||||
navbar.sections.convertFrom=Převést z PDF
|
navbar.sections.convertFrom=Převést z PDF
|
||||||
@@ -527,7 +528,7 @@ replace-color.selectText.8=Zlutý text na černém pozadí
|
|||||||
replace-color.selectText.9=Zelený text na černém pozadí
|
replace-color.selectText.9=Zelený text na černém pozadí
|
||||||
replace-color.selectText.10=Vyberte barvu textu
|
replace-color.selectText.10=Vyberte barvu textu
|
||||||
replace-color.selectText.11=Vyberte barvu pozadí
|
replace-color.selectText.11=Vyberte barvu pozadí
|
||||||
replace-color.submit= Nahradit
|
replace-color.submit=Nahradit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(např. 1,3,2 nebo 4-8,2,10-12 nebo 2n-1)
|
|||||||
multiTool.title=Vícefunkční nástroj pro PDF
|
multiTool.title=Vícefunkční nástroj pro PDF
|
||||||
multiTool.header=Vícefunkční nástroj pro PDF
|
multiTool.header=Vícefunkční nástroj pro PDF
|
||||||
multiTool.uploadPrompts=Název souboru
|
multiTool.uploadPrompts=Název souboru
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Zobrazit PDF
|
viewPdf.title=Zobrazit PDF
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ pipelineOptions.validateButton=Validér
|
|||||||
enterpriseEdition.button=Opgrader til Pro
|
enterpriseEdition.button=Opgrader til Pro
|
||||||
enterpriseEdition.warning=Denne funktion er kun tilgængelig for Pro-brugere.
|
enterpriseEdition.warning=Denne funktion er kun tilgængelig for Pro-brugere.
|
||||||
enterpriseEdition.yamlAdvert=Stirling PDF Pro understøtter YAML-konfigurationsfiler og andre SSO-funktioner.
|
enterpriseEdition.yamlAdvert=Stirling PDF Pro understøtter YAML-konfigurationsfiler og andre SSO-funktioner.
|
||||||
enterpriseEdition.ssoAdvert= søger du flere funktioner til brugerstyring? Prøv Stirling PDF Pro
|
enterpriseEdition.ssoAdvert=søger du flere funktioner til brugerstyring? Prøv Stirling PDF Pro
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
@@ -141,6 +141,7 @@ navbar.language=Sprog
|
|||||||
navbar.settings=Indstillinger
|
navbar.settings=Indstillinger
|
||||||
navbar.allTools=Værktøjer
|
navbar.allTools=Værktøjer
|
||||||
navbar.multiTool=Multi Værktøjer
|
navbar.multiTool=Multi Værktøjer
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organisér
|
navbar.sections.organize=Organisér
|
||||||
navbar.sections.convertTo=Konvertér til PDF
|
navbar.sections.convertTo=Konvertér til PDF
|
||||||
navbar.sections.convertFrom=Konvertér fra PDF
|
navbar.sections.convertFrom=Konvertér fra PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(f.eks. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
|
|||||||
multiTool.title=PDF Multi Værktøj
|
multiTool.title=PDF Multi Værktøj
|
||||||
multiTool.header=PDF Multi Værktøj
|
multiTool.header=PDF Multi Værktøj
|
||||||
multiTool.uploadPrompts=Filnavn
|
multiTool.uploadPrompts=Filnavn
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Se PDF
|
viewPdf.title=Se PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Sprachen
|
|||||||
navbar.settings=Einstellungen
|
navbar.settings=Einstellungen
|
||||||
navbar.allTools=Werkzeuge
|
navbar.allTools=Werkzeuge
|
||||||
navbar.multiTool=Multitools
|
navbar.multiTool=Multitools
|
||||||
|
navbar.search=Suche
|
||||||
navbar.sections.organize=Organisieren
|
navbar.sections.organize=Organisieren
|
||||||
navbar.sections.convertTo=In PDF konvertieren
|
navbar.sections.convertTo=In PDF konvertieren
|
||||||
navbar.sections.convertFrom=Konvertieren von PDF
|
navbar.sections.convertFrom=Konvertieren von PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
|
|||||||
multiTool.title=PDF-Multitool
|
multiTool.title=PDF-Multitool
|
||||||
multiTool.header=PDF-Multitool
|
multiTool.header=PDF-Multitool
|
||||||
multiTool.uploadPrompts=Dateiname
|
multiTool.uploadPrompts=Dateiname
|
||||||
|
multiTool.selectAll=Alle auswählen
|
||||||
|
multiTool.deselectAll=Auswahl aufheben
|
||||||
|
multiTool.selectPages=Seiten auswählen
|
||||||
|
multiTool.selectedPages=Ausgewählte Seiten
|
||||||
|
multiTool.page=Seite
|
||||||
|
multiTool.deleteSelected=Auswahl löschen
|
||||||
|
multiTool.downloadAll=Downloaden
|
||||||
|
multiTool.downloadSelected=Auswahl downloaden
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF anzeigen
|
viewPdf.title=PDF anzeigen
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Γλώσσες
|
|||||||
navbar.settings=Ρυθμίσεις
|
navbar.settings=Ρυθμίσεις
|
||||||
navbar.allTools=Εργαλεία
|
navbar.allTools=Εργαλεία
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Οργάνωση
|
navbar.sections.organize=Οργάνωση
|
||||||
navbar.sections.convertTo=Μετατροπή σε PDF
|
navbar.sections.convertTo=Μετατροπή σε PDF
|
||||||
navbar.sections.convertFrom=Μετατροπή από PDF
|
navbar.sections.convertFrom=Μετατροπή από PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1)
|
|||||||
multiTool.title=PDF Πολυεργαλείο
|
multiTool.title=PDF Πολυεργαλείο
|
||||||
multiTool.header=PDF Πολυεργαλείο
|
multiTool.header=PDF Πολυεργαλείο
|
||||||
multiTool.uploadPrompts=Όνομα αρχείου
|
multiTool.uploadPrompts=Όνομα αρχείου
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Προβολή PDF
|
viewPdf.title=Προβολή PDF
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ page=Page
|
|||||||
pages=Pages
|
pages=Pages
|
||||||
loading=Loading...
|
loading=Loading...
|
||||||
addToDoc=Add to Document
|
addToDoc=Add to Document
|
||||||
|
reset=Reset
|
||||||
|
|
||||||
legal.privacy=Privacy Policy
|
legal.privacy=Privacy Policy
|
||||||
legal.terms=Terms and Conditions
|
legal.terms=Terms and Conditions
|
||||||
@@ -141,6 +142,7 @@ navbar.language=Languages
|
|||||||
navbar.settings=Settings
|
navbar.settings=Settings
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multi Tool
|
navbar.multiTool=Multi Tool
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organize
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=Convert to PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Convert from PDF
|
||||||
@@ -933,6 +935,17 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Languages
|
|||||||
navbar.settings=Settings
|
navbar.settings=Settings
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multi Tool
|
navbar.multiTool=Multi Tool
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organize
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=Convert to PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Convert from PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|||||||
multiTool.title=PDF Multi Tool
|
multiTool.title=PDF Multi Tool
|
||||||
multiTool.header=PDF Multi Tool
|
multiTool.header=PDF Multi Tool
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Idiomas
|
|||||||
navbar.settings=Configuración
|
navbar.settings=Configuración
|
||||||
navbar.allTools=Herramientas
|
navbar.allTools=Herramientas
|
||||||
navbar.multiTool=Multi herramientas
|
navbar.multiTool=Multi herramientas
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizar
|
navbar.sections.organize=Organizar
|
||||||
navbar.sections.convertTo=Convertir a PDF
|
navbar.sections.convertTo=Convertir a PDF
|
||||||
navbar.sections.convertFrom=Convertir desde PDF
|
navbar.sections.convertFrom=Convertir desde PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
|
|||||||
multiTool.title=Multi-herramienta PDF
|
multiTool.title=Multi-herramienta PDF
|
||||||
multiTool.header=Multi-herramienta PDF
|
multiTool.header=Multi-herramienta PDF
|
||||||
multiTool.uploadPrompts=Nombre del archivo
|
multiTool.uploadPrompts=Nombre del archivo
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Ver PDF
|
viewPdf.title=Ver PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Languages
|
|||||||
navbar.settings=Ezarpenak
|
navbar.settings=Ezarpenak
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organize
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=Convert to PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Convert from PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|||||||
multiTool.title=PDF erabilera anitzeko tresna
|
multiTool.title=PDF erabilera anitzeko tresna
|
||||||
multiTool.header=PDF erabilera anitzeko tresna
|
multiTool.header=PDF erabilera anitzeko tresna
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=View PDF
|
viewPdf.title=View PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Langues
|
|||||||
navbar.settings=Paramètres
|
navbar.settings=Paramètres
|
||||||
navbar.allTools=Outils
|
navbar.allTools=Outils
|
||||||
navbar.multiTool=Outils Multiples
|
navbar.multiTool=Outils Multiples
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organisation
|
navbar.sections.organize=Organisation
|
||||||
navbar.sections.convertTo=Convertir en PDF
|
navbar.sections.convertTo=Convertir en PDF
|
||||||
navbar.sections.convertFrom=Convertir depuis PDF
|
navbar.sections.convertFrom=Convertir depuis PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(par exemple 1,3,2 ou 4-8,2,10-12 ou 2n-1)
|
|||||||
multiTool.title=Outil multifonction PDF
|
multiTool.title=Outil multifonction PDF
|
||||||
multiTool.header=Outil multifonction PDF
|
multiTool.header=Outil multifonction PDF
|
||||||
multiTool.uploadPrompts=Nom du fichier
|
multiTool.uploadPrompts=Nom du fichier
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualiser un PDF
|
viewPdf.title=Visualiser un PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Teangacha
|
|||||||
navbar.settings=Socruithe
|
navbar.settings=Socruithe
|
||||||
navbar.allTools=Uirlisí
|
navbar.allTools=Uirlisí
|
||||||
navbar.multiTool=Uirlisí Il
|
navbar.multiTool=Uirlisí Il
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Eagraigh
|
navbar.sections.organize=Eagraigh
|
||||||
navbar.sections.convertTo=Tiontaigh go PDF
|
navbar.sections.convertTo=Tiontaigh go PDF
|
||||||
navbar.sections.convertFrom=Tiontaigh ó PDF
|
navbar.sections.convertFrom=Tiontaigh ó PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(m.sh. 1,3,2 nó 4-8,2,10-12 nó 2n-1)
|
|||||||
multiTool.title=Il-uirlis PDF
|
multiTool.title=Il-uirlis PDF
|
||||||
multiTool.header=Il-uirlis PDF
|
multiTool.header=Il-uirlis PDF
|
||||||
multiTool.uploadPrompts=Ainm comhaid
|
multiTool.uploadPrompts=Ainm comhaid
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Féach PDF
|
viewPdf.title=Féach PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=भाषा
|
|||||||
navbar.settings=सेटिंग्स
|
navbar.settings=सेटिंग्स
|
||||||
navbar.allTools=साधन
|
navbar.allTools=साधन
|
||||||
navbar.multiTool=विभिन्न साधन
|
navbar.multiTool=विभिन्न साधन
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=संगठित करें
|
navbar.sections.organize=संगठित करें
|
||||||
navbar.sections.convertTo=पीडीएफ में कनवर्ट करें
|
navbar.sections.convertTo=पीडीएफ में कनवर्ट करें
|
||||||
navbar.sections.convertFrom=पीडीएफ से कनवर्ट करें
|
navbar.sections.convertFrom=पीडीएफ से कनवर्ट करें
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(जैसे 1,3,2 या 4-8,2,10-12 या 2n-1)
|
|||||||
multiTool.title=पीडीएफ मल्टी टूल
|
multiTool.title=पीडीएफ मल्टी टूल
|
||||||
multiTool.header=पीडीएफ मल्टी टूल
|
multiTool.header=पीडीएफ मल्टी टूल
|
||||||
multiTool.uploadPrompts=फाइल का नाम
|
multiTool.uploadPrompts=फाइल का नाम
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=पीडीएफ देखें
|
viewPdf.title=पीडीएफ देखें
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Jezici
|
|||||||
navbar.settings=Postavke
|
navbar.settings=Postavke
|
||||||
navbar.allTools=Alati
|
navbar.allTools=Alati
|
||||||
navbar.multiTool=Multi Tools (Alati)
|
navbar.multiTool=Multi Tools (Alati)
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizirati
|
navbar.sections.organize=Organizirati
|
||||||
navbar.sections.convertTo=Pretvori u PDF
|
navbar.sections.convertTo=Pretvori u PDF
|
||||||
navbar.sections.convertFrom=Pretvori iz PDF
|
navbar.sections.convertFrom=Pretvori iz PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(npr. 1,3,2 ili 4-8,2,10-12 ili 2n-1)
|
|||||||
multiTool.title=PDF Višenamjenski alat
|
multiTool.title=PDF Višenamjenski alat
|
||||||
multiTool.header=PDF Višenamjenski alat
|
multiTool.header=PDF Višenamjenski alat
|
||||||
multiTool.uploadPrompts=Naziv datoteke
|
multiTool.uploadPrompts=Naziv datoteke
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Pogledaj
|
viewPdf.title=Pogledaj
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Nyelvek
|
|||||||
navbar.settings=Beállítások
|
navbar.settings=Beállítások
|
||||||
navbar.allTools=Eszközök
|
navbar.allTools=Eszközök
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Összeállítás
|
navbar.sections.organize=Összeállítás
|
||||||
navbar.sections.convertTo=Átalakítás PDF-be
|
navbar.sections.convertTo=Átalakítás PDF-be
|
||||||
navbar.sections.convertFrom=PDF-ből átalakítás
|
navbar.sections.convertFrom=PDF-ből átalakítás
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(pl.: 1,3,2 vagy 4-8,2,10-12 vagy 2n-1)
|
|||||||
multiTool.title=PDF többfunkciós eszköz
|
multiTool.title=PDF többfunkciós eszköz
|
||||||
multiTool.header=PDF többfunkciós eszköz
|
multiTool.header=PDF többfunkciós eszköz
|
||||||
multiTool.uploadPrompts=Fájl neve
|
multiTool.uploadPrompts=Fájl neve
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF megtekintése
|
viewPdf.title=PDF megtekintése
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Bahasa
|
|||||||
navbar.settings=Pengaturan
|
navbar.settings=Pengaturan
|
||||||
navbar.allTools=Alat
|
navbar.allTools=Alat
|
||||||
navbar.multiTool=Alat Multi
|
navbar.multiTool=Alat Multi
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Atur
|
navbar.sections.organize=Atur
|
||||||
navbar.sections.convertTo=Konversi ke PDF
|
navbar.sections.convertTo=Konversi ke PDF
|
||||||
navbar.sections.convertFrom=Konversi dari PDF
|
navbar.sections.convertFrom=Konversi dari PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(misalnya 1,3,2 atau 4-8,2,10-12 atau 2n-1)
|
|||||||
multiTool.title=Alat Multi PDF
|
multiTool.title=Alat Multi PDF
|
||||||
multiTool.header=Alat Multi PDF
|
multiTool.header=Alat Multi PDF
|
||||||
multiTool.uploadPrompts=Nama Berkas
|
multiTool.uploadPrompts=Nama Berkas
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Lihat PDF
|
viewPdf.title=Lihat PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Lingue
|
|||||||
navbar.settings=Impostazioni
|
navbar.settings=Impostazioni
|
||||||
navbar.allTools=Strumenti
|
navbar.allTools=Strumenti
|
||||||
navbar.multiTool=Strumenti multipli
|
navbar.multiTool=Strumenti multipli
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizza
|
navbar.sections.organize=Organizza
|
||||||
navbar.sections.convertTo=Converti in PDF
|
navbar.sections.convertTo=Converti in PDF
|
||||||
navbar.sections.convertFrom=Converti da PDF
|
navbar.sections.convertFrom=Converti da PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)
|
|||||||
multiTool.title=Multifunzione PDF
|
multiTool.title=Multifunzione PDF
|
||||||
multiTool.header=Multifunzione PDF
|
multiTool.header=Multifunzione PDF
|
||||||
multiTool.uploadPrompts=Nome file
|
multiTool.uploadPrompts=Nome file
|
||||||
|
multiTool.selectAll=Seleziona tutto
|
||||||
|
multiTool.deselectAll=Deseleziona tutto
|
||||||
|
multiTool.selectPages=Seleziona pagina
|
||||||
|
multiTool.selectedPages=Seleziona pagine
|
||||||
|
multiTool.page=Pagina
|
||||||
|
multiTool.deleteSelected=Elimina selezionata
|
||||||
|
multiTool.downloadAll=Esporta
|
||||||
|
multiTool.downloadSelected=Esporta selezionata
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualizza PDF
|
viewPdf.title=Visualizza PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=言語
|
|||||||
navbar.settings=設定
|
navbar.settings=設定
|
||||||
navbar.allTools=ツール
|
navbar.allTools=ツール
|
||||||
navbar.multiTool=マルチツール
|
navbar.multiTool=マルチツール
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=整理
|
navbar.sections.organize=整理
|
||||||
navbar.sections.convertTo=PDFへ変換
|
navbar.sections.convertTo=PDFへ変換
|
||||||
navbar.sections.convertFrom=PDFから変換
|
navbar.sections.convertFrom=PDFから変換
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
|
|||||||
multiTool.title=PDFマルチツール
|
multiTool.title=PDFマルチツール
|
||||||
multiTool.header=PDFマルチツール
|
multiTool.header=PDFマルチツール
|
||||||
multiTool.uploadPrompts=ファイル名
|
multiTool.uploadPrompts=ファイル名
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDFを表示
|
viewPdf.title=PDFを表示
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=언어
|
|||||||
navbar.settings=설정
|
navbar.settings=설정
|
||||||
navbar.allTools=도구
|
navbar.allTools=도구
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=조직
|
navbar.sections.organize=조직
|
||||||
navbar.sections.convertTo=PDF로 변환
|
navbar.sections.convertTo=PDF로 변환
|
||||||
navbar.sections.convertFrom=PDF에서 변환
|
navbar.sections.convertFrom=PDF에서 변환
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1)
|
|||||||
multiTool.title=PDF 멀티툴
|
multiTool.title=PDF 멀티툴
|
||||||
multiTool.header=PDF 멀티툴
|
multiTool.header=PDF 멀티툴
|
||||||
multiTool.uploadPrompts=파일 이름
|
multiTool.uploadPrompts=파일 이름
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF 뷰어
|
viewPdf.title=PDF 뷰어
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Talen
|
|||||||
navbar.settings=Instellingen
|
navbar.settings=Instellingen
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multitools
|
navbar.multiTool=Multitools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizeren
|
navbar.sections.organize=Organizeren
|
||||||
navbar.sections.convertTo=Converteren naar PDF
|
navbar.sections.convertTo=Converteren naar PDF
|
||||||
navbar.sections.convertFrom=Converteren van PDF
|
navbar.sections.convertFrom=Converteren van PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(bijv. 1,3,2 of 4-8,2,10-12 of 2n-1)
|
|||||||
multiTool.title=PDF Multitool
|
multiTool.title=PDF Multitool
|
||||||
multiTool.header=PDF Multitool
|
multiTool.header=PDF Multitool
|
||||||
multiTool.uploadPrompts=Bestandsnaam
|
multiTool.uploadPrompts=Bestandsnaam
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF bekijken
|
viewPdf.title=PDF bekijken
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Språk
|
|||||||
navbar.settings=Innstillinger
|
navbar.settings=Innstillinger
|
||||||
navbar.allTools=Verktøy
|
navbar.allTools=Verktøy
|
||||||
navbar.multiTool=Multi Verktøy
|
navbar.multiTool=Multi Verktøy
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organisere
|
navbar.sections.organize=Organisere
|
||||||
navbar.sections.convertTo=Konverter til PDF
|
navbar.sections.convertTo=Konverter til PDF
|
||||||
navbar.sections.convertFrom=Konverter fra PDF
|
navbar.sections.convertFrom=Konverter fra PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(f.eks. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
|
|||||||
multiTool.title=PDF-multiverktøy
|
multiTool.title=PDF-multiverktøy
|
||||||
multiTool.header=PDF-multiverktøy
|
multiTool.header=PDF-multiverktøy
|
||||||
multiTool.uploadPrompts=Filnavn
|
multiTool.uploadPrompts=Filnavn
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Vis PDF
|
viewPdf.title=Vis PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Języki
|
|||||||
navbar.settings=Ustawienia
|
navbar.settings=Ustawienia
|
||||||
navbar.allTools=Narzędzia
|
navbar.allTools=Narzędzia
|
||||||
navbar.multiTool=Narzędzie Wielofunkcyjne
|
navbar.multiTool=Narzędzie Wielofunkcyjne
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizuj
|
navbar.sections.organize=Organizuj
|
||||||
navbar.sections.convertTo=Przetwórz na PDF
|
navbar.sections.convertTo=Przetwórz na PDF
|
||||||
navbar.sections.convertFrom=Przetwórz z PDF
|
navbar.sections.convertFrom=Przetwórz z PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(przykład 1,3,2 lub 4-8,2,10-12 lub 2n-1)
|
|||||||
multiTool.title=Narzędzie Wielofunkcyjne PDF
|
multiTool.title=Narzędzie Wielofunkcyjne PDF
|
||||||
multiTool.header=Narzędzie Wielofunkcyjne PDF
|
multiTool.header=Narzędzie Wielofunkcyjne PDF
|
||||||
multiTool.uploadPrompts=Nazwa pliku
|
multiTool.uploadPrompts=Nazwa pliku
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Podejrzyj PDF
|
viewPdf.title=Podejrzyj PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Idiomas
|
|||||||
navbar.settings=Configurações
|
navbar.settings=Configurações
|
||||||
navbar.allTools=Ferramentas
|
navbar.allTools=Ferramentas
|
||||||
navbar.multiTool=Multiferramentas
|
navbar.multiTool=Multiferramentas
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizar
|
navbar.sections.organize=Organizar
|
||||||
navbar.sections.convertTo=Converter para PDF
|
navbar.sections.convertTo=Converter para PDF
|
||||||
navbar.sections.convertFrom=Converter de PDF
|
navbar.sections.convertFrom=Converter de PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(por exemplo 1,3,2 ou 4-8,2,10-12 ou 2n-1)
|
|||||||
multiTool.title=Multiferramenta de PDF
|
multiTool.title=Multiferramenta de PDF
|
||||||
multiTool.header=Multiferramenta de PDF
|
multiTool.header=Multiferramenta de PDF
|
||||||
multiTool.uploadPrompts=Nome do arquivo
|
multiTool.uploadPrompts=Nome do arquivo
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualizar PDF
|
viewPdf.title=Visualizar PDF
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ pipeline.configureButton=Configurar
|
|||||||
pipeline.defaultOption=Personalizar
|
pipeline.defaultOption=Personalizar
|
||||||
pipeline.submitButton=Submeter
|
pipeline.submitButton=Submeter
|
||||||
pipeline.help=Pipeline Help
|
pipeline.help=Pipeline Help
|
||||||
pipeline.scanHelp= Ajuda ao Escaneamento de Pastas
|
pipeline.scanHelp=Ajuda ao Escaneamento de Pastas
|
||||||
pipeline.deletePrompt=Tem a certeza que quer eliminar o pipeline?
|
pipeline.deletePrompt=Tem a certeza que quer eliminar o pipeline?
|
||||||
|
|
||||||
######################
|
######################
|
||||||
@@ -141,6 +141,7 @@ navbar.language=Idiomas
|
|||||||
navbar.settings=Configurações
|
navbar.settings=Configurações
|
||||||
navbar.allTools=Ferramentas
|
navbar.allTools=Ferramentas
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizar
|
navbar.sections.organize=Organizar
|
||||||
navbar.sections.convertTo=Converter para PDF
|
navbar.sections.convertTo=Converter para PDF
|
||||||
navbar.sections.convertFrom=Converter de PDF
|
navbar.sections.convertFrom=Converter de PDF
|
||||||
@@ -191,7 +192,7 @@ account.changePassword=Alterar a Senha
|
|||||||
account.confirmNewPassword=Confirmar Nova Senha
|
account.confirmNewPassword=Confirmar Nova Senha
|
||||||
account.signOut=Sair do Sistema
|
account.signOut=Sair do Sistema
|
||||||
account.yourApiKey=Sua Chave API
|
account.yourApiKey=Sua Chave API
|
||||||
account.syncTitle= sincronizar definições do navegador com a conta
|
account.syncTitle=sincronizar definições do navegador com a conta
|
||||||
account.settingsCompare=Comparação das Definições:
|
account.settingsCompare=Comparação das Definições:
|
||||||
account.property=Propriedade
|
account.property=Propriedade
|
||||||
account.webBrowserSettings=Configurações do Navegador Web
|
account.webBrowserSettings=Configurações do Navegador Web
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(ex: 1,3,2 ou 4-8,2,10-12 ou 2n-1)
|
|||||||
multiTool.title=Multiferramenta de PDF
|
multiTool.title=Multiferramenta de PDF
|
||||||
multiTool.header=Multiferramenta de PDF
|
multiTool.header=Multiferramenta de PDF
|
||||||
multiTool.uploadPrompts=Nome do Arquivo
|
multiTool.uploadPrompts=Nome do Arquivo
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visualizar PDF
|
viewPdf.title=Visualizar PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Limbi
|
|||||||
navbar.settings=Setări
|
navbar.settings=Setări
|
||||||
navbar.allTools=Instrumente
|
navbar.allTools=Instrumente
|
||||||
navbar.multiTool=Instrumente Multiple
|
navbar.multiTool=Instrumente Multiple
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organizează
|
navbar.sections.organize=Organizează
|
||||||
navbar.sections.convertTo=Convertește în PDF
|
navbar.sections.convertTo=Convertește în PDF
|
||||||
navbar.sections.convertFrom=Convertește din PDF
|
navbar.sections.convertFrom=Convertește din PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(ex. 1,3,2 sau 4-8,2,10-12 sau 2n-1)
|
|||||||
multiTool.title=Instrument PDF multiplu
|
multiTool.title=Instrument PDF multiplu
|
||||||
multiTool.header=Instrument PDF multiplu
|
multiTool.header=Instrument PDF multiplu
|
||||||
multiTool.uploadPrompts=Nume Fișier
|
multiTool.uploadPrompts=Nume Fișier
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Vizualizează PDF
|
viewPdf.title=Vizualizează PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Языки
|
|||||||
navbar.settings=Настройки
|
navbar.settings=Настройки
|
||||||
navbar.allTools=Конвейеры
|
navbar.allTools=Конвейеры
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Организация
|
navbar.sections.organize=Организация
|
||||||
navbar.sections.convertTo=Перевести в PDF
|
navbar.sections.convertTo=Перевести в PDF
|
||||||
navbar.sections.convertFrom=Перевести из PDF
|
navbar.sections.convertFrom=Перевести из PDF
|
||||||
@@ -359,7 +360,7 @@ PDFToHTML.tags=web content,browser friendly
|
|||||||
|
|
||||||
home.PDFToXML.title=PDF в XML
|
home.PDFToXML.title=PDF в XML
|
||||||
home.PDFToXML.desc=Преобразование PDF в формат XML
|
home.PDFToXML.desc=Преобразование PDF в формат XML
|
||||||
PDFToXML.tags= extraksi data,структурированный контент,interop,преобразование,конвертация
|
PDFToXML.tags=extraksi data,структурированный контент,interop,преобразование,конвертация
|
||||||
|
|
||||||
home.ScannerImageSplit.title=Обнаружение/разделение отсканированных фотографий
|
home.ScannerImageSplit.title=Обнаружение/разделение отсканированных фотографий
|
||||||
home.ScannerImageSplit.desc=Разделяет несколько фотографий из фото/PDF
|
home.ScannerImageSplit.desc=Разделяет несколько фотографий из фото/PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1
|
|||||||
multiTool.title=Мультиинструмент PDF
|
multiTool.title=Мультиинструмент PDF
|
||||||
multiTool.header=Мультиинструмент PDF
|
multiTool.header=Мультиинструмент PDF
|
||||||
multiTool.uploadPrompts=Имя файла
|
multiTool.uploadPrompts=Имя файла
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Просмотреть PDF
|
viewPdf.title=Просмотреть PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Languages
|
|||||||
navbar.settings=Nastavenia
|
navbar.settings=Nastavenia
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organize
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=Convert to PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Convert from PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(napr. 1,3,2 alebo 4-8,2,10-12 alebo 2n-1)
|
|||||||
multiTool.title=PDF Multi Nástroj
|
multiTool.title=PDF Multi Nástroj
|
||||||
multiTool.header=PDF Multi Nástroj
|
multiTool.header=PDF Multi Nástroj
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Zobraziť PDF
|
viewPdf.title=Zobraziť PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Languages
|
|||||||
navbar.settings=Podešavanja
|
navbar.settings=Podešavanja
|
||||||
navbar.allTools=Tools
|
navbar.allTools=Tools
|
||||||
navbar.multiTool=Multi Tools
|
navbar.multiTool=Multi Tools
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organize
|
navbar.sections.organize=Organize
|
||||||
navbar.sections.convertTo=Convert to PDF
|
navbar.sections.convertTo=Convert to PDF
|
||||||
navbar.sections.convertFrom=Convert from PDF
|
navbar.sections.convertFrom=Convert from PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
|
|||||||
multiTool.title=PDF Multi Alatka
|
multiTool.title=PDF Multi Alatka
|
||||||
multiTool.header=PDF Multi Alatka
|
multiTool.header=PDF Multi Alatka
|
||||||
multiTool.uploadPrompts=File Name
|
multiTool.uploadPrompts=File Name
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Prikaz
|
viewPdf.title=Prikaz
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Språk
|
|||||||
navbar.settings=Inställningar
|
navbar.settings=Inställningar
|
||||||
navbar.allTools=Verktyg
|
navbar.allTools=Verktyg
|
||||||
navbar.multiTool=Multiverktyg
|
navbar.multiTool=Multiverktyg
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Organisera
|
navbar.sections.organize=Organisera
|
||||||
navbar.sections.convertTo=Konvertera till PDF
|
navbar.sections.convertTo=Konvertera till PDF
|
||||||
navbar.sections.convertFrom=Konvertera från PDF
|
navbar.sections.convertFrom=Konvertera från PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(t.ex. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
|
|||||||
multiTool.title=PDF-multiverktyg
|
multiTool.title=PDF-multiverktyg
|
||||||
multiTool.header=PDF Multi-verktyg
|
multiTool.header=PDF Multi-verktyg
|
||||||
multiTool.uploadPrompts=Filnamn
|
multiTool.uploadPrompts=Filnamn
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visa PDF
|
viewPdf.title=Visa PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=ภาษา
|
|||||||
navbar.settings=การตั้งค่า
|
navbar.settings=การตั้งค่า
|
||||||
navbar.allTools=เครื่องมือทั้งหมด
|
navbar.allTools=เครื่องมือทั้งหมด
|
||||||
navbar.multiTool=เครื่องมือหลายตัว
|
navbar.multiTool=เครื่องมือหลายตัว
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=จัดระเบียบ
|
navbar.sections.organize=จัดระเบียบ
|
||||||
navbar.sections.convertTo=แปลงเป็น PDF
|
navbar.sections.convertTo=แปลงเป็น PDF
|
||||||
navbar.sections.convertFrom=แปลงจาก PDF
|
navbar.sections.convertFrom=แปลงจาก PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(เช่น 1,3,2 หรือ 4-8,2,10-12 หรื
|
|||||||
multiTool.title=เครื่องมือ PDF หลายตัว
|
multiTool.title=เครื่องมือ PDF หลายตัว
|
||||||
multiTool.header=เครื่องมือ PDF หลายตัว
|
multiTool.header=เครื่องมือ PDF หลายตัว
|
||||||
multiTool.uploadPrompts=ชื่อไฟล์
|
multiTool.uploadPrompts=ชื่อไฟล์
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=ดู PDF
|
viewPdf.title=ดู PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Diller
|
|||||||
navbar.settings=Ayarlar
|
navbar.settings=Ayarlar
|
||||||
navbar.allTools=Araçlar
|
navbar.allTools=Araçlar
|
||||||
navbar.multiTool=Çoklu Araçlar
|
navbar.multiTool=Çoklu Araçlar
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Düzenle
|
navbar.sections.organize=Düzenle
|
||||||
navbar.sections.convertTo=PDF'ye dönüştür
|
navbar.sections.convertTo=PDF'ye dönüştür
|
||||||
navbar.sections.convertFrom=PDF'den dönüştür
|
navbar.sections.convertFrom=PDF'den dönüştür
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1)
|
|||||||
multiTool.title=PDF Çoklu Araç
|
multiTool.title=PDF Çoklu Araç
|
||||||
multiTool.header=PDF Çoklu Araç
|
multiTool.header=PDF Çoklu Araç
|
||||||
multiTool.uploadPrompts=Dosya Adı
|
multiTool.uploadPrompts=Dosya Adı
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=PDF Görüntüle
|
viewPdf.title=PDF Görüntüle
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Мови
|
|||||||
navbar.settings=Налаштування
|
navbar.settings=Налаштування
|
||||||
navbar.allTools=Інструменти
|
navbar.allTools=Інструменти
|
||||||
navbar.multiTool=Мультіінструмент
|
navbar.multiTool=Мультіінструмент
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Організувати
|
navbar.sections.organize=Організувати
|
||||||
navbar.sections.convertTo=Конвертувати в PDF
|
navbar.sections.convertTo=Конвертувати в PDF
|
||||||
navbar.sections.convertFrom=Конвертувати з PDF
|
navbar.sections.convertFrom=Конвертувати з PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n
|
|||||||
multiTool.title=Мультіінструмент PDF
|
multiTool.title=Мультіінструмент PDF
|
||||||
multiTool.header=Мультіінструмент PDF
|
multiTool.header=Мультіінструмент PDF
|
||||||
multiTool.uploadPrompts=Ім'я файлу
|
multiTool.uploadPrompts=Ім'я файлу
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Переглянути PDF
|
viewPdf.title=Переглянути PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=Ngôn ngữ
|
|||||||
navbar.settings=Cài đặt
|
navbar.settings=Cài đặt
|
||||||
navbar.allTools=Công cụ
|
navbar.allTools=Công cụ
|
||||||
navbar.multiTool=Đa công cụ
|
navbar.multiTool=Đa công cụ
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=Sắp xếp
|
navbar.sections.organize=Sắp xếp
|
||||||
navbar.sections.convertTo=Chuyển đổi sang PDF
|
navbar.sections.convertTo=Chuyển đổi sang PDF
|
||||||
navbar.sections.convertFrom=Chuyển đổi từ PDF
|
navbar.sections.convertFrom=Chuyển đổi từ PDF
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(ví dụ: 1,3,2 hoặc 4-8,2,10-12 hoặc 2n-1)
|
|||||||
multiTool.title=Công cụ đa năng PDF
|
multiTool.title=Công cụ đa năng PDF
|
||||||
multiTool.header=Công cụ đa năng PDF
|
multiTool.header=Công cụ đa năng PDF
|
||||||
multiTool.uploadPrompts=Tên tệp
|
multiTool.uploadPrompts=Tên tệp
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Xem PDF
|
viewPdf.title=Xem PDF
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ navbar.language=语言
|
|||||||
navbar.settings=设置
|
navbar.settings=设置
|
||||||
navbar.allTools=工具箱
|
navbar.allTools=工具箱
|
||||||
navbar.multiTool=多功能工具
|
navbar.multiTool=多功能工具
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=组织
|
navbar.sections.organize=组织
|
||||||
navbar.sections.convertTo=转换成PDF
|
navbar.sections.convertTo=转换成PDF
|
||||||
navbar.sections.convertFrom=从PDF转换
|
navbar.sections.convertFrom=从PDF转换
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(例如:1,3,2 或 4-8,2,10-12 或 2n-1)
|
|||||||
multiTool.title=PDF多功能工具
|
multiTool.title=PDF多功能工具
|
||||||
multiTool.header=PDF多功能工具
|
multiTool.header=PDF多功能工具
|
||||||
multiTool.uploadPrompts=文件名
|
multiTool.uploadPrompts=文件名
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=浏览PDF
|
viewPdf.title=浏览PDF
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ info=資訊
|
|||||||
pro=專業版
|
pro=專業版
|
||||||
page=頁面
|
page=頁面
|
||||||
pages=頁面
|
pages=頁面
|
||||||
loading=Loading...
|
loading=載入中...
|
||||||
addToDoc=Add to Document
|
addToDoc=新增至文件
|
||||||
|
|
||||||
legal.privacy=隱私權政策
|
legal.privacy=隱私權政策
|
||||||
legal.terms=使用條款
|
legal.terms=使用條款
|
||||||
@@ -140,7 +140,8 @@ navbar.darkmode=深色模式
|
|||||||
navbar.language=語言
|
navbar.language=語言
|
||||||
navbar.settings=設定
|
navbar.settings=設定
|
||||||
navbar.allTools=工具
|
navbar.allTools=工具
|
||||||
navbar.multiTool=多功能工具
|
navbar.multiTool=複合工具
|
||||||
|
navbar.search=Search
|
||||||
navbar.sections.organize=整理
|
navbar.sections.organize=整理
|
||||||
navbar.sections.convertTo=轉換為 PDF
|
navbar.sections.convertTo=轉換為 PDF
|
||||||
navbar.sections.convertFrom=從 PDF 轉換
|
navbar.sections.convertFrom=從 PDF 轉換
|
||||||
@@ -246,7 +247,7 @@ database.fileNullOrEmpty=檔案不得為空或空白
|
|||||||
database.failedImportFile=匯入檔案失敗
|
database.failedImportFile=匯入檔案失敗
|
||||||
|
|
||||||
session.expired=您的工作階段已過期。請重新整理頁面並再試一次。
|
session.expired=您的工作階段已過期。請重新整理頁面並再試一次。
|
||||||
session.refreshPage=Refresh Page
|
session.refreshPage=重新整理頁面
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -751,7 +752,7 @@ certSign.showSig=顯示簽章
|
|||||||
certSign.reason=原因
|
certSign.reason=原因
|
||||||
certSign.location=位置
|
certSign.location=位置
|
||||||
certSign.name=名稱
|
certSign.name=名稱
|
||||||
certSign.showLogo=Show Logo
|
certSign.showLogo=顯示 Logo
|
||||||
certSign.submit=簽章 PDF
|
certSign.submit=簽章 PDF
|
||||||
|
|
||||||
|
|
||||||
@@ -786,9 +787,9 @@ compare.highlightColor.2=標示顏色 2:
|
|||||||
compare.document.1=文件 1
|
compare.document.1=文件 1
|
||||||
compare.document.2=文件 2
|
compare.document.2=文件 2
|
||||||
compare.submit=比較
|
compare.submit=比較
|
||||||
compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
|
compare.complex.message=選擇的檔案大小太大(其中一個或兩者皆是),可能會影響比較的精確度
|
||||||
compare.large.file.message=One or Both of the provided documents are too large to process
|
compare.large.file.message=選擇的檔案大小超出系統限制(其中一個或兩者皆是),無法處理
|
||||||
compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
|
compare.no.text.message=選擇的 PDF 檔案未包含文字(其中一個或兩者皆是)。請選擇含有文字的 PDF 進行比較
|
||||||
|
|
||||||
#BookToPDF
|
#BookToPDF
|
||||||
BookToPDF.title=電子書和漫畫轉 PDF
|
BookToPDF.title=電子書和漫畫轉 PDF
|
||||||
@@ -805,17 +806,17 @@ PDFToBook.submit=轉換
|
|||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=簽章
|
sign.title=簽章
|
||||||
sign.header=簽章 PDF
|
sign.header=簽署 PDF
|
||||||
sign.upload=上傳影像
|
sign.upload=上傳影像
|
||||||
sign.draw=繪製簽章
|
sign.draw=繪製簽章
|
||||||
sign.text=文字輸入
|
sign.text=文字輸入
|
||||||
sign.clear=清除
|
sign.clear=清除
|
||||||
sign.add=新增
|
sign.add=新增
|
||||||
sign.saved=Saved Signatures
|
sign.saved=已儲存的簽章
|
||||||
sign.save=Save Signature
|
sign.save=儲存簽章
|
||||||
sign.personalSigs=Personal Signatures
|
sign.personalSigs=個人簽章
|
||||||
sign.sharedSigs=Shared Signatures
|
sign.sharedSigs=共用簽章
|
||||||
sign.noSavedSigs=No saved signatures found
|
sign.noSavedSigs=尚未儲存任何簽章
|
||||||
|
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
@@ -933,6 +934,17 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1)
|
|||||||
multiTool.title=PDF 複合工具
|
multiTool.title=PDF 複合工具
|
||||||
multiTool.header=PDF 複合工具
|
multiTool.header=PDF 複合工具
|
||||||
multiTool.uploadPrompts=檔名
|
multiTool.uploadPrompts=檔名
|
||||||
|
multiTool.selectAll=Select All
|
||||||
|
multiTool.deselectAll=Deselect All
|
||||||
|
multiTool.selectPages=Page Select
|
||||||
|
multiTool.selectedPages=Selected Pages
|
||||||
|
multiTool.page=Page
|
||||||
|
multiTool.deleteSelected=Delete Selected
|
||||||
|
multiTool.downloadAll=Export
|
||||||
|
multiTool.downloadSelected=Export Selected
|
||||||
|
|
||||||
|
#multiTool-advert
|
||||||
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=檢視 PDF
|
viewPdf.title=檢視 PDF
|
||||||
|
|||||||
@@ -102,3 +102,22 @@ metrics:
|
|||||||
AutomaticallyGenerated:
|
AutomaticallyGenerated:
|
||||||
key: example
|
key: example
|
||||||
UUID: example
|
UUID: example
|
||||||
|
|
||||||
|
processExecutor:
|
||||||
|
sessionLimit: # Process executor instances limits
|
||||||
|
libreOfficeSessionLimit: 1
|
||||||
|
pdfToHtmlSessionLimit: 1
|
||||||
|
ocrMyPdfSessionLimit: 2
|
||||||
|
pythonOpenCvSessionLimit: 8
|
||||||
|
ghostScriptSessionLimit: 16
|
||||||
|
weasyPrintSessionLimit: 16
|
||||||
|
installAppSessionLimit: 1
|
||||||
|
calibreSessionLimit: 1
|
||||||
|
timeoutMinutes: # Process executor timeout in minutes
|
||||||
|
libreOfficetimeoutMinutes: 30
|
||||||
|
pdfToHtmltimeoutMinutes: 20
|
||||||
|
pythonOpenCvtimeoutMinutes: 30
|
||||||
|
ghostScripttimeoutMinutes: 30
|
||||||
|
weasyPrinttimeoutMinutes: 30
|
||||||
|
installApptimeoutMinutes: 60
|
||||||
|
calibretimeoutMinutes: 30
|
||||||
|
|||||||
@@ -212,15 +212,81 @@ label {
|
|||||||
.page-number {
|
.page-number {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
right: 0px;
|
left: 5px;
|
||||||
color: var(--md-sys-color-on-surface);
|
color: var(--md-sys-color-on-secondary);
|
||||||
background-color: var(--md-sys-color-surface-5);
|
background-color: rgba(162, 201, 255, 0.8);
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
font-weight: 450;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-header {
|
.tool-header {
|
||||||
margin: 0.5rem 1rem 2rem;
|
margin: 0.5rem 1rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#select-pages-button {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-pages-container {
|
||||||
|
background-color: var(--md-sys-color-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 15px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-pages-container h3 {
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pages-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
max-height: 10rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-item {
|
||||||
|
background-color: var(--md-sys-color-surface-container-low);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 7rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-page-number {
|
||||||
|
width: 4rem;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
font-size: medium;
|
||||||
|
}
|
||||||
|
|||||||
@@ -126,3 +126,28 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
|||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.pdf-actions_checkbox {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 3px;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
background-color: var(--md-sys-color-surface-5);
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.pdf-actions_insert-file-blank-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 75%;
|
||||||
|
right: 50%;
|
||||||
|
translate: 0% -50%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
src: url(../../fonts/google-symbol.woff2) format('woff2');
|
src: url(../../fonts/google-symbol.woff2) format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.material-symbols-rounded {
|
.material-symbols-rounded {
|
||||||
font-family: 'Material Symbols Rounded';
|
font-family: 'Material Symbols Rounded';
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
|||||||
@@ -64,6 +64,8 @@
|
|||||||
await handleSingleDownload(url, formData);
|
await handleSingleDownload(url, formData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearFileInput();
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
$("#submitBtn").text(originalButtonText);
|
$("#submitBtn").text(originalButtonText);
|
||||||
|
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
clearFileInput();
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
handleDownloadError(error);
|
handleDownloadError(error);
|
||||||
$("#submitBtn").text(originalButtonText);
|
$("#submitBtn").text(originalButtonText);
|
||||||
@@ -93,20 +96,71 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getPDFPageCount(file) {
|
||||||
|
try {
|
||||||
|
if (file.type !== 'application/pdf') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a URL for the file
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ensure the worker is properly set
|
||||||
|
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the PDF document
|
||||||
|
const loadingTask = pdfjsLib.getDocument(url);
|
||||||
|
const pdf = await loadingTask.promise;
|
||||||
|
|
||||||
|
// Get the page count
|
||||||
|
const pageCount = pdf.numPages;
|
||||||
|
return pageCount;
|
||||||
|
} finally {
|
||||||
|
// Clean up the URL
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error counting PDF pages:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackFileProcessing(file, startTime, success, errorMessage = null) {
|
||||||
|
const endTime = performance.now();
|
||||||
|
const processingTime = endTime - startTime;
|
||||||
|
|
||||||
|
posthog.capture('file_processing', {
|
||||||
|
success: success,
|
||||||
|
file_type: file.type || 'unknown',
|
||||||
|
file_size: file.size,
|
||||||
|
processing_time: processingTime,
|
||||||
|
error_message: errorMessage,
|
||||||
|
pdf_pages: file.type === 'application/pdf' ? getPDFPageCount(file) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
||||||
|
const startTime = performance.now();
|
||||||
|
const file = formData.get('fileInput');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, { method: "POST", body: formData });
|
const response = await fetch(url, { method: "POST", body: formData });
|
||||||
const contentType = response.headers.get("content-type");
|
const contentType = response.headers.get("content-type");
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
// Handle 401 Unauthorized error
|
|
||||||
showSessionExpiredPrompt();
|
showSessionExpiredPrompt();
|
||||||
|
trackFileProcessing(file, startTime, false, 'unauthorized');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (contentType && contentType.includes("application/json")) {
|
if (contentType && contentType.includes("application/json")) {
|
||||||
console.error("Throwing error banner, response was not okay");
|
console.error("Throwing error banner, response was not okay");
|
||||||
return handleJsonResponse(response);
|
const jsonResponse = await handleJsonResponse(response);
|
||||||
|
trackFileProcessing(file, startTime, false, jsonResponse?.error || 'unknown_error');
|
||||||
|
return jsonResponse;
|
||||||
}
|
}
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -115,13 +169,21 @@
|
|||||||
let filename = getFilenameFromContentDisposition(contentDisposition);
|
let filename = getFilenameFromContentDisposition(contentDisposition);
|
||||||
|
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
|
trackFileProcessing(file, startTime, true, null);
|
||||||
|
|
||||||
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
|
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
|
||||||
|
clearFileInput();
|
||||||
return handleResponse(blob, filename, !isMulti, isZip);
|
return handleResponse(blob, filename, !isMulti, isZip);
|
||||||
} else {
|
} else {
|
||||||
|
clearFileInput();
|
||||||
return handleResponse(blob, filename, false, isZip);
|
return handleResponse(blob, filename, false, isZip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
clearFileInput();
|
||||||
console.error("Error in handleSingleDownload:", error);
|
console.error("Error in handleSingleDownload:", error);
|
||||||
|
trackFileProcessing(file, startTime, false, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,8 +224,7 @@
|
|||||||
if (considerViewOptions) {
|
if (considerViewOptions) {
|
||||||
if (downloadOption === "sameWindow") {
|
if (downloadOption === "sameWindow") {
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
window.location.href = url;
|
return { filename, blob, url };
|
||||||
return;
|
|
||||||
} else if (downloadOption === "newWindow") {
|
} else if (downloadOption === "newWindow") {
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
window.open(url, "_blank");
|
window.open(url, "_blank");
|
||||||
@@ -291,4 +352,27 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear file input after job
|
||||||
|
function clearFileInput(){
|
||||||
|
let pathname = document.location.pathname;
|
||||||
|
if(pathname != "/merge-pdfs"){
|
||||||
|
let formElement = document.querySelector("#fileInput-input");
|
||||||
|
formElement.value = '';
|
||||||
|
let editSectionElement = document.querySelector("#editSection");
|
||||||
|
if(editSectionElement){
|
||||||
|
editSectionElement.style.display = "none";
|
||||||
|
}
|
||||||
|
let cropPdfCanvas = document.querySelector("#crop-pdf-canvas");
|
||||||
|
let overlayCanvas = document.querySelector("#overlayCanvas");
|
||||||
|
if(cropPdfCanvas && overlayCanvas){
|
||||||
|
cropPdfCanvas.width = 0;
|
||||||
|
cropPdfCanvas.heigth = 0;
|
||||||
|
|
||||||
|
overlayCanvas.width = 0;
|
||||||
|
overlayCanvas.heigth = 0;
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
console.log("Disabled for 'Merge'");
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ async function checkForUpdate() {
|
|||||||
document.getElementById("update-btn").style.display = "block";
|
document.getElementById("update-btn").style.display = "block";
|
||||||
}
|
}
|
||||||
if (updateLink !== null) {
|
if (updateLink !== null) {
|
||||||
document.getElementById("app-update").innerHTML = updateAvailable.replace("{0}", '<b>' + currentVersion + '</b>').replace("{1}", '<b>' + latestVersion + '</b>');
|
document.getElementById("app-update").innerText = updateAvailable.replace("{0}", '<b>' + currentVersion + '</b>').replace("{1}", '<b>' + latestVersion + '</b>');
|
||||||
if (updateLink.classList.contains("visually-hidden")) {
|
if (updateLink.classList.contains("visually-hidden")) {
|
||||||
updateLink.classList.remove("visually-hidden");
|
updateLink.classList.remove("visually-hidden");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,14 +83,16 @@ function syncFavorites() {
|
|||||||
cards.forEach(card => {
|
cards.forEach(card => {
|
||||||
const isFavorite = localStorage.getItem(card.id) === "favorite";
|
const isFavorite = localStorage.getItem(card.id) === "favorite";
|
||||||
const starIcon = card.querySelector(".favorite-icon span.material-symbols-rounded");
|
const starIcon = card.querySelector(".favorite-icon span.material-symbols-rounded");
|
||||||
if (isFavorite) {
|
if (starIcon) {
|
||||||
starIcon.classList.remove("no-fill");
|
if (isFavorite) {
|
||||||
starIcon.classList.add("fill");
|
starIcon.classList.remove("no-fill");
|
||||||
card.classList.add("favorite");
|
starIcon.classList.add("fill");
|
||||||
} else {
|
card.classList.add("favorite");
|
||||||
starIcon.classList.remove("fill");
|
} else {
|
||||||
starIcon.classList.add("no-fill");
|
starIcon.classList.remove("fill");
|
||||||
card.classList.remove("favorite");
|
starIcon.classList.add("no-fill");
|
||||||
|
card.classList.remove("favorite");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
updateFavoritesSection();
|
updateFavoritesSection();
|
||||||
@@ -260,4 +262,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
showFavoritesOnly();
|
showFavoritesOnly();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,27 +21,55 @@ async function displayFiles(files) {
|
|||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const pageCount = await getPDFPageCount(files[i]);
|
const pageCount = await getPDFPageCount(files[i]);
|
||||||
const pageLabel = pageCount === 1 ? pageTranslation : pagesTranslation;
|
const pageLabel = pageCount === 1 ? pageTranslation : pagesTranslation;
|
||||||
|
|
||||||
|
// Create list item
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
item.className = "list-group-item";
|
item.className = "list-group-item";
|
||||||
item.innerHTML = `
|
|
||||||
<div class="d-flex justify-content-between align-items-center w-100">
|
// Create filename div and set textContent to sanitize
|
||||||
<div class="filename">${files[i].name}</div>
|
const fileNameDiv = document.createElement("div");
|
||||||
<div class="page-info">
|
fileNameDiv.className = "filename";
|
||||||
<span class="page-count">${pageCount} ${pageLabel}</span>
|
fileNameDiv.textContent = files[i].name;
|
||||||
</div>
|
|
||||||
<div class="arrows d-flex">
|
// Create page info div and set textContent to sanitize
|
||||||
<button class="btn btn-secondary move-up"><span>↑</span></button>
|
const pageInfoDiv = document.createElement("div");
|
||||||
<button class="btn btn-secondary move-down"><span>↓</span></button>
|
pageInfoDiv.className = "page-info";
|
||||||
<button class="btn btn-danger remove-file"><span>×</span></button>
|
const pageCountSpan = document.createElement("span");
|
||||||
</div>
|
pageCountSpan.className = "page-count";
|
||||||
</div>
|
pageCountSpan.textContent = `${pageCount} ${pageLabel}`;
|
||||||
`;
|
pageInfoDiv.appendChild(pageCountSpan);
|
||||||
|
|
||||||
|
// Create arrows div with buttons
|
||||||
|
const arrowsDiv = document.createElement("div");
|
||||||
|
arrowsDiv.className = "arrows d-flex";
|
||||||
|
|
||||||
|
const moveUpButton = document.createElement("button");
|
||||||
|
moveUpButton.className = "btn btn-secondary move-up";
|
||||||
|
moveUpButton.innerHTML = "<span>↑</span>";
|
||||||
|
|
||||||
|
const moveDownButton = document.createElement("button");
|
||||||
|
moveDownButton.className = "btn btn-secondary move-down";
|
||||||
|
moveDownButton.innerHTML = "<span>↓</span>";
|
||||||
|
|
||||||
|
const removeButton = document.createElement("button");
|
||||||
|
removeButton.className = "btn btn-danger remove-file";
|
||||||
|
removeButton.innerHTML = "<span>×</span>";
|
||||||
|
|
||||||
|
arrowsDiv.append(moveUpButton, moveDownButton, removeButton);
|
||||||
|
|
||||||
|
// Append elements to item and then to list
|
||||||
|
const itemContainer = document.createElement("div");
|
||||||
|
itemContainer.className = "d-flex justify-content-between align-items-center w-100";
|
||||||
|
itemContainer.append(fileNameDiv, pageInfoDiv, arrowsDiv);
|
||||||
|
|
||||||
|
item.appendChild(itemContainer);
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachMoveButtons();
|
attachMoveButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function getPDFPageCount(file) {
|
async function getPDFPageCount(file) {
|
||||||
const blobUrl = URL.createObjectURL(file);
|
const blobUrl = URL.createObjectURL(file);
|
||||||
const pdf = await pdfjsLib.getDocument(blobUrl).promise;
|
const pdf = await pdfjsLib.getDocument(blobUrl).promise;
|
||||||
@@ -145,3 +173,18 @@ function updateFiles() {
|
|||||||
}
|
}
|
||||||
document.getElementById("fileInput-input").files = dataTransfer.files;
|
document.getElementById("fileInput-input").files = dataTransfer.files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.querySelector("#resetFileInputBtn").addEventListener("click", ()=>{
|
||||||
|
let formElement = document.querySelector("#fileInput-input");
|
||||||
|
formElement.value = '';
|
||||||
|
clearLiElements();
|
||||||
|
updateFiles();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearLiElements(){
|
||||||
|
let listGroupItemNodeList = document.querySelectorAll(".list-group-item");
|
||||||
|
for (let i = 0; i < listGroupItemNodeList.length; i++) {
|
||||||
|
listGroupItemNodeList[i].remove();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
class PdfActionsManager {
|
class PdfActionsManager {
|
||||||
pageDirection;
|
pageDirection;
|
||||||
pagesContainer;
|
pagesContainer;
|
||||||
|
static selectedPages = []; // Static property shared across all instances
|
||||||
|
|
||||||
constructor(id) {
|
constructor(id) {
|
||||||
this.pagesContainer = document.getElementById(id);
|
this.pagesContainer = document.getElementById(id);
|
||||||
@@ -73,6 +74,11 @@ class PdfActionsManager {
|
|||||||
this.addFiles(imgContainer);
|
this.addFiles(imgContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertFileBlankButtonCallback(e) {
|
||||||
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
|
this.addFiles(imgContainer, true);
|
||||||
|
}
|
||||||
|
|
||||||
splitFileButtonCallback(e) {
|
splitFileButtonCallback(e) {
|
||||||
var imgContainer = this.getPageContainer(e.target);
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
imgContainer.classList.toggle("split-before");
|
imgContainer.classList.toggle("split-before");
|
||||||
@@ -89,9 +95,11 @@ class PdfActionsManager {
|
|||||||
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
|
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
|
||||||
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
|
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
|
||||||
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
|
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
|
||||||
|
this.insertFileBlankButtonCallback = this.insertFileBlankButtonCallback.bind(this);
|
||||||
this.splitFileButtonCallback = this.splitFileButtonCallback.bind(this);
|
this.splitFileButtonCallback = this.splitFileButtonCallback.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
adapt(div) {
|
adapt(div) {
|
||||||
div.classList.add("pdf-actions_container");
|
div.classList.add("pdf-actions_container");
|
||||||
const leftDirection = this.pageDirection === "rtl" ? "right" : "left";
|
const leftDirection = this.pageDirection === "rtl" ? "right" : "left";
|
||||||
@@ -132,6 +140,45 @@ class PdfActionsManager {
|
|||||||
|
|
||||||
div.appendChild(buttonContainer);
|
div.appendChild(buttonContainer);
|
||||||
|
|
||||||
|
//enerate checkbox to select individual pages
|
||||||
|
const selectCheckbox = document.createElement("input");
|
||||||
|
selectCheckbox.type = "checkbox";
|
||||||
|
selectCheckbox.classList.add("pdf-actions_checkbox", "form-check-input");
|
||||||
|
selectCheckbox.id = `selectPageCheckbox`;
|
||||||
|
selectCheckbox.checked = window.selectAll;
|
||||||
|
|
||||||
|
div.appendChild(selectCheckbox);
|
||||||
|
|
||||||
|
//only show whenpage select mode is active
|
||||||
|
if (!window.selectPage) {
|
||||||
|
selectCheckbox.classList.add("hidden");
|
||||||
|
} else {
|
||||||
|
selectCheckbox.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCheckbox.onchange = () => {
|
||||||
|
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
||||||
|
if (selectCheckbox.checked) {
|
||||||
|
//adds to array of selected pages
|
||||||
|
window.selectedPages.push(pageNumber);
|
||||||
|
} else {
|
||||||
|
//remove page from selected pages array
|
||||||
|
const index = window.selectedPages.indexOf(pageNumber);
|
||||||
|
if (index !== -1) {
|
||||||
|
window.selectedPages.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.selectedPages.length > 0 && !window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
if (window.selectedPages.length == 0 && window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateSelectedPagesDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
const insertFileButtonContainer = document.createElement("div");
|
const insertFileButtonContainer = document.createElement("div");
|
||||||
|
|
||||||
insertFileButtonContainer.classList.add(
|
insertFileButtonContainer.classList.add(
|
||||||
@@ -152,6 +199,12 @@ class PdfActionsManager {
|
|||||||
splitFileButton.onclick = this.splitFileButtonCallback;
|
splitFileButton.onclick = this.splitFileButtonCallback;
|
||||||
insertFileButtonContainer.appendChild(splitFileButton);
|
insertFileButtonContainer.appendChild(splitFileButton);
|
||||||
|
|
||||||
|
const insertFileBlankButton = document.createElement("button");
|
||||||
|
insertFileBlankButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-blank-button");
|
||||||
|
insertFileBlankButton.innerHTML = `<span class="material-symbols-rounded">insert_page_break</span>`;
|
||||||
|
insertFileBlankButton.onclick = this.insertFileBlankButtonCallback;
|
||||||
|
insertFileButtonContainer.appendChild(insertFileBlankButton);
|
||||||
|
|
||||||
div.appendChild(insertFileButtonContainer);
|
div.appendChild(insertFileButtonContainer);
|
||||||
|
|
||||||
// add this button to every element, but only show it on the last one :D
|
// add this button to every element, but only show it on the last one :D
|
||||||
@@ -179,15 +232,29 @@ class PdfActionsManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
div.addEventListener("mouseenter", () => {
|
div.addEventListener("mouseenter", () => {
|
||||||
|
window.updatePageNumbersAndCheckboxes();
|
||||||
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
||||||
adaptPageNumber(pageNumber, div);
|
adaptPageNumber(pageNumber, div);
|
||||||
|
const checkbox = document.getElementById(`selectPageCheckbox-${pageNumber}`);
|
||||||
|
if (checkbox && !window.selectPage) {
|
||||||
|
checkbox.classList.remove("hidden");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
div.addEventListener("mouseleave", () => {
|
div.addEventListener("mouseleave", () => {
|
||||||
|
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
||||||
const pageNumberElement = div.querySelector(".page-number");
|
const pageNumberElement = div.querySelector(".page-number");
|
||||||
if (pageNumberElement) {
|
if (pageNumberElement) {
|
||||||
div.removeChild(pageNumberElement);
|
div.removeChild(pageNumberElement);
|
||||||
}
|
}
|
||||||
|
const checkbox = document.getElementById(`selectPageCheckbox-${pageNumber}`);
|
||||||
|
if (checkbox && !window.selectPage) {
|
||||||
|
checkbox.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("selectedPagesUpdated", () => {
|
||||||
|
window.updateSelectedPagesDisplay();
|
||||||
});
|
});
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ class PdfContainer {
|
|||||||
this.nameAndArchiveFiles = this.nameAndArchiveFiles.bind(this);
|
this.nameAndArchiveFiles = this.nameAndArchiveFiles.bind(this);
|
||||||
this.splitPDF = this.splitPDF.bind(this);
|
this.splitPDF = this.splitPDF.bind(this);
|
||||||
this.splitAll = this.splitAll.bind(this);
|
this.splitAll = this.splitAll.bind(this);
|
||||||
|
this.deleteSelected = this.deleteSelected.bind(this);
|
||||||
|
this.toggleSelectAll = this.toggleSelectAll.bind(this);
|
||||||
|
this.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay.bind(this);
|
||||||
|
this.toggleSelectPageVisibility = this.toggleSelectPageVisibility.bind(this);
|
||||||
|
this.updatePagesFromCSV = this.updatePagesFromCSV.bind(this);
|
||||||
|
this.addFilesBlankAll = this.addFilesBlankAll.bind(this)
|
||||||
|
this.removeAllElements = this.removeAllElements.bind(this);
|
||||||
|
|
||||||
this.pdfAdapters = pdfAdapters;
|
this.pdfAdapters = pdfAdapters;
|
||||||
|
|
||||||
@@ -31,6 +38,7 @@ class PdfContainer {
|
|||||||
addFiles: this.addFiles,
|
addFiles: this.addFiles,
|
||||||
rotateElement: this.rotateElement,
|
rotateElement: this.rotateElement,
|
||||||
updateFilename: this.updateFilename,
|
updateFilename: this.updateFilename,
|
||||||
|
deleteSelected: this.deleteSelected,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,6 +46,15 @@ class PdfContainer {
|
|||||||
window.exportPdf = this.exportPdf;
|
window.exportPdf = this.exportPdf;
|
||||||
window.rotateAll = this.rotateAll;
|
window.rotateAll = this.rotateAll;
|
||||||
window.splitAll = this.splitAll;
|
window.splitAll = this.splitAll;
|
||||||
|
window.deleteSelected = this.deleteSelected;
|
||||||
|
window.toggleSelectAll = this.toggleSelectAll;
|
||||||
|
window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay;
|
||||||
|
window.toggleSelectPageVisibility = this.toggleSelectPageVisibility;
|
||||||
|
window.updatePagesFromCSV = this.updatePagesFromCSV;
|
||||||
|
window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay;
|
||||||
|
window.updatePageNumbersAndCheckboxes = this.updatePageNumbersAndCheckboxes;
|
||||||
|
window.addFilesBlankAll = this.addFilesBlankAll
|
||||||
|
window.removeAllElements = this.removeAllElements;
|
||||||
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const downloadBtn = document.getElementById("export-button");
|
const downloadBtn = document.getElementById("export-button");
|
||||||
@@ -77,19 +94,27 @@ class PdfContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addFiles(nextSiblingElement) {
|
addFiles(nextSiblingElement, blank = false) {
|
||||||
var input = document.createElement("input");
|
if (blank) {
|
||||||
input.type = "file";
|
|
||||||
input.multiple = true;
|
|
||||||
input.setAttribute("accept", "application/pdf,image/*");
|
|
||||||
input.onchange = async (e) => {
|
|
||||||
const files = e.target.files;
|
|
||||||
|
|
||||||
this.addFilesFromFiles(files, nextSiblingElement);
|
this.addFilesBlank(nextSiblingElement);
|
||||||
this.updateFilename(files ? files[0].name : "");
|
|
||||||
};
|
|
||||||
|
|
||||||
input.click();
|
} else {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.multiple = true;
|
||||||
|
input.setAttribute("accept", "application/pdf,image/*");
|
||||||
|
input.onchange = async (e) => {
|
||||||
|
const files = e.target.files;
|
||||||
|
|
||||||
|
this.addFilesFromFiles(files, nextSiblingElement);
|
||||||
|
this.updateFilename(files ? files[0].name : "");
|
||||||
|
const selectAll = document.getElementById("select-pages-container");
|
||||||
|
selectAll.classList.toggle("hidden", false);
|
||||||
|
};
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addFilesFromFiles(files, nextSiblingElement) {
|
async addFilesFromFiles(files, nextSiblingElement) {
|
||||||
@@ -108,6 +133,47 @@ class PdfContainer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addFilesBlank(nextSiblingElement) {
|
||||||
|
const pdfContent = `
|
||||||
|
%PDF-1.4
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /Catalog /Pages 2 0 R >>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 5 0 R >>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Length 44 >>
|
||||||
|
stream
|
||||||
|
0 0 0 595 0 842 re
|
||||||
|
W
|
||||||
|
n
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 6
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000071 00000 n
|
||||||
|
0000000121 00000 n
|
||||||
|
0000000205 00000 n
|
||||||
|
0000000400 00000 n
|
||||||
|
trailer
|
||||||
|
<< /Size 6 /Root 1 0 R >>
|
||||||
|
startxref
|
||||||
|
278
|
||||||
|
%%EOF
|
||||||
|
`;
|
||||||
|
const blob = new Blob([pdfContent], { type: 'application/pdf' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const file = new File([blob], "blank_page.pdf", { type: "application/pdf" });
|
||||||
|
await this.addPdfFile(file, nextSiblingElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
rotateElement(element, deg) {
|
rotateElement(element, deg) {
|
||||||
var lastTransform = element.style.rotate;
|
var lastTransform = element.style.rotate;
|
||||||
if (!lastTransform) {
|
if (!lastTransform) {
|
||||||
@@ -215,28 +281,256 @@ class PdfContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rotateAll(deg) {
|
rotateAll(deg) {
|
||||||
for (var i = 0; i < this.pagesContainer.childNodes.length; i++) {
|
for (let i = 0; i < this.pagesContainer.childNodes.length; i++) {
|
||||||
const child = this.pagesContainer.children[i];
|
const child = this.pagesContainer.children[i];
|
||||||
if (!child) continue;
|
if (!child) continue;
|
||||||
|
|
||||||
|
const pageIndex = i + 1;
|
||||||
|
//if in page select mode is active rotate only selected pages
|
||||||
|
if (window.selectPage && !window.selectedPages.includes(pageIndex)) continue;
|
||||||
|
|
||||||
const img = child.querySelector("img");
|
const img = child.querySelector("img");
|
||||||
if (!img) continue;
|
if (!img) continue;
|
||||||
|
|
||||||
this.rotateElement(img, deg);
|
this.rotateElement(img, deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeAllElements(){
|
||||||
|
let pageContainerNodeList = document.querySelectorAll(".page-container");
|
||||||
|
for (var i = 0; i < pageContainerNodeList.length; i++) {
|
||||||
|
pageContainerNodeList[i].remove();
|
||||||
|
}
|
||||||
|
document.querySelectorAll(".enable-on-file").forEach((element) => {
|
||||||
|
element.disabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSelected() {
|
||||||
|
window.selectedPages.sort((a, b) => a - b);
|
||||||
|
let deletions = 0;
|
||||||
|
|
||||||
|
window.selectedPages.forEach((pageIndex) => {
|
||||||
|
const adjustedIndex = pageIndex - 1 - deletions;
|
||||||
|
const child = this.pagesContainer.children[adjustedIndex];
|
||||||
|
if (child) {
|
||||||
|
this.pagesContainer.removeChild(child);
|
||||||
|
deletions++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
if (filenameInput)
|
||||||
|
filenameInput.disabled = true;
|
||||||
|
filenameInput.value = "";
|
||||||
|
if (filenameParagraph)
|
||||||
|
filenameParagraph.innerText = "";
|
||||||
|
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.selectedPages = [];
|
||||||
|
this.updatePageNumbersAndCheckboxes();
|
||||||
|
document.dispatchEvent(new Event("selectedPagesUpdated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSelectAll() {
|
||||||
|
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
|
||||||
|
window.selectAll = !window.selectAll;
|
||||||
|
const selectIcon = document.getElementById("select-icon");
|
||||||
|
const deselectIcon = document.getElementById("deselect-icon");
|
||||||
|
|
||||||
|
if (selectIcon.style.display === "none") {
|
||||||
|
selectIcon.style.display = "inline";
|
||||||
|
deselectIcon.style.display = "none";
|
||||||
|
} else {
|
||||||
|
selectIcon.style.display = "none";
|
||||||
|
deselectIcon.style.display = "inline";
|
||||||
|
}
|
||||||
|
checkboxes.forEach((checkbox) => {
|
||||||
|
|
||||||
|
checkbox.checked = window.selectAll;
|
||||||
|
|
||||||
|
const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1;
|
||||||
|
|
||||||
|
if (checkbox.checked) {
|
||||||
|
if (!window.selectedPages.includes(pageNumber)) {
|
||||||
|
window.selectedPages.push(pageNumber);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const index = window.selectedPages.indexOf(pageNumber);
|
||||||
|
if (index !== -1) {
|
||||||
|
window.selectedPages.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateSelectedPagesDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
parseCSVInput(csvInput, maxPageIndex) {
|
||||||
|
const pages = new Set();
|
||||||
|
|
||||||
|
csvInput.split(",").forEach((item) => {
|
||||||
|
const range = item.split("-").map((p) => parseInt(p.trim()));
|
||||||
|
if (range.length === 2) {
|
||||||
|
const [start, end] = range;
|
||||||
|
for (let i = start; i <= end && i <= maxPageIndex; i++) {
|
||||||
|
if (i > 0) { // Ensure the page number is greater than 0
|
||||||
|
pages.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (range.length === 1 && Number.isInteger(range[0])) {
|
||||||
|
const page = range[0];
|
||||||
|
if (page > 0 && page <= maxPageIndex) { // Ensure page is within valid range
|
||||||
|
pages.add(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(pages).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePagesFromCSV() {
|
||||||
|
const csvInput = document.getElementById("csv-input").value;
|
||||||
|
|
||||||
|
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||||
|
const maxPageIndex = allPages.length;
|
||||||
|
|
||||||
|
window.selectedPages = this.parseCSVInput(csvInput, maxPageIndex);
|
||||||
|
|
||||||
|
this.updateSelectedPagesDisplay();
|
||||||
|
|
||||||
|
const allCheckboxes = document.querySelectorAll(".pdf-actions_checkbox");
|
||||||
|
allCheckboxes.forEach((checkbox) => {
|
||||||
|
const page = parseInt(checkbox.getAttribute("data-page-number"));
|
||||||
|
checkbox.checked = window.selectedPages.includes(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formatSelectedPages(pages) {
|
||||||
|
if (pages.length === 0) return "";
|
||||||
|
|
||||||
|
pages.sort((a, b) => a - b); // Sort the page numbers in ascending order
|
||||||
|
const ranges = [];
|
||||||
|
let start = pages[0];
|
||||||
|
let end = start;
|
||||||
|
|
||||||
|
for (let i = 1; i < pages.length; i++) {
|
||||||
|
if (pages[i] === end + 1) {
|
||||||
|
// Consecutive page, update end
|
||||||
|
end = pages[i];
|
||||||
|
} else {
|
||||||
|
// Non-consecutive page, finalize current range
|
||||||
|
ranges.push(start === end ? `${start}` : `${start}-${end}`);
|
||||||
|
start = pages[i];
|
||||||
|
end = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the last range
|
||||||
|
ranges.push(start === end ? `${start}` : `${start}-${end}`);
|
||||||
|
|
||||||
|
return ranges.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedPagesDisplay() {
|
||||||
|
const selectedPagesList = document.getElementById("selected-pages-list");
|
||||||
|
const selectedPagesInput = document.getElementById("csv-input");
|
||||||
|
selectedPagesList.innerHTML = ""; // Clear the list
|
||||||
|
|
||||||
|
window.selectedPages.forEach((page) => {
|
||||||
|
const pageItem = document.createElement("div");
|
||||||
|
pageItem.className = "page-item";
|
||||||
|
|
||||||
|
const pageNumber = document.createElement("span");
|
||||||
|
const pagelabel = /*[[#{multiTool.page}]]*/ 'Page';
|
||||||
|
pageNumber.className = "selected-page-number";
|
||||||
|
pageNumber.innerText = `${pagelabel} ${page}`;
|
||||||
|
pageItem.appendChild(pageNumber);
|
||||||
|
|
||||||
|
const removeBtn = document.createElement("span");
|
||||||
|
removeBtn.className = "remove-btn";
|
||||||
|
removeBtn.innerHTML = "✕";
|
||||||
|
|
||||||
|
// Remove page from selected pages list and update display and checkbox
|
||||||
|
removeBtn.onclick = () => {
|
||||||
|
window.selectedPages = window.selectedPages.filter((p) => p !== page);
|
||||||
|
this.updateSelectedPagesDisplay();
|
||||||
|
|
||||||
|
const checkbox = document.getElementById(`selectPageCheckbox-${page}`);
|
||||||
|
if (checkbox) {
|
||||||
|
checkbox.checked = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pageItem.appendChild(removeBtn);
|
||||||
|
selectedPagesList.appendChild(pageItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the input field with the formatted page list
|
||||||
|
selectedPagesInput.value = this.formatSelectedPages(window.selectedPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePageRanges(ranges) {
|
||||||
|
const pages = new Set();
|
||||||
|
|
||||||
|
ranges.split(',').forEach(range => {
|
||||||
|
const [start, end] = range.split('-').map(Number);
|
||||||
|
if (end) {
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
pages.add(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pages.add(start);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(pages).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
addFilesBlankAll() {
|
||||||
|
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||||
|
allPages.forEach((page, index) => {
|
||||||
|
if (index !== 0) {
|
||||||
|
this.addFiles(page, true)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
splitAll() {
|
splitAll() {
|
||||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||||
if (this.pagesContainer.querySelectorAll(".split-before").length > 0) {
|
|
||||||
allPages.forEach(page => {
|
if (!window.selectPage) {
|
||||||
page.classList.remove("split-before");
|
const hasSplit = this.pagesContainer.querySelectorAll(".split-before").length > 0;
|
||||||
});
|
if (hasSplit) {
|
||||||
} else {
|
allPages.forEach(page => {
|
||||||
allPages.forEach(page => {
|
page.classList.remove("split-before");
|
||||||
page.classList.add("split-before");
|
});
|
||||||
});
|
} else {
|
||||||
|
allPages.forEach(page => {
|
||||||
|
page.classList.add("split-before");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allPages.forEach((page, index) => {
|
||||||
|
const pageIndex = index;
|
||||||
|
if (window.selectPage && !window.selectedPages.includes(pageIndex)) return;
|
||||||
|
|
||||||
|
if (page.classList.contains("split-before")) {
|
||||||
|
page.classList.remove("split-before");
|
||||||
|
} else {
|
||||||
|
page.classList.add("split-before");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async splitPDF(baseDocBytes, splitters) {
|
async splitPDF(baseDocBytes, splitters) {
|
||||||
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
|
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
|
||||||
const pageNum = baseDocument.getPages().length;
|
const pageNum = baseDocument.getPages().length;
|
||||||
@@ -279,52 +573,54 @@ class PdfContainer {
|
|||||||
return zip;
|
return zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
async exportPdf() {
|
async exportPdf(selected) {
|
||||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||||
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
|
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
|
||||||
for (var i = 0; i < pageContainers.length; i++) {
|
for (var i = 0; i < pageContainers.length; i++) {
|
||||||
const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
|
if (!selected || window.selectedPages.includes(i + 1)) {
|
||||||
if (!img) continue;
|
const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
|
||||||
let page;
|
if (!img) continue;
|
||||||
if (img.doc) {
|
let page;
|
||||||
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
|
if (img.doc) {
|
||||||
page = pages[0];
|
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
|
||||||
pdfDoc.addPage(page);
|
page = pages[0];
|
||||||
} else {
|
pdfDoc.addPage(page);
|
||||||
page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]);
|
} else {
|
||||||
const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer());
|
page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]);
|
||||||
const uint8Array = new Uint8Array(imageBytes);
|
const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer());
|
||||||
const imageType = detectImageType(uint8Array);
|
const uint8Array = new Uint8Array(imageBytes);
|
||||||
|
const imageType = detectImageType(uint8Array);
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
switch (imageType) {
|
switch (imageType) {
|
||||||
case 'PNG':
|
case 'PNG':
|
||||||
image = await pdfDoc.embedPng(imageBytes);
|
image = await pdfDoc.embedPng(imageBytes);
|
||||||
break;
|
break;
|
||||||
case 'JPEG':
|
case 'JPEG':
|
||||||
image = await pdfDoc.embedJpg(imageBytes);
|
image = await pdfDoc.embedJpg(imageBytes);
|
||||||
break;
|
break;
|
||||||
case 'TIFF':
|
case 'TIFF':
|
||||||
image = await pdfDoc.embedTiff(imageBytes);
|
image = await pdfDoc.embedTiff(imageBytes);
|
||||||
break;
|
break;
|
||||||
case 'GIF':
|
case 'GIF':
|
||||||
console.warn(`Unsupported image type: ${imageType}`);
|
console.warn(`Unsupported image type: ${imageType}`);
|
||||||
continue; // Skip this image
|
continue; // Skip this image
|
||||||
default:
|
default:
|
||||||
console.warn(`Unsupported image type: ${imageType}`);
|
console.warn(`Unsupported image type: ${imageType}`);
|
||||||
continue; // Skip this image
|
continue; // Skip this image
|
||||||
|
}
|
||||||
|
page.drawImage(image, {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: img.naturalWidth,
|
||||||
|
height: img.naturalHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const rotation = img.style.rotate;
|
||||||
|
if (rotation) {
|
||||||
|
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
|
||||||
|
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
|
||||||
}
|
}
|
||||||
page.drawImage(image, {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: img.naturalWidth,
|
|
||||||
height: img.naturalHeight,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const rotation = img.style.rotate;
|
|
||||||
if (rotation) {
|
|
||||||
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
|
|
||||||
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pdfDoc.setCreator(stirlingPDFLabel);
|
pdfDoc.setCreator(stirlingPDFLabel);
|
||||||
@@ -436,7 +732,44 @@ class PdfContainer {
|
|||||||
// filenameInput.value.replace('.','');
|
// filenameInput.value.replace('.','');
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toggleSelectPageVisibility() {
|
||||||
|
window.selectPage = !window.selectPage;
|
||||||
|
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
checkbox.classList.toggle("hidden", !window.selectPage);
|
||||||
|
});
|
||||||
|
const deleteButton = document.getElementById("delete-button");
|
||||||
|
deleteButton.classList.toggle("hidden", !window.selectPage);
|
||||||
|
const selectedPages = document.getElementById("selected-pages-display");
|
||||||
|
selectedPages.classList.toggle("hidden", !window.selectPage);
|
||||||
|
const selectAll = document.getElementById("select-All-Container");
|
||||||
|
selectedPages.classList.toggle("hidden", !window.selectPage);
|
||||||
|
const exportSelected = document.getElementById("export-selected-button");
|
||||||
|
exportSelected.classList.toggle("hidden", !window.selectPage);
|
||||||
|
const selectPagesButton = document.getElementById("select-pages-button");
|
||||||
|
selectPagesButton.style.opacity = window.selectPage ? "1" : "0.5";
|
||||||
|
|
||||||
|
if (window.selectPage) {
|
||||||
|
this.updatePageNumbersAndCheckboxes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updatePageNumbersAndCheckboxes() {
|
||||||
|
const pageDivs = document.querySelectorAll(".pdf-actions_container");
|
||||||
|
|
||||||
|
pageDivs.forEach((div, index) => {
|
||||||
|
const pageNumber = index + 1;
|
||||||
|
const checkbox = div.querySelector(".pdf-actions_checkbox");
|
||||||
|
checkbox.id = `selectPageCheckbox-${pageNumber}`;
|
||||||
|
checkbox.setAttribute("data-page-number", pageNumber);
|
||||||
|
checkbox.checked = window.selectedPages.includes(pageNumber);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectImageType(uint8Array) {
|
function detectImageType(uint8Array) {
|
||||||
// Check for PNG signature
|
// Check for PNG signature
|
||||||
if (uint8Array[0] === 137 && uint8Array[1] === 80 && uint8Array[2] === 78 && uint8Array[3] === 71) {
|
if (uint8Array[0] === 137 && uint8Array[1] === 80 && uint8Array[2] === 78 && uint8Array[3] === 71) {
|
||||||
@@ -450,7 +783,7 @@ function detectImageType(uint8Array) {
|
|||||||
|
|
||||||
// Check for TIFF signature (little-endian and big-endian)
|
// Check for TIFF signature (little-endian and big-endian)
|
||||||
if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
|
if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
|
||||||
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
|
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
|
||||||
return 'TIFF';
|
return 'TIFF';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,4 +794,7 @@ function detectImageType(uint8Array) {
|
|||||||
|
|
||||||
return 'UNKNOWN';
|
return 'UNKNOWN';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default PdfContainer;
|
export default PdfContainer;
|
||||||
|
|||||||
@@ -70,3 +70,37 @@ document.querySelector("#navbarSearchInput").addEventListener("input", function
|
|||||||
resultsBox.style.width = window.navItemMaxWidth + "px";
|
resultsBox.style.width = window.navItemMaxWidth + "px";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const searchDropdown = document.getElementById('searchDropdown');
|
||||||
|
const searchInput = document.getElementById('navbarSearchInput');
|
||||||
|
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
|
||||||
|
|
||||||
|
// Handle dropdown shown event
|
||||||
|
searchDropdown.addEventListener('shown.bs.dropdown', function () {
|
||||||
|
searchInput.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle hover opening
|
||||||
|
searchDropdown.addEventListener('mouseenter', function () {
|
||||||
|
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
|
||||||
|
dropdownInstance.show();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
searchInput.focus();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle mouse leave
|
||||||
|
searchDropdown.addEventListener('mouseleave', function () {
|
||||||
|
// Check if current value is empty (including if user typed and then deleted)
|
||||||
|
if (searchInput.value.trim().length === 0) {
|
||||||
|
searchInput.blur();
|
||||||
|
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
|
||||||
|
dropdownInstance.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchDropdown.addEventListener('hidden.bs.dropdown', function () {
|
||||||
|
if (searchInput.value.trim().length === 0) {
|
||||||
|
searchInput.blur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<span class="tool-header-text" th:text="#{MarkdownToPDF.header}"></span>
|
<span class="tool-header-text" th:text="#{MarkdownToPDF.header}"></span>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/markdown/pdf'}">
|
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/markdown/pdf'}">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/markdown')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='.md')}"></div>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>
|
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>
|
||||||
|
|||||||
@@ -24,16 +24,20 @@
|
|||||||
<input id="height" type="hidden" name="height">
|
<input id="height" type="hidden" name="height">
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
<div style="position: relative; display: inline-block;">
|
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
|
||||||
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
|
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
|
||||||
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
|
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script>
|
<script>
|
||||||
let pdfCanvas = document.getElementById('crop-pdf-canvas');
|
let pdfCanvas = document.getElementById('cropPdfCanvas');
|
||||||
let overlayCanvas = document.getElementById('overlayCanvas');
|
let overlayCanvas = document.getElementById('overlayCanvas');
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
canvasesContainer.style.display = "none";
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
let context = pdfCanvas.getContext('2d');
|
let context = pdfCanvas.getContext('2d');
|
||||||
let overlayContext = overlayCanvas.getContext('2d');
|
let overlayContext = overlayCanvas.getContext('2d');
|
||||||
@@ -59,8 +63,11 @@
|
|||||||
let rectWidth = 0;
|
let rectWidth = 0;
|
||||||
let rectHeight = 0;
|
let rectHeight = 0;
|
||||||
|
|
||||||
fileInput.addEventListener('change', function(e) {
|
|
||||||
let file = e.target.files[0];
|
let pageScale = 1; // The scale which the pdf page renders
|
||||||
|
let timeId = null; // timeout id for resizing canvases event
|
||||||
|
|
||||||
|
function renderPageFromFile(file) {
|
||||||
if (file.type === 'application/pdf') {
|
if (file.type === 'application/pdf') {
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onload = function(ev) {
|
reader.onload = function(ev) {
|
||||||
@@ -74,6 +81,35 @@
|
|||||||
};
|
};
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
clearTimeout(timeId);
|
||||||
|
|
||||||
|
timeId = setTimeout(function () {
|
||||||
|
if (fileInput.files.length == 0) return;
|
||||||
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
|
||||||
|
|
||||||
|
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
||||||
|
|
||||||
|
pdfCanvas.width = containerRect.width;
|
||||||
|
pdfCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
overlayCanvas.width = containerRect.width;
|
||||||
|
overlayCanvas.height = containerRect.height;
|
||||||
|
|
||||||
|
let file = fileInput.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', function(e) {
|
||||||
|
canvasesContainer.style.display = "block"; // set for visual purposes
|
||||||
|
let file = e.target.files[0];
|
||||||
|
renderPageFromFile(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
cropForm.addEventListener('submit', function(e) {
|
cropForm.addEventListener('submit', function(e) {
|
||||||
@@ -81,8 +117,8 @@
|
|||||||
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
|
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
|
||||||
xInput.value = 0;
|
xInput.value = 0;
|
||||||
yInput.value = 0;
|
yInput.value = 0;
|
||||||
widthInput.value = pdfCanvas.width;
|
widthInput.value = containerRect.width;
|
||||||
heightInput.value = pdfCanvas.height;
|
heightInput.value = containerRect.height;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,10 +153,10 @@
|
|||||||
|
|
||||||
let flippedY = pdfCanvas.height - e.offsetY;
|
let flippedY = pdfCanvas.height - e.offsetY;
|
||||||
|
|
||||||
xInput.value = startX;
|
xInput.value = startX / pageScale;
|
||||||
yInput.value = flippedY;
|
yInput.value = flippedY / pageScale;
|
||||||
widthInput.value = rectWidth;
|
widthInput.value = rectWidth / pageScale;
|
||||||
heightInput.value = rectHeight;
|
heightInput.value = rectHeight /pageScale;
|
||||||
|
|
||||||
// Draw the final rectangle on the main canvas
|
// Draw the final rectangle on the main canvas
|
||||||
context.strokeStyle = 'red';
|
context.strokeStyle = 'red';
|
||||||
@@ -131,7 +167,16 @@
|
|||||||
|
|
||||||
function renderPage(pageNumber) {
|
function renderPage(pageNumber) {
|
||||||
pdfDoc.getPage(pageNumber).then(function(page) {
|
pdfDoc.getPage(pageNumber).then(function(page) {
|
||||||
let viewport = page.getViewport({ scale: 1.0 });
|
let canvasesContainer = document.getElementById('canvasesContainer');
|
||||||
|
let containerRect = canvasesContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
pageScale = containerRect.width / page.getViewport({ scale: 1 }).width; // The new scale
|
||||||
|
|
||||||
|
let viewport = page.getViewport({ scale: containerRect.width / page.getViewport({ scale: 1 }).width });
|
||||||
|
|
||||||
|
canvasesContainer.width =viewport.width;
|
||||||
|
canvasesContainer.height = viewport.height;
|
||||||
|
|
||||||
pdfCanvas.width = viewport.width;
|
pdfCanvas.width = viewport.width;
|
||||||
pdfCanvas.height = viewport.height;
|
pdfCanvas.height = viewport.height;
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,44 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||||
<head>
|
xmlns:th="https://www.thymeleaf.org">
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}"></th:block>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<div id="page-container">
|
<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}">
|
||||||
<div id="content-wrap">
|
</th:block>
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
</head>
|
||||||
<br><br>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-6 bg-card">
|
|
||||||
<div class="tool-header">
|
|
||||||
<span class="material-symbols-rounded tool-header-icon organize">upload</span>
|
|
||||||
<span class="tool-header-text" th:text="#{pageExtracter.header}"></span>
|
|
||||||
</div>
|
|
||||||
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
|
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
|
||||||
<input type="hidden" id="customMode" name="customMode" value="">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
|
||||||
<input type="text" class="form-control" id="pageOrder" name="pageNumbers" th:placeholder="#{pageExtracter.placeholder}" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageExtracter.submit}"></button>
|
<body>
|
||||||
</form>
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
|
<br><br>
|
||||||
|
<th:block th:insert="~{fragments/multi-toolAdvert.html :: multi-toolAdvert}"></th:block>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6 bg-card">
|
||||||
|
<div class="tool-header">
|
||||||
|
<span class="material-symbols-rounded tool-header-icon organize">upload</span>
|
||||||
|
<span class="tool-header-text" th:text="#{pageExtracter.header}"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="customMode" name="customMode" value="">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
||||||
|
<input type="text" class="form-control" id="pageOrder" name="pageNumbers"
|
||||||
|
th:placeholder="#{pageExtracter.placeholder}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageExtracter.submit}"></button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -201,6 +201,7 @@
|
|||||||
window.stirlingPDF.error = /*[[#{error}]]*/ "Error";
|
window.stirlingPDF.error = /*[[#{error}]]*/ "Error";
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script th:src="@{'/js/downloader.js'}"></script>
|
<script th:src="@{'/js/downloader.js'}"></script>
|
||||||
|
|
||||||
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
||||||
|
|||||||
57
src/main/resources/templates/fragments/multi-toolAdvert.html
Normal file
57
src/main/resources/templates/fragments/multi-toolAdvert.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<div th:fragment="multi-toolAdvert" class="mx-auto">
|
||||||
|
<div id="multi-toolAdvert" class="multi-toolAdvert">
|
||||||
|
<div>
|
||||||
|
<span th:utext="#{multiTool-advert.message(|/multi-tool|)}"></span>
|
||||||
|
<button id="closeMultiToolAdvert" style="position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 12px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1;" aria-label="Close">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.multi-toolAdvert {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
max-width: 52rem;
|
||||||
|
z-index: 0;
|
||||||
|
background-color: var(--md-sys-color-surface-5);
|
||||||
|
border-radius: 2rem;
|
||||||
|
padding: 10px 27px 10px 20px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multi-toolAdvert a {
|
||||||
|
color: #007bff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multi-toolAdvert a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const advert = document.getElementById('multi-toolAdvert');
|
||||||
|
const closeBtn = document.getElementById('closeMultiToolAdvert');
|
||||||
|
|
||||||
|
const cacheKey = `closeMultiToolAdvert_${window.location.pathname}`;
|
||||||
|
|
||||||
|
if (localStorage.getItem(cacheKey) !== 'true') {
|
||||||
|
advert.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
advert.style.display = 'none';
|
||||||
|
localStorage.setItem(cacheKey, 'true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
@@ -9,8 +9,10 @@
|
|||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const currentVersion = /*[[${@appVersion}]]*/ '';
|
const currentVersion = /*[[${@appVersion}]]*/ '';
|
||||||
const noFavourites = /*[[#{noFavourites}]]*/ '';
|
const noFavourites = /*[[#{noFavourites}]]*/ '';
|
||||||
const updateAvailable = /*[[#{settings.updateAvailable}]]*/ '';
|
console.log(noFavourites);
|
||||||
|
const updateAvailable = /*[[#{settings.updateAvailable}]]*/ '';
|
||||||
</script>
|
</script>
|
||||||
|
<script th:src="@{'/js/homecard.js'}"></script>
|
||||||
<script th:src="@{'/js/githubVersion.js'}"></script>
|
<script th:src="@{'/js/githubVersion.js'}"></script>
|
||||||
<nav class="navbar navbar-expand-xl">
|
<nav class="navbar navbar-expand-xl">
|
||||||
<div class="container ">
|
<div class="container ">
|
||||||
@@ -308,10 +310,10 @@
|
|||||||
</li> -->
|
</li> -->
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav flex-nowrap">
|
<ul class="navbar-nav flex-nowrap">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" id="navbarDropdown-5" href="#" role="button" data-bs-toggle="dropdown"
|
<a class="nav-link" id="navbarDropdown-5" href="#" role="button" data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false">
|
aria-haspopup="true" aria-expanded="false" th:title="#{navbar.favorite}">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
star
|
star
|
||||||
</span>
|
</span>
|
||||||
@@ -324,7 +326,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" id="dark-mode-toggle" href="#">
|
<a class="nav-link" id="dark-mode-toggle" href="#" th:title="#{navbar.darkmode}">
|
||||||
<span class="material-symbols-rounded" id="dark-mode-icon">
|
<span class="material-symbols-rounded" id="dark-mode-icon">
|
||||||
dark_mode
|
dark_mode
|
||||||
</span>
|
</span>
|
||||||
@@ -333,7 +335,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="#" id="languageDropdown" role="button" data-bs-toggle="dropdown"
|
<a class="nav-link" href="#" id="languageDropdown" role="button" data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false">
|
aria-haspopup="true" aria-expanded="false" th:title="#{navbar.language}">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
language
|
language
|
||||||
</span>
|
</span>
|
||||||
@@ -349,7 +351,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:title="#{navbar.search}">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
search
|
search
|
||||||
</span>
|
</span>
|
||||||
@@ -358,7 +360,7 @@
|
|||||||
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
|
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
|
||||||
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
||||||
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
|
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
|
||||||
<input class="form-control search-input" type="search" placeholder="Search" aria-label="Search" id="navbarSearchInput">
|
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}" aria-label="Search" id="navbarSearchInput">
|
||||||
</form>
|
</form>
|
||||||
<!-- Search Results -->
|
<!-- Search Results -->
|
||||||
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
|
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
|
||||||
@@ -368,13 +370,13 @@
|
|||||||
|
|
||||||
<li class="nav-item" th:if="${!@runningEE}">
|
<li class="nav-item" th:if="${!@runningEE}">
|
||||||
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank" rel="noopener noreferrer">
|
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank" rel="noopener noreferrer">
|
||||||
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
|
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<!-- Settings Button -->
|
<!-- Settings Button -->
|
||||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal" th:title="#{navbar.settings}">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
settings
|
settings
|
||||||
</span>
|
</span>
|
||||||
@@ -405,14 +407,14 @@
|
|||||||
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
|
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3 mt-3">
|
<div class="d-flex justify-content-between align-items-center mb-3 mt-3">
|
||||||
<div class="footer-center" style="flex-direction: row;">
|
<div class="footer-center" style="flex-direction: row;">
|
||||||
<a href="https://github.com/Stirling-Tools/Stirling-PDF" class="mx-1" role="button"
|
<a href="https://github.com/Stirling-Tools/Stirling-PDF" class="mx-1" role="button" target="_blank"
|
||||||
th:title="#{visitGithub}">
|
th:title="#{visitGithub}">
|
||||||
<img th:src="@{'/images/github.svg'}" alt="github">
|
<img th:src="@{'/images/github.svg'}" alt="github">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hub.docker.com/r/frooodle/s-pdf" class="mx-1" role="button" th:title="#{seeDockerHub}">
|
<a href="https://hub.docker.com/r/frooodle/s-pdf" class="mx-1" role="button" target="_blank"th:title="#{seeDockerHub}">
|
||||||
<img th:src="@{'/images/docker.svg'}" alt="docker">
|
<img th:src="@{'/images/docker.svg'}" alt="docker">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" th:title="#{joinDiscord}">
|
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" target="_blank" th:title="#{joinDiscord}">
|
||||||
<img th:src="@{'/images/discord.svg'}" alt="discord">
|
<img th:src="@{'/images/discord.svg'}" alt="discord">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -90,8 +90,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check m-2 mb-3">
|
<div class="form-check m-2 mb-3">
|
||||||
<input type="checkbox" id="remember" value="remember-me">
|
<input type="checkbox" name="remember-me" id="remember-me">
|
||||||
<label for="remember" th:text="#{login.rememberme}"></label>
|
<label for="remember-me" th:text="#{login.rememberme}"></label>
|
||||||
</div>
|
</div>
|
||||||
<button class="w-100 btn btn-lg btn-primary" type="submit" th:text="#{login.signin}">Sign in</button>
|
<button class="w-100 btn btn-lg btn-primary" type="submit" th:text="#{login.signin}">Sign in</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
<th:block th:insert="~{fragments/multi-toolAdvert.html :: multi-toolAdvert}"></th:block>
|
||||||
<div class="container" id="dropContainer">
|
<div class="container" id="dropContainer">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 bg-card">
|
<div class="col-md-6 bg-card">
|
||||||
@@ -39,6 +40,9 @@
|
|||||||
<button type="button" id="sortByDateBtn" class="btn btn-info" th:text="#{merge.sortByDate}"></button>
|
<button type="button" id="sortByDateBtn" class="btn btn-info" th:text="#{merge.sortByDate}"></button>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="button" id="resetFileInputBtn" class="btn btn-danger" th:text="#{reset}">Reset</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<span id="pageTranslation" th:text="#{page}" style="display:none;"></span>
|
<span id="pageTranslation" th:text="#{page}" style="display:none;"></span>
|
||||||
<span id="pagesTranslation" th:text="#{pages}" style="display:none;"></span>
|
<span id="pagesTranslation" th:text="#{pages}" style="display:none;"></span>
|
||||||
|
|||||||
@@ -47,13 +47,56 @@
|
|||||||
cut
|
cut
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf()" disabled>
|
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
|
||||||
|
th:title="#{multiTool.selectPages}" onclick="toggleSelectPageVisibility()" disabled>
|
||||||
|
<span id="select-pages-button" class="material-symbols-rounded">
|
||||||
|
event_list
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
|
||||||
|
onclick="toggleSelectAll()" disabled>
|
||||||
|
<span th:title="#{multiTool.selectAll}" class="material-symbols-rounded"
|
||||||
|
id="select-icon">select_all</span>
|
||||||
|
<span th:title="#{multiTool.deselectAll}" class="material-symbols-rounded" style="display: none;"
|
||||||
|
id="deselect-icon">deselect</span>
|
||||||
|
</button>
|
||||||
|
<div class=" button-container">
|
||||||
|
<button th:title="#{multiTool.deleteSelected}" id="delete-button" class="btn btn-danger hidden"
|
||||||
|
onclick="deleteSelected()">
|
||||||
|
<span class="material-symbols-rounded">delete</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button id="export-selected-button" class="btn btn-primary enable-on-file hidden"
|
||||||
|
onclick="exportPdf(true)" disabled>
|
||||||
|
<span th:title="#{multiTool.downloadSelected}" class="material-symbols-rounded">
|
||||||
|
file_save
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary enable-on-file" onclick="addFilesBlankAll()" disabled>
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
|
insert_page_break
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)" disabled>
|
||||||
|
<span th:title="#{multiTool.downloadAll}" class="material-symbols-rounded">
|
||||||
download
|
download
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="button" id="resetFileInputBtn" class="btn btn-danger" onclick="removeAllElements()" th:text="#{reset}">Reset</button>
|
||||||
|
</div>
|
||||||
|
<div id="selected-pages-display" class="selected-pages-container hidden">
|
||||||
|
<div style="display:flex; height:3rem; margin-right:1rem">
|
||||||
|
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
|
||||||
|
Pages</h5>
|
||||||
|
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
|
||||||
|
value="">
|
||||||
|
</div>
|
||||||
|
<ul id="selected-pages-list" class="pages-list"></ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="multi-tool-container">
|
<div class="multi-tool-container">
|
||||||
<div class="d-flex flex-wrap" id="pages-container-wrapper">
|
<div class="d-flex flex-wrap" id="pages-container-wrapper">
|
||||||
<div id="pages-container">
|
<div id="pages-container">
|
||||||
@@ -77,6 +120,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
||||||
|
<script>
|
||||||
|
window.selectedPages = [];
|
||||||
|
window.selectPage = false;
|
||||||
|
window.selectAll = false;
|
||||||
|
const csvInput = document.getElementById("csv-input");
|
||||||
|
csvInput.addEventListener("keydown", function (event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
updatePagesFromCSV();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
csvInput.addEventListener("blur", function () {
|
||||||
|
updatePagesFromCSV();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import PdfContainer from './js/multitool/PdfContainer.js';
|
import PdfContainer from './js/multitool/PdfContainer.js';
|
||||||
import DragDropManager from "./js/multitool/DragDropManager.js";
|
import DragDropManager from "./js/multitool/DragDropManager.js";
|
||||||
@@ -90,7 +147,6 @@
|
|||||||
// enables the default action buttons on each file
|
// enables the default action buttons on each file
|
||||||
const pdfActionsManager = new PdfActionsManager('pages-container');
|
const pdfActionsManager = new PdfActionsManager('pages-container');
|
||||||
const fileDragManager = new FileDragManager();
|
const fileDragManager = new FileDragManager();
|
||||||
|
|
||||||
// Scroll the wrapper horizontally
|
// Scroll the wrapper horizontally
|
||||||
|
|
||||||
// Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons.
|
// Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons.
|
||||||
|
|||||||
@@ -1,60 +1,69 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||||
<head>
|
xmlns:th="https://www.thymeleaf.org">
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pdfOrganiser.title}, header=#{pdfOrganiser.header})}"></th:block>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<div id="page-container">
|
<th:block th:insert="~{fragments/common :: head(title=#{pdfOrganiser.title}, header=#{pdfOrganiser.header})}">
|
||||||
<div id="content-wrap">
|
</th:block>
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
</head>
|
||||||
<br><br>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-6 bg-card">
|
|
||||||
<div class="tool-header">
|
|
||||||
<span class="material-symbols-rounded tool-header-icon organize">format_list_bulleted</span>
|
|
||||||
<span class="tool-header-text" th:text="#{pdfOrganiser.header}"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
|
<body>
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
<div id="page-container">
|
||||||
<div class="mb-3">
|
<div id="content-wrap">
|
||||||
<label for="customMode" th:text="#{pdfOrganiser.mode}">Mode</label>
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
<select class="form-control" id="customMode" name="customMode">
|
<br><br>
|
||||||
<option value="" th:text="#{pdfOrganiser.mode.1}">Custom Page Order</option>
|
<th:block th:insert="~{fragments/multi-toolAdvert.html :: multi-toolAdvert}"></th:block>
|
||||||
<option value="REVERSE_ORDER" th:text="#{pdfOrganiser.mode.2}">Reverse Order</option>
|
<div class="container">
|
||||||
<option value="DUPLEX_SORT" th:text="#{pdfOrganiser.mode.3}">Duplex Sort</option>
|
<div class="row justify-content-center">
|
||||||
<option value="BOOKLET_SORT" th:text="#{pdfOrganiser.mode.4}">Booklet Sort</option>
|
<div class="col-md-6 bg-card">
|
||||||
<option value="SIDE_STITCH_BOOKLET_SORT" th:text="#{pdfOrganiser.mode.5}">Side Stitch Booklet Sort</option>
|
<div class="tool-header">
|
||||||
<option value="ODD_EVEN_SPLIT" th:text="#{pdfOrganiser.mode.6}">Odd-Even Split</option>
|
<span class="material-symbols-rounded tool-header-icon organize">format_list_bulleted</span>
|
||||||
<option value="ODD_EVEN_MERGE" th:text="#{pdfOrganiser.mode.10}">Odd-Even Merge</option>
|
<span class="tool-header-text" th:text="#{pdfOrganiser.header}"></span>
|
||||||
<option value="REMOVE_FIRST" th:text="#{pdfOrganiser.mode.7}">Remove First</option>
|
|
||||||
<option value="REMOVE_LAST" th:text="#{pdfOrganiser.mode.8}">Remove Last</option>
|
|
||||||
<option value="REMOVE_FIRST_AND_LAST" th:text="#{pdfOrganiser.mode.9}">Remove First and Last</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
|
||||||
<input type="text" class="form-control" id="pageOrder" name="pageNumbers" th:placeholder="#{pdfOrganiser.placeholder}" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
document.getElementById('customMode').addEventListener('change', function () {
|
|
||||||
var pageOrderInput = document.getElementById('pageOrder');
|
|
||||||
if (this.value === "") {
|
|
||||||
pageOrderInput.disabled = false;
|
|
||||||
} else {
|
|
||||||
pageOrderInput.disabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="customMode" th:text="#{pdfOrganiser.mode}">Mode</label>
|
||||||
|
<select class="form-control" id="customMode" name="customMode">
|
||||||
|
<option value="" th:text="#{pdfOrganiser.mode.1}">Custom Page Order</option>
|
||||||
|
<option value="REVERSE_ORDER" th:text="#{pdfOrganiser.mode.2}">Reverse Order</option>
|
||||||
|
<option value="DUPLEX_SORT" th:text="#{pdfOrganiser.mode.3}">Duplex Sort</option>
|
||||||
|
<option value="BOOKLET_SORT" th:text="#{pdfOrganiser.mode.4}">Booklet Sort</option>
|
||||||
|
<option value="SIDE_STITCH_BOOKLET_SORT" th:text="#{pdfOrganiser.mode.5}">Side Stitch Booklet Sort
|
||||||
|
</option>
|
||||||
|
<option value="ODD_EVEN_SPLIT" th:text="#{pdfOrganiser.mode.6}">Odd-Even Split</option>
|
||||||
|
<option value="ODD_EVEN_MERGE" th:text="#{pdfOrganiser.mode.10}">Odd-Even Merge</option>
|
||||||
|
<option value="REMOVE_FIRST" th:text="#{pdfOrganiser.mode.7}">Remove First</option>
|
||||||
|
<option value="REMOVE_LAST" th:text="#{pdfOrganiser.mode.8}">Remove Last</option>
|
||||||
|
<option value="REMOVE_FIRST_AND_LAST" th:text="#{pdfOrganiser.mode.9}">Remove First and Last</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
||||||
|
<input type="text" class="form-control" id="pageOrder" name="pageNumbers"
|
||||||
|
th:placeholder="#{pdfOrganiser.placeholder}" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
document.getElementById('customMode').addEventListener('change', function () {
|
||||||
|
var pageOrderInput = document.getElementById('pageOrder');
|
||||||
|
if (this.value === "") {
|
||||||
|
pageOrderInput.disabled = false;
|
||||||
|
} else {
|
||||||
|
pageOrderInput.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user