Compare commits
38 Commits
v0.32.0
...
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 | ||
|
|
e7356a1d38 | ||
|
|
1405e4f5ee | ||
|
|
322e7dee0d | ||
|
|
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-disabled
vendored
47
.github/workflows/lint-helm-charts.yml-disabled
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' }}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
97
README.md
97
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 | | ✔️
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: v2
|
|
||||||
appVersion: 0.32.0
|
|
||||||
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.1.0
|
|
||||||
@@ -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,14 +190,15 @@ public class EndpointConfiguration {
|
|||||||
// LibreOffice
|
// LibreOffice
|
||||||
addEndpointToGroup("LibreOffice", "repair");
|
addEndpointToGroup("LibreOffice", "repair");
|
||||||
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
addEndpointToGroup("LibreOffice", "file-to-pdf");
|
||||||
addEndpointToGroup("Unoconv", "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");
|
||||||
@@ -251,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");
|
||||||
|
|||||||
@@ -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 = "")
|
||||||
@@ -134,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -127,6 +127,22 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
|||||||
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 {
|
.pdf-actions_insert-file-blank-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 75%;
|
top: 75%;
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ 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
|
// Create list item
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
item.className = "list-group-item";
|
item.className = "list-group-item";
|
||||||
@@ -173,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);
|
||||||
@@ -98,6 +99,7 @@ class PdfActionsManager {
|
|||||||
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";
|
||||||
@@ -138,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(
|
||||||
@@ -191,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,7 +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.addFilesBlankAll = this.addFilesBlankAll.bind(this)
|
||||||
|
this.removeAllElements = this.removeAllElements.bind(this);
|
||||||
|
|
||||||
this.pdfAdapters = pdfAdapters;
|
this.pdfAdapters = pdfAdapters;
|
||||||
|
|
||||||
@@ -32,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,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,7 +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.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");
|
||||||
@@ -94,6 +109,8 @@ class PdfContainer {
|
|||||||
|
|
||||||
this.addFilesFromFiles(files, nextSiblingElement);
|
this.addFilesFromFiles(files, nextSiblingElement);
|
||||||
this.updateFilename(files ? files[0].name : "");
|
this.updateFilename(files ? files[0].name : "");
|
||||||
|
const selectAll = document.getElementById("select-pages-container");
|
||||||
|
selectAll.classList.toggle("hidden", false);
|
||||||
};
|
};
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
@@ -264,15 +281,216 @@ 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() {
|
addFilesBlankAll() {
|
||||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||||
@@ -283,20 +501,36 @@ class PdfContainer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -339,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);
|
||||||
@@ -496,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) {
|
||||||
@@ -521,4 +794,7 @@ function detectImageType(uint8Array) {
|
|||||||
|
|
||||||
return 'UNKNOWN';
|
return 'UNKNOWN';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default PdfContainer;
|
export default PdfContainer;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,18 +47,56 @@
|
|||||||
cut
|
cut
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<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>
|
<button class="btn btn-secondary enable-on-file" onclick="addFilesBlankAll()" disabled>
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
insert_page_break
|
insert_page_break
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf()" disabled>
|
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)" disabled>
|
||||||
<span class="material-symbols-rounded">
|
<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">
|
||||||
@@ -82,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";
|
||||||
@@ -95,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>
|
||||||
@@ -1,40 +1,48 @@
|
|||||||
<!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=#{pageRemover.title}, header=#{pageRemover.header})}"></th:block>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<div id="page-container">
|
<th:block th:insert="~{fragments/common :: head(title=#{pageRemover.title}, header=#{pageRemover.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">delete</span>
|
|
||||||
<span class="tool-header-text" th:text="#{pageRemover.header}"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form th:action="@{'/api/v1/general/remove-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="fileInput" th:text="#{pageRemover.pagesToDelete}"></label>
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
<input type="text" class="form-control" id="fileInput" name="pageNumbers" th:placeholder="#{pageRemover.placeholder}" required>
|
<br><br>
|
||||||
</div>
|
<th:block th:insert="~{fragments/multi-toolAdvert.html :: multi-toolAdvert}"></th:block>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
|
<div class="container">
|
||||||
</form>
|
<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">delete</span>
|
||||||
|
<span class="tool-header-text" th:text="#{pageRemover.header}"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form th:action="@{'/api/v1/general/remove-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="fileInput" th:text="#{pageRemover.pagesToDelete}"></label>
|
||||||
|
<input type="text" class="form-control" id="fileInput" name="pageNumbers"
|
||||||
|
th:placeholder="#{pageRemover.placeholder}" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
document.getElementById('fileInput').addEventListener('input', function(){
|
</div>
|
||||||
this.value =this.value.replace(/\s+/g, '');;
|
<script>
|
||||||
});
|
document.getElementById('fileInput').addEventListener('input', function () {
|
||||||
</script>
|
this.value = this.value.replace(/\s+/g, '');;
|
||||||
</body>
|
});
|
||||||
</html>
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,110 +1,121 @@
|
|||||||
<!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">
|
||||||
|
|
||||||
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{rotate.title}, header=#{rotate.header})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{rotate.title}, header=#{rotate.header})}"></th:block>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<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>
|
||||||
<div class="container">
|
<th:block th:insert="~{fragments/multi-toolAdvert.html :: multi-toolAdvert}"></th:block>
|
||||||
<div class="row justify-content-center">
|
<div class="container">
|
||||||
<div class="col-md-6 bg-card">
|
<div class="row justify-content-center">
|
||||||
<div class="tool-header">
|
<div class="col-md-6 bg-card">
|
||||||
<span class="material-symbols-rounded tool-header-icon organize">rotate_right</span>
|
<div class="tool-header">
|
||||||
<span class="tool-header-text" th:text="#{rotate.header}"></span>
|
<span class="material-symbols-rounded tool-header-icon organize">rotate_right</span>
|
||||||
</div>
|
<span class="tool-header-text" th:text="#{rotate.header}"></span>
|
||||||
|
|
||||||
<form action="#" th:action="@{'/api/v1/general/rotate-pdf'}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
|
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
|
||||||
<input type="hidden" id="angleInput" name="angle" value="0">
|
|
||||||
|
|
||||||
<div id="editSection" style="display: none">
|
|
||||||
<div id="previewContainer">
|
|
||||||
<!-- pdf-preview -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
|
|
||||||
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
|
||||||
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
|
||||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form action="#" th:action="@{'/api/v1/general/rotate-pdf'}" th:object="${rotateForm}" method="post"
|
||||||
|
enctype="multipart/form-data">
|
||||||
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="angleInput" name="angle" value="0">
|
||||||
|
|
||||||
|
<div id="editSection" style="display: none">
|
||||||
|
<div id="previewContainer">
|
||||||
|
<!-- pdf-preview -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttonContainer">
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
|
||||||
|
<path
|
||||||
|
d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{rotate.submit}"></button>
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
||||||
|
<path
|
||||||
|
d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
<script>
|
</div>
|
||||||
const angleInput = document.getElementById("angleInput");
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
const fileInput = document.getElementById("fileInput-input");
|
<script>
|
||||||
const previewContainer = document.getElementById("previewContainer");
|
const angleInput = document.getElementById("angleInput");
|
||||||
// const preview = document.getElementById("pdf-preview");
|
const fileInput = document.getElementById("fileInput-input");
|
||||||
fileInput.addEventListener("change", async function() {
|
const previewContainer = document.getElementById("previewContainer");
|
||||||
console.log("loading pdf");
|
// const preview = document.getElementById("pdf-preview");
|
||||||
|
fileInput.addEventListener("change", async function () {
|
||||||
|
console.log("loading pdf");
|
||||||
|
|
||||||
document.querySelector("#editSection").style.display = "";
|
document.querySelector("#editSection").style.display = "block";
|
||||||
|
|
||||||
const existingPreview = document.getElementById("pdf-preview");
|
const existingPreview = document.getElementById("pdf-preview");
|
||||||
if (existingPreview) {
|
if (existingPreview) {
|
||||||
existingPreview.remove();
|
existingPreview.remove();
|
||||||
}
|
|
||||||
var url = URL.createObjectURL(fileInput.files[0])
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'
|
|
||||||
const pdf = await pdfjsLib.getDocument(url).promise;
|
|
||||||
const page = await pdf.getPage(1);
|
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
|
||||||
|
|
||||||
// set the canvas size to the size of the page
|
|
||||||
if (page.rotate == 90 || page.rotate == 270) {
|
|
||||||
canvas.width = page.view[3];
|
|
||||||
canvas.height = page.view[2];
|
|
||||||
} else {
|
|
||||||
canvas.width = page.view[2];
|
|
||||||
canvas.height = page.view[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
// render the page onto the canvas
|
|
||||||
var renderContext = {
|
|
||||||
canvasContext: canvas.getContext("2d"),
|
|
||||||
viewport: page.getViewport({ scale: 1 })
|
|
||||||
};
|
|
||||||
|
|
||||||
await page.render(renderContext).promise;
|
|
||||||
const preview = document.createElement("img");
|
|
||||||
preview.id = "pdf-preview";
|
|
||||||
preview.alt = "preview";
|
|
||||||
preview.src = canvas.toDataURL();
|
|
||||||
previewContainer.appendChild(preview);
|
|
||||||
});
|
|
||||||
|
|
||||||
function rotate(deg) {
|
|
||||||
const preview = document.getElementById("pdf-preview");
|
|
||||||
var lastTransform = preview.style.rotate;
|
|
||||||
if (!lastTransform) {
|
|
||||||
lastTransform = "0";
|
|
||||||
}
|
|
||||||
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
|
||||||
const newAngle = lastAngle + deg;
|
|
||||||
preview.style.rotate = newAngle + "deg";
|
|
||||||
angleInput.value = newAngle;
|
|
||||||
}
|
}
|
||||||
</script>
|
var url = URL.createObjectURL(fileInput.files[0])
|
||||||
</body>
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'
|
||||||
|
const pdf = await pdfjsLib.getDocument(url).promise;
|
||||||
|
const page = await pdf.getPage(1);
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
|
||||||
|
// set the canvas size to the size of the page
|
||||||
|
if (page.rotate == 90 || page.rotate == 270) {
|
||||||
|
canvas.width = page.view[3];
|
||||||
|
canvas.height = page.view[2];
|
||||||
|
} else {
|
||||||
|
canvas.width = page.view[2];
|
||||||
|
canvas.height = page.view[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the page onto the canvas
|
||||||
|
var renderContext = {
|
||||||
|
canvasContext: canvas.getContext("2d"),
|
||||||
|
viewport: page.getViewport({ scale: 1 })
|
||||||
|
};
|
||||||
|
|
||||||
|
await page.render(renderContext).promise;
|
||||||
|
const preview = document.createElement("img");
|
||||||
|
preview.id = "pdf-preview";
|
||||||
|
preview.alt = "preview";
|
||||||
|
preview.src = canvas.toDataURL();
|
||||||
|
previewContainer.appendChild(preview);
|
||||||
|
});
|
||||||
|
|
||||||
|
function rotate(deg) {
|
||||||
|
const preview = document.getElementById("pdf-preview");
|
||||||
|
var lastTransform = preview.style.rotate;
|
||||||
|
if (!lastTransform) {
|
||||||
|
lastTransform = "0";
|
||||||
|
}
|
||||||
|
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
|
||||||
|
const newAngle = lastAngle + deg;
|
||||||
|
preview.style.rotate = newAngle + "deg";
|
||||||
|
angleInput.value = newAngle;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -30,10 +30,8 @@
|
|||||||
<!-- Button to download the JSON -->
|
<!-- Button to download the JSON -->
|
||||||
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
|
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
|
||||||
</div>
|
</div>
|
||||||
|
<script th:src="@{'/js/fetch-utils.js'}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { fetchWithCsrf } from 'js/fetch-utils.js';
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
|
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -156,4 +154,4 @@
|
|||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -31,11 +31,13 @@
|
|||||||
<div class="mb-3 form-check">
|
<div class="mb-3 form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="includeMetadata" name="includeMetadata">
|
<input type="checkbox" class="form-check-input" id="includeMetadata" name="includeMetadata">
|
||||||
<label class="form-check-label" for="includeMetadata" th:text="#{splitByChapters.includeMetadata}"></label>
|
<label class="form-check-label" for="includeMetadata" th:text="#{splitByChapters.includeMetadata}"></label>
|
||||||
|
<input type="hidden" name="includeMetadata" value="false" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3 form-check">
|
<div class="mb-3 form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="allowDuplicates" name="allowDuplicates">
|
<input type="checkbox" class="form-check-input" id="allowDuplicates" name="allowDuplicates">
|
||||||
<label class="form-check-label" for="allowDuplicates" th:text="#{splitByChapters.allowDuplicates}"></label>
|
<label class="form-check-label" for="allowDuplicates" th:text="#{splitByChapters.allowDuplicates}"></label>
|
||||||
|
<input type="hidden" name="allowDuplicates" value="false" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -11,6 +11,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">
|
<div class="container">
|
||||||
<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">
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
|
|
||||||
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
|
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
|
||||||
<div id="secondaryToolbarButtonContainer">
|
<div id="secondaryToolbarButtonContainer">
|
||||||
<button id="secondaryOpenFile" class="secondaryToolbarButton" hidden="true" title="Open File" tabindex="51" data-l10n-id="pdfjs-open-file-button">
|
<button id="secondaryOpenFile" class="secondaryToolbarButton visibleMediumView" title="Open File" tabindex="51" data-l10n-id="pdfjs-open-file-button">
|
||||||
<span data-l10n-id="pdfjs-open-file-button-label">Open</span>
|
<span data-l10n-id="pdfjs-open-file-button-label">Open</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user