mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-08-06 13:48:58 +02:00
Merge branch 'main' into filePath
This commit is contained in:
commit
d7a3708a13
13
.github/workflows/PR-Demo-Comment.yml
vendored
13
.github/workflows/PR-Demo-Comment.yml
vendored
@ -4,9 +4,15 @@ on:
|
|||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-comment:
|
check-comment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
if: |
|
if: |
|
||||||
github.event.issue.pull_request &&
|
github.event.issue.pull_request &&
|
||||||
(
|
(
|
||||||
@ -68,6 +74,9 @@ jobs:
|
|||||||
deploy-pr:
|
deploy-pr:
|
||||||
needs: check-comment
|
needs: check-comment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@ -98,7 +107,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Get version number
|
- name: Get version number
|
||||||
id: versionNumber
|
id: versionNumber
|
||||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
run: |
|
||||||
|
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||||
|
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
|
3
.github/workflows/PR-Demo-cleanup.yml
vendored
3
.github/workflows/PR-Demo-cleanup.yml
vendored
@ -4,7 +4,8 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened, closed]
|
types: [opened, synchronize, reopened, closed]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
||||||
|
3
.github/workflows/auto-labeler.yml
vendored
3
.github/workflows/auto-labeler.yml
vendored
@ -3,7 +3,8 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -6,13 +6,15 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
actions: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
171
.github/workflows/check_properties.yml
vendored
171
.github/workflows/check_properties.yml
vendored
@ -8,12 +8,14 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # Allow read access to repository content
|
contents: read # Allow read access to repository content
|
||||||
issues: write # Allow posting comments on issues/PRs
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-files:
|
check-files:
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write # Allow posting comments on issues/PRs
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
@ -28,61 +30,141 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
|
- name: Get PR data
|
||||||
|
id: get-pr-data
|
||||||
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const prNumber = context.payload.pull_request.number;
|
||||||
|
const repoOwner = context.payload.repository.owner.login;
|
||||||
|
const repoName = context.payload.repository.name;
|
||||||
|
const branch = context.payload.pull_request.head.ref;
|
||||||
|
|
||||||
|
console.log(`PR Number: ${prNumber}`);
|
||||||
|
console.log(`Repo Owner: ${repoOwner}`);
|
||||||
|
console.log(`Repo Name: ${repoName}`);
|
||||||
|
console.log(`Branch: ${branch}`);
|
||||||
|
|
||||||
|
core.setOutput("pr_number", prNumber);
|
||||||
|
core.setOutput("repo_owner", repoOwner);
|
||||||
|
core.setOutput("repo_name", repoName);
|
||||||
|
core.setOutput("branch", branch);
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Fetch PR changed files
|
- name: Fetch PR changed files
|
||||||
id: fetch-pr-changes
|
id: fetch-pr-changes
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
echo "Fetching PR changed files..."
|
echo "Fetching PR changed files..."
|
||||||
|
|
||||||
gh repo set-default ${{ github.event.pull_request.head.repo.full_name }} # Set the fork repository as default
|
|
||||||
|
|
||||||
# Fetch the list of changed files in the PR
|
|
||||||
echo "Getting list of changed files from PR..."
|
echo "Getting list of changed files from PR..."
|
||||||
gh pr view ${{ github.event.pull_request.number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
|
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files
|
||||||
|
|
||||||
- name: Determine reference file
|
- name: Determine reference file test
|
||||||
id: determine-file
|
id: determine-file
|
||||||
env:
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
with:
|
||||||
run: |
|
script: |
|
||||||
echo "Determining reference file..."
|
const fs = require("fs");
|
||||||
REPO_OWNER=$(gh pr view ${{ github.event.pull_request.number }} --json author -q '.author.login') # Get PR author's username
|
const path = require("path");
|
||||||
REPO_NAME=$(gh pr view ${{ github.event.pull_request.number }} --json headRepository -q '.headRepository.name') # Get PR repository name
|
|
||||||
BRANCH=$(gh pr view ${{ github.event.pull_request.number }} --json headRefName -q '.headRefName') # Get PR branch name
|
|
||||||
|
|
||||||
mkdir -p pr-branch # Create a directory for PR files
|
const prNumber = ${{ steps.get-pr-data.outputs.pr_number }};
|
||||||
|
const repoOwner = "${{ steps.get-pr-data.outputs.repo_owner }}";
|
||||||
|
const repoName = "${{ steps.get-pr-data.outputs.repo_name }}";
|
||||||
|
|
||||||
# Download the content of each changed file
|
const prRepoOwner = "${{ github.event.pull_request.head.repo.owner.login }}";
|
||||||
while IFS= read -r file; do
|
const prRepoName = "${{ github.event.pull_request.head.repo.name }}";
|
||||||
mkdir -p "pr-branch/$(dirname "$file")" # Create directories for files
|
const branch = "${{ steps.get-pr-data.outputs.branch }}";
|
||||||
gh api repos/$REPO_OWNER/$REPO_NAME/contents/$file?ref=$BRANCH --jq '.content' | base64 -d > "pr-branch/src/main/resources/$(basename "$file")" # Save decoded file content
|
|
||||||
done < changed_files.txt
|
|
||||||
|
|
||||||
# Generate a list of files without the "pr-branch/" prefix
|
console.log(`Determining reference file for PR #${prNumber}`);
|
||||||
find pr-branch/ -type f | awk -F'pr-branch/' '{print $2}' > file_list.txt
|
|
||||||
|
|
||||||
mapfile -t FILES_LIST < file_list.txt # Read the file list into an array
|
// Validate inputs
|
||||||
FILES_LIST_STR="${FILES_LIST[*]}" # Join array into a space-separated string
|
const validateInput = (input, regex, name) => {
|
||||||
echo "FILES_LIST=${FILES_LIST_STR}" >> $GITHUB_ENV # Export the file list to the environment
|
if (!regex.test(input)) {
|
||||||
echo "Changed files: ${FILES_LIST_STR}"
|
throw new Error(`Invalid ${name}: ${input}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
cat file_list.txt # Display the file list
|
validateInput(repoOwner, /^[a-zA-Z0-9_-]+$/, "repository owner");
|
||||||
|
validateInput(repoName, /^[a-zA-Z0-9._-]+$/, "repository name");
|
||||||
|
validateInput(branch, /^[a-zA-Z0-9._/-]+$/, "branch name");
|
||||||
|
|
||||||
# Determine which reference file to use
|
// Get the list of changed files in the PR
|
||||||
if grep -Fxq "src/main/resources/messages_en_GB.properties" changed_files.txt; then
|
const { data: files } = await github.rest.pulls.listFiles({
|
||||||
echo "Using PR branch reference file"
|
owner: repoOwner,
|
||||||
REFERENCE_FILE="pr-branch-messages_en_GB.properties"
|
repo: repoName,
|
||||||
gh api repos/$REPO_OWNER/$REPO_NAME/contents/src/main/resources/messages_en_GB.properties?ref=${{ github.event.pull_request.head.ref }} \
|
pull_number: prNumber,
|
||||||
--jq '.content' | base64 -d > $REFERENCE_FILE # Save PR branch reference file
|
});
|
||||||
else
|
|
||||||
echo "Using main branch reference file"
|
|
||||||
REFERENCE_FILE="main-branch-messages_en_GB.properties"
|
|
||||||
gh api repos/Ludy87/test_java/contents/src/main/resources/messages_en_GB.properties?ref=main \
|
|
||||||
--jq '.content' | base64 -d > $REFERENCE_FILE # Save main branch reference file
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "REFERENCE_FILE=$REFERENCE_FILE" >> $GITHUB_ENV # Export reference file path to the environment
|
// Filter for relevant files based on the PR changes
|
||||||
|
const changedFiles = files
|
||||||
|
.map(file => file.filename)
|
||||||
|
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file));
|
||||||
|
|
||||||
|
console.log("Changed files:", changedFiles);
|
||||||
|
|
||||||
|
// Create a temporary directory for PR files
|
||||||
|
const tempDir = "pr-branch";
|
||||||
|
if (!fs.existsSync(tempDir)) {
|
||||||
|
fs.mkdirSync(tempDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download and save each changed file
|
||||||
|
for (const file of changedFiles) {
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: prRepoOwner,
|
||||||
|
repo: prRepoName,
|
||||||
|
path: file,
|
||||||
|
ref: branch,
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
const filePath = path.join(tempDir, file);
|
||||||
|
const dirPath = path.dirname(filePath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(dirPath)) {
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, content);
|
||||||
|
console.log(`Saved file: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the list of changed files for further processing
|
||||||
|
const fileList = changedFiles.join(" ");
|
||||||
|
core.exportVariable("FILES_LIST", fileList);
|
||||||
|
console.log("Files saved and listed in FILES_LIST.");
|
||||||
|
|
||||||
|
// Determine reference file
|
||||||
|
let referenceFilePath;
|
||||||
|
if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) {
|
||||||
|
console.log("Using PR branch reference file.");
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: prRepoOwner,
|
||||||
|
repo: prRepoName,
|
||||||
|
path: "src/main/resources/messages_en_GB.properties",
|
||||||
|
ref: branch,
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceFilePath = "pr-branch-messages_en_GB.properties";
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
fs.writeFileSync(referenceFilePath, content);
|
||||||
|
} else {
|
||||||
|
console.log("Using main branch reference file.");
|
||||||
|
const { data: fileContent } = await github.rest.repos.getContent({
|
||||||
|
owner: repoOwner,
|
||||||
|
repo: repoName,
|
||||||
|
path: "src/main/resources/messages_en_GB.properties",
|
||||||
|
ref: "main",
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceFilePath = "main-branch-messages_en_GB.properties";
|
||||||
|
const content = Buffer.from(fileContent.content, "base64").toString("utf-8");
|
||||||
|
fs.writeFileSync(referenceFilePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Reference file path: ${referenceFilePath}`);
|
||||||
|
core.exportVariable("REFERENCE_FILE", referenceFilePath);
|
||||||
|
|
||||||
- name: Run Python script to check files
|
- name: Run Python script to check files
|
||||||
id: run-check
|
id: run-check
|
||||||
@ -92,7 +174,8 @@ jobs:
|
|||||||
--actor ${{ github.event.pull_request.user.login }} \
|
--actor ${{ github.event.pull_request.user.login }} \
|
||||||
--reference-file "${REFERENCE_FILE}" \
|
--reference-file "${REFERENCE_FILE}" \
|
||||||
--branch "pr-branch" \
|
--branch "pr-branch" \
|
||||||
--files "${FILES_LIST[@]}" > result.txt || true
|
--files "${FILES_LIST[@]}" > result.txt
|
||||||
|
continue-on-error: true # Continue the job even if this step fails
|
||||||
|
|
||||||
- name: Capture output
|
- name: Capture output
|
||||||
id: capture-output
|
id: capture-output
|
||||||
@ -124,13 +207,13 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
||||||
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
||||||
const prNumber = context.issue.number;
|
const issueNumber = context.issue.number;
|
||||||
|
|
||||||
// Find existing comment
|
// Find existing comment
|
||||||
const comments = await github.rest.issues.listComments({
|
const comments = await github.rest.issues.listComments({
|
||||||
owner: repoOwner,
|
owner: repoOwner,
|
||||||
repo: repoName,
|
repo: repoName,
|
||||||
issue_number: prNumber
|
issue_number: issueNumber
|
||||||
});
|
});
|
||||||
|
|
||||||
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
||||||
@ -152,7 +235,7 @@ jobs:
|
|||||||
await github.rest.issues.createComment({
|
await github.rest.issues.createComment({
|
||||||
owner: repoOwner,
|
owner: repoOwner,
|
||||||
repo: repoName,
|
repo: repoName,
|
||||||
issue_number: prNumber,
|
issue_number: issueNumber,
|
||||||
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n`
|
||||||
});
|
});
|
||||||
console.log("Created new comment.");
|
console.log("Created new comment.");
|
||||||
|
5
.github/workflows/licenses-update.yml
vendored
5
.github/workflows/licenses-update.yml
vendored
@ -7,7 +7,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- "build.gradle"
|
- "build.gradle"
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate-license-report:
|
generate-license-report:
|
||||||
@ -52,7 +53,7 @@ jobs:
|
|||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "Update 3rd Party Licenses"
|
commit-message: "Update 3rd Party Licenses"
|
||||||
|
3
.github/workflows/manage-label.yml
vendored
3
.github/workflows/manage-label.yml
vendored
@ -4,7 +4,8 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: "30 20 * * *"
|
- cron: "30 20 * * *"
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
|
5
.github/workflows/multiOSReleases.yml
vendored
5
.github/workflows/multiOSReleases.yml
vendored
@ -5,7 +5,8 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-installers:
|
build-installers:
|
||||||
@ -42,7 +43,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
# Install Windows dependencies
|
# Install Windows dependencies
|
||||||
- name: Install WiX Toolset
|
- name: Install WiX Toolset
|
||||||
|
7
.github/workflows/pre_commit.yml
vendored
7
.github/workflows/pre_commit.yml
vendored
@ -4,7 +4,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update:
|
||||||
@ -19,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
- name: Run Pre-Commit Hooks
|
- name: Run Pre-Commit Hooks
|
||||||
@ -35,7 +36,7 @@ jobs:
|
|||||||
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
|
git diff --staged --quiet || git commit -m ":file_folder: pre-commit
|
||||||
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
|
> Made via .github/workflows/pre_commit.yml" || echo "pre-commit: no changes"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "ci: 🤖 format everything with pre-commit"
|
commit-message: "ci: 🤖 format everything with pre-commit"
|
||||||
|
7
.github/workflows/push-docker.yml
vendored
7
.github/workflows/push-docker.yml
vendored
@ -9,14 +9,13 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
@ -33,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
- name: Run Gradle Command
|
- name: Run Gradle Command
|
||||||
run: ./gradlew clean build
|
run: ./gradlew clean build
|
||||||
@ -42,7 +41,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: sigstore/cosign-installer@v3.7.0
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
with:
|
with:
|
||||||
cosign-release: 'v2.4.1'
|
cosign-release: 'v2.4.1'
|
||||||
|
|
||||||
|
5
.github/workflows/releaseArtifacts.yml
vendored
5
.github/workflows/releaseArtifacts.yml
vendored
@ -5,7 +5,8 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
@ -37,7 +38,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.12
|
||||||
|
|
||||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||||
run: ./gradlew clean createExe
|
run: ./gradlew clean createExe
|
||||||
|
3
.github/workflows/stale.yml
vendored
3
.github/workflows/stale.yml
vendored
@ -5,7 +5,8 @@ on:
|
|||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
|
3
.github/workflows/swagger.yml
vendored
3
.github/workflows/swagger.yml
vendored
@ -6,7 +6,8 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
|
5
.github/workflows/sync_files.yml
vendored
5
.github/workflows/sync_files.yml
vendored
@ -9,7 +9,8 @@ on:
|
|||||||
- "src/main/resources/messages_*.properties"
|
- "src/main/resources/messages_*.properties"
|
||||||
- "scripts/ignore_translation.toml"
|
- "scripts/ignore_translation.toml"
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync-readme:
|
sync-readme:
|
||||||
@ -42,7 +43,7 @@ jobs:
|
|||||||
git diff --staged --quiet || git commit -m ":memo: Sync README
|
git diff --staged --quiet || git commit -m ":memo: Sync README
|
||||||
> Made via sync_files.yml" || echo "no changes"
|
> Made via sync_files.yml" || echo "no changes"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: Update files
|
commit-message: Update files
|
||||||
|
5
.github/workflows/update-translations.yml
vendored
5
.github/workflows/update-translations.yml
vendored
@ -6,7 +6,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- "src/main/resources/messages_en_GB.properties"
|
- "src/main/resources/messages_en_GB.properties"
|
||||||
|
|
||||||
permissions: read-all
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-translations-main:
|
update-translations-main:
|
||||||
@ -50,7 +51,7 @@ jobs:
|
|||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "Update translation files"
|
commit-message: "Update translation files"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Build the application
|
# Build the application
|
||||||
FROM gradle:8.11-jdk17 AS build
|
FROM gradle:8.12-jdk17 AS build
|
||||||
|
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
28
README.md
28
README.md
@ -4,9 +4,9 @@
|
|||||||
[](https://hub.docker.com/r/frooodle/s-pdf)
|
[](https://hub.docker.com/r/frooodle/s-pdf)
|
||||||
[](https://discord.gg/HYmhKj45pU)
|
[](https://discord.gg/HYmhKj45pU)
|
||||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||||
|
[](https://scorecard.dev/viewer/?uri=github.com/Stirling-Tools/Stirling-PDF)
|
||||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||||
|
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
[](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)
|
||||||
|
|
||||||
@ -116,43 +116,43 @@ Stirling-PDF currently supports 38 languages!
|
|||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| 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) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| 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) |  |
|
||||||
|
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ spotless {
|
|||||||
// rules=['unused-dependency']
|
// rules=['unused-dependency']
|
||||||
// }
|
// }
|
||||||
tasks.wrapper {
|
tasks.wrapper {
|
||||||
gradleVersion = "8.7"
|
gradleVersion = "8.12"
|
||||||
}
|
}
|
||||||
//tasks.withType(JavaCompile) {
|
//tasks.withType(JavaCompile) {
|
||||||
// options.compilerArgs << "-Xlint:deprecation"
|
// options.compilerArgs << "-Xlint:deprecation"
|
||||||
@ -299,7 +299,6 @@ dependencies {
|
|||||||
|
|
||||||
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
||||||
|
|
||||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
|
||||||
// Don't upgrade h2database
|
// Don't upgrade h2database
|
||||||
runtimeOnly "com.h2database:h2:2.3.232"
|
runtimeOnly "com.h2database:h2:2.3.232"
|
||||||
constraints {
|
constraints {
|
||||||
|
@ -66,7 +66,7 @@ Feature: API Validation
|
|||||||
| pageNumbers | file_count |
|
| pageNumbers | file_count |
|
||||||
| 1,3,5-9 | 8 |
|
| 1,3,5-9 | 8 |
|
||||||
| all | 20 |
|
| all | 20 |
|
||||||
| 2n+1 | 11 |
|
| 2n+1 | 10 |
|
||||||
| 3n | 7 |
|
| 3n | 7 |
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,22 +22,31 @@ import jakarta.servlet.FilterChain;
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
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.model.ApiKeyAuthenticationToken;
|
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final SessionPersistentRegistry sessionPersistentRegistry;
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
private final boolean loginEnabledValue;
|
private final boolean loginEnabledValue;
|
||||||
|
|
||||||
public UserAuthenticationFilter(
|
public UserAuthenticationFilter(
|
||||||
|
@Lazy ApplicationProperties applicationProperties,
|
||||||
@Lazy UserService userService,
|
@Lazy UserService userService,
|
||||||
SessionPersistentRegistry sessionPersistentRegistry,
|
SessionPersistentRegistry sessionPersistentRegistry,
|
||||||
@Qualifier("loginEnabled") boolean loginEnabledValue) {
|
@Qualifier("loginEnabled") boolean loginEnabledValue) {
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
||||||
this.loginEnabledValue = loginEnabledValue;
|
this.loginEnabledValue = loginEnabledValue;
|
||||||
@ -121,33 +130,67 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// Check if the authenticated user is disabled and invalidate their session if so
|
// Check if the authenticated user is disabled and invalidate their session if so
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
|
||||||
|
Security securityProp = applicationProperties.getSecurity();
|
||||||
|
LoginMethod loginMethod = LoginMethod.UNKNOWN;
|
||||||
|
|
||||||
|
boolean blockRegistration = false;
|
||||||
|
|
||||||
|
// Extract username and determine the login method
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
String username = null;
|
String username = null;
|
||||||
if (principal instanceof UserDetails) {
|
if (principal instanceof UserDetails) {
|
||||||
username = ((UserDetails) principal).getUsername();
|
username = ((UserDetails) principal).getUsername();
|
||||||
|
loginMethod = LoginMethod.USERDETAILS;
|
||||||
} else if (principal instanceof OAuth2User) {
|
} else if (principal instanceof OAuth2User) {
|
||||||
username = ((OAuth2User) principal).getName();
|
username = ((OAuth2User) principal).getName();
|
||||||
|
loginMethod = LoginMethod.OAUTH2USER;
|
||||||
|
OAUTH2 oAuth = securityProp.getOauth2();
|
||||||
|
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
|
||||||
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
|
||||||
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
username = ((CustomSaml2AuthenticatedPrincipal) principal).getName();
|
||||||
|
loginMethod = LoginMethod.SAML2USER;
|
||||||
|
SAML2 saml2 = securityProp.getSaml2();
|
||||||
|
blockRegistration = saml2 != null && saml2.getBlockRegistration();
|
||||||
} else if (principal instanceof String) {
|
} else if (principal instanceof String) {
|
||||||
username = (String) principal;
|
username = (String) principal;
|
||||||
|
loginMethod = LoginMethod.STRINGUSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve all active sessions for the user
|
||||||
List<SessionInformation> sessionsInformations =
|
List<SessionInformation> sessionsInformations =
|
||||||
sessionPersistentRegistry.getAllSessions(principal, false);
|
sessionPersistentRegistry.getAllSessions(principal, false);
|
||||||
|
|
||||||
|
// Check if the user exists, is disabled, or needs session invalidation
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
|
log.debug("Validating user: {}", username);
|
||||||
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
|
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
|
||||||
boolean isUserDisabled = userService.isUserDisabled(username);
|
boolean isUserDisabled = userService.isUserDisabled(username);
|
||||||
|
|
||||||
|
boolean notSsoLogin =
|
||||||
|
!loginMethod.equals(LoginMethod.OAUTH2USER)
|
||||||
|
&& !loginMethod.equals(LoginMethod.SAML2USER);
|
||||||
|
|
||||||
|
// Block user registration if not allowed by configuration
|
||||||
|
if (blockRegistration && !isUserExists) {
|
||||||
|
log.warn("Blocked registration for OAuth2/SAML user: {}", username);
|
||||||
|
response.sendRedirect(
|
||||||
|
request.getContextPath() + "/logout?oauth2_admin_blocked_user=true");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire sessions and logout if the user does not exist or is disabled
|
||||||
if (!isUserExists || isUserDisabled) {
|
if (!isUserExists || isUserDisabled) {
|
||||||
|
log.info(
|
||||||
|
"Invalidating session for disabled or non-existent user: {}", username);
|
||||||
for (SessionInformation sessionsInformation : sessionsInformations) {
|
for (SessionInformation sessionsInformation : sessionsInformations) {
|
||||||
sessionsInformation.expireNow();
|
sessionsInformation.expireNow();
|
||||||
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
|
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUserExists) {
|
// Redirect to logout if credentials are invalid
|
||||||
|
if (!isUserExists && notSsoLogin) {
|
||||||
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
|
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -161,6 +204,25 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum LoginMethod {
|
||||||
|
USERDETAILS("UserDetails"),
|
||||||
|
OAUTH2USER("OAuth2User"),
|
||||||
|
STRINGUSER("StringUser"),
|
||||||
|
UNKNOWN("Unknown"),
|
||||||
|
SAML2USER("Saml2User");
|
||||||
|
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
LoginMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
|
@ -163,6 +163,9 @@ public class AccountWebController {
|
|||||||
case "invalid_destination":
|
case "invalid_destination":
|
||||||
erroroauth = "login.invalid_destination";
|
erroroauth = "login.invalid_destination";
|
||||||
break;
|
break;
|
||||||
|
case "relying_party_registration_not_found":
|
||||||
|
erroroauth = "login.relyingPartyRegistrationNotFound";
|
||||||
|
break;
|
||||||
// Valid InResponseTo was not available from the validation context, unable to
|
// Valid InResponseTo was not available from the validation context, unable to
|
||||||
// evaluate
|
// evaluate
|
||||||
case "invalid_in_response_to":
|
case "invalid_in_response_to":
|
||||||
|
@ -13,6 +13,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.simpleyaml.configuration.file.YamlFile;
|
import org.simpleyaml.configuration.file.YamlFile;
|
||||||
import org.simpleyaml.configuration.file.YamlFileWrapper;
|
import org.simpleyaml.configuration.file.YamlFileWrapper;
|
||||||
@ -221,32 +223,51 @@ public class GeneralUtils {
|
|||||||
throw new IllegalArgumentException("Invalid expression");
|
throw new IllegalArgumentException("Invalid expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = 0;
|
for (int n = 1; n <= maxValue; n++) {
|
||||||
while (true) {
|
|
||||||
// Replace 'n' with the current value of n, correctly handling numbers before
|
// Replace 'n' with the current value of n, correctly handling numbers before
|
||||||
// 'n'
|
// 'n'
|
||||||
String sanitizedExpression = insertMultiplicationBeforeN(expression, n);
|
String sanitizedExpression = sanitizeNFunction(expression, n);
|
||||||
Double result = evaluator.evaluate(sanitizedExpression);
|
Double result = evaluator.evaluate(sanitizedExpression);
|
||||||
|
|
||||||
// Check if the result is null or not within bounds
|
// Check if the result is null or not within bounds
|
||||||
if (result == null || result <= 0 || result.intValue() > maxValue) {
|
if (result == null)
|
||||||
if (n != 0) break;
|
break;
|
||||||
} else {
|
|
||||||
|
if (result.intValue() > 0 && result.intValue() <= maxValue)
|
||||||
results.add(result.intValue());
|
results.add(result.intValue());
|
||||||
}
|
}
|
||||||
n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String sanitizeNFunction(String expression, int nValue) {
|
||||||
|
String sanitizedExpression = expression.replace(" ", "");
|
||||||
|
String multiplyByOpeningRoundBracketPattern = "([0-9n)])\\("; // example: n(n-1), 9(n-1), (n-1)(n-2)
|
||||||
|
sanitizedExpression = sanitizedExpression.replaceAll(multiplyByOpeningRoundBracketPattern, "$1*(");
|
||||||
|
|
||||||
|
String multiplyByClosingRoundBracketPattern = "\\)([0-9n)])"; // example: (n-1)n, (n-1)9, (n-1)(n-2)
|
||||||
|
sanitizedExpression = sanitizedExpression.replaceAll(multiplyByClosingRoundBracketPattern, ")*$1");
|
||||||
|
|
||||||
|
sanitizedExpression = insertMultiplicationBeforeN(sanitizedExpression, nValue);
|
||||||
|
return sanitizedExpression;
|
||||||
|
}
|
||||||
|
|
||||||
private static String insertMultiplicationBeforeN(String expression, int nValue) {
|
private static String insertMultiplicationBeforeN(String expression, int nValue) {
|
||||||
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
// Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n")
|
||||||
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
String withMultiplication = expression.replaceAll("(\\d)n", "$1*n");
|
||||||
|
withMultiplication = formatConsecutiveNsForNFunction(withMultiplication);
|
||||||
// Now replace 'n' with its current value
|
// Now replace 'n' with its current value
|
||||||
return withMultiplication.replace("n", String.valueOf(nValue));
|
return withMultiplication.replace("n", String.valueOf(nValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String formatConsecutiveNsForNFunction(String expression) {
|
||||||
|
String text = expression;
|
||||||
|
while (text.matches(".*n{2,}.*")) {
|
||||||
|
text = text.replaceAll("(?<!n)n{2}", "n*n");
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
private static List<Integer> handlePart(String part, int totalPages, int offset) {
|
||||||
List<Integer> partResult = new ArrayList<>();
|
List<Integer> partResult = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=طلب غير صالح
|
|||||||
login.oauth2AccessDenied=تم رفض الوصول
|
login.oauth2AccessDenied=تم رفض الوصول
|
||||||
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
|
login.oauth2InvalidTokenResponse=استجابة الرمز المميز غير صالحة
|
||||||
login.oauth2InvalidIdToken=رمز الهوية غير صالح
|
login.oauth2InvalidIdToken=رمز الهوية غير صالح
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
|
login.userIsDisabled=تم تعطيل المستخدم، تم حظر تسجيل الدخول حاليًا باستخدام اسم المستخدم هذا. يرجى الاتصال بالمسؤول.
|
||||||
login.alreadyLoggedIn=لقد تسجل دخولًا إلى
|
login.alreadyLoggedIn=لقد تسجل دخولًا إلى
|
||||||
login.alreadyLoggedIn2=أجهزة أخرى. يرجى تسجيل الخروج من الأجهزة وحاول مرة أخرى.
|
login.alreadyLoggedIn2=أجهزة أخرى. يرجى تسجيل الخروج من الأجهزة وحاول مرة أخرى.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=إصلاح
|
repair.title=إصلاح
|
||||||
repair.header=إصلاح ملفات PDF
|
repair.header=إصلاح ملفات PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=تقطيع ملف PDF
|
|||||||
fileChooser.click=انقر هنا
|
fileChooser.click=انقر هنا
|
||||||
fileChooser.or=أو
|
fileChooser.or=أو
|
||||||
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
|
fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
|
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Etibarsız Sorğu
|
|||||||
login.oauth2AccessDenied=Giriş rədd edildi
|
login.oauth2AccessDenied=Giriş rədd edildi
|
||||||
login.oauth2InvalidTokenResponse=Etibarsız Token Cavabı
|
login.oauth2InvalidTokenResponse=Etibarsız Token Cavabı
|
||||||
login.oauth2InvalidIdToken=Etibarsız Id Token
|
login.oauth2InvalidIdToken=Etibarsız Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=İstifadəçi deaktivləşdirilmişdir, bu istifadəçi adı ilə giriş hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
|
login.userIsDisabled=İstifadəçi deaktivləşdirilmişdir, bu istifadəçi adı ilə giriş hal-hazırda bloklanmışdır. Zəhmət olmasa, administratorla əlaqə saxlayın.
|
||||||
login.alreadyLoggedIn=Siz artıq daxil olmusunuz
|
login.alreadyLoggedIn=Siz artıq daxil olmusunuz
|
||||||
login.alreadyLoggedIn2=cihazlar. Zəhmət olmasa, cihazlardan çıxış edin və yenidən cəhd edin.
|
login.alreadyLoggedIn2=cihazlar. Zəhmət olmasa, cihazlardan çıxış edin və yenidən cəhd edin.
|
||||||
@ -830,7 +831,7 @@ sign.first=İlk səhifə
|
|||||||
sign.last=Son səhifə
|
sign.last=Son səhifə
|
||||||
sign.next=Növbəti səhifə
|
sign.next=Növbəti səhifə
|
||||||
sign.previous=Əvvəlki səhifə
|
sign.previous=Əvvəlki səhifə
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Bərpa Et
|
repair.title=Bərpa Et
|
||||||
repair.header=PDFləri Bərpa Et
|
repair.header=PDFləri Bərpa Et
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF-i Ayır
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Невалидна заявка
|
|||||||
login.oauth2AccessDenied=Отказан достъп
|
login.oauth2AccessDenied=Отказан достъп
|
||||||
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
||||||
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
|
login.userIsDisabled=Потребителят е деактивиран, влизането в момента е блокирано с това потребителско име. Моля, свържете се с администратора.
|
||||||
login.alreadyLoggedIn=Вече сте влезли в
|
login.alreadyLoggedIn=Вече сте влезли в
|
||||||
login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
|
login.alreadyLoggedIn2=устройства. Моля, излезте от устройствата и опитайте отново.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Поправи
|
repair.title=Поправи
|
||||||
repair.header=Поправи PDF-и
|
repair.header=Поправи PDF-и
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Разделяне на PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Sol·licitud no vàlida
|
|||||||
login.oauth2AccessDenied=Accés denegat
|
login.oauth2AccessDenied=Accés denegat
|
||||||
login.oauth2InvalidTokenResponse=Resposta de token no vàlida
|
login.oauth2InvalidTokenResponse=Resposta de token no vàlida
|
||||||
login.oauth2InvalidIdToken=ID Token no vàlid
|
login.oauth2InvalidIdToken=ID Token no vàlid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=L'usuari està desactivat, l'inici de sessió està actualment bloquejat amb aquest nom d'usuari. Si us plau, contacta amb l'administrador.
|
login.userIsDisabled=L'usuari està desactivat, l'inici de sessió està actualment bloquejat amb aquest nom d'usuari. Si us plau, contacta amb l'administrador.
|
||||||
login.alreadyLoggedIn=Ja has iniciat sessió a
|
login.alreadyLoggedIn=Ja has iniciat sessió a
|
||||||
login.alreadyLoggedIn2=dispositius. Si us plau, tanca la sessió en els dispositius i torna-ho a intentar.
|
login.alreadyLoggedIn2=dispositius. Si us plau, tanca la sessió en els dispositius i torna-ho a intentar.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Repara els PDF
|
repair.header=Repara els PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Divideix PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neplatný požadavek
|
|||||||
login.oauth2AccessDenied=Přístup zazobán
|
login.oauth2AccessDenied=Přístup zazobán
|
||||||
login.oauth2InvalidTokenResponse=Neplatná odpověď tokenu
|
login.oauth2InvalidTokenResponse=Neplatná odpověď tokenu
|
||||||
login.oauth2InvalidIdToken=Neplatný identifikační token
|
login.oauth2InvalidIdToken=Neplatný identifikační token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Uživatel je deaktivován, přihlášení aktuálně pro tuto uživatelskou jmena je zakázáno. Kontaktujte správce.
|
login.userIsDisabled=Uživatel je deaktivován, přihlášení aktuálně pro tuto uživatelskou jmena je zakázáno. Kontaktujte správce.
|
||||||
login.alreadyLoggedIn=Jste již přihlášeni na
|
login.alreadyLoggedIn=Jste již přihlášeni na
|
||||||
login.alreadyLoggedIn2=zariadení. Odhlašujte se z těchto zařízení a zkuste to znovu.
|
login.alreadyLoggedIn2=zariadení. Odhlašujte se z těchto zařízení a zkuste to znovu.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Opravit
|
repair.title=Opravit
|
||||||
repair.header=Opravit PDF
|
repair.header=Opravit PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Podělit se PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig Anmodning
|
|||||||
login.oauth2AccessDenied=Adgang Nægtet
|
login.oauth2AccessDenied=Adgang Nægtet
|
||||||
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
|
login.oauth2InvalidTokenResponse=Ugyldigt Token Svar
|
||||||
login.oauth2InvalidIdToken=Ugyldigt Id Token
|
login.oauth2InvalidIdToken=Ugyldigt Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
|
login.userIsDisabled=Bruger er deaktiveret, login er i øjeblikket blokeret med dette brugernavn. Kontakt venligst administratoren.
|
||||||
login.alreadyLoggedIn=Du er allerede logget ind på
|
login.alreadyLoggedIn=Du er allerede logget ind på
|
||||||
login.alreadyLoggedIn2=enheder. Log ud af disse enheder og prøv igen.
|
login.alreadyLoggedIn2=enheder. Log ud af disse enheder og prøv igen.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparér
|
repair.title=Reparér
|
||||||
repair.header=Reparér PDF'er
|
repair.header=Reparér PDF'er
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Splitter PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=ungültige Anfrage
|
|||||||
login.oauth2AccessDenied=Zugriff abgelehnt
|
login.oauth2AccessDenied=Zugriff abgelehnt
|
||||||
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
||||||
login.oauth2InvalidIdToken=Ungültiges ID-Token
|
login.oauth2InvalidIdToken=Ungültiges ID-Token
|
||||||
|
login.relyingPartyRegistrationNotFound=Keine Relying-Party-Registrierung gefunden
|
||||||
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
|
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
|
||||||
login.alreadyLoggedIn=Sie sind bereits an
|
login.alreadyLoggedIn=Sie sind bereits an
|
||||||
login.alreadyLoggedIn2=Geräten angemeldet. Bitte melden Sie sich dort ab und versuchen es dann erneut.
|
login.alreadyLoggedIn2=Geräten angemeldet. Bitte melden Sie sich dort ab und versuchen es dann erneut.
|
||||||
@ -830,7 +831,7 @@ sign.first=Erste Seite
|
|||||||
sign.last=Letzte Seite
|
sign.last=Letzte Seite
|
||||||
sign.next=Nächste Seite
|
sign.next=Nächste Seite
|
||||||
sign.previous=Vorherige Seite
|
sign.previous=Vorherige Seite
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparieren
|
repair.title=Reparieren
|
||||||
repair.header=PDFs reparieren
|
repair.header=PDFs reparieren
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF teilen
|
|||||||
fileChooser.click=Klicken
|
fileChooser.click=Klicken
|
||||||
fileChooser.or=oder
|
fileChooser.or=oder
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Αρχαίως απαιτήσεις πληροφορ
|
|||||||
login.oauth2AccessDenied=Πρόσβαση ορισμένες τοποθετήσεις
|
login.oauth2AccessDenied=Πρόσβαση ορισμένες τοποθετήσεις
|
||||||
login.oauth2InvalidTokenResponse=Απάντηση άρκετο ρόταμα
|
login.oauth2InvalidTokenResponse=Απάντηση άρκετο ρόταμα
|
||||||
login.oauth2InvalidIdToken=Λιδός όρκου μη αρκετός
|
login.oauth2InvalidIdToken=Λιδός όρκου μη αρκετός
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Ο χρήστης είναι δευτεροβαθμιακά διακωμένος, το σύστημα άλλαξε τον καθώς βρεθεί. Παρακαλείστε τους αρχηγούς να επιβεβαιώσουν τη συμπεριφορά χρήστη.
|
login.userIsDisabled=Ο χρήστης είναι δευτεροβαθμιακά διακωμένος, το σύστημα άλλαξε τον καθώς βρεθεί. Παρακαλείστε τους αρχηγούς να επιβεβαιώσουν τη συμπεριφορά χρήστη.
|
||||||
login.alreadyLoggedIn=Είστε ήδη σύνδεδες σε
|
login.alreadyLoggedIn=Είστε ήδη σύνδεδες σε
|
||||||
login.alreadyLoggedIn2=κατοχόι. Παρακαλώ δυσκέντρωση τους και προσπαθήστε ξανά.
|
login.alreadyLoggedIn2=κατοχόι. Παρακαλώ δυσκέντρωση τους και προσπαθήστε ξανά.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Επιδιόρθωση
|
repair.title=Επιδιόρθωση
|
||||||
repair.header=Επιδιόρθωση PDFs
|
repair.header=Επιδιόρθωση PDFs
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Διαλύστε το PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repair
|
repair.title=Repair
|
||||||
repair.header=Repair PDFs
|
repair.header=Repair PDFs
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repair
|
repair.title=Repair
|
||||||
repair.header=Repair PDFs
|
repair.header=Repair PDFs
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Solicitud no válida
|
|||||||
login.oauth2AccessDenied=Acceso denegado
|
login.oauth2AccessDenied=Acceso denegado
|
||||||
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
||||||
login.oauth2InvalidIdToken=Token de identificación no válido
|
login.oauth2InvalidIdToken=Token de identificación no válido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
|
login.userIsDisabled=El usuario está desactivado, actualmente el acceso está bloqueado para ese nombre de usuario. Por favor, póngase en contacto con el administrador.
|
||||||
login.alreadyLoggedIn=Ya has iniciado sesión en
|
login.alreadyLoggedIn=Ya has iniciado sesión en
|
||||||
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
|
login.alreadyLoggedIn2=dispositivos. Cierra sesión en los dispositivos y vuelve a intentarlo.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar archivos PDF
|
repair.header=Reparar archivos PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Dividir PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Konpondu
|
repair.title=Konpondu
|
||||||
repair.header=Konpondu PDF fitxategiak
|
repair.header=Konpondu PDF fitxategiak
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=درخواست نامعتبر
|
|||||||
login.oauth2AccessDenied=دسترسی ممنوع
|
login.oauth2AccessDenied=دسترسی ممنوع
|
||||||
login.oauth2InvalidTokenResponse=پاسخ توکن نامعتبر است
|
login.oauth2InvalidTokenResponse=پاسخ توکن نامعتبر است
|
||||||
login.oauth2InvalidIdToken=توکن شناسه نامعتبر است
|
login.oauth2InvalidIdToken=توکن شناسه نامعتبر است
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=کاربر غیرفعال شده است، ورود با این نام کاربری در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
|
login.userIsDisabled=کاربر غیرفعال شده است، ورود با این نام کاربری در حال حاضر مسدود است. لطفاً با مدیر تماس بگیرید.
|
||||||
login.alreadyLoggedIn=شما قبلاً وارد شدهاید در
|
login.alreadyLoggedIn=شما قبلاً وارد شدهاید در
|
||||||
login.alreadyLoggedIn2=دستگاهها. لطفاً از دستگاهها خارج شده و دوباره تلاش کنید.
|
login.alreadyLoggedIn2=دستگاهها. لطفاً از دستگاهها خارج شده و دوباره تلاش کنید.
|
||||||
@ -830,7 +831,7 @@ sign.first=صفحه اول
|
|||||||
sign.last=صفحه آخر
|
sign.last=صفحه آخر
|
||||||
sign.next=صفحه بعدی
|
sign.next=صفحه بعدی
|
||||||
sign.previous=صفحه قبلی
|
sign.previous=صفحه قبلی
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=تعمیر
|
repair.title=تعمیر
|
||||||
repair.header=تعمیر PDFها
|
repair.header=تعمیر PDFها
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=تقسیم PDF
|
|||||||
fileChooser.click=کلیک کنید
|
fileChooser.click=کلیک کنید
|
||||||
fileChooser.or=یا
|
fileChooser.or=یا
|
||||||
fileChooser.dragAndDrop=بکشید و رها کنید
|
fileChooser.dragAndDrop=بکشید و رها کنید
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
|
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requête invalide
|
|||||||
login.oauth2AccessDenied=Accès refusé
|
login.oauth2AccessDenied=Accès refusé
|
||||||
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
||||||
login.oauth2InvalidIdToken=Jeton d'identification invalide
|
login.oauth2InvalidIdToken=Jeton d'identification invalide
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=L'utilisateur est désactivé, la connexion est actuellement bloquée avec ce nom d'utilisateur. Veuillez contacter l'administrateur.
|
login.userIsDisabled=L'utilisateur est désactivé, la connexion est actuellement bloquée avec ce nom d'utilisateur. Veuillez contacter l'administrateur.
|
||||||
login.alreadyLoggedIn=Vous êtes déjà connecté sur
|
login.alreadyLoggedIn=Vous êtes déjà connecté sur
|
||||||
login.alreadyLoggedIn2=appareils. Veuillez vous déconnecter des appareils et réessayer.
|
login.alreadyLoggedIn2=appareils. Veuillez vous déconnecter des appareils et réessayer.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Réparer
|
repair.title=Réparer
|
||||||
repair.header=Réparer
|
repair.header=Réparer
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Diviser le PDF
|
|||||||
fileChooser.click=Cliquez
|
fileChooser.click=Cliquez
|
||||||
fileChooser.or=ou
|
fileChooser.or=ou
|
||||||
fileChooser.dragAndDrop=Glisser & Déposer
|
fileChooser.dragAndDrop=Glisser & Déposer
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
|
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Iarratas Neamhbhailí
|
|||||||
login.oauth2AccessDenied=Rochtain Diúltaithe
|
login.oauth2AccessDenied=Rochtain Diúltaithe
|
||||||
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
||||||
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Deisiúchán
|
repair.title=Deisiúchán
|
||||||
repair.header=PDF a dheisiú
|
repair.header=PDF a dheisiú
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=गलत याचना
|
|||||||
login.oauth2AccessDenied=इनपुट उम्मीदवार डिसकार
|
login.oauth2AccessDenied=इनपुट उम्मीदवार डिसकार
|
||||||
login.oauth2InvalidTokenResponse=अमान्तरित सिक्वेंस जवाब कैसे है
|
login.oauth2InvalidTokenResponse=अमान्तरित सिक्वेंस जवाब कैसे है
|
||||||
login.oauth2InvalidIdToken=गलत इड टोकन
|
login.oauth2InvalidIdToken=गलत इड टोकन
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=उपयोगकर्ता डिसबाल, यह वर्षा सभी उपयोगकर्ता जूझे वर्षाकरण बारा मिल गई है। कृपया संबवादक से संपर्क करें.
|
login.userIsDisabled=उपयोगकर्ता डिसबाल, यह वर्षा सभी उपयोगकर्ता जूझे वर्षाकरण बारा मिल गई है। कृपया संबवादक से संपर्क करें.
|
||||||
login.alreadyLoggedIn=आप पहले से ही
|
login.alreadyLoggedIn=आप पहले से ही
|
||||||
login.alreadyLoggedIn2=सुनिश्चित करने वाले डिवाइस्स पर लॉग-इन हैं। कृपया इन डिवाइस से लॉगआउट करें और पुनः प्रयास करें
|
login.alreadyLoggedIn2=सुनिश्चित करने वाले डिवाइस्स पर लॉग-इन हैं। कृपया इन डिवाइस से लॉगआउट करें और पुनः प्रयास करें
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=मरम्मत
|
repair.title=मरम्मत
|
||||||
repair.header=पीडीएफ़ मरम्मत करें
|
repair.header=पीडीएफ़ मरम्मत करें
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF विभाजित
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Neispravan zahtjev
|
|||||||
login.oauth2AccessDenied=Pristup odbijen
|
login.oauth2AccessDenied=Pristup odbijen
|
||||||
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
||||||
login.oauth2InvalidIdToken=Nevažeći ID token
|
login.oauth2InvalidIdToken=Nevažeći ID token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Korisnik je deaktiviran, prijava sa ovim korisničkim imenom je trenutno zakazana. Molimo Vas da kontaktirate administratorske osobe.
|
login.userIsDisabled=Korisnik je deaktiviran, prijava sa ovim korisničkim imenom je trenutno zakazana. Molimo Vas da kontaktirate administratorske osobe.
|
||||||
login.alreadyLoggedIn=Već ste se prijavili na
|
login.alreadyLoggedIn=Već ste se prijavili na
|
||||||
login.alreadyLoggedIn2=ure. Odjavite se s ure i pokušajte ponovo.
|
login.alreadyLoggedIn2=ure. Odjavite se s ure i pokušajte ponovo.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Popravi
|
repair.title=Popravi
|
||||||
repair.header=Popravi PDF datoteku
|
repair.header=Popravi PDF datoteku
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Podijeli PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Érvénytelen kérelem
|
|||||||
login.oauth2AccessDenied=Hozzáférés megtagadva
|
login.oauth2AccessDenied=Hozzáférés megtagadva
|
||||||
login.oauth2InvalidTokenResponse=Érvénytelen token-válasz
|
login.oauth2InvalidTokenResponse=Érvénytelen token-válasz
|
||||||
login.oauth2InvalidIdToken=Érvénytelen azonosító token
|
login.oauth2InvalidIdToken=Érvénytelen azonosító token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=A felhasználó deaktivált, a bejelentkezés jelenleg megszakítva ezzel a felhasználónévvel. Kérjen segítséget a rendszergazdától.
|
login.userIsDisabled=A felhasználó deaktivált, a bejelentkezés jelenleg megszakítva ezzel a felhasználónévvel. Kérjen segítséget a rendszergazdától.
|
||||||
login.alreadyLoggedIn=Már be van jelentkezve az
|
login.alreadyLoggedIn=Már be van jelentkezve az
|
||||||
login.alreadyLoggedIn2=eszközökre. Kijelentkezzen ezekből a eszközökből, majd próbálja újra bejelentkezni.
|
login.alreadyLoggedIn2=eszközökre. Kijelentkezzen ezekből a eszközökből, majd próbálja újra bejelentkezni.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Javítás
|
repair.title=Javítás
|
||||||
repair.header=PDF-ek javítása
|
repair.header=PDF-ek javítása
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF osztás
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Permintaan Tidak Valid
|
|||||||
login.oauth2AccessDenied=Akses Ditolak
|
login.oauth2AccessDenied=Akses Ditolak
|
||||||
login.oauth2InvalidTokenResponse=Respons Token Tidak Valid
|
login.oauth2InvalidTokenResponse=Respons Token Tidak Valid
|
||||||
login.oauth2InvalidIdToken=Token ID Tidak Valid
|
login.oauth2InvalidIdToken=Token ID Tidak Valid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Pengguna dinonaktifkan, login saat ini diblokir dengan nama pengguna ini. Silakan hubungi administrator.
|
login.userIsDisabled=Pengguna dinonaktifkan, login saat ini diblokir dengan nama pengguna ini. Silakan hubungi administrator.
|
||||||
login.alreadyLoggedIn=Anda sudah login ke
|
login.alreadyLoggedIn=Anda sudah login ke
|
||||||
login.alreadyLoggedIn2=perangkat. Silakan keluar dari perangkat dan coba lagi.
|
login.alreadyLoggedIn2=perangkat. Silakan keluar dari perangkat dan coba lagi.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Perbaiki
|
repair.title=Perbaiki
|
||||||
repair.header=Perbaiki PDF
|
repair.header=Perbaiki PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Pecah PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Richiesta non valida
|
|||||||
login.oauth2AccessDenied=Accesso negato
|
login.oauth2AccessDenied=Accesso negato
|
||||||
login.oauth2InvalidTokenResponse=Risposta token non valida
|
login.oauth2InvalidTokenResponse=Risposta token non valida
|
||||||
login.oauth2InvalidIdToken=Id Token non valido
|
login.oauth2InvalidIdToken=Id Token non valido
|
||||||
|
login.relyingPartyRegistrationNotFound=Nessuna registrazione di parte affidabile trovata
|
||||||
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
|
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
|
||||||
login.alreadyLoggedIn=Hai già effettuato l'accesso a
|
login.alreadyLoggedIn=Hai già effettuato l'accesso a
|
||||||
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
|
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
|
||||||
@ -830,7 +831,7 @@ sign.first=Prima pagina
|
|||||||
sign.last=Ultima pagina
|
sign.last=Ultima pagina
|
||||||
sign.next=Prossima pagina
|
sign.next=Prossima pagina
|
||||||
sign.previous=Pagina precedente
|
sign.previous=Pagina precedente
|
||||||
|
sign.maintainRatio=Attiva il mantenimento delle proporzioni
|
||||||
#repair
|
#repair
|
||||||
repair.title=Ripara
|
repair.title=Ripara
|
||||||
repair.header=Ripara PDF
|
repair.header=Ripara PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Dividi PDF
|
|||||||
fileChooser.click=Clicca
|
fileChooser.click=Clicca
|
||||||
fileChooser.or=o
|
fileChooser.or=o
|
||||||
fileChooser.dragAndDrop=Trascina & Rilascia
|
fileChooser.dragAndDrop=Trascina & Rilascia
|
||||||
|
fileChooser.dragAndDropPDF=Trascina & rilascia il file PDF
|
||||||
|
fileChooser.dragAndDropImage=Trascina & rilascia il file immagine
|
||||||
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
|
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=無効なリクエスト
|
|||||||
login.oauth2AccessDenied=アクセス拒否
|
login.oauth2AccessDenied=アクセス拒否
|
||||||
login.oauth2InvalidTokenResponse=無効なトークン応答
|
login.oauth2InvalidTokenResponse=無効なトークン応答
|
||||||
login.oauth2InvalidIdToken=無効なIDトークン
|
login.oauth2InvalidIdToken=無効なIDトークン
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
login.userIsDisabled=ユーザーは非アクティブ化されており、現在このユーザー名でのログインはブロックされています。管理者に連絡してください。
|
||||||
login.alreadyLoggedIn=すでにログインしています
|
login.alreadyLoggedIn=すでにログインしています
|
||||||
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
|
login.alreadyLoggedIn2=デバイスからログアウトしてもう一度お試しください。
|
||||||
@ -830,7 +831,7 @@ sign.first=最初のページ
|
|||||||
sign.last=最後のページ
|
sign.last=最後のページ
|
||||||
sign.next=次のページ
|
sign.next=次のページ
|
||||||
sign.previous=前のページ
|
sign.previous=前のページ
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=修復
|
repair.title=修復
|
||||||
repair.header=PDFを修復
|
repair.header=PDFを修復
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDFを分割
|
|||||||
fileChooser.click=クリック
|
fileChooser.click=クリック
|
||||||
fileChooser.or=または
|
fileChooser.or=または
|
||||||
fileChooser.dragAndDrop=ドラッグ&ドロップ
|
fileChooser.dragAndDrop=ドラッグ&ドロップ
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=복구
|
repair.title=복구
|
||||||
repair.header=PDF 복구
|
repair.header=PDF 복구
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF 분할
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ongeldig verzoek
|
|||||||
login.oauth2AccessDenied=Toegang geweigerd
|
login.oauth2AccessDenied=Toegang geweigerd
|
||||||
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
||||||
login.oauth2InvalidIdToken=Ongeldige ID token
|
login.oauth2InvalidIdToken=Ongeldige ID token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=De gebruiker is gedesactiveerd, inloggen is momenteel geblokkeerd voor deze gebruikersnaam. Neem contact op met de beheerder.
|
login.userIsDisabled=De gebruiker is gedesactiveerd, inloggen is momenteel geblokkeerd voor deze gebruikersnaam. Neem contact op met de beheerder.
|
||||||
login.alreadyLoggedIn=U zit reeds ingelogd bij
|
login.alreadyLoggedIn=U zit reeds ingelogd bij
|
||||||
login.alreadyLoggedIn2=apparaten. U moet u a.u.b. uitloggen van de apparaten en opnieuw proberen.
|
login.alreadyLoggedIn2=apparaten. U moet u a.u.b. uitloggen van de apparaten en opnieuw proberen.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repareren
|
repair.title=Repareren
|
||||||
repair.header=PDF's repareren
|
repair.header=PDF's repareren
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=PDF splitsen
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ugyldig forespørsel
|
|||||||
login.oauth2AccessDenied=Tilgang nektet
|
login.oauth2AccessDenied=Tilgang nektet
|
||||||
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
||||||
login.oauth2InvalidIdToken=Ugyldig Id Token
|
login.oauth2InvalidIdToken=Ugyldig Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparer
|
repair.title=Reparer
|
||||||
repair.header=Reparer PDF-er
|
repair.header=Reparer PDF-er
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Nieprawidłowe żądanie
|
|||||||
login.oauth2AccessDenied=Brak dostępu
|
login.oauth2AccessDenied=Brak dostępu
|
||||||
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
||||||
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
|
login.userIsDisabled=Użytkownik jest nieaktywny, logowanie przy użyciu tej nazwy użytkownika jest obecnie zablokowane. Prosimy o kontakt z administratorem.
|
||||||
login.alreadyLoggedIn=Jesteś już zalogowany na
|
login.alreadyLoggedIn=Jesteś już zalogowany na
|
||||||
login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
|
login.alreadyLoggedIn2=urządzeniach. Wyloguj się z tych urządzeń i spróbuj ponownie.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Napraw
|
repair.title=Napraw
|
||||||
repair.header=Napraw dokument(y) PDF
|
repair.header=Napraw dokument(y) PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Podziel PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisição Inválida
|
|||||||
login.oauth2AccessDenied=Acesso Negado
|
login.oauth2AccessDenied=Acesso Negado
|
||||||
login.oauth2InvalidTokenResponse=Resposta de Token Inválida
|
login.oauth2InvalidTokenResponse=Resposta de Token Inválida
|
||||||
login.oauth2InvalidIdToken=Id de Token Inválido
|
login.oauth2InvalidIdToken=Id de Token Inválido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
|
login.userIsDisabled=O usuário está desativado, o login está atualmente bloqueado com este nome de usuário. Entre em contato com o administrador.
|
||||||
login.alreadyLoggedIn=Você já está conectado em
|
login.alreadyLoggedIn=Você já está conectado em
|
||||||
login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
|
login.alreadyLoggedIn2=aparelhos. Por favor saia dos aparelhos e tente novamente.
|
||||||
@ -830,7 +831,7 @@ sign.first=Primeira página
|
|||||||
sign.last=Última página
|
sign.last=Última página
|
||||||
sign.next=Próxima página
|
sign.next=Próxima página
|
||||||
sign.previous=Página anterior
|
sign.previous=Página anterior
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar
|
repair.header=Reparar
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Dividir
|
|||||||
fileChooser.click=Clique
|
fileChooser.click=Clique
|
||||||
fileChooser.or=ou
|
fileChooser.or=ou
|
||||||
fileChooser.dragAndDrop=Arraste & Solte
|
fileChooser.dragAndDrop=Arraste & Solte
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
|
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Requisito inválido
|
|||||||
login.oauth2AccessDenied=Acesso negado
|
login.oauth2AccessDenied=Acesso negado
|
||||||
login.oauth2InvalidTokenResponse=Resposta de token inválida
|
login.oauth2InvalidTokenResponse=Resposta de token inválida
|
||||||
login.oauth2InvalidIdToken=Token de identificação inválido
|
login.oauth2InvalidIdToken=Token de identificação inválido
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=O utilizador foi desativado, o login está atualmente bloqueado com esta conta. Por favor, contacte o administrador.
|
login.userIsDisabled=O utilizador foi desativado, o login está atualmente bloqueado com esta conta. Por favor, contacte o administrador.
|
||||||
login.alreadyLoggedIn=Já está logado em
|
login.alreadyLoggedIn=Já está logado em
|
||||||
login.alreadyLoggedIn2=dispositivos. Por favor, faça logout nos dispositivos e tente novamente.
|
login.alreadyLoggedIn2=dispositivos. Por favor, faça logout nos dispositivos e tente novamente.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparar
|
repair.title=Reparar
|
||||||
repair.header=Reparar PDFs
|
repair.header=Reparar PDFs
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Dividir o PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Cerere Invalidă
|
|||||||
login.oauth2AccessDenied=Acces Refuzat
|
login.oauth2AccessDenied=Acces Refuzat
|
||||||
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
|
login.oauth2InvalidTokenResponse=Răspuns Invalid la Token
|
||||||
login.oauth2InvalidIdToken=Token de Id Invalid
|
login.oauth2InvalidIdToken=Token de Id Invalid
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
|
login.userIsDisabled=Utilizatorul este dezactivat, conectarea este în prezent blocată cu acest nume de utilizator. Te rugăm să contactezi administratorul.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Repară
|
repair.title=Repară
|
||||||
repair.header=Repară documente PDF
|
repair.header=Repară documente PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Неверный запрос
|
|||||||
login.oauth2AccessDenied=Доступ запрещен
|
login.oauth2AccessDenied=Доступ запрещен
|
||||||
login.oauth2InvalidTokenResponse=Недействительный ответ токена
|
login.oauth2InvalidTokenResponse=Недействительный ответ токена
|
||||||
login.oauth2InvalidIdToken=Недействительный идентификационный токен
|
login.oauth2InvalidIdToken=Недействительный идентификационный токен
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Пользователь деактивирован, вход с данным именем пользователя заблокирован. Пожалуйста, обратитесь к администратору.
|
login.userIsDisabled=Пользователь деактивирован, вход с данным именем пользователя заблокирован. Пожалуйста, обратитесь к администратору.
|
||||||
login.alreadyLoggedIn=Вы уже вошли в
|
login.alreadyLoggedIn=Вы уже вошли в
|
||||||
login.alreadyLoggedIn2=устройства. Выполните выход из устройств и попробуйте снова.
|
login.alreadyLoggedIn2=устройства. Выполните выход из устройств и попробуйте снова.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Ремонт
|
repair.title=Ремонт
|
||||||
repair.header=Ремонт PDF ов
|
repair.header=Ремонт PDF ов
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Разделить PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Opraviť
|
repair.title=Opraviť
|
||||||
repair.header=Opraviť PDF
|
repair.header=Opraviť PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request
|
|||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Popravi
|
repair.title=Popravi
|
||||||
repair.header=Popravi PDF fajlove
|
repair.header=Popravi PDF fajlove
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -250,7 +250,7 @@ database.fileNullOrEmpty=Filen får inte vara null eller tom
|
|||||||
database.failedImportFile=Misslyckades med att importera fil
|
database.failedImportFile=Misslyckades med att importera fil
|
||||||
|
|
||||||
session.expired=Din session har löpt ut. Uppdatera sidan och försök igen.
|
session.expired=Din session har löpt ut. Uppdatera sidan och försök igen.
|
||||||
session.refreshPage=Refresh Page
|
session.refreshPage=Uppdatera sida
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -519,9 +519,9 @@ home.validateSignature.desc=Verify digital signatures and certificates in PDF do
|
|||||||
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Replace-Invert-Color
|
replace-color.title=Ersätt-Invertera-Färg
|
||||||
replace-color.header=Ersätt-Invertera färg på PDF
|
replace-color.header=Ersätt-Invertera färg på PDF
|
||||||
home.replaceColorPdf.title=Replace and Invert Color
|
home.replaceColorPdf.title=Ersätt och Invertera färg
|
||||||
home.replaceColorPdf.desc=Ersätt färg för text och bakgrund i PDF och invertera hela färgen på PDF för att minska filstorlek
|
home.replaceColorPdf.desc=Ersätt färg för text och bakgrund i PDF och invertera hela färgen på PDF för att minska filstorlek
|
||||||
replaceColorPdf.tags=Ersätt Färg, Sidåtgärder, Bakomliggande, Serversid
|
replaceColorPdf.tags=Ersätt Färg, Sidåtgärder, Bakomliggande, Serversid
|
||||||
replace-color.selectText.1=Ersätt eller Invertera färgalternativ
|
replace-color.selectText.1=Ersätt eller Invertera färgalternativ
|
||||||
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Ogiltig begäran
|
|||||||
login.oauth2AccessDenied=Åtkomst nekad
|
login.oauth2AccessDenied=Åtkomst nekad
|
||||||
login.oauth2InvalidTokenResponse=Ogiltigt token-svar
|
login.oauth2InvalidTokenResponse=Ogiltigt token-svar
|
||||||
login.oauth2InvalidIdToken=Ogiltigt Id-token
|
login.oauth2InvalidIdToken=Ogiltigt Id-token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Användaren är inaktiverad, inloggning är för närvarande blockerad med detta användarnamn. Kontakta administratören.
|
login.userIsDisabled=Användaren är inaktiverad, inloggning är för närvarande blockerad med detta användarnamn. Kontakta administratören.
|
||||||
login.alreadyLoggedIn=Du är redan inloggad på
|
login.alreadyLoggedIn=Du är redan inloggad på
|
||||||
login.alreadyLoggedIn2=enheter. Logga ut från enheterna och försök igen.
|
login.alreadyLoggedIn2=enheter. Logga ut från enheterna och försök igen.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparera
|
repair.title=Reparera
|
||||||
repair.header=Reparera PDF-filer
|
repair.header=Reparera PDF-filer
|
||||||
@ -968,17 +969,17 @@ multiTool.undo=Undo
|
|||||||
multiTool.redo=Redo
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#decrypt
|
#decrypt
|
||||||
decrypt.passwordPrompt=This file is password-protected. Please enter the password:
|
decrypt.passwordPrompt=Denna fil är lösenordsskyddad. Fyll i lösenord:
|
||||||
decrypt.cancelled=Operation cancelled for PDF: {0}
|
decrypt.cancelled=Operation misslyckades för PDF: {0}
|
||||||
decrypt.noPassword=No password provided for encrypted PDF: {0}
|
decrypt.noPassword=Inget lösenord angivet för krypterad PDF: {0}
|
||||||
decrypt.invalidPassword=Please try again with the correct password.
|
decrypt.invalidPassword=Försök igen med korrekt lösenord.
|
||||||
decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0}
|
decrypt.invalidPasswordHeader=Felaktigt lösenord eller osupportad kryptering för PDF: {0}
|
||||||
decrypt.unexpectedError=There was an error processing the file. Please try again.
|
decrypt.unexpectedError=Det uppstod ett fel vid processering av filen. Vänligen försök igen.
|
||||||
decrypt.serverError=Server error while decrypting: {0}
|
decrypt.serverError=Serverfel vid avkryptering: {0}
|
||||||
decrypt.success=File decrypted successfully.
|
decrypt.success=Fil avkrypterad.
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=Denna funktion finns också tillgänglig i vår <a href="{0}">multi-tool page</a>. Spana in den för bättre sida-för-sida anpassning och ytterligare funktioner!
|
||||||
|
|
||||||
#view pdf
|
#view pdf
|
||||||
viewPdf.title=Visa PDF
|
viewPdf.title=Visa PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Dela upp PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=คำขอไม่ถูกต้อง
|
|||||||
login.oauth2AccessDenied=การเข้าถึงถูกปฏิเสธ
|
login.oauth2AccessDenied=การเข้าถึงถูกปฏิเสธ
|
||||||
login.oauth2InvalidTokenResponse=การตอบกลับโทเค็นไม่ถูกต้อง
|
login.oauth2InvalidTokenResponse=การตอบกลับโทเค็นไม่ถูกต้อง
|
||||||
login.oauth2InvalidIdToken=โทเค็น Id ไม่ถูกต้อง
|
login.oauth2InvalidIdToken=โทเค็น Id ไม่ถูกต้อง
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=ผู้ใช้งานถูกระงับการใช้งาน ไม่สามารถเข้าสู่ระบบด้วยชื่อผู้ใช้นี้ได้ กรุณาติดต่อผู้ดูแลระบบ
|
login.userIsDisabled=ผู้ใช้งานถูกระงับการใช้งาน ไม่สามารถเข้าสู่ระบบด้วยชื่อผู้ใช้นี้ได้ กรุณาติดต่อผู้ดูแลระบบ
|
||||||
login.alreadyLoggedIn=คุณได้เข้าสู่ระบบใน
|
login.alreadyLoggedIn=คุณได้เข้าสู่ระบบใน
|
||||||
login.alreadyLoggedIn2=อุปกรณ์แล้ว กรุณาออกจากระบบจากอุปกรณ์ที่ใช้งานอยู่แล้ว จากนั้นลองใหม่อีกครั้ง
|
login.alreadyLoggedIn2=อุปกรณ์แล้ว กรุณาออกจากระบบจากอุปกรณ์ที่ใช้งานอยู่แล้ว จากนั้นลองใหม่อีกครั้ง
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=ซ่อมแซม
|
repair.title=ซ่อมแซม
|
||||||
repair.header=ซ่อมแซม PDF
|
repair.header=ซ่อมแซม PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=แบ่งไฟล์ PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Geçersiz İstek
|
|||||||
login.oauth2AccessDenied=Erişim Reddedildi
|
login.oauth2AccessDenied=Erişim Reddedildi
|
||||||
login.oauth2InvalidTokenResponse=Geçersiz Belirteç Yanıtı
|
login.oauth2InvalidTokenResponse=Geçersiz Belirteç Yanıtı
|
||||||
login.oauth2InvalidIdToken=Geçersiz Kimlik Belirteci
|
login.oauth2InvalidIdToken=Geçersiz Kimlik Belirteci
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=Kullanıcı devre dışı bırakıldı, şu anda bu kullanıcı adıyla giriş engellendi. Lütfen yöneticiyle iletişime geçin.
|
login.userIsDisabled=Kullanıcı devre dışı bırakıldı, şu anda bu kullanıcı adıyla giriş engellendi. Lütfen yöneticiyle iletişime geçin.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Onar
|
repair.title=Onar
|
||||||
repair.header=PDF'leri Onar
|
repair.header=PDF'leri Onar
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Недійсний запит
|
|||||||
login.oauth2AccessDenied=Доступ заблоковано
|
login.oauth2AccessDenied=Доступ заблоковано
|
||||||
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
|
login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном
|
||||||
login.oauth2InvalidIdToken=Недійсний Id токен
|
login.oauth2InvalidIdToken=Недійсний Id токен
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Ремонт
|
repair.title=Ремонт
|
||||||
repair.header=Ремонт PDF
|
repair.header=Ремонт PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=Yêu cầu không hợp lệ
|
|||||||
login.oauth2AccessDenied=Truy cập bị từ chối
|
login.oauth2AccessDenied=Truy cập bị từ chối
|
||||||
login.oauth2InvalidTokenResponse=Phản hồi token không hợp lệ
|
login.oauth2InvalidTokenResponse=Phản hồi token không hợp lệ
|
||||||
login.oauth2InvalidIdToken=Id Token không hợp lệ
|
login.oauth2InvalidIdToken=Id Token không hợp lệ
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=You are already logged in to
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
||||||
@ -830,7 +831,7 @@ sign.first=First page
|
|||||||
sign.last=Last page
|
sign.last=Last page
|
||||||
sign.next=Next page
|
sign.next=Next page
|
||||||
sign.previous=Previous page
|
sign.previous=Previous page
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=Sửa chữa
|
repair.title=Sửa chữa
|
||||||
repair.header=Sửa chữa PDF
|
repair.header=Sửa chữa PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=Split PDF
|
|||||||
fileChooser.click=Click
|
fileChooser.click=Click
|
||||||
fileChooser.or=or
|
fileChooser.or=or
|
||||||
fileChooser.dragAndDrop=Drag & Drop
|
fileChooser.dragAndDrop=Drag & Drop
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=无效请求
|
|||||||
login.oauth2AccessDenied=拒绝访问
|
login.oauth2AccessDenied=拒绝访问
|
||||||
login.oauth2InvalidTokenResponse=无效的 Token 响应
|
login.oauth2InvalidTokenResponse=无效的 Token 响应
|
||||||
login.oauth2InvalidIdToken=无效的 Token
|
login.oauth2InvalidIdToken=无效的 Token
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=用户被禁用,登录已被阻止。请联系管理员。
|
login.userIsDisabled=用户被禁用,登录已被阻止。请联系管理员。
|
||||||
login.alreadyLoggedIn=您已经登录到了
|
login.alreadyLoggedIn=您已经登录到了
|
||||||
login.alreadyLoggedIn2=设备,请注销设备后重试。
|
login.alreadyLoggedIn2=设备,请注销设备后重试。
|
||||||
@ -830,7 +831,7 @@ sign.first=首页
|
|||||||
sign.last=末页
|
sign.last=末页
|
||||||
sign.next=下一页
|
sign.next=下一页
|
||||||
sign.previous=上一页
|
sign.previous=上一页
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=修复
|
repair.title=修复
|
||||||
repair.header=修复 PDF
|
repair.header=修复 PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=拆分 PDF
|
|||||||
fileChooser.click=单击
|
fileChooser.click=单击
|
||||||
fileChooser.or=或
|
fileChooser.or=或
|
||||||
fileChooser.dragAndDrop=拖放文件
|
fileChooser.dragAndDrop=拖放文件
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=拖放文件到此处
|
fileChooser.hoveredDragAndDrop=拖放文件到此处
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -561,6 +561,7 @@ login.oauth2invalidRequest=無效的回應
|
|||||||
login.oauth2AccessDenied=存取被拒
|
login.oauth2AccessDenied=存取被拒
|
||||||
login.oauth2InvalidTokenResponse=無效的權杖回應
|
login.oauth2InvalidTokenResponse=無效的權杖回應
|
||||||
login.oauth2InvalidIdToken=無效的識別權杖
|
login.oauth2InvalidIdToken=無效的識別權杖
|
||||||
|
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||||
login.userIsDisabled=使用者已停用,目前此使用者無法登入。請聯絡系統管理員。
|
login.userIsDisabled=使用者已停用,目前此使用者無法登入。請聯絡系統管理員。
|
||||||
login.alreadyLoggedIn=您已經登入了
|
login.alreadyLoggedIn=您已經登入了
|
||||||
login.alreadyLoggedIn2=個裝置。請登出其他裝置後再試一次。
|
login.alreadyLoggedIn2=個裝置。請登出其他裝置後再試一次。
|
||||||
@ -830,7 +831,7 @@ sign.first=第一頁
|
|||||||
sign.last=最後一頁
|
sign.last=最後一頁
|
||||||
sign.next=下一頁
|
sign.next=下一頁
|
||||||
sign.previous=上一頁
|
sign.previous=上一頁
|
||||||
|
sign.maintainRatio=Toggle maintain aspect ratio
|
||||||
#repair
|
#repair
|
||||||
repair.title=修復
|
repair.title=修復
|
||||||
repair.header=修復 PDF
|
repair.header=修復 PDF
|
||||||
@ -1284,6 +1285,8 @@ splitByChapters.submit=分割 PDF
|
|||||||
fileChooser.click=點選
|
fileChooser.click=點選
|
||||||
fileChooser.or=或
|
fileChooser.or=或
|
||||||
fileChooser.dragAndDrop=拖放檔案
|
fileChooser.dragAndDrop=拖放檔案
|
||||||
|
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||||
|
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||||
fileChooser.hoveredDragAndDrop=將檔案拖放至此
|
fileChooser.hoveredDragAndDrop=將檔案拖放至此
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
|
@ -2,22 +2,32 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pdf-canvas {
|
#pdf-canvas {
|
||||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-buttons-box {
|
.draggable-buttons-box {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 100%;
|
width: calc(100% + 4.4rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
z-index: 5;
|
||||||
|
margin-left: -2.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-buttons-box>button {
|
.draggable-buttons-box>button {
|
||||||
z-index: 10;
|
z-index: 4;
|
||||||
background-color: rgba(13, 110, 253, 0.1);
|
background-color: rgba(13, 110, 253, 0.1);
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 2.5rem;
|
||||||
|
max-width: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.draggable-canvas {
|
.draggable-canvas {
|
||||||
border: 1px solid red;
|
border: 1px solid red;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -26,3 +36,20 @@
|
|||||||
top: 0px;
|
top: 0px;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-with-icon {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icon .icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icon input {
|
||||||
|
padding-left: 2.2rem;
|
||||||
|
}
|
||||||
|
@ -118,6 +118,7 @@
|
|||||||
row-gap: 1px;
|
row-gap: 1px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
|
top:4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
@ -165,8 +166,8 @@
|
|||||||
height: 15px;
|
height: 15px;
|
||||||
width: 15px;
|
width: 15px;
|
||||||
|
|
||||||
right: 10px;
|
right: 0px;
|
||||||
top: -5px;
|
top: -17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-selected-file * {
|
.remove-selected-file * {
|
||||||
@ -219,3 +220,54 @@
|
|||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
border: 1px solid rgb(105, 116, 134, 0.5);
|
border: 1px solid rgb(105, 116, 134, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.draggable-image-overlay{
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: none;
|
||||||
|
z-index: 10;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
pointer-events: none;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
height:100%;
|
||||||
|
width:100%;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-file-container:hover .drag-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-icon {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
color: white;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 14px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imagePreviewModal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
@ -22,18 +22,38 @@ select#font-select option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.draggable-buttons-box {
|
.draggable-buttons-box {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 100%;
|
width: calc(100% + 4.4rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
margin-left: -2.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-buttons-box>button {
|
.draggable-buttons-box>button {
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
background-color: rgba(13, 110, 253, 0.1);
|
background-color: rgba(13, 110, 253, 0.1);
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 2.5rem;
|
||||||
|
max-width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.rotation-handle {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid #3498db;
|
||||||
|
background-color: rgba(52, 152, 219, 0.1);
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
cursor: grab;
|
||||||
|
top: -30px;
|
||||||
|
left: calc(50% - 10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-canvas {
|
.draggable-canvas {
|
||||||
@ -113,3 +133,33 @@ select#font-select option {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-with-icon {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icon .icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icon input {
|
||||||
|
padding-left: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-file-container-saved {
|
||||||
|
padding-top: 1px;
|
||||||
|
position: relative;
|
||||||
|
row-gap: 1px;
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-file-container-saved:hover .drag-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@ -7,75 +7,137 @@ const DraggableUtils = {
|
|||||||
elementAllPages: [],
|
elementAllPages: [],
|
||||||
documentsMap: new Map(),
|
documentsMap: new Map(),
|
||||||
lastInteracted: null,
|
lastInteracted: null,
|
||||||
|
padding: 15,
|
||||||
|
maintainRatioEnabled: true,
|
||||||
init() {
|
init() {
|
||||||
interact('.draggable-canvas')
|
interact('.draggable-canvas')
|
||||||
.draggable({
|
.draggable({
|
||||||
listeners: {
|
listeners: {
|
||||||
|
start(event) {
|
||||||
|
const target = event.target;
|
||||||
|
x = parseFloat(target.getAttribute('data-bs-x'));
|
||||||
|
y = parseFloat(target.getAttribute('data-bs-y'));
|
||||||
|
},
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
const x = (parseFloat(target.getAttribute('data-bs-x')) || 0) + event.dx;
|
|
||||||
const y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + event.dy;
|
|
||||||
|
|
||||||
|
// Retrieve position attributes
|
||||||
|
let x = parseFloat(target.getAttribute('data-bs-x')) || 0;
|
||||||
|
let y = parseFloat(target.getAttribute('data-bs-y')) || 0;
|
||||||
|
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
|
||||||
|
|
||||||
|
// Update position based on drag movement
|
||||||
|
x += event.dx;
|
||||||
|
y += event.dy;
|
||||||
|
|
||||||
|
// Apply translation to the parent container (bounding box)
|
||||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
|
||||||
|
// Preserve rotation on the inner canvas
|
||||||
|
const canvas = target.querySelector('.display-canvas');
|
||||||
|
|
||||||
|
const canvasWidth = parseFloat(canvas.style.width);
|
||||||
|
const canvasHeight = parseFloat(canvas.style.height);
|
||||||
|
|
||||||
|
const cosAngle = Math.abs(Math.cos(angle));
|
||||||
|
const sinAngle = Math.abs(Math.sin(angle));
|
||||||
|
|
||||||
|
const rotatedWidth = canvasWidth * cosAngle + canvasHeight * sinAngle;
|
||||||
|
const rotatedHeight = canvasWidth * sinAngle + canvasHeight * cosAngle;
|
||||||
|
|
||||||
|
const offsetX = (rotatedWidth - canvasWidth) / 2;
|
||||||
|
const offsetY = (rotatedHeight - canvasHeight) / 2;
|
||||||
|
|
||||||
|
canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${angle}rad)`;
|
||||||
|
|
||||||
|
// Update attributes for persistence
|
||||||
target.setAttribute('data-bs-x', x);
|
target.setAttribute('data-bs-x', x);
|
||||||
target.setAttribute('data-bs-y', y);
|
target.setAttribute('data-bs-y', y);
|
||||||
|
|
||||||
this.onInteraction(target);
|
// Set the last interacted element
|
||||||
//update the last interacted element
|
this.lastInteracted = target;
|
||||||
this.lastInteracted = event.target;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.resizable({
|
.resizable({
|
||||||
edges: { left: true, right: true, bottom: true, top: true },
|
edges: { left: true, right: true, bottom: true, top: true },
|
||||||
listeners: {
|
listeners: {
|
||||||
|
start: (event) => {
|
||||||
|
const target = event.target;
|
||||||
|
x = parseFloat(target.getAttribute('data-bs-x')) || 0;
|
||||||
|
y = parseFloat(target.getAttribute('data-bs-y')) || 0;
|
||||||
|
},
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
var target = event.target;
|
const target = event.target;
|
||||||
var x = parseFloat(target.getAttribute('data-bs-x')) || 0;
|
|
||||||
var y = parseFloat(target.getAttribute('data-bs-y')) || 0;
|
|
||||||
|
|
||||||
// check if control key is pressed
|
const MAX_CHANGE = 60;
|
||||||
if (event.ctrlKey) {
|
|
||||||
const aspectRatio = target.offsetWidth / target.offsetHeight;
|
|
||||||
// preserve aspect ratio
|
|
||||||
let width = event.rect.width;
|
|
||||||
let height = event.rect.height;
|
|
||||||
|
|
||||||
|
let width = event.rect.width - 2 * this.padding;
|
||||||
|
let height = event.rect.height - 2 * this.padding;
|
||||||
|
|
||||||
|
const canvas = target.querySelector('.display-canvas');
|
||||||
|
if (canvas) {
|
||||||
|
const originalWidth = parseFloat(canvas.style.width) || canvas.width;
|
||||||
|
const originalHeight = parseFloat(canvas.style.height) || canvas.height;
|
||||||
|
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
|
||||||
|
|
||||||
|
const aspectRatio = originalWidth / originalHeight;
|
||||||
|
|
||||||
|
if (!event.ctrlKey && this.maintainRatioEnabled) {
|
||||||
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
||||||
height = width / aspectRatio;
|
height = width / aspectRatio;
|
||||||
} else {
|
} else {
|
||||||
width = height * aspectRatio;
|
width = height * aspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.rect.width = width;
|
|
||||||
event.rect.height = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.style.width = event.rect.width + 'px';
|
const widthChange = width - originalWidth;
|
||||||
target.style.height = event.rect.height + 'px';
|
const heightChange = height - originalHeight;
|
||||||
|
|
||||||
// translate when resizing from top or left edges
|
if (Math.abs(widthChange) > MAX_CHANGE || Math.abs(heightChange) > MAX_CHANGE) {
|
||||||
x += event.deltaRect.left;
|
const scale = MAX_CHANGE / Math.max(Math.abs(widthChange), Math.abs(heightChange));
|
||||||
y += event.deltaRect.top;
|
width = originalWidth + widthChange * scale;
|
||||||
|
height = originalHeight + heightChange * scale;
|
||||||
|
}
|
||||||
|
|
||||||
target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
|
const cosAngle = Math.abs(Math.cos(angle));
|
||||||
|
const sinAngle = Math.abs(Math.sin(angle));
|
||||||
|
const boundingWidth = width * cosAngle + height * sinAngle;
|
||||||
|
const boundingHeight = width * sinAngle + height * cosAngle;
|
||||||
|
|
||||||
|
if (event.edges.left) {
|
||||||
|
const dx = event.deltaRect.left;
|
||||||
|
x += dx;
|
||||||
|
}
|
||||||
|
if (event.edges.top) {
|
||||||
|
const dy = event.deltaRect.top;
|
||||||
|
y += dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
target.style.width = `${boundingWidth + 2 * this.padding}px`;
|
||||||
|
target.style.height = `${boundingHeight + 2 * this.padding}px`;
|
||||||
|
|
||||||
|
canvas.style.width = `${width}px`;
|
||||||
|
canvas.style.height = `${height}px`;
|
||||||
|
canvas.style.transform = `translate(${(boundingWidth - width) / 2}px, ${(boundingHeight - height) / 2
|
||||||
|
}px) rotate(${angle}rad)`;
|
||||||
|
|
||||||
target.setAttribute('data-bs-x', x);
|
target.setAttribute('data-bs-x', x);
|
||||||
target.setAttribute('data-bs-y', y);
|
target.setAttribute('data-bs-y', y);
|
||||||
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
|
|
||||||
|
|
||||||
this.onInteraction(target);
|
this.lastInteracted = target;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
modifiers: [
|
modifiers: [
|
||||||
interact.modifiers.restrictSize({
|
interact.modifiers.restrictSize({
|
||||||
min: {width: 5, height: 5},
|
min: { width: 50, height: 50 },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
inertia: true,
|
inertia: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
//Arrow key Support for Add-Image and Sign pages
|
//Arrow key Support for Add-Image and Sign pages
|
||||||
if (window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
|
if (window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener('keydown', (event) => {
|
||||||
@ -117,7 +179,8 @@ const DraggableUtils = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update position
|
// Update position
|
||||||
target.style.transform = `translate(${x}px, ${y}px)`;
|
const angle = parseFloat(target.getAttribute('data-angle')) || 0;
|
||||||
|
target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}rad)`;
|
||||||
target.setAttribute('data-bs-x', x);
|
target.setAttribute('data-bs-x', x);
|
||||||
target.setAttribute('data-bs-y', y);
|
target.setAttribute('data-bs-y', y);
|
||||||
|
|
||||||
@ -125,72 +188,97 @@ const DraggableUtils = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onInteraction(target) {
|
onInteraction(target) {
|
||||||
this.boxDragContainer.appendChild(target);
|
this.lastInteracted = target;
|
||||||
},
|
// this.boxDragContainer.appendChild(target);
|
||||||
|
// target.appendChild(target.querySelector(".display-canvas"));
|
||||||
createDraggableCanvas() {
|
|
||||||
const createdCanvas = document.createElement('canvas');
|
|
||||||
createdCanvas.id = `draggable-canvas-${this.nextId++}`;
|
|
||||||
createdCanvas.classList.add('draggable-canvas');
|
|
||||||
|
|
||||||
const x = 0;
|
|
||||||
const y = 20;
|
|
||||||
createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
|
|
||||||
createdCanvas.setAttribute('data-bs-x', x);
|
|
||||||
createdCanvas.setAttribute('data-bs-y', y);
|
|
||||||
|
|
||||||
//Click element in order to enable arrow keys
|
|
||||||
createdCanvas.addEventListener('click', () => {
|
|
||||||
this.lastInteracted = createdCanvas;
|
|
||||||
});
|
|
||||||
|
|
||||||
createdCanvas.onclick = (e) => this.onInteraction(e.target);
|
|
||||||
|
|
||||||
this.boxDragContainer.appendChild(createdCanvas);
|
|
||||||
|
|
||||||
//Enable Arrow keys directly after the element is created
|
|
||||||
this.lastInteracted = createdCanvas;
|
|
||||||
|
|
||||||
return createdCanvas;
|
|
||||||
},
|
},
|
||||||
createDraggableCanvasFromUrl(dataUrl) {
|
createDraggableCanvasFromUrl(dataUrl) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
var myImage = new Image();
|
const canvasContainer = document.createElement('div');
|
||||||
|
const createdCanvas = document.createElement('canvas'); // Inner canvas
|
||||||
|
const padding = this.padding;
|
||||||
|
|
||||||
|
canvasContainer.id = `draggable-canvas-${this.nextId++}`;
|
||||||
|
canvasContainer.classList.add('draggable-canvas');
|
||||||
|
createdCanvas.classList.add('display-canvas');
|
||||||
|
|
||||||
|
canvasContainer.style.position = 'absolute';
|
||||||
|
canvasContainer.style.padding = `${padding}px`;
|
||||||
|
canvasContainer.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
let x = 0,
|
||||||
|
y = 30,
|
||||||
|
angle = 0;
|
||||||
|
canvasContainer.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
canvasContainer.setAttribute('data-bs-x', x);
|
||||||
|
canvasContainer.setAttribute('data-bs-y', y);
|
||||||
|
canvasContainer.setAttribute('data-angle', angle);
|
||||||
|
|
||||||
|
canvasContainer.addEventListener('click', () => {
|
||||||
|
this.lastInteracted = canvasContainer;
|
||||||
|
this.showRotationControls(canvasContainer);
|
||||||
|
});
|
||||||
|
canvasContainer.appendChild(createdCanvas);
|
||||||
|
this.boxDragContainer.appendChild(canvasContainer);
|
||||||
|
|
||||||
|
const myImage = new Image();
|
||||||
myImage.src = dataUrl;
|
myImage.src = dataUrl;
|
||||||
myImage.onload = () => {
|
myImage.onload = () => {
|
||||||
var createdCanvas = this.createDraggableCanvas();
|
const context = createdCanvas.getContext('2d');
|
||||||
|
|
||||||
createdCanvas.width = myImage.width;
|
createdCanvas.width = myImage.width;
|
||||||
createdCanvas.height = myImage.height;
|
createdCanvas.height = myImage.height;
|
||||||
|
|
||||||
const imgAspect = myImage.width / myImage.height;
|
const imgAspect = myImage.width / myImage.height;
|
||||||
const pdfAspect = this.boxDragContainer.offsetWidth / this.boxDragContainer.offsetHeight;
|
const containerWidth = this.boxDragContainer.offsetWidth;
|
||||||
|
const containerHeight = this.boxDragContainer.offsetHeight;
|
||||||
|
|
||||||
var scaleMultiplier;
|
let scaleMultiplier = Math.min(containerWidth / myImage.width, containerHeight / myImage.height);
|
||||||
if (imgAspect > pdfAspect) {
|
const scaleFactor = 0.5;
|
||||||
scaleMultiplier = this.boxDragContainer.offsetWidth / myImage.width;
|
|
||||||
} else {
|
|
||||||
scaleMultiplier = this.boxDragContainer.offsetHeight / myImage.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newWidth = createdCanvas.width;
|
const newWidth = myImage.width * scaleMultiplier * scaleFactor;
|
||||||
var newHeight = createdCanvas.height;
|
const newHeight = myImage.height * scaleMultiplier * scaleFactor;
|
||||||
if (scaleMultiplier < 1) {
|
|
||||||
newWidth = newWidth * scaleMultiplier;
|
|
||||||
newHeight = newHeight * scaleMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
createdCanvas.style.width = newWidth + 'px';
|
// Calculate initial bounding box size
|
||||||
createdCanvas.style.height = newHeight + 'px';
|
const cosAngle = Math.abs(Math.cos(angle));
|
||||||
|
const sinAngle = Math.abs(Math.sin(angle));
|
||||||
|
const boundingWidth = newWidth * cosAngle + newHeight * sinAngle;
|
||||||
|
const boundingHeight = newWidth * sinAngle + newHeight * cosAngle;
|
||||||
|
|
||||||
var myContext = createdCanvas.getContext('2d');
|
createdCanvas.style.width = `${newWidth}px`;
|
||||||
myContext.drawImage(myImage, 0, 0);
|
createdCanvas.style.height = `${newHeight}px`;
|
||||||
resolve(createdCanvas);
|
|
||||||
|
canvasContainer.style.width = `${boundingWidth + 2 * padding}px`;
|
||||||
|
canvasContainer.style.height = `${boundingHeight + 2 * padding}px`;
|
||||||
|
|
||||||
|
context.imageSmoothingEnabled = true;
|
||||||
|
context.imageSmoothingQuality = 'high';
|
||||||
|
context.drawImage(myImage, 0, 0, myImage.width, myImage.height);
|
||||||
|
this.showRotationControls(canvasContainer);
|
||||||
|
this.lastInteracted = canvasContainer;
|
||||||
|
|
||||||
|
resolve(canvasContainer);
|
||||||
|
};
|
||||||
|
|
||||||
|
myImage.onerror = () => {
|
||||||
|
console.error('Failed to load the image.');
|
||||||
|
resolve(null);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
toggleMaintainRatio() {
|
||||||
|
this.maintainRatioEnabled = !this.maintainRatioEnabled;
|
||||||
|
const button = document.getElementById('ratioToggleBtn');
|
||||||
|
if (this.maintainRatioEnabled) {
|
||||||
|
button.classList.remove('btn-danger');
|
||||||
|
button.classList.add('btn-outline-secondary');
|
||||||
|
} else {
|
||||||
|
button.classList.remove('btn-outline-secondary');
|
||||||
|
button.classList.add('btn-danger');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
deleteAllDraggableCanvases() {
|
deleteAllDraggableCanvases() {
|
||||||
this.boxDragContainer.querySelectorAll('.draggable-canvas').forEach((el) => el.remove());
|
this.boxDragContainer.querySelectorAll('.draggable-canvas').forEach((el) => el.remove());
|
||||||
},
|
},
|
||||||
@ -266,9 +354,61 @@ const DraggableUtils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getLastInteracted() {
|
getLastInteracted() {
|
||||||
return this.boxDragContainer.querySelector('.draggable-canvas:last-of-type');
|
return this.lastInteracted;
|
||||||
},
|
},
|
||||||
|
showRotationControls(element) {
|
||||||
|
const rotationControls = document.getElementById('rotation-controls');
|
||||||
|
const rotationInput = document.getElementById('rotation-input');
|
||||||
|
rotationControls.style.display = 'flex';
|
||||||
|
rotationInput.value = Math.round((parseFloat(element.getAttribute('data-angle')) * 180) / Math.PI);
|
||||||
|
rotationInput.addEventListener('input', this.handleRotationInputChange);
|
||||||
|
},
|
||||||
|
hideRotationControls() {
|
||||||
|
const rotationControls = document.getElementById('rotation-controls');
|
||||||
|
const rotationInput = document.getElementById('rotation-input');
|
||||||
|
rotationControls.style.display = 'none';
|
||||||
|
rotationInput.addEventListener('input', this.handleRotationInputChange);
|
||||||
|
},
|
||||||
|
applyRotationToElement(element, degrees) {
|
||||||
|
const radians = degrees * (Math.PI / 180); // Convert degrees to radians
|
||||||
|
|
||||||
|
// Get current position
|
||||||
|
const x = parseFloat(element.getAttribute('data-bs-x')) || 0;
|
||||||
|
const y = parseFloat(element.getAttribute('data-bs-y')) || 0;
|
||||||
|
|
||||||
|
// Get the inner canvas (image)
|
||||||
|
const canvas = element.querySelector('.display-canvas');
|
||||||
|
if (canvas) {
|
||||||
|
const originalWidth = parseFloat(canvas.style.width);
|
||||||
|
const originalHeight = parseFloat(canvas.style.height);
|
||||||
|
const padding = this.padding; // Access the padding value
|
||||||
|
|
||||||
|
// Calculate rotated bounding box dimensions
|
||||||
|
const cosAngle = Math.abs(Math.cos(radians));
|
||||||
|
const sinAngle = Math.abs(Math.sin(radians));
|
||||||
|
const boundingWidth = originalWidth * cosAngle + originalHeight * sinAngle + 2 * padding;
|
||||||
|
const boundingHeight = originalWidth * sinAngle + originalHeight * cosAngle + 2 * padding;
|
||||||
|
|
||||||
|
// Update parent container to fit the rotated bounding box
|
||||||
|
element.style.width = `${boundingWidth}px`;
|
||||||
|
element.style.height = `${boundingHeight}px`;
|
||||||
|
|
||||||
|
// Center the canvas within the bounding box, accounting for padding
|
||||||
|
const offsetX = (boundingWidth - originalWidth) / 2 - padding;
|
||||||
|
const offsetY = (boundingHeight - originalHeight) / 2 - padding;
|
||||||
|
|
||||||
|
canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${radians}rad)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the bounding box positioned properly
|
||||||
|
element.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
element.setAttribute('data-angle', radians);
|
||||||
|
},
|
||||||
|
handleRotationInputChange() {
|
||||||
|
const rotationInput = document.getElementById('rotation-input');
|
||||||
|
const degrees = parseFloat(rotationInput.value) || 0;
|
||||||
|
DraggableUtils.applyRotationToElement(DraggableUtils.lastInteracted, degrees);
|
||||||
|
},
|
||||||
storePageContents() {
|
storePageContents() {
|
||||||
var pagesMap = this.documentsMap.get(this.pdfDoc);
|
var pagesMap = this.documentsMap.get(this.pdfDoc);
|
||||||
if (!pagesMap) {
|
if (!pagesMap) {
|
||||||
@ -352,8 +492,6 @@ const DraggableUtils = {
|
|||||||
this.loadPageContents();
|
this.loadPageContents();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
parseTransform(element) {},
|
|
||||||
async getOverlayedPdfDocument() {
|
async getOverlayedPdfDocument() {
|
||||||
const pdfBytes = await this.pdfDoc.getData();
|
const pdfBytes = await this.pdfDoc.getData();
|
||||||
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
|
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
|
||||||
@ -367,7 +505,6 @@ const DraggableUtils = {
|
|||||||
if (pageIdx.includes('offset')) {
|
if (pageIdx.includes('offset')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log(typeof pageIdx);
|
|
||||||
|
|
||||||
const page = pdfDocModified.getPage(parseInt(pageIdx));
|
const page = pdfDocModified.getPage(parseInt(pageIdx));
|
||||||
let draggablesData = pagesMap[pageIdx];
|
let draggablesData = pagesMap[pageIdx];
|
||||||
@ -376,20 +513,35 @@ const DraggableUtils = {
|
|||||||
const offsetHeight = pagesMap[pageIdx + '-offsetHeight'];
|
const offsetHeight = pagesMap[pageIdx + '-offsetHeight'];
|
||||||
|
|
||||||
for (const draggableData of draggablesData) {
|
for (const draggableData of draggablesData) {
|
||||||
// embed the draggable canvas
|
// Embed the draggable canvas
|
||||||
const draggableElement = draggableData.element;
|
const draggableElement = draggableData.element.querySelector('.display-canvas');
|
||||||
const response = await fetch(draggableElement.toDataURL());
|
const response = await fetch(draggableElement.toDataURL());
|
||||||
const draggableImgBytes = await response.arrayBuffer();
|
const draggableImgBytes = await response.arrayBuffer();
|
||||||
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
|
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
|
||||||
|
|
||||||
// calculate the position in the pdf document
|
// Extract transformation data
|
||||||
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, '');
|
const transform = draggableData.element.style.transform || '';
|
||||||
const transformComponents = tansform.split(',');
|
const translateRegex = /translate\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/;
|
||||||
|
|
||||||
|
const translateMatch = transform.match(translateRegex);
|
||||||
|
|
||||||
|
const translateX = translateMatch ? parseFloat(translateMatch[1]) : 0;
|
||||||
|
const translateY = translateMatch ? parseFloat(translateMatch[2]) : 0;
|
||||||
|
|
||||||
|
const childTransform = draggableElement.style.transform || '';
|
||||||
|
const childTranslateMatch = childTransform.match(translateRegex);
|
||||||
|
|
||||||
|
const childOffsetX = childTranslateMatch ? parseFloat(childTranslateMatch[1]) : 0;
|
||||||
|
const childOffsetY = childTranslateMatch ? parseFloat(childTranslateMatch[2]) : 0;
|
||||||
|
|
||||||
|
const rotateAngle = parseFloat(draggableData.element.getAttribute('data-angle')) || 0;
|
||||||
|
|
||||||
const draggablePositionPixels = {
|
const draggablePositionPixels = {
|
||||||
x: parseFloat(transformComponents[0]),
|
x: translateX + childOffsetX + this.padding + 2,
|
||||||
y: parseFloat(transformComponents[1]),
|
y: translateY + childOffsetY + this.padding + 2,
|
||||||
width: draggableData.offsetWidth,
|
width: parseFloat(draggableElement.style.width),
|
||||||
height: draggableData.offsetHeight,
|
height: parseFloat(draggableElement.style.height),
|
||||||
|
angle: rotateAngle, // Store rotation
|
||||||
};
|
};
|
||||||
|
|
||||||
// Auxiliary variables
|
// Auxiliary variables
|
||||||
@ -397,24 +549,25 @@ const DraggableUtils = {
|
|||||||
let heightAdjusted = page.getHeight();
|
let heightAdjusted = page.getHeight();
|
||||||
const rotation = page.getRotation();
|
const rotation = page.getRotation();
|
||||||
|
|
||||||
//Normalizing angle
|
// Normalize page rotation angle
|
||||||
let normalizedAngle = rotation.angle % 360;
|
let normalizedAngle = rotation.angle % 360;
|
||||||
if (normalizedAngle < 0) {
|
if (normalizedAngle < 0) {
|
||||||
normalizedAngle += 360;
|
normalizedAngle += 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Changing the page dimension if the angle is 90 or 270
|
// Adjust page dimensions for rotated pages
|
||||||
if (normalizedAngle === 90 || normalizedAngle === 270) {
|
if (normalizedAngle === 90 || normalizedAngle === 270) {
|
||||||
let widthTemp = widthAdjusted;
|
[widthAdjusted, heightAdjusted] = [heightAdjusted, widthAdjusted];
|
||||||
widthAdjusted = heightAdjusted;
|
|
||||||
heightAdjusted = widthTemp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const draggablePositionRelative = {
|
const draggablePositionRelative = {
|
||||||
x: draggablePositionPixels.x / offsetWidth,
|
x: draggablePositionPixels.x / offsetWidth,
|
||||||
y: draggablePositionPixels.y / offsetHeight,
|
y: draggablePositionPixels.y / offsetHeight,
|
||||||
width: draggablePositionPixels.width / offsetWidth,
|
width: draggablePositionPixels.width / offsetWidth,
|
||||||
height: draggablePositionPixels.height / offsetHeight,
|
height: draggablePositionPixels.height / offsetHeight,
|
||||||
|
angle: draggablePositionPixels.angle,
|
||||||
};
|
};
|
||||||
|
|
||||||
const draggablePositionPdf = {
|
const draggablePositionPdf = {
|
||||||
x: draggablePositionRelative.x * widthAdjusted,
|
x: draggablePositionRelative.x * widthAdjusted,
|
||||||
y: draggablePositionRelative.y * heightAdjusted,
|
y: draggablePositionRelative.y * heightAdjusted,
|
||||||
@ -422,11 +575,13 @@ const DraggableUtils = {
|
|||||||
height: draggablePositionRelative.height * heightAdjusted,
|
height: draggablePositionRelative.height * heightAdjusted,
|
||||||
};
|
};
|
||||||
|
|
||||||
//Defining the image if the page has a 0-degree angle
|
// Calculate position based on normalized page rotation
|
||||||
let x = draggablePositionPdf.x;
|
let x = draggablePositionPdf.x;
|
||||||
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
|
let y = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
|
||||||
|
|
||||||
//Defining the image position if it is at other angles
|
let originx = x + draggablePositionPdf.width / 2;
|
||||||
|
let originy = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height / 2;
|
||||||
|
|
||||||
if (normalizedAngle === 90) {
|
if (normalizedAngle === 90) {
|
||||||
x = draggablePositionPdf.y + draggablePositionPdf.height;
|
x = draggablePositionPdf.y + draggablePositionPdf.height;
|
||||||
y = draggablePositionPdf.x;
|
y = draggablePositionPdf.x;
|
||||||
@ -437,17 +592,32 @@ const DraggableUtils = {
|
|||||||
x = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
|
x = heightAdjusted - draggablePositionPdf.y - draggablePositionPdf.height;
|
||||||
y = widthAdjusted - draggablePositionPdf.x;
|
y = widthAdjusted - draggablePositionPdf.x;
|
||||||
}
|
}
|
||||||
|
// let angle = draggablePositionPixels.angle % 360;
|
||||||
// draw the image
|
// if (angle < 0) angle += 360; // Normalize to positive angle
|
||||||
|
const radians = -draggablePositionPixels.angle; // Convert angle to radians
|
||||||
|
page.pushOperators(
|
||||||
|
PDFLib.pushGraphicsState(),
|
||||||
|
PDFLib.concatTransformationMatrix(1, 0, 0, 1, originx, originy),
|
||||||
|
PDFLib.concatTransformationMatrix(
|
||||||
|
Math.cos(radians),
|
||||||
|
Math.sin(radians),
|
||||||
|
-Math.sin(radians),
|
||||||
|
Math.cos(radians),
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
),
|
||||||
|
PDFLib.concatTransformationMatrix(1, 0, 0, 1, -1 * originx, -1 * originy)
|
||||||
|
);
|
||||||
page.drawImage(pdfImageObject, {
|
page.drawImage(pdfImageObject, {
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
width: draggablePositionPdf.width,
|
width: draggablePositionPdf.width,
|
||||||
height: draggablePositionPdf.height,
|
height: draggablePositionPdf.height,
|
||||||
rotate: rotation,
|
|
||||||
});
|
});
|
||||||
|
page.pushOperators(PDFLib.popGraphicsState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadPageContents();
|
this.loadPageContents();
|
||||||
return pdfDocModified;
|
return pdfDocModified;
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@ if (!isScriptExecuted) {
|
|||||||
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
|
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let hasDroppedImage = false;
|
||||||
|
|
||||||
function setupFileInput(chooser) {
|
function setupFileInput(chooser) {
|
||||||
const elementId = chooser.getAttribute('data-bs-element-id');
|
const elementId = chooser.getAttribute('data-bs-element-id');
|
||||||
@ -18,6 +19,11 @@ function setupFileInput(chooser) {
|
|||||||
|
|
||||||
let inputContainer = document.getElementById(inputContainerId);
|
let inputContainer = document.getElementById(inputContainerId);
|
||||||
|
|
||||||
|
if (inputContainer.id === 'pdf-upload-input-container') {
|
||||||
|
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropPDF;
|
||||||
|
} else if (inputContainer.id === 'image-upload-input-container') {
|
||||||
|
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropImage;
|
||||||
|
}
|
||||||
let allFiles = [];
|
let allFiles = [];
|
||||||
let overlay;
|
let overlay;
|
||||||
let dragCounter = 0;
|
let dragCounter = 0;
|
||||||
@ -141,12 +147,17 @@ function setupFileInput(chooser) {
|
|||||||
files.forEach((file) => dataTransfer.items.add(file));
|
files.forEach((file) => dataTransfer.items.add(file));
|
||||||
return dataTransfer;
|
return dataTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileInputChange(inputElement) {
|
function handleFileInputChange(inputElement) {
|
||||||
const files = allFiles;
|
const files = allFiles;
|
||||||
showOrHideSelectedFilesContainer(files);
|
showOrHideSelectedFilesContainer(files);
|
||||||
|
|
||||||
const filesInfo = files.map((f) => ({name: f.name, size: f.size, uniqueId: f.uniqueId}));
|
const filesInfo = files.map((f) => ({
|
||||||
|
name: f.name,
|
||||||
|
size: f.size,
|
||||||
|
uniqueId: f.uniqueId,
|
||||||
|
type: f.type,
|
||||||
|
url: URL.createObjectURL(f),
|
||||||
|
}));
|
||||||
|
|
||||||
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
|
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
|
||||||
selectedFilesContainer.empty();
|
selectedFilesContainer.empty();
|
||||||
@ -157,30 +168,111 @@ function setupFileInput(chooser) {
|
|||||||
$(fileContainer).addClass(fileContainerClasses);
|
$(fileContainer).addClass(fileContainerClasses);
|
||||||
$(fileContainer).attr('id', info.uniqueId);
|
$(fileContainer).attr('id', info.uniqueId);
|
||||||
|
|
||||||
let fileIconContainer = createFileIconContainer(info);
|
let fileIconContainer = document.createElement('div');
|
||||||
|
const isDragAndDropEnabled =
|
||||||
|
window.location.pathname.includes('add-image') || window.location.pathname.includes('sign');
|
||||||
|
if (info.type.startsWith('image/')) {
|
||||||
|
let imgPreview = document.createElement('img');
|
||||||
|
imgPreview.src = info.url;
|
||||||
|
imgPreview.alt = 'Preview';
|
||||||
|
imgPreview.style.width = '50px';
|
||||||
|
imgPreview.style.height = '50px';
|
||||||
|
imgPreview.style.objectFit = 'cover';
|
||||||
|
$(fileIconContainer).append(imgPreview);
|
||||||
|
|
||||||
|
if (isDragAndDropEnabled) {
|
||||||
|
let dragIcon = document.createElement('div');
|
||||||
|
dragIcon.classList.add('drag-icon');
|
||||||
|
dragIcon.innerHTML =
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M360-160q-33 0-56.5-23.5T280-240q0-33 23.5-56.5T360-320q33 0 56.5 23.5T440-240q0 33-23.5 56.5T360-160Zm240 0q-33 0-56.5-23.5T520-240q0-33 23.5-56.5T600-320q33 0 56.5 23.5T680-240q0 33-23.5 56.5T600-160ZM360-400q-33 0-56.5-23.5T280-480q0-33 23.5-56.5T360-560q33 0 56.5 23.5T440-480q0 33-23.5 56.5T360-400Zm240 0q-33 0-56.5-23.5T520-480q0-33 23.5-56.5T600-560q33 0 56.5 23.5T680-480q0 33-23.5 56.5T600-400ZM360-640q-33 0-56.5-23.5T280-720q0-33 23.5-56.5T360-800q33 0 56.5 23.5T440-720q0 33-23.5 56.5T360-640Zm240 0q-33 0-56.5-23.5T520-720q0-33 23.5-56.5T600-800q33 0 56.5 23.5T680-720q0 33-23.5 56.5T600-640Z"/></svg>';
|
||||||
|
fileContainer.appendChild(dragIcon);
|
||||||
|
|
||||||
|
$(fileContainer).attr('draggable', 'true');
|
||||||
|
$(fileContainer).on('dragstart', (e) => {
|
||||||
|
e.originalEvent.dataTransfer.setData('fileUrl', info.url);
|
||||||
|
e.originalEvent.dataTransfer.setData('uniqueId', info.uniqueId);
|
||||||
|
e.originalEvent.dataTransfer.setDragImage(imgPreview, imgPreview.width / 2, imgPreview.height / 2);
|
||||||
|
});
|
||||||
|
enableImagePreviewOnClick(fileIconContainer);
|
||||||
|
} else {
|
||||||
|
$(fileContainer).removeAttr('draggable');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileIconContainer = createFileIconContainer(info);
|
||||||
|
}
|
||||||
|
|
||||||
let fileInfoContainer = createFileInfoContainer(info);
|
let fileInfoContainer = createFileInfoContainer(info);
|
||||||
|
|
||||||
|
if (!isDragAndDropEnabled) {
|
||||||
let removeBtn = document.createElement('div');
|
let removeBtn = document.createElement('div');
|
||||||
removeBtn.classList.add('remove-selected-file');
|
removeBtn.classList.add('remove-selected-file');
|
||||||
|
|
||||||
let removeBtnIconHTML = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#C02223"><path d="m339-288 141-141 141 141 51-51-141-141 141-141-51-51-141 141-141-141-51 51 141 141-141 141 51 51ZM480-96q-79 0-149-30t-122.5-82.5Q156-261 126-331T96-480q0-80 30-149.5t82.5-122Q261-804 331-834t149-30q80 0 149.5 30t122 82.5Q804-699 834-629.5T864-480q0 79-30 149t-82.5 122.5Q699-156 629.5-126T480-96Z"/></svg>`;
|
let removeBtnIconHTML = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#C02223"><path d="m339-288 141-141 141 141 51-51-141-141 141-141-51-51-141 141-141-141-51 51 141 141-141 141 51 51ZM480-96q-79 0-149-30t-122.5-82.5Q156-261 126-331T96-480q0-80 30-149.5t82.5-122Q261-804 331-834t149-30q80 0 149.5 30t122 82.5Q804-699 834-629.5T864-480q0 79-30 149t-82.5 122.5Q699-156 629.5-126T480-96Z"/></svg>`;
|
||||||
$(removeBtn).append(removeBtnIconHTML);
|
$(removeBtn).append(removeBtnIconHTML);
|
||||||
$(removeBtn).attr('data-file-id', info.uniqueId).click(removeFileListener);
|
$(removeBtn).attr('data-file-id', info.uniqueId).click(removeFileListener);
|
||||||
|
|
||||||
$(fileContainer).append(fileIconContainer);
|
|
||||||
$(fileContainer).append(fileInfoContainer);
|
|
||||||
$(fileContainer).append(removeBtn);
|
$(fileContainer).append(removeBtn);
|
||||||
|
}
|
||||||
|
$(fileContainer).append(fileIconContainer, fileInfoContainer);
|
||||||
|
|
||||||
selectedFilesContainer.append(fileContainer);
|
selectedFilesContainer.append(fileContainer);
|
||||||
});
|
});
|
||||||
|
const pageContainers = $('#box-drag-container');
|
||||||
|
pageContainers.off('dragover').on('dragover', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
showOrHideSelectedFilesContainer(filesInfo);
|
pageContainers.off('drop').on('drop', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const fileUrl = e.originalEvent.dataTransfer.getData('fileUrl');
|
||||||
|
|
||||||
|
if (fileUrl) {
|
||||||
|
const existingImages = $(e.target).find(`img[src="${fileUrl}"]`);
|
||||||
|
if (existingImages.length === 0) {
|
||||||
|
DraggableUtils.createDraggableCanvasFromUrl(fileUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const overlayElement = chooser.querySelector('.drag-drop-overlay');
|
||||||
|
if (overlayElement) {
|
||||||
|
overlayElement.style.display = 'none';
|
||||||
|
}
|
||||||
|
hasDroppedImage = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
showOrHideSelectedFilesContainer(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showOrHideSelectedFilesContainer(files) {
|
function showOrHideSelectedFilesContainer(files) {
|
||||||
if (files && files.length > 0) chooser.style.setProperty('--selected-files-display', 'flex');
|
if (files && files.length > 0) {
|
||||||
else chooser.style.setProperty('--selected-files-display', 'none');
|
chooser.style.setProperty('--selected-files-display', 'flex');
|
||||||
|
} else {
|
||||||
|
chooser.style.setProperty('--selected-files-display', 'none');
|
||||||
|
}
|
||||||
|
const isDragAndDropEnabled =
|
||||||
|
(window.location.pathname.includes('add-image') || window.location.pathname.includes('sign')) &&
|
||||||
|
files.some((file) => file.type.startsWith('image/'));
|
||||||
|
|
||||||
|
if (!isDragAndDropEnabled) return;
|
||||||
|
|
||||||
|
const selectedFilesContainer = chooser.querySelector('.selected-files');
|
||||||
|
|
||||||
|
let overlayElement = chooser.querySelector('.drag-drop-overlay');
|
||||||
|
if (!overlayElement) {
|
||||||
|
selectedFilesContainer.style.position = 'relative';
|
||||||
|
overlayElement = document.createElement('div');
|
||||||
|
overlayElement.classList.add('draggable-image-overlay');
|
||||||
|
|
||||||
|
overlayElement.innerHTML = 'Drag images to add them to the page';
|
||||||
|
selectedFilesContainer.appendChild(overlayElement);
|
||||||
|
}
|
||||||
|
if (hasDroppedImage) overlayElement.style.display = files && files.length > 0 ? 'flex' : 'none';
|
||||||
|
|
||||||
|
selectedFilesContainer.addEventListener('mouseenter', () => {
|
||||||
|
overlayElement.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedFilesContainer.addEventListener('mouseleave', () => {
|
||||||
|
if (!hasDroppedImage) overlayElement.style.display = files && files.length > 0 ? 'flex' : 'none';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFileListener(e) {
|
function removeFileListener(e) {
|
||||||
@ -235,4 +327,52 @@ function setupFileInput(chooser) {
|
|||||||
removeFileById(fileId, inputElement);
|
removeFileById(fileId, inputElement);
|
||||||
showOrHideSelectedFilesContainer(allFiles);
|
showOrHideSelectedFilesContainer(allFiles);
|
||||||
});
|
});
|
||||||
|
function enableImagePreviewOnClick(container) {
|
||||||
|
const imagePreviewModal = document.getElementById('imagePreviewModal') || createImagePreviewModal();
|
||||||
|
|
||||||
|
container.querySelectorAll('img').forEach((img) => {
|
||||||
|
if (!img.hasPreviewListener) {
|
||||||
|
img.addEventListener('mouseup', function () {
|
||||||
|
const imgElement = imagePreviewModal.querySelector('img');
|
||||||
|
imgElement.src = this.src;
|
||||||
|
imagePreviewModal.style.display = 'flex';
|
||||||
|
});
|
||||||
|
img.hasPreviewListener = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function createImagePreviewModal() {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.id = 'imagePreviewModal';
|
||||||
|
modal.style.position = 'fixed';
|
||||||
|
modal.style.top = '0';
|
||||||
|
modal.style.left = '0';
|
||||||
|
modal.style.width = '100vw';
|
||||||
|
modal.style.height = '100vh';
|
||||||
|
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modal.style.justifyContent = 'center';
|
||||||
|
modal.style.alignItems = 'center';
|
||||||
|
modal.style.zIndex = '2000';
|
||||||
|
|
||||||
|
const imgElement = document.createElement('img');
|
||||||
|
imgElement.style.maxWidth = '90%';
|
||||||
|
imgElement.style.maxHeight = '90%';
|
||||||
|
|
||||||
|
modal.appendChild(imgElement);
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
modal.addEventListener('click', () => {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape' && modal.style.display === 'flex') {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
|
window.goToFirstOrLastPage = goToFirstOrLastPage;
|
||||||
|
|
||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
document.getElementById('download-pdf').addEventListener('click', async () => {
|
||||||
|
const downloadButton = document.getElementById('download-pdf');
|
||||||
|
const originalContent = downloadButton.innerHTML;
|
||||||
|
|
||||||
|
downloadButton.disabled = true;
|
||||||
|
downloadButton.innerHTML = `
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
const modifiedPdfBytes = await modifiedPdf.save();
|
||||||
const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
|
const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
|
||||||
@ -6,6 +17,10 @@ document.getElementById('download-pdf').addEventListener('click', async () => {
|
|||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = originalFileName + '_addedImage.pdf';
|
link.download = originalFileName + '_addedImage.pdf';
|
||||||
link.click();
|
link.click();
|
||||||
|
} finally {
|
||||||
|
downloadButton.disabled = false;
|
||||||
|
downloadButton.innerHTML = originalContent;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let originalFileName = '';
|
let originalFileName = '';
|
||||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
||||||
@ -30,6 +45,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
||||||
el.style.cssText = 'display:none !important';
|
el.style.cssText = 'display:none !important';
|
||||||
});
|
});
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Delete') {
|
||||||
|
DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
const imageUpload = document.querySelector('input[name=image-upload]');
|
||||||
@ -45,3 +65,12 @@ imageUpload.addEventListener('change', (e) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function goToFirstOrLastPage(page) {
|
||||||
|
if (page) {
|
||||||
|
const lastPage = DraggableUtils.pdfDoc.numPages;
|
||||||
|
await DraggableUtils.goToPage(lastPage - 1);
|
||||||
|
} else {
|
||||||
|
await DraggableUtils.goToPage(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,6 +73,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
||||||
el.style.cssText = 'display:none !important';
|
el.style.cssText = 'display:none !important';
|
||||||
});
|
});
|
||||||
|
document.querySelectorAll('.small-file-container-saved img ').forEach((img) => {
|
||||||
|
img.addEventListener('dragstart', (e) => {
|
||||||
|
e.dataTransfer.setData('fileUrl', img.src);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Delete') {
|
||||||
|
DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
const imageUpload = document.querySelector('input[name=image-upload]');
|
||||||
@ -203,6 +213,15 @@ async function goToFirstOrLastPage(page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
document.getElementById('download-pdf').addEventListener('click', async () => {
|
||||||
|
const downloadButton = document.getElementById('download-pdf');
|
||||||
|
const originalContent = downloadButton.innerHTML;
|
||||||
|
|
||||||
|
downloadButton.disabled = true;
|
||||||
|
downloadButton.innerHTML = `
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
const modifiedPdfBytes = await modifiedPdf.save();
|
||||||
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
||||||
@ -210,4 +229,10 @@ document.getElementById('download-pdf').addEventListener('click', async () => {
|
|||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = originalFileName + '_signed.pdf';
|
link.download = originalFileName + '_signed.pdf';
|
||||||
link.click();
|
link.click();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading PDF:', error);
|
||||||
|
} finally {
|
||||||
|
downloadButton.disabled = false;
|
||||||
|
downloadButton.innerHTML = originalContent;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
85
src/main/resources/static/js/sign/signature-canvas.js
Normal file
85
src/main/resources/static/js/sign/signature-canvas.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const signaturePadCanvas = document.getElementById('drawing-pad-canvas');
|
||||||
|
const signaturePad = new SignaturePad(signaturePadCanvas, {
|
||||||
|
minWidth: 1,
|
||||||
|
maxWidth: 2,
|
||||||
|
penColor: 'black',
|
||||||
|
});
|
||||||
|
|
||||||
|
function addDraggableFromPad() {
|
||||||
|
if (signaturePad.isEmpty()) return;
|
||||||
|
const startTime = Date.now();
|
||||||
|
const croppedDataUrl = getCroppedCanvasDataUrl(signaturePadCanvas);
|
||||||
|
console.log(Date.now() - startTime);
|
||||||
|
DraggableUtils.createDraggableCanvasFromUrl(croppedDataUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCroppedCanvasDataUrl(canvas) {
|
||||||
|
let originalCtx = canvas.getContext('2d');
|
||||||
|
let originalWidth = canvas.width;
|
||||||
|
let originalHeight = canvas.height;
|
||||||
|
let imageData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
|
||||||
|
|
||||||
|
let minX = originalWidth + 1,
|
||||||
|
maxX = -1,
|
||||||
|
minY = originalHeight + 1,
|
||||||
|
maxY = -1,
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
currentPixelColorValueIndex;
|
||||||
|
|
||||||
|
for (y = 0; y < originalHeight; y++) {
|
||||||
|
for (x = 0; x < originalWidth; x++) {
|
||||||
|
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
|
||||||
|
let currentPixelAlphaValue = imageData.data[currentPixelColorValueIndex + 3];
|
||||||
|
if (currentPixelAlphaValue > 0) {
|
||||||
|
if (minX > x) minX = x;
|
||||||
|
if (maxX < x) maxX = x;
|
||||||
|
if (minY > y) minY = y;
|
||||||
|
if (maxY < y) maxY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let croppedWidth = maxX - minX;
|
||||||
|
let croppedHeight = maxY - minY;
|
||||||
|
if (croppedWidth < 0 || croppedHeight < 0) return null;
|
||||||
|
let cuttedImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
|
||||||
|
|
||||||
|
let croppedCanvas = document.createElement('canvas'),
|
||||||
|
croppedCtx = croppedCanvas.getContext('2d');
|
||||||
|
|
||||||
|
croppedCanvas.width = croppedWidth;
|
||||||
|
croppedCanvas.height = croppedHeight;
|
||||||
|
croppedCtx.putImageData(cuttedImageData, 0, 0);
|
||||||
|
|
||||||
|
return croppedCanvas.toDataURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMobile() {
|
||||||
|
const userAgentCheck = /Mobi|Android|iPhone|iPad|iPod|Windows Phone|Opera Mini/i.test(navigator.userAgent);
|
||||||
|
const viewportCheck = window.matchMedia('(max-width: 768px)').matches;
|
||||||
|
return userAgentCheck || viewportCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeviceScalingFactor() {
|
||||||
|
return isMobile() ? 3 : 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
const ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||||
|
const additionalFactor = getDeviceScalingFactor();
|
||||||
|
|
||||||
|
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio * additionalFactor;
|
||||||
|
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio * additionalFactor;
|
||||||
|
signaturePadCanvas.getContext('2d').scale(ratio * additionalFactor, ratio * additionalFactor);
|
||||||
|
|
||||||
|
signaturePad.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
new IntersectionObserver((entries, observer) => {
|
||||||
|
if (entries.some((entry) => entry.intersectionRatio > 0)) {
|
||||||
|
resizeCanvas();
|
||||||
|
}
|
||||||
|
}).observe(signaturePadCanvas);
|
||||||
|
|
||||||
|
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
@ -213,7 +213,10 @@
|
|||||||
unexpectedError: '[[#{decrypt.unexpectedError}]]',
|
unexpectedError: '[[#{decrypt.unexpectedError}]]',
|
||||||
serverError: '[[#{decrypt.serverError}]]',
|
serverError: '[[#{decrypt.serverError}]]',
|
||||||
success: '[[#{decrypt.success}]]',
|
success: '[[#{decrypt.success}]]',
|
||||||
};</script>
|
};
|
||||||
|
window.fileInput = {
|
||||||
|
dragAndDropPDF : '[[#{fileChooser.dragAndDropPDF}]]',
|
||||||
|
dragAndDropImage : '[[#{fileChooser.dragAndDropImage}]]'};</script>
|
||||||
<div class="custom-file-chooser mb-3" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-element-container-id=${name+'-input-container'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
<div class="custom-file-chooser mb-3" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-element-container-id=${name+'-input-container'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
|
||||||
<div class="mb-3 d-flex flex-row justify-content-center align-items-center flex-wrap input-container" th:name="${name}+'-input'" th:id="${name}+'-input-container'" th:data-text="#{fileChooser.hoveredDragAndDrop}">
|
<div class="mb-3 d-flex flex-row justify-content-center align-items-center flex-wrap input-container" th:name="${name}+'-input'" th:id="${name}+'-input-container'" th:data-text="#{fileChooser.hoveredDragAndDrop}">
|
||||||
<label class="file-input-btn d-none">
|
<label class="file-input-btn d-none">
|
||||||
@ -222,7 +225,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<div th:text="#{fileChooser.click}"></div>
|
<div th:text="#{fileChooser.click}"></div>
|
||||||
<div th:text="#{fileChooser.or}"></div>
|
<div th:text="#{fileChooser.or}"></div>
|
||||||
<div th:text="#{fileChooser.dragAndDrop}"></div>
|
<div th:text="#{fileChooser.dragAndDrop}" id="dragAndDrop"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="selected-files flex-wrap"></div>
|
<div class="selected-files flex-wrap"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<!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()}"
|
||||||
|
xmlns:th="https://www.thymeleaf.org">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title}, header=#{addImage.header})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title}, header=#{addImage.header})}"></th:block>
|
||||||
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
||||||
@ -20,40 +22,95 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- pdf selector -->
|
<!-- pdf selector -->
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script type="module" th:src="@{'/js/pages/add-image.js'}"></script>
|
<script type="module" th:src="@{'/js/pages/add-image.js'}"></script>
|
||||||
<div class="tab-group show-on-file-selected">
|
<div class="tab-group show-on-file-selected">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}"></div>
|
<div
|
||||||
|
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="draggable-buttons-box ignore-rtl">
|
||||||
|
<button class="btn btn-outline-secondary"
|
||||||
|
onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())"
|
||||||
|
style="color: #C02223; border-color: #C02223; background-color: rgba(255, 0, 0, 0.1);">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash"
|
||||||
|
viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z" />
|
||||||
|
<path
|
||||||
|
d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z" />
|
||||||
|
</svg>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.delete}"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary"
|
||||||
|
onclick="DraggableUtils.addAllPagesDraggableCanvas(DraggableUtils.getLastInteracted())">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
content_copy
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.addToAll}"></span>
|
||||||
|
</button>
|
||||||
|
<div id="rotation-controls" class="align-items-center" style="display: none;">
|
||||||
|
<div class="input-with-icon">
|
||||||
|
<span class="material-symbols-rounded icon" style="margin-right: 3px;">
|
||||||
|
screen_rotation
|
||||||
|
</span>
|
||||||
|
<input type="number" id="rotation-input" class="form-control form-control-sm me-2" value="0" step="10"
|
||||||
|
style="width: 6rem" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="ratioToggleBtn" class="btn btn-outline-secondary"
|
||||||
|
onclick="DraggableUtils.toggleMaintainRatio()">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
Aspect_Ratio
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.maintainRatio}"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(false)" style="margin-left:auto">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
keyboard_double_arrow_left
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.first}"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" id="incrementPage"
|
||||||
|
onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-chevron-left" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" />
|
||||||
|
</svg>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.previous}"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" id="decrementPage"
|
||||||
|
onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||||
|
class="bi bi-chevron-right" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z" />
|
||||||
|
</svg>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.next}"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(true)">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
keyboard_double_arrow_right
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.last}"></span>
|
||||||
|
</button>
|
||||||
|
<button id="download-pdf" class="btn btn-outline-secondary"
|
||||||
|
style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
download
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{downloadPdf}"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<!-- draggables box -->
|
<!-- draggables box -->
|
||||||
<div id="box-drag-container" class="show-on-file-selected">
|
<div id="box-drag-container" class="show-on-file-selected">
|
||||||
<canvas id="pdf-canvas"></canvas>
|
<canvas id="pdf-canvas"></canvas>
|
||||||
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
||||||
<div class="draggable-buttons-box ignore-rtl">
|
|
||||||
<button class="btn btn-outline-secondary" onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
|
|
||||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
|
|
||||||
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.incrementPage() : DraggableUtils.decrementPage()" style="margin-left:auto">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-left" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- download button -->
|
|
||||||
<div class="margin-auto-parent">
|
|
||||||
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center" th:text="#{downloadPdf}"></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,4 +119,5 @@
|
|||||||
<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>
|
@ -93,8 +93,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
|
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
|
||||||
onclick="toggleSelectAll()" disabled>
|
onclick="toggleSelectAll()" disabled>
|
||||||
<span class="material-symbols-rounded"
|
<span class="material-symbols-rounded" id="select-icon">select_all</span>
|
||||||
id="select-icon">select_all</span>
|
|
||||||
<span class="btn-tooltip" th:text="#{multiTool.selectAll}"></span>
|
<span class="btn-tooltip" th:text="#{multiTool.selectAll}"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
@ -104,15 +103,16 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-left:auto">
|
<div style="margin-left:auto">
|
||||||
<button id="export-selected-button" class="btn btn-primary enable-on-file hidden"
|
<button id="export-selected-button"
|
||||||
onclick="exportPdf(true)" disabled>
|
style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
|
||||||
|
class="btn btn-primary enable-on-file hidden" onclick="exportPdf(true)" disabled>
|
||||||
<span class="btn-tooltip" th:text="#{multiTool.downloadSelected}"></span>
|
<span class="btn-tooltip" th:text="#{multiTool.downloadSelected}"></span>
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
file_save
|
file_save
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)"
|
<button style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
|
||||||
disabled>
|
id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)" disabled>
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
download
|
download
|
||||||
</span>
|
</span>
|
||||||
@ -124,7 +124,8 @@
|
|||||||
<div style="display:flex; height:3rem; margin-right:1rem">
|
<div style="display:flex; height:3rem; margin-right:1rem">
|
||||||
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
|
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
|
||||||
Pages</h5>
|
Pages</h5>
|
||||||
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10" value="">
|
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
|
||||||
|
value="">
|
||||||
</div>
|
</div>
|
||||||
<ul id="selected-pages-list" class="pages-list"></ul>
|
<ul id="selected-pages-list" class="pages-list"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
</th:block>
|
</th:block>
|
||||||
<script th:src="@{'/js/thirdParty/signature_pad.umd.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/signature_pad.umd.min.js'}"></script>
|
||||||
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
<script th:src="@{'/js/thirdParty/interact.min.js'}"></script>
|
||||||
<script type="module" th:src="@{'/js/pages/sign.js'}"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -32,7 +30,7 @@
|
|||||||
<br><br>
|
<br><br>
|
||||||
<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-7 bg-card">
|
||||||
<div class="tool-header">
|
<div class="tool-header">
|
||||||
<span class="material-symbols-rounded tool-header-icon sign">signature</span>
|
<span class="material-symbols-rounded tool-header-icon sign">signature</span>
|
||||||
<span class="tool-header-text" th:text="#{sign.header}"></span>
|
<span class="tool-header-text" th:text="#{sign.header}"></span>
|
||||||
@ -46,7 +44,7 @@
|
|||||||
<div class="tab-group show-on-file-selected">
|
<div class="tab-group show-on-file-selected">
|
||||||
<div class="tab-container" th:title="#{sign.upload}">
|
<div class="tab-container" th:title="#{sign.upload}">
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -61,13 +59,6 @@
|
|||||||
|
|
||||||
<div class="tab-container" th:title="#{sign.saved}">
|
<div class="tab-container" th:title="#{sign.saved}">
|
||||||
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
||||||
<!-- View Toggle Button -->
|
|
||||||
<div class="view-toggle mb-3">
|
|
||||||
<button class="btn btn-outline-secondary btn-sm" onclick="toggleSignatureView()">
|
|
||||||
<span class="material-symbols-rounded grid-view-text">view_list</span>
|
|
||||||
<span class="material-symbols-rounded list-view-text" style="display: none;">grid_view</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Preview Modal -->
|
<!-- Preview Modal -->
|
||||||
<div class="modal fade" id="signaturePreview" tabindex="-1">
|
<div class="modal fade" id="signaturePreview" tabindex="-1">
|
||||||
@ -89,66 +80,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Grid View -->
|
|
||||||
<div id="gridView">
|
|
||||||
<!-- Personal Signatures -->
|
<!-- Personal Signatures -->
|
||||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
<div th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
||||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
<h5 th:text="#{sign.personalSigs}"></h5>
|
||||||
<div class="signature-grid">
|
<div class="selected-files flex-wrap" style="position: relative; display:flex">
|
||||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}" class="signature-item">
|
|
||||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
|
||||||
th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;"
|
|
||||||
onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)" />
|
|
||||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Shared Signatures -->
|
|
||||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
|
||||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
|
||||||
<div class="signature-grid">
|
|
||||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}" class="signature-item">
|
|
||||||
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
|
||||||
th:data-filename="${sig.fileName}" style="max-width: 200px; cursor: pointer;"
|
|
||||||
onclick="DraggableUtils.createDraggableCanvasFromUrl(this.src)" />
|
|
||||||
<div class="signature-name" th:text="${sig.fileName}"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- List View (Initially Hidden) -->
|
|
||||||
<div id="listView" style="display: none;">
|
|
||||||
<!-- Personal Signatures -->
|
|
||||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Personal'])}">
|
|
||||||
<h5 th:text="#{sign.personalSigs}"></h5>
|
|
||||||
<div class="signature-list">
|
|
||||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}"
|
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Personal'}"
|
||||||
class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}"
|
class="small-file-container-saved d-flex flex-column justify-content-center align-items-center">
|
||||||
onclick="previewSignature(this)">
|
<div class="drag-icon"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||||
<div class="signature-list-info">
|
viewBox="0 -960 960 960" width="24px" fill="#e8eaed">
|
||||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
<path
|
||||||
|
d="M360-160q-33 0-56.5-23.5T280-240q0-33 23.5-56.5T360-320q33 0 56.5 23.5T440-240q0 33-23.5 56.5T360-160Zm240 0q-33 0-56.5-23.5T520-240q0-33 23.5-56.5T600-320q33 0 56.5 23.5T680-240q0 33-23.5 56.5T600-160ZM360-400q-33 0-56.5-23.5T280-480q0-33 23.5-56.5T360-560q33 0 56.5 23.5T440-480q0 33-23.5 56.5T360-400Zm240 0q-33 0-56.5-23.5T520-480q0-33 23.5-56.5T600-560q33 0 56.5 23.5T680-480q0 33-23.5 56.5T600-400ZM360-640q-33 0-56.5-23.5T280-720q0-33 23.5-56.5T360-800q33 0 56.5 23.5T440-720q0 33-23.5 56.5T360-640Zm240 0q-33 0-56.5-23.5T520-720q0-33 23.5-56.5T600-800q33 0 56.5 23.5T680-720q0 33-23.5 56.5T600-640Z">
|
||||||
|
</path>
|
||||||
|
</svg></div>
|
||||||
|
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
||||||
|
th:data-filename="${sig.fileName}" style="width: 50px; height: 50px; object-fit: cover;" />
|
||||||
|
<div class="file-info d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Shared Signatures -->
|
<!-- Shared Signatures -->
|
||||||
<div class="signature-category" th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
<div th:if="${not #lists.isEmpty(signatures.?[category == 'Shared'])}">
|
||||||
<h5 th:text="#{sign.sharedSigs}"></h5>
|
<h5 th:text="#{sign.sharedSigs}"></h5>
|
||||||
<div class="signature-list">
|
<div class="selected-files flex-wrap" style="position: relative; display: flex;">
|
||||||
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}"
|
<div th:each="sig : ${signatures}" th:if="${sig.category == 'Shared'}"
|
||||||
class="signature-list-item" th:data-src="@{'/api/v1/general/sign/' + ${sig.fileName}}"
|
class="small-file-container-saved d-flex flex-column justify-content-center align-items-center">
|
||||||
onclick="previewSignature(this)">
|
<div class="drag-icon"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||||
<div class="signature-list-info">
|
viewBox="0 -960 960 960" width="24px" fill="#e8eaed">
|
||||||
<span th:text="${sig.fileName}" class="signature-list-name"></span>
|
<path
|
||||||
</div>
|
d="M360-160q-33 0-56.5-23.5T280-240q0-33 23.5-56.5T360-320q33 0 56.5 23.5T440-240q0 33-23.5 56.5T360-160Zm240 0q-33 0-56.5-23.5T520-240q0-33 23.5-56.5T600-320q33 0 56.5 23.5T680-240q0 33-23.5 56.5T600-160ZM360-400q-33 0-56.5-23.5T280-480q0-33 23.5-56.5T360-560q33 0 56.5 23.5T440-480q0 33-23.5 56.5T360-400Zm240 0q-33 0-56.5-23.5T520-480q0-33 23.5-56.5T600-560q33 0 56.5 23.5T680-480q0 33-23.5 56.5T600-400ZM360-640q-33 0-56.5-23.5T280-720q0-33 23.5-56.5T360-800q33 0 56.5 23.5T440-720q0 33-23.5 56.5T360-640Zm240 0q-33 0-56.5-23.5T520-720q0-33 23.5-56.5T600-800q33 0 56.5 23.5T680-720q0 33-23.5 56.5T600-640Z">
|
||||||
|
</path>
|
||||||
|
</svg></div>
|
||||||
|
<img th:src="@{'/api/v1/general/sign/' + ${sig.fileName}}" th:alt="${sig.fileName}"
|
||||||
|
th:data-filename="${sig.fileName}" style="width: 50px; height: 50px; object-fit: cover;" />
|
||||||
|
<div class="file-info d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<div class="signature-name" th:text="${sig.fileName}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div th:if="${#lists.isEmpty(signatures)}" class="text-center p-3">
|
<div th:if="${#lists.isEmpty(signatures)}" class="text-center p-3">
|
||||||
<p th:text="#{sign.noSavedSigs}">No saved signatures found</p>
|
<p th:text="#{sign.noSavedSigs}">No saved signatures found</p>
|
||||||
@ -169,13 +143,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script th:src="@{'/js/sign/signature-canvas.js'}"></script>
|
||||||
<!-- draggables box -->
|
<!-- draggables box -->
|
||||||
<div id="box-drag-container" class="show-on-file-selected">
|
|
||||||
<canvas id="pdf-canvas"></canvas>
|
|
||||||
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
|
||||||
<div class="draggable-buttons-box ignore-rtl">
|
<div class="draggable-buttons-box ignore-rtl">
|
||||||
<button class="btn btn-outline-secondary"
|
<button class="btn btn-outline-secondary"
|
||||||
onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())">
|
onclick="DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted())"
|
||||||
|
style="border-color: #C02223; color: #C02223; background: rgba(255, 0, 0, 0.1)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash"
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash"
|
||||||
viewBox="0 0 16 16">
|
viewBox="0 0 16 16">
|
||||||
<path
|
<path
|
||||||
@ -192,6 +165,22 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="btn-tooltip" th:text="#{sign.addToAll}"></span>
|
<span class="btn-tooltip" th:text="#{sign.addToAll}"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<div id="rotation-controls" class="align-items-center" style="display: none;">
|
||||||
|
<div class="input-with-icon">
|
||||||
|
<span class="material-symbols-rounded icon" style="margin-right: 3px;">
|
||||||
|
screen_rotation
|
||||||
|
</span>
|
||||||
|
<input type="number" id="rotation-input" class="form-control form-control-sm me-2" value="0" step="10"
|
||||||
|
style="width: 6rem" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="ratioToggleBtn" class="btn btn-outline-secondary"
|
||||||
|
onclick="DraggableUtils.toggleMaintainRatio()">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
Aspect_Ratio
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{sign.maintainRatio}"></span>
|
||||||
|
</button>
|
||||||
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(false)" style="margin-left:auto">
|
<button class="btn btn-outline-secondary" onclick="goToFirstOrLastPage(false)" style="margin-left:auto">
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
keyboard_double_arrow_left
|
keyboard_double_arrow_left
|
||||||
@ -222,19 +211,31 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="btn-tooltip" th:text="#{sign.last}"></span>
|
<span class="btn-tooltip" th:text="#{sign.last}"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<button id="download-pdf" class="btn btn-outline-secondary"
|
||||||
|
style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)">
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
download
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip" th:text="#{downloadPdf}"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="box-drag-container" class="show-on-file-selected">
|
||||||
|
|
||||||
|
<canvas id="pdf-canvas"></canvas>
|
||||||
|
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/fabric@latest/dist/index.min.js"></script>
|
||||||
|
<script th:src="@{'/js/draggable-utils.js'}"></script>
|
||||||
|
<script type="module" th:src="@{'/js/pages/sign.js'}"></script>
|
||||||
</div>
|
</div>
|
||||||
<!-- download button -->
|
|
||||||
<div class="margin-auto-parent">
|
|
||||||
<button id="download-pdf" class="btn btn-primary mb-2 show-on-file-selected margin-center"
|
|
||||||
th:text="#{downloadPdf}"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -52,14 +52,68 @@ public class GeneralUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
void nFuncAdvanced3() {
|
void nFuncAdvanced3() {
|
||||||
List<Integer> result = GeneralUtils.parsePageList(new String[]{"4n+1"}, 9, true);
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"4n+1"}, 9, true);
|
||||||
assertEquals(List.of(1, 5, 9), result, "'All' keyword should return all pages.");
|
assertEquals(List.of(5, 9), result, "'All' keyword should return all pages.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_spaces() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"n + 1"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 3, 4, 5, 6, 7, 8, 9), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_consecutive_Ns_nnn() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"nnn"}, 9, true);
|
||||||
|
assertEquals(List.of(1, 8), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_consecutive_Ns_nn() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"nn"}, 9, true);
|
||||||
|
assertEquals(List.of(1, 4, 9), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_opening_closing_round_brackets() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"(n-1)(n-2)"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 6), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_opening_round_brackets() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"2(n-1)"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 4, 6, 8), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_opening_round_brackets_n() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"n(n-1)"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 6), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_closing_round_brackets() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"(n-1)2"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 4, 6, 8), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_closing_round_brackets_n() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"(n-1)n"}, 9, true);
|
||||||
|
assertEquals(List.of(2, 6), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nFunc_function_surrounded_with_brackets() {
|
||||||
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"(n-1)"}, 9, true);
|
||||||
|
assertEquals(List.of(1, 2, 3, 4, 5, 6, 7, 8), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void nFuncAdvanced4() {
|
void nFuncAdvanced4() {
|
||||||
List<Integer> result = GeneralUtils.parsePageList(new String[]{"3+2n"}, 9, true);
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"3+2n"}, 9, true);
|
||||||
assertEquals(List.of(3, 5, 7, 9), result, "'All' keyword should return all pages.");
|
assertEquals(List.of(5, 7, 9), result, "'All' keyword should return all pages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -80,7 +134,6 @@ public class GeneralUtilsTest {
|
|||||||
assertEquals(List.of(1, 2, 3), result, "Range should be parsed correctly.");
|
assertEquals(List.of(1, 2, 3), result, "Range should be parsed correctly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testParsePageListWithRangeZeroBaseOutput() {
|
void testParsePageListWithRangeZeroBaseOutput() {
|
||||||
List<Integer> result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, false);
|
List<Integer> result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, false);
|
||||||
|
Loading…
Reference in New Issue
Block a user